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
28 changes: 25 additions & 3 deletions src/Service/RequestService.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ public function run(): bool
$this->consoleWriter->writeSuccess();
$this->consoleWriter->writeFormattedResult($content);
} else {
$this->consoleWriter->writeFailure(
(string)($content['error_description'] ?? $content['message'] ?? 'Unknown (Status ' . $status . ')')
);
$this->consoleWriter->writeFailure(self::createFailureReason($content, $status));
return false;
}
} catch (ExceptionInterface|\InvalidArgumentException $e) {
Expand All @@ -86,4 +84,28 @@ public function run(): bool

return true;
}

/**
* Describe a failed request for the console.
*
* Next to the API's message, this reports the HTTP status and the API's own
* error code. The code is what identifies the failing branch on the server:
* TER answers `{"status": 500, "code": 1603956982, "message": "An error
* occured on handling the request."}` for every masked exception, so the
* message alone cannot tell two unrelated defects apart.
*
* @param array<string, mixed> $content Decoded response body
*/
public static function createFailureReason(array $content, int $status): string
{
$reason = (string)($content['error_description'] ?? $content['message'] ?? '');
$details = ['HTTP ' . $status];
$code = $content['code'] ?? null;

if ((is_int($code) || is_string($code)) && !in_array((string)$code, ['', '0'], true)) {
$details[] = 'code ' . $code;
}

return ($reason !== '' ? $reason : 'Unknown') . ' (' . implode(', ', $details) . ')';
}
}
73 changes: 73 additions & 0 deletions tests/Unit/Service/RequestServiceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

/*
* This file is part of the TYPO3 project - inspiring people to share!
* (c) 2020-2024 Oliver Bartsch, Benni Mack & Elias Häußler
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace TYPO3\Tailor\Tests\Unit\Service;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use TYPO3\Tailor\Service\RequestService;

final class RequestServiceTest extends TestCase
{
/**
* @param array<string, mixed> $content
*/
#[Test]
#[DataProvider('createFailureReasonTestDataProvider')]
public function createFailureReasonTest(array $content, int $status, string $expected): void
{
self::assertSame($expected, RequestService::createFailureReason($content, $status));
}

/**
* Data provider for createFailureReasonTest
*/
public static function createFailureReasonTestDataProvider(): \Generator
{
yield 'Message and error code' => [
['status' => 500, 'code' => 1603956982, 'message' => 'An error occured on handling the request.'],
500,
'An error occured on handling the request. (HTTP 500, code 1603956982)',
];
yield 'Message without error code' => [
['message' => 'Extension key not found.'],
404,
'Extension key not found. (HTTP 404)',
];
yield 'OAuth style error description wins over message' => [
['error_description' => 'The access token is invalid.', 'message' => 'Unauthorized'],
401,
'The access token is invalid. (HTTP 401)',
];
yield 'Empty response body' => [
[],
502,
'Unknown (HTTP 502)',
];
yield 'Error code sent as string' => [
['message' => 'Denied.', 'code' => 'invalid_grant'],
400,
'Denied. (HTTP 400, code invalid_grant)',
];
yield 'Meaningless error code is omitted' => [
['message' => 'Denied.', 'code' => 0],
400,
'Denied. (HTTP 400)',
];
yield 'Non scalar error code is omitted' => [
['message' => 'Denied.', 'code' => ['nested']],
400,
'Denied. (HTTP 400)',
];
}
}
Loading