Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/Exception/FailedModPortalRequestException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace FactorioItemBrowser\CombinationApi\Server\Exception;

use Throwable;

/**
* The exception when a request to the mod portal has failed. This excludes not found exceptions.
*
* @author BluePsyduck <bluepsyduck@gmx.com>
* @license http://opensource.org/licenses/GPL-3.0 GPL v3
*/
class FailedModPortalRequestException extends ServerException
{
private const MESSAGE = 'Request to the Factorio Mod Portal failed: %s';

public function __construct(string $message, ?Throwable $previous = null)
{
parent::__construct(sprintf(self::MESSAGE, $message), 503, $previous);
}
}
30 changes: 19 additions & 11 deletions src/Service/ModPortalService.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
use BluePsyduck\FactorioModPortalClient\Entity\Release;
use BluePsyduck\FactorioModPortalClient\Entity\Version;
use BluePsyduck\FactorioModPortalClient\Exception\ClientException;
use BluePsyduck\FactorioModPortalClient\Exception\ErrorResponseException;
use BluePsyduck\FactorioModPortalClient\Request\FullModRequest;
use BluePsyduck\FactorioModPortalClient\Utils\ModUtils;
use GuzzleHttp\Promise\PromiseInterface;
use FactorioItemBrowser\CombinationApi\Server\Exception\FailedModPortalRequestException;
use GuzzleHttp\Promise\Utils;

/**
Expand All @@ -33,28 +34,35 @@ public function __construct(ClientInterface $modPortalClient)
* Requests the mods from the Mod Portal API. Not known mods will be missing in the result array.
* @param array<string> $modNames
* @return array<string, Mod>
* @throws FailedModPortalRequestException
*/
public function requestMods(array $modNames): array
{
$mods = [];
$promises = [];
try {
foreach ($modNames as $modName) {
$request = new FullModRequest();
$request->setName($modName);
$promises[$modName] = $this->modPortalClient->sendRequest($request);

$promises[] = $this->modPortalClient->sendRequest($request)->then(
function (Mod $mod) use (&$mods): void {
$mods[$mod->getName()] = $mod;
},
function (ClientException $exception): void {
// Ignore mods not existing on the mod portal.
if ($exception instanceof ErrorResponseException && $exception->getCode() === 404) {
return;
}
throw new FailedModPortalRequestException($exception->getMessage(), $exception);
},
);
}
} catch (ClientException $e) {
throw new FailedModPortalRequestException($e->getMessage(), $e);
}

$mods = [];
$responses = Utils::settle($promises)->wait();
foreach ($responses as $response) {
if ($response['state'] === PromiseInterface::FULFILLED) {
/** @var Mod $mod */
$mod = $response['value'];
$mods[$mod->getName()] = $mod;
}
}
Utils::all($promises)->wait();
return $mods;
}

Expand Down
2 changes: 2 additions & 0 deletions src/Service/ValidationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use FactorioItemBrowser\CombinationApi\Client\Constant\ValidationProblemType;
use FactorioItemBrowser\CombinationApi\Client\Transfer\ValidatedMod;
use FactorioItemBrowser\CombinationApi\Client\Transfer\ValidationProblem;
use FactorioItemBrowser\CombinationApi\Server\Exception\FailedModPortalRequestException;
use FactorioItemBrowser\Common\Constant\Constant;

