Skip to content

Commit 030f9ce

Browse files
authored
Merge pull request #2 from factorio-item-browser/bugfix/failed-portal-request
bugfix/failed-portal-request
2 parents 111ac7f + 92d0160 commit 030f9ce

File tree

5 files changed

+142
-12
lines changed

5 files changed

+142
-12
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FactorioItemBrowser\CombinationApi\Server\Exception;
6+
7+
use Throwable;
8+
9+
/**
10+
* The exception when a request to the mod portal has failed. This excludes not found exceptions.
11+
*
12+
* @author BluePsyduck <bluepsyduck@gmx.com>
13+
* @license http://opensource.org/licenses/GPL-3.0 GPL v3
14+
*/
15+
class FailedModPortalRequestException extends ServerException
16+
{
17+
private const MESSAGE = 'Request to the Factorio Mod Portal failed: %s';
18+
19+
public function __construct(string $message, ?Throwable $previous = null)
20+
{
21+
parent::__construct(sprintf(self::MESSAGE, $message), 503, $previous);
22+
}
23+
}

src/Service/ModPortalService.php

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
use BluePsyduck\FactorioModPortalClient\Entity\Release;
1010
use BluePsyduck\FactorioModPortalClient\Entity\Version;
1111
use BluePsyduck\FactorioModPortalClient\Exception\ClientException;
12+
use BluePsyduck\FactorioModPortalClient\Exception\ErrorResponseException;
1213
use BluePsyduck\FactorioModPortalClient\Request\FullModRequest;
1314
use BluePsyduck\FactorioModPortalClient\Utils\ModUtils;
14-
use GuzzleHttp\Promise\PromiseInterface;
15+
use FactorioItemBrowser\CombinationApi\Server\Exception\FailedModPortalRequestException;
1516
use GuzzleHttp\Promise\Utils;
1617

1718
/**
@@ -33,28 +34,35 @@ public function __construct(ClientInterface $modPortalClient)
3334
* Requests the mods from the Mod Portal API. Not known mods will be missing in the result array.
3435
* @param array<string> $modNames
3536
* @return array<string, Mod>
37+
* @throws FailedModPortalRequestException
3638
*/
3739
public function requestMods(array $modNames): array
3840
{
41+
$mods = [];
3942
$promises = [];
4043
try {
4144
foreach ($modNames as $modName) {
4245
$request = new FullModRequest();
4346
$request->setName($modName);
44-
$promises[$modName] = $this->modPortalClient->sendRequest($request);
47+
48+
$promises[] = $this->modPortalClient->sendRequest($request)->then(
49+
function (Mod $mod) use (&$mods): void {
50+
$mods[$mod->getName()] = $mod;
51+
},
52+
function (ClientException $exception): void {
53+
// Ignore mods not existing on the mod portal.
54+
if ($exception instanceof ErrorResponseException && $exception->getCode() === 404) {
55+
return;
56+
}
57+
throw new FailedModPortalRequestException($exception->getMessage(), $exception);
58+
},
59+
);
4560
}
4661
} catch (ClientException $e) {
62+
throw new FailedModPortalRequestException($e->getMessage(), $e);
4763
}
4864

49-
$mods = [];
50-
$responses = Utils::settle($promises)->wait();
51-
foreach ($responses as $response) {
52-
if ($response['state'] === PromiseInterface::FULFILLED) {
53-
/** @var Mod $mod */
54-
$mod = $response['value'];
55-
$mods[$mod->getName()] = $mod;
56-
}
57-
}
65+
Utils::all($promises)->wait();
5866
return $mods;
5967
}
6068

src/Service/ValidationService.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use FactorioItemBrowser\CombinationApi\Client\Constant\ValidationProblemType;
1212
use FactorioItemBrowser\CombinationApi\Client\Transfer\ValidatedMod;
1313
use FactorioItemBrowser\CombinationApi\Client\Transfer\ValidationProblem;
14+
use FactorioItemBrowser\CombinationApi\Server\Exception\FailedModPortalRequestException;
1415
use FactorioItemBrowser\Common\Constant\Constant;
1516