/**
Expand All @@ -33,6 +34,7 @@ public function __construct(ModPortalService $modPortalService)
* @param array<string> $modNames
* @param Version $factorioVersion
* @return array<string, ValidatedMod>
* @throws FailedModPortalRequestException
*/
public function validate(array $modNames, Version $factorioVersion): array
{
Expand Down
31 changes: 31 additions & 0 deletions test/src/Exception/FailedModPortalRequestExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace FactorioItemBrowserTest\CombinationApi\Server\Exception;

use FactorioItemBrowser\CombinationApi\Server\Exception\FailedModPortalRequestException;
use PHPUnit\Framework\TestCase;
use Throwable;

/**
* The PHPUnit test of the FailedModPortalRequestException class.
*
* @author BluePsyduck <bluepsyduck@gmx.com>
* @license http://opensource.org/licenses/GPL-3.0 GPL v3
* @covers \FactorioItemBrowser\CombinationApi\Server\Exception\FailedModPortalRequestException
*/
class FailedModPortalRequestExceptionTest extends TestCase
{
public function test(): void
{
$message = 'abc';
$previous = $this->createMock(Throwable::class);

$exception = new FailedModPortalRequestException($message, $previous);

$this->assertSame('Request to the Factorio Mod Portal failed: abc', $exception->getMessage());
$this->assertSame(503, $exception->getCode());
$this->assertSame($previous, $exception->getPrevious());
}
}
68 changes: 67 additions & 1 deletion test/src/Service/ModPortalServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
use BluePsyduck\FactorioModPortalClient\Entity\Release;
use BluePsyduck\FactorioModPortalClient\Entity\Version;
use BluePsyduck\FactorioModPortalClient\Exception\ClientException;
use BluePsyduck\FactorioModPortalClient\Exception\ErrorResponseException;
use BluePsyduck\FactorioModPortalClient\Request\FullModRequest;
use FactorioItemBrowser\CombinationApi\Server\Exception\FailedModPortalRequestException;
use FactorioItemBrowser\CombinationApi\Server\Service\ModPortalService;
use GuzzleHttp\Promise\FulfilledPromise;
use GuzzleHttp\Promise\RejectedPromise;
use PHPUnit\Framework\TestCase;

/**
Expand All @@ -23,6 +26,9 @@
*/
class ModPortalServiceTest extends TestCase
{
/**
* @throws FailedModPortalRequestException
*/
public function testRequestMods(): void
{
$modNames = ['abc', 'def', 'ghi'];
Expand All @@ -38,6 +44,7 @@ public function testRequestMods(): void
$mod2->setName('def');
$promise1 = new FulfilledPromise($mod1);
$promise2 = new FulfilledPromise($mod2);
$promise3 = new RejectedPromise(new ErrorResponseException('test', 404, '', ''));
$expectedResult = [
'abc' => $mod1,
'def' => $mod2,
Expand All @@ -54,7 +61,7 @@ public function testRequestMods(): void
->willReturnOnConsecutiveCalls(
$promise1,
$promise2,
$this->throwException($this->createMock(ClientException::class)),
$promise3,
);

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

public function testRequestModsWithServerException(): void
{
$modNames = ['abc', 'def'];
$expectedRequest1 = new FullModRequest();
$expectedRequest1->setName('abc');
$expectedRequest2 = new FullModRequest();
$expectedRequest2->setName('def');
$mod1 = new Mod();
$mod1->setName('abc');
$promise1 = new FulfilledPromise($mod1);
$promise2 = new RejectedPromise(new ErrorResponseException('test', 500, '', ''));

$modPortalClient = $this->createMock(ClientInterface::class);
$modPortalClient->expects($this->exactly(2))
->method('sendRequest')
->withConsecutive(
[$this->equalTo($expectedRequest1)],
[$this->equalTo($expectedRequest2)],
)
->willReturnOnConsecutiveCalls(
$promise1,
$promise2,
);

$this->expectException(FailedModPortalRequestException::class);

$instance = new ModPortalService($modPortalClient);
$instance->requestMods($modNames);
}

public function testRequestModsWithInitialException(): void
{
$modNames = ['abc', 'def'];
$expectedRequest1 = new FullModRequest();
$expectedRequest1->setName('abc');
$expectedRequest2 = new FullModRequest();
$expectedRequest2->setName('def');
$mod1 = new Mod();
$mod1->setName('abc');
$promise1 = new FulfilledPromise($mod1);

$modPortalClient = $this->createMock(ClientInterface::class);
$modPortalClient->expects($this->exactly(2))
->method('sendRequest')
->withConsecutive(
[$this->equalTo($expectedRequest1)],
[$this->equalTo($expectedRequest2)],
)
->willReturnOnConsecutiveCalls(
$promise1,
$this->throwException($this->createMock(ClientException::class)),
);

$this->expectException(FailedModPortalRequestException::class);

$instance = new ModPortalService($modPortalClient);
$instance->requestMods($modNames);
}

public function testSelectLatestReleases(): void
{
$release1 = new Release();
Expand Down