1617
/**
@@ -33,6 +34,7 @@ public function __construct(ModPortalService $modPortalService)
3334
* @param array<string> $modNames
3435
* @param Version $factorioVersion
3536
* @return array<string, ValidatedMod>
37+
* @throws FailedModPortalRequestException
3638
*/
3739
public function validate(array $modNames, Version $factorioVersion): array
3840
{
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FactorioItemBrowserTest\CombinationApi\Server\Exception;
6+
7+
use FactorioItemBrowser\CombinationApi\Server\Exception\FailedModPortalRequestException;
8+
use PHPUnit\Framework\TestCase;
9+
use Throwable;
10+
11+
/**
12+
* The PHPUnit test of the FailedModPortalRequestException class.
13+
*
14+
* @author BluePsyduck <bluepsyduck@gmx.com>
15+
* @license http://opensource.org/licenses/GPL-3.0 GPL v3
16+
* @covers \FactorioItemBrowser\CombinationApi\Server\Exception\FailedModPortalRequestException
17+
*/
18+
class FailedModPortalRequestExceptionTest extends TestCase
19+
{
20+
public function test(): void
21+
{
22+
$message = 'abc';
23+
$previous = $this->createMock(Throwable::class);
24+
25+
$exception = new FailedModPortalRequestException($message, $previous);
26+
27+
$this->assertSame('Request to the Factorio Mod Portal failed: abc', $exception->getMessage());
28+
$this->assertSame(503, $exception->getCode());
29+
$this->assertSame($previous, $exception->getPrevious());
30+
}
31+
}

test/src/Service/ModPortalServiceTest.php

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
use BluePsyduck\FactorioModPortalClient\Entity\Release;
1010
use BluePsyduck\FactorioModPortalClient\Entity\Version;
1111
use BluePsyduck\FactorioModPortalClient\Exception\ClientException;
12+
use BluePsyduck\FactorioModPortalClient\Exception\ErrorResponseException;
1213
use BluePsyduck\FactorioModPortalClient\Request\FullModRequest;
14+
use FactorioItemBrowser\CombinationApi\Server\Exception\FailedModPortalRequestException;
1315
use FactorioItemBrowser\CombinationApi\Server\Service\ModPortalService;
1416
use GuzzleHttp\Promise\FulfilledPromise;
17+
use GuzzleHttp\Promise\RejectedPromise;
1518
use PHPUnit\Framework\TestCase;
1619

1720
/**
@@ -23,6 +26,9 @@
2326
*/
2427
class ModPortalServiceTest extends TestCase
2528
{
29+
/**
30+
* @throws FailedModPortalRequestException
31+
*/
2632
public function testRequestMods(): void
2733
{
2834
$modNames = ['abc', 'def', 'ghi'];
@@ -38,6 +44,7 @@ public function testRequestMods(): void
3844
$mod2->setName('def');
3945
$promise1 = new FulfilledPromise($mod1);
4046
$promise2 = new FulfilledPromise($mod2);
47+
$promise3 = new RejectedPromise(new ErrorResponseException('test', 404, '', ''));
4148
$expectedResult = [
4249
'abc' => $mod1,
4350
'def' => $mod2,
@@ -54,7 +61,7 @@ public function testRequestMods(): void
5461
->willReturnOnConsecutiveCalls(
5562
$promise1,
5663
$promise2,
57-
$this->throwException($this->createMock(ClientException::class)),
64+
$promise3,
5865
);
5966

6067
$instance = new ModPortalService($modPortalClient);
@@ -63,6 +70,65 @@ public function testRequestMods(): void
6370
$this->assertEquals($expectedResult, $result);
6471
}
6572

73+
public function testRequestModsWithServerException(): void
74+
{
75+
$modNames = ['abc', 'def'];
76+
$expectedRequest1 = new FullModRequest();
77+
$expectedRequest1->setName('abc');
78+
$expectedRequest2 = new FullModRequest();
79+
$expectedRequest2->setName('def');
80+
$mod1 = new Mod();
81+
$mod1->setName('abc');
82+
$promise1 = new FulfilledPromise($mod1);
83+
$promise2 = new RejectedPromise(new ErrorResponseException('test', 500, '', ''));
84+
85+
$modPortalClient = $this->createMock(ClientInterface::class);
86+
$modPortalClient->expects($this->exactly(2))
87+
->method('sendRequest')
88+
->withConsecutive(
89+
[$this->equalTo($expectedRequest1)],
90+
[$this->equalTo($expectedRequest2)],
91+
)
92+
->willReturnOnConsecutiveCalls(
93+
$promise1,
94+
$promise2,
95+
);
96+
97+
$this->expectException(FailedModPortalRequestException::class);
98+
99+
$instance = new ModPortalService($modPortalClient);
100+
$instance->requestMods($modNames);
101+
}
102+
103+
public function testRequestModsWithInitialException(): void
104+
{
105+
$modNames = ['abc', 'def'];
106+
$expectedRequest1 = new FullModRequest();
107+
$expectedRequest1->setName('abc');
108+
$expectedRequest2 = new FullModRequest();
109+
$expectedRequest2->setName('def');
110+
$mod1 = new Mod();
111+
$mod1->setName('abc');
112+
$promise1 = new FulfilledPromise($mod1);
113+
114+
$modPortalClient = $this->createMock(ClientInterface::class);
115+
$modPortalClient->expects($this->exactly(2))
116+
->method('sendRequest')
117+
->withConsecutive(
118+
[$this->equalTo($expectedRequest1)],
119+
[$this->equalTo($expectedRequest2)],
120+
)
121+
->willReturnOnConsecutiveCalls(
122+
$promise1,
123+
$this->throwException($this->createMock(ClientException::class)),
124+
);
125+
126+
$this->expectException(FailedModPortalRequestException::class);
127+
128+
$instance = new ModPortalService($modPortalClient);
129+
$instance->requestMods($modNames);
130+
}
131+
66132
public function testSelectLatestReleases(): void
67133
{
68134
$release1 = new Release();

0 commit comments

Comments
 (0)