From 645bfca2ac8eb86eb0c111b04785494700d9ed98 Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Thu, 1 Oct 2020 22:13:14 +0200 Subject: [PATCH 01/16] Increase default versions for PHP and XDebug in `Dockerfile` --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 58b765b..59c571f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ ARG PHP_VERSION=7.4 FROM php:${PHP_VERSION}-cli-alpine -ARG XDEBUG_VERSION=2.9.8 +ARG XDEBUG_VERSION=3.1.1 COPY --from=composer /usr/bin/composer /usr/bin/composer RUN composer --version From f0934ad7bc152ac3585f2c2274ecf8f1a6dfeac9 Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Mon, 15 Nov 2021 15:59:32 +0100 Subject: [PATCH 02/16] [FT] Support Guzzle 7 & PSR-18 (#47) Fixes #39 # Conflicts: # Makefile # composer.json # src/Constants/Constants.php # src/Registry/Decorators/CachingDecorator.php # src/Registry/GuzzlePromiseAsyncRegistry.php # src/Requests/Functions.php # test/IntegrationTest.php # test/Registry/GuzzlePromiseAsyncRegistryTest.php # test/Requests/FunctionsTest.php --- README.md | 30 +-- composer.json | 1 + src/AsynchronousRegistry.php | 3 - .../AbstractSchemaRegistryException.php | 5 +- src/Exception/ExceptionMap.php | 84 +++---- src/Exception/LogicException.php | 17 ++ src/Exception/RuntimeException.php | 17 ++ src/Registry/Cache/AvroObjectCacheAdapter.php | 4 - src/Registry/{ => Cache}/CacheAdapter.php | 2 +- src/Registry/Cache/CacheItemPoolAdapter.php | 1 - src/Registry/Cache/DoctrineCacheAdapter.php | 4 - src/Registry/Cache/SimpleCacheAdapter.php | 1 - .../BlockingDecorator.php} | 7 +- .../CachingDecorator.php} | 9 +- ...try.php => GuzzlePromiseAsyncRegistry.php} | 32 ++- src/Registry/Psr18SyncRegistry.php | 142 ++++++++++++ src/Requests/Functions.php | 51 ++++- src/SynchronousRegistry.php | 3 - test/Exception/ExceptionMapTest.php | 196 +++++++--------- .../Cache/AbstractCacheAdapterTestCase.php | 2 +- .../Cache/AvroObjectCacheAdapterTest.php | 2 +- .../Cache/CacheItemPoolAdapterTest.php | 2 +- .../Cache/DoctrineCacheAdapterTest.php | 2 +- .../Registry/Cache/SimpleCacheAdapterTest.php | 2 +- .../BlockingDecoratorTest.php} | 8 +- .../CachingDecoratorTest.php} | 14 +- ...php => GuzzlePromiseAsyncRegistryTest.php} | 64 +++--- test/Registry/Psr18SyncRegistryTest.php | 214 ++++++++++++++++++ test/Requests/FunctionsTest.php | 7 +- 29 files changed, 651 insertions(+), 275 deletions(-) create mode 100644 src/Exception/LogicException.php create mode 100644 src/Exception/RuntimeException.php rename src/Registry/{ => Cache}/CacheAdapter.php (98%) rename src/Registry/{BlockingRegistry.php => Decorators/BlockingDecorator.php} (95%) rename src/Registry/{CachedRegistry.php => Decorators/CachingDecorator.php} (97%) rename src/Registry/{PromisingRegistry.php => GuzzlePromiseAsyncRegistry.php} (85%) create mode 100644 src/Registry/Psr18SyncRegistry.php rename test/Registry/{BlockingRegistryTest.php => Decorators/BlockingDecoratorTest.php} (94%) rename test/Registry/{CachedRegistryTest.php => Decorators/CachingDecoratorTest.php} (96%) rename test/Registry/{PromisingRegistryTest.php => GuzzlePromiseAsyncRegistryTest.php} (76%) create mode 100644 test/Registry/Psr18SyncRegistryTest.php diff --git a/README.md b/README.md index 975ec71..5e3ba3c 100644 --- a/README.md +++ b/README.md @@ -77,10 +77,10 @@ major version upgrades will have incompatibilities that will be released in the 'registry.example.com']) ); @@ -140,12 +140,12 @@ $schemaId = $registry->schemaId('test-subject', $schema)->wait(); ```php 'registry.example.com']) ) ); @@ -173,21 +173,21 @@ It supports both async and sync APIs. ```php 'registry.example.com']) ); -$syncApi = new BlockingRegistry($asyncApi); +$syncApi = new BlockingDecorator($asyncApi); -$doctrineCachedSyncApi = new CachedRegistry( +$doctrineCachedSyncApi = new CachingDecorator( $asyncApi, new DoctrineCacheAdapter( new ArrayCache() @@ -195,8 +195,8 @@ $doctrineCachedSyncApi = new CachedRegistry( ); // All adapters support both APIs, for async APIs additional fulfillment callbacks will be registered. -$avroObjectCachedAsyncApi = new CachedRegistry( - $syncApi, +$avroObjectCachedAsyncApi = new CachingDecorator( + $asyncApi, new AvroObjectCacheAdapter() ); @@ -212,7 +212,7 @@ $sha1HashFunction = static function (AvroSchema $schema) { }; // Pass the hash function as optional 3rd parameter to the CachedRegistry constructor -$avroObjectCachedAsyncApi = new CachedRegistry( +$avroObjectCachedAsyncApi = new CachingDecorator( $syncApi, new AvroObjectCacheAdapter(), $sha1HashFunction diff --git a/composer.json b/composer.json index e25dd84..321702b 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "php": "^7.4|^8.0|8.1.*", "ext-curl": "*", "ext-json": "*", + "psr/http-client": "~1.0", "guzzlehttp/guzzle": "^7.0", "guzzlehttp/promises": "^1.4.0", "guzzlehttp/psr7": "^1.7", diff --git a/src/AsynchronousRegistry.php b/src/AsynchronousRegistry.php index 0bf0a71..d307f3a 100644 --- a/src/AsynchronousRegistry.php +++ b/src/AsynchronousRegistry.php @@ -8,9 +8,6 @@ use FlixTech\SchemaRegistryApi\Schema\AvroReference; use GuzzleHttp\Promise\PromiseInterface; -/** - * {@inheritdoc} - */ interface AsynchronousRegistry extends Registry { /** diff --git a/src/Exception/AbstractSchemaRegistryException.php b/src/Exception/AbstractSchemaRegistryException.php index e94c049..aa922c0 100644 --- a/src/Exception/AbstractSchemaRegistryException.php +++ b/src/Exception/AbstractSchemaRegistryException.php @@ -4,10 +4,9 @@ namespace FlixTech\SchemaRegistryApi\Exception; -use LogicException; -use RuntimeException; +use RuntimeException as PHPRuntimeException; -abstract class AbstractSchemaRegistryException extends RuntimeException implements SchemaRegistryException +abstract class AbstractSchemaRegistryException extends PHPRuntimeException implements SchemaRegistryException { public const ERROR_CODE = 0; diff --git a/src/Exception/ExceptionMap.php b/src/Exception/ExceptionMap.php index 455f8c8..81aceb4 100644 --- a/src/Exception/ExceptionMap.php +++ b/src/Exception/ExceptionMap.php @@ -5,10 +5,9 @@ namespace FlixTech\SchemaRegistryApi\Exception; use Exception; -use GuzzleHttp\Exception\RequestException; use Psr\Http\Message\ResponseInterface; -use RuntimeException; use function array_key_exists; +use function FlixTech\SchemaRegistryApi\Requests\jsonDecode; use function sprintf; final class ExceptionMap @@ -31,38 +30,56 @@ public static function instance(): ExceptionMap return self::$instance; } + /** + * @var array + */ + private $map; + private function __construct() { + $factoryFn = static function (string $exceptionClass): callable { + return static function (int $errorCode, string $errorMessage) use ($exceptionClass): SchemaRegistryException { + return new $exceptionClass($errorMessage, $errorCode); + }; + }; + + $this->map = [ + IncompatibleAvroSchemaException::errorCode() => $factoryFn(IncompatibleAvroSchemaException::class), + BackendDataStoreException::errorCode() => $factoryFn(BackendDataStoreException::class), + OperationTimedOutException::errorCode() => $factoryFn(OperationTimedOutException::class), + MasterProxyException::errorCode() => $factoryFn(MasterProxyException::class), + InvalidVersionException::errorCode() => $factoryFn(InvalidVersionException::class), + InvalidAvroSchemaException::errorCode() => $factoryFn(InvalidAvroSchemaException::class), + SchemaNotFoundException::errorCode() => $factoryFn(SchemaNotFoundException::class), + SubjectNotFoundException::errorCode() => $factoryFn(SubjectNotFoundException::class), + VersionNotFoundException::errorCode() => $factoryFn(VersionNotFoundException::class), + InvalidCompatibilityLevelException::errorCode() => $factoryFn(InvalidCompatibilityLevelException::class), + ]; } /** - * Maps a RequestException to the internal SchemaRegistryException types. + * Maps a ResponseInterface to the internal SchemaRegistryException types. * - * @param RequestException $exception + * @param ResponseInterface $response * * @return SchemaRegistryException * * @throws RuntimeException */ - public function __invoke(RequestException $exception): SchemaRegistryException + public function exceptionFor(ResponseInterface $response): SchemaRegistryException { - $response = $this->guardAgainstMissingResponse($exception); $decodedBody = $this->guardAgainstMissingErrorCode($response); $errorCode = $decodedBody[self::ERROR_CODE_FIELD_NAME]; - $errorMessage = $decodedBody[self::ERROR_MESSAGE_FIELD_NAME]; + $errorMessage = $decodedBody[self::ERROR_MESSAGE_FIELD_NAME] ?? "Unknown Error"; return $this->mapErrorCodeToException($errorCode, $errorMessage); } - private function guardAgainstMissingResponse(RequestException $exception): ResponseInterface + public function hasMappableError(ResponseInterface $response): bool { - $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); - if (!$response) { - throw new RuntimeException('RequestException has no response to inspect', 0, $exception); - } - - return $response; + return $statusCode >= 400 && $statusCode < 600; } /** @@ -72,7 +89,7 @@ private function guardAgainstMissingResponse(RequestException $exception): Respo private function guardAgainstMissingErrorCode(ResponseInterface $response): array { try { - $decodedBody = \GuzzleHttp\json_decode((string) $response->getBody(), true); + $decodedBody = jsonDecode((string) $response->getBody()); if (!is_array($decodedBody) || !array_key_exists(self::ERROR_CODE_FIELD_NAME, $decodedBody)) { throw new RuntimeException( @@ -98,39 +115,10 @@ private function guardAgainstMissingErrorCode(ResponseInterface $response): arra private function mapErrorCodeToException(int $errorCode, string $errorMessage): SchemaRegistryException { - switch ($errorCode) { - case IncompatibleAvroSchemaException::errorCode(): - return new IncompatibleAvroSchemaException($errorMessage, $errorCode); - - case BackendDataStoreException::errorCode(): - return new BackendDataStoreException($errorMessage, $errorCode); - - case OperationTimedOutException::errorCode(): - return new OperationTimedOutException($errorMessage, $errorCode); - - case MasterProxyException::errorCode(): - return new MasterProxyException($errorMessage, $errorCode); - - case InvalidVersionException::errorCode(): - return new InvalidVersionException($errorMessage, $errorCode); - - case InvalidAvroSchemaException::errorCode(): - return new InvalidAvroSchemaException($errorMessage, $errorCode); - - case SchemaNotFoundException::errorCode(): - return new SchemaNotFoundException($errorMessage, $errorCode); - - case SubjectNotFoundException::errorCode(): - return new SubjectNotFoundException($errorMessage, $errorCode); - - case VersionNotFoundException::errorCode(): - return new VersionNotFoundException($errorMessage, $errorCode); - - case InvalidCompatibilityLevelException::errorCode(): - return new InvalidCompatibilityLevelException($errorMessage, $errorCode); - - default: - throw new RuntimeException(sprintf('Unknown error code "%d"', $errorCode)); + if (!array_key_exists($errorCode, $this->map)) { + throw new RuntimeException(sprintf('Unknown error code "%d"', $errorCode)); } + + return $this->map[$errorCode]($errorCode, $errorMessage); } } diff --git a/src/Exception/LogicException.php b/src/Exception/LogicException.php new file mode 100644 index 0000000..60b151a --- /dev/null +++ b/src/Exception/LogicException.php @@ -0,0 +1,17 @@ +client = $client; $exceptionMap = ExceptionMap::instance(); - $this->rejectedCallback = static function (RequestException $exception) use ($exceptionMap) { - return $exceptionMap($exception); + $responseExistenceGuard = static function (RequestException $exception): ResponseInterface { + $response = $exception->getResponse(); + + if (!$response) { + throw new RuntimeException( + "RequestException does not provide a response to inspect.", + $exception->getCode(), + $exception + ); + } + + return $response; + }; + + $this->rejectedCallback = static function (RequestException $exception) use ( + $exceptionMap, + $responseExistenceGuard + ): SchemaRegistryException { + return $exceptionMap->exceptionFor($responseExistenceGuard($exception)); }; } diff --git a/src/Registry/Psr18SyncRegistry.php b/src/Registry/Psr18SyncRegistry.php new file mode 100644 index 0000000..90c7fbd --- /dev/null +++ b/src/Registry/Psr18SyncRegistry.php @@ -0,0 +1,142 @@ +client = $client; + $this->map = ExceptionMap::instance(); + } + + public function register(string $subject, AvroSchema $schema): int + { + $request = registerNewSchemaVersionWithSubjectRequest((string) $schema, $subject); + + $response = $this->makeRequest($request); + $this->guardAgainstErrorResponse($response); + + return decodeResponse($response)['id']; + } + + public function schemaVersion(string $subject, AvroSchema $schema): int + { + $request = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + + $response = $this->makeRequest($request); + $this->guardAgainstErrorResponse($response); + + return decodeResponse($response)['version']; + } + + public function latestVersion(string $subject): AvroSchema + { + $request = singleSubjectVersionRequest($subject, VERSION_LATEST); + + $response = $this->makeRequest($request); + $this->guardAgainstErrorResponse($response); + + return $this->parseAvroSchema(decodeResponse($response)['schema']); + } + + public function schemaId(string $subject, AvroSchema $schema): int + { + $request = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + + $response = $this->makeRequest($request); + $this->guardAgainstErrorResponse($response); + + return decodeResponse($response)['id']; + } + + public function schemaForId(int $schemaId): AvroSchema + { + $request = schemaRequest(validateSchemaId($schemaId)); + + $response = $this->makeRequest($request); + $this->guardAgainstErrorResponse($response); + + return $this->parseAvroSchema(decodeResponse($response)['schema']); + } + + public function schemaForSubjectAndVersion(string $subject, int $version): AvroSchema + { + $request = singleSubjectVersionRequest($subject, validateVersionId($version)); + + $response = $this->makeRequest($request); + $this->guardAgainstErrorResponse($response); + + return $this->parseAvroSchema(decodeResponse($response)['schema']); + } + + /** + * @param ResponseInterface $response + * + * @throws SchemaRegistryException + */ + private function guardAgainstErrorResponse(ResponseInterface $response): void + { + if ($this->map->hasMappableError($response)) { + throw $this->map->exceptionFor($response); + } + } + + private function makeRequest(RequestInterface $request): ResponseInterface + { + try { + return $this->client->sendRequest($request); + } catch (ClientExceptionInterface $exception) { + throw new RuntimeException( + "Unexpected error during client request", + RuntimeException::ERROR_CODE, + $exception + ); + } + } + + private function parseAvroSchema(string $schema): AvroSchema + { + try { + return AvroSchema::parse($schema); + } catch (AvroSchemaParseException $e) { + throw new InvalidAvroSchemaException( + "Could not parse schema: $schema", + InvalidAvroSchemaException::ERROR_CODE, + $e + ); + } + } +} diff --git a/src/Requests/Functions.php b/src/Requests/Functions.php index 9462a92..a7b9884 100644 --- a/src/Requests/Functions.php +++ b/src/Requests/Functions.php @@ -1,11 +1,14 @@ - + */ +function decodeResponse(ResponseInterface $response): array +{ + $body = (string) $response->getBody(); + + try { + return jsonDecode($body); + } catch (JsonException $e) { + throw new InvalidArgumentException( + sprintf('%s - with content "%s"', $e->getMessage(), $body), + $e->getCode(), + $e + ); + } +} function allSubjectsRequest(): RequestInterface { diff --git a/src/SynchronousRegistry.php b/src/SynchronousRegistry.php index 49f261f..c263a6d 100644 --- a/src/SynchronousRegistry.php +++ b/src/SynchronousRegistry.php @@ -7,9 +7,6 @@ use AvroSchema; use FlixTech\SchemaRegistryApi\Schema\AvroReference; -/** - * {@inheritdoc} - */ interface SynchronousRegistry extends Registry { /** diff --git a/test/Exception/ExceptionMapTest.php b/test/Exception/ExceptionMapTest.php index a74cbdf..1de8877 100644 --- a/test/Exception/ExceptionMapTest.php +++ b/test/Exception/ExceptionMapTest.php @@ -17,8 +17,7 @@ use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; use FlixTech\SchemaRegistryApi\Exception\SubjectNotFoundException; use FlixTech\SchemaRegistryApi\Exception\VersionNotFoundException; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Psr7\Request; +use Generator; use GuzzleHttp\Psr7\Response; use LogicException; use PHPUnit\Framework\TestCase; @@ -35,15 +34,11 @@ public function it_should_handle_InvalidAvroSchema_code(): void InvalidAvroSchemaException::class, 'Invalid Avro schema', 42201, - (ExceptionMap::instance())( - new RequestException( - '422 Unprocessable Entity', - new Request('GET', '/'), - new Response( - 422, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":42201,"message": "Invalid Avro schema"}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 422, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":42201,"message": "Invalid Avro schema"}' ) ) ); @@ -58,15 +53,11 @@ public function it_should_handle_IncompatibleAvroSchema_code(): void IncompatibleAvroSchemaException::class, 'Incompatible Avro schema', 409, - (ExceptionMap::instance())( - new RequestException( - '409 Conflict', - new Request('GET', '/'), - new Response( - 409, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":409,"message": "Incompatible Avro schema"}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 409, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":409,"message": "Incompatible Avro schema"}' ) ) ); @@ -81,15 +72,11 @@ public function it_should_handle_BackendDataStore_code(): void BackendDataStoreException::class, 'Error in the backend datastore', 50001, - (ExceptionMap::instance())( - new RequestException( - '500 Internal Server Error', - new Request('GET', '/'), - new Response( - 500, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":50001,"message": "Error in the backend datastore"}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 500, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":50001,"message": "Error in the backend datastore"}' ) ) ); @@ -104,15 +91,11 @@ public function it_should_handle_InvalidCompatibilityLevel_code(): void InvalidCompatibilityLevelException::class, 'Invalid compatibility level', 42203, - (ExceptionMap::instance())( - new RequestException( - '422 Unprocessable Entity', - new Request('GET', '/'), - new Response( - 422, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":42203,"message": "Invalid compatibility level"}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 422, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":42203,"message": "Invalid compatibility level"}' ) ) ); @@ -127,15 +110,11 @@ public function it_should_handle_InvalidVersion_code(): void InvalidVersionException::class, 'Invalid version', 42202, - (ExceptionMap::instance())( - new RequestException( - '422 Unprocessable Entity', - new Request('GET', '/'), - new Response( - 422, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":42202,"message": "Invalid version"}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 422, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":42202,"message": "Invalid version"}' ) ) ); @@ -150,15 +129,11 @@ public function it_should_handle_MasterProxy_code(): void MasterProxyException::class, 'Error while forwarding the request to the master', 50003, - (ExceptionMap::instance())( - new RequestException( - '500 Internal server Error', - new Request('GET', '/'), - new Response( - 500, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":50003,"message": "Error while forwarding the request to the master"}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 500, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":50003,"message": "Error while forwarding the request to the master"}' ) ) ); @@ -173,15 +148,11 @@ public function it_should_handle_OperationTimedOut_code(): void OperationTimedOutException::class, 'Operation timed out', 50002, - (ExceptionMap::instance())( - new RequestException( - '500 Internal server Error', - new Request('GET', '/'), - new Response( - 500, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":50002,"message": "Operation timed out"}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 500, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":50002,"message": "Operation timed out"}' ) ) ); @@ -196,15 +167,11 @@ public function it_should_handle_SchemaNotFound_code(): void SchemaNotFoundException::class, 'Schema not found', 40403, - (ExceptionMap::instance())( - new RequestException( - '404 Not Found', - new Request('GET', '/'), - new Response( - 404, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":40403,"message": "Schema not found"}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 404, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":40403,"message": "Schema not found"}' ) ) ); @@ -219,15 +186,11 @@ public function it_should_handle_SubjectNotFound_code(): void SubjectNotFoundException::class, 'Subject not found', 40401, - (ExceptionMap::instance())( - new RequestException( - '404 Not Found', - new Request('GET', '/'), - new Response( - 404, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":40401,"message": "Subject not found"}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 404, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":40401,"message": "Subject not found"}' ) ) ); @@ -242,15 +205,11 @@ public function it_should_handle_VersionNotFound_code(): void VersionNotFoundException::class, 'Version not found', 40402, - (ExceptionMap::instance())( - new RequestException( - '404 Not Found', - new Request('GET', '/'), - new Response( - 404, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":40402,"message": "Version not found"}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 404, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":40402,"message": "Version not found"}' ) ) ); @@ -258,18 +217,20 @@ public function it_should_handle_VersionNotFound_code(): void /** * @test + * @dataProvider statusCodeProvider + * @param int $statusCode + * @param bool $expected */ - public function it_should_not_process_exceptions_with_missing_response(): void + public function it_should_be_able_to_determine_if_an_exception_should_be_thrown(int $statusCode, bool $expected): void { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage("RequestException has no response to inspect"); - - (ExceptionMap::instance())( - new RequestException( - '404 Not Found', - new Request('GET', '/') + $actual = (ExceptionMap::instance())->hasMappableError( + new Response( + $statusCode, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":40402,"message": "Version not found"}' ) ); + self::assertEquals($expected, $actual); } /** @@ -289,15 +250,11 @@ public function it_should_not_process_exceptions_with_missing_error_codes(): voi $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Invalid message body received - cannot find "error_code" field in response body'); - (ExceptionMap::instance())( - new RequestException( - '404 Not Found', - new Request('GET', '/'), - new Response( - 404, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"message": "This JSON has no \'error_code\' field."}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 404, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"message": "This JSON has no \'error_code\' field."}' ) ); } @@ -310,15 +267,11 @@ public function it_should_not_process_unknown_error_codes(): void $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Unknown error code "99999"'); - (ExceptionMap::instance())( - new RequestException( - '404 Not Found', - new Request('GET', '/'), - new Response( - 404, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":99999,"message": "Subject not found"}' - ) + (ExceptionMap::instance())->exceptionFor( + new Response( + 404, + ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], + '{"error_code":99999,"message": "Subject not found"}' ) ); } @@ -334,6 +287,13 @@ private function assertSchemaRegistryException( self::assertEquals($errorCode, $exception->getCode()); self::assertEquals($expectedMessage, $exception->getMessage()); } + + public function statusCodeProvider(): ?Generator + { + yield 'Valid 400' => [400, true]; + yield 'Valid 599' => [599, true]; + yield 'Invalid 399' => [399, false]; + } } class InvalidNewSchemaRegistryException extends AbstractSchemaRegistryException diff --git a/test/Registry/Cache/AbstractCacheAdapterTestCase.php b/test/Registry/Cache/AbstractCacheAdapterTestCase.php index 228859f..500e19a 100644 --- a/test/Registry/Cache/AbstractCacheAdapterTestCase.php +++ b/test/Registry/Cache/AbstractCacheAdapterTestCase.php @@ -6,7 +6,7 @@ use AvroSchema; use AvroSchemaParseException; -use FlixTech\SchemaRegistryApi\Registry\CacheAdapter; +use FlixTech\SchemaRegistryApi\Registry\Cache\CacheAdapter; use PHPUnit\Framework\TestCase; abstract class AbstractCacheAdapterTestCase extends TestCase diff --git a/test/Registry/Cache/AvroObjectCacheAdapterTest.php b/test/Registry/Cache/AvroObjectCacheAdapterTest.php index 91e6dc3..6850c2a 100644 --- a/test/Registry/Cache/AvroObjectCacheAdapterTest.php +++ b/test/Registry/Cache/AvroObjectCacheAdapterTest.php @@ -5,7 +5,7 @@ namespace FlixTech\SchemaRegistryApi\Test\Registry\Cache; use FlixTech\SchemaRegistryApi\Registry\Cache\AvroObjectCacheAdapter; -use FlixTech\SchemaRegistryApi\Registry\CacheAdapter; +use FlixTech\SchemaRegistryApi\Registry\Cache\CacheAdapter; class AvroObjectCacheAdapterTest extends AbstractCacheAdapterTestCase { diff --git a/test/Registry/Cache/CacheItemPoolAdapterTest.php b/test/Registry/Cache/CacheItemPoolAdapterTest.php index 642c198..5972948 100644 --- a/test/Registry/Cache/CacheItemPoolAdapterTest.php +++ b/test/Registry/Cache/CacheItemPoolAdapterTest.php @@ -4,8 +4,8 @@ namespace FlixTech\SchemaRegistryApi\Test\Registry\Cache; +use FlixTech\SchemaRegistryApi\Registry\Cache\CacheAdapter; use FlixTech\SchemaRegistryApi\Registry\Cache\CacheItemPoolAdapter; -use FlixTech\SchemaRegistryApi\Registry\CacheAdapter; use Symfony\Component\Cache\Adapter\ArrayAdapter; class CacheItemPoolAdapterTest extends AbstractCacheAdapterTestCase diff --git a/test/Registry/Cache/DoctrineCacheAdapterTest.php b/test/Registry/Cache/DoctrineCacheAdapterTest.php index 541f944..c6d4a6e 100644 --- a/test/Registry/Cache/DoctrineCacheAdapterTest.php +++ b/test/Registry/Cache/DoctrineCacheAdapterTest.php @@ -5,8 +5,8 @@ namespace FlixTech\SchemaRegistryApi\Test\Registry\Cache; use Doctrine\Common\Cache\ArrayCache; +use FlixTech\SchemaRegistryApi\Registry\Cache\CacheAdapter; use FlixTech\SchemaRegistryApi\Registry\Cache\DoctrineCacheAdapter; -use FlixTech\SchemaRegistryApi\Registry\CacheAdapter; class DoctrineCacheAdapterTest extends AbstractCacheAdapterTestCase { diff --git a/test/Registry/Cache/SimpleCacheAdapterTest.php b/test/Registry/Cache/SimpleCacheAdapterTest.php index 0546185..1b9e0b9 100644 --- a/test/Registry/Cache/SimpleCacheAdapterTest.php +++ b/test/Registry/Cache/SimpleCacheAdapterTest.php @@ -4,8 +4,8 @@ namespace FlixTech\SchemaRegistryApi\Test\Registry\Cache; +use FlixTech\SchemaRegistryApi\Registry\Cache\CacheAdapter; use FlixTech\SchemaRegistryApi\Registry\Cache\SimpleCacheAdapter; -use FlixTech\SchemaRegistryApi\Registry\CacheAdapter; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Psr16Cache; diff --git a/test/Registry/BlockingRegistryTest.php b/test/Registry/Decorators/BlockingDecoratorTest.php similarity index 94% rename from test/Registry/BlockingRegistryTest.php rename to test/Registry/Decorators/BlockingDecoratorTest.php index 0f36b1b..02a7bce 100644 --- a/test/Registry/BlockingRegistryTest.php +++ b/test/Registry/Decorators/BlockingDecoratorTest.php @@ -2,20 +2,20 @@ declare(strict_types=1); -namespace FlixTech\SchemaRegistryApi\Test\Registry; +namespace FlixTech\SchemaRegistryApi\Test\Registry\Decorators; use AvroSchema; use AvroSchemaParseException; use FlixTech\SchemaRegistryApi\AsynchronousRegistry; use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; -use FlixTech\SchemaRegistryApi\Registry\BlockingRegistry; +use FlixTech\SchemaRegistryApi\Registry\Decorators\BlockingDecorator; use FlixTech\SchemaRegistryApi\SynchronousRegistry; use GuzzleHttp\Promise\FulfilledPromise; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use RuntimeException; -class BlockingRegistryTest extends TestCase +class BlockingDecoratorTest extends TestCase { /** * @var AsynchronousRegistry|MockObject @@ -32,7 +32,7 @@ class BlockingRegistryTest extends TestCase protected function setUp(): void { $this->asyncRegistry = $this->getMockForAbstractClass(AsynchronousRegistry::class); - $this->blockingRegistry = new BlockingRegistry($this->asyncRegistry); + $this->blockingRegistry = new BlockingDecorator($this->asyncRegistry); } /** diff --git a/test/Registry/CachedRegistryTest.php b/test/Registry/Decorators/CachingDecoratorTest.php similarity index 96% rename from test/Registry/CachedRegistryTest.php rename to test/Registry/Decorators/CachingDecoratorTest.php index 2d7a702..182603f 100644 --- a/test/Registry/CachedRegistryTest.php +++ b/test/Registry/Decorators/CachingDecoratorTest.php @@ -2,21 +2,21 @@ declare(strict_types=1); -namespace FlixTech\SchemaRegistryApi\Test\Registry; +namespace FlixTech\SchemaRegistryApi\Test\Registry\Decorators; use AvroSchema; use AvroSchemaParseException; use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; use FlixTech\SchemaRegistryApi\Exception\SubjectNotFoundException; use FlixTech\SchemaRegistryApi\Registry; -use FlixTech\SchemaRegistryApi\Registry\CacheAdapter; -use FlixTech\SchemaRegistryApi\Registry\CachedRegistry; +use FlixTech\SchemaRegistryApi\Registry\Cache\CacheAdapter; +use FlixTech\SchemaRegistryApi\Registry\Decorators\CachingDecorator; use GuzzleHttp\Promise\FulfilledPromise; use GuzzleHttp\Promise\PromiseInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -class CachedRegistryTest extends TestCase +class CachingDecoratorTest extends TestCase { /** * @var Registry|MockObject @@ -29,7 +29,7 @@ class CachedRegistryTest extends TestCase private $cacheAdapter; /** - * @var CachedRegistry + * @var CachingDecorator */ private $cachedRegistry; @@ -61,7 +61,7 @@ protected function setUp(): void return md5((string) $schema); }; - $this->cachedRegistry = new CachedRegistry($this->registryMock, $this->cacheAdapter); + $this->cachedRegistry = new CachingDecorator($this->registryMock, $this->cacheAdapter); } /** @@ -234,7 +234,7 @@ public function it_should_accept_different_hash_algo_functions(): void return sha1((string) $schema); }; - $this->cachedRegistry = new CachedRegistry($this->registryMock, $this->cacheAdapter, $sha1HashFunction); + $this->cachedRegistry = new CachingDecorator($this->registryMock, $this->cacheAdapter, $sha1HashFunction); $this->registryMock ->expects(self::never()) diff --git a/test/Registry/PromisingRegistryTest.php b/test/Registry/GuzzlePromiseAsyncRegistryTest.php similarity index 76% rename from test/Registry/PromisingRegistryTest.php rename to test/Registry/GuzzlePromiseAsyncRegistryTest.php index 01e3171..0b5ac43 100644 --- a/test/Registry/PromisingRegistryTest.php +++ b/test/Registry/GuzzlePromiseAsyncRegistryTest.php @@ -9,7 +9,7 @@ use Exception; use FlixTech\SchemaRegistryApi\Exception\SchemaNotFoundException; use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; -use FlixTech\SchemaRegistryApi\Registry\PromisingRegistry; +use FlixTech\SchemaRegistryApi\Registry\GuzzlePromiseAsyncRegistry; use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; @@ -18,6 +18,8 @@ use PHPUnit\Framework\TestCase; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; +use const FlixTech\SchemaRegistryApi\Constants\ACCEPT; +use const FlixTech\SchemaRegistryApi\Constants\CONTENT_TYPE; use const FlixTech\SchemaRegistryApi\Constants\VERSION_LATEST; use function FlixTech\SchemaRegistryApi\Requests\checkIfSubjectHasSchemaRegisteredRequest; use function FlixTech\SchemaRegistryApi\Requests\registerNewSchemaVersionWithSubjectRequest; @@ -26,11 +28,11 @@ use function FlixTech\SchemaRegistryApi\Requests\validateSchemaId; use function FlixTech\SchemaRegistryApi\Requests\validateVersionId; -class PromisingRegistryTest extends TestCase +class GuzzlePromiseAsyncRegistryTest extends TestCase { /** - * @var PromisingRegistry + * @var GuzzlePromiseAsyncRegistry */ private $registry; @@ -49,12 +51,11 @@ public function it_should_register_schemas(): void $expectedRequest = registerNewSchemaVersionWithSubjectRequest((string) $schema, $subject); $container = []; - $this->registry = new PromisingRegistry($this->clientWithMockResponses($responses, $container)); + $client = $this->clientWithMockResponses($responses, $container); - $promise = $this->registry->register( - $subject, - $schema - ); + $this->registry = new GuzzlePromiseAsyncRegistry($client); + + $promise = $this->registry->register($subject, $schema); self::assertEquals(3, $promise->wait()); $this->assertRequestCallable($expectedRequest)($container[0]['request']); @@ -75,12 +76,9 @@ public function it_can_get_the_schema_id_for_a_schema_and_subject(): void $expectedRequest = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); $container = []; - $this->registry = new PromisingRegistry($this->clientWithMockResponses($responses, $container)); + $this->registry = new GuzzlePromiseAsyncRegistry($this->clientWithMockResponses($responses, $container)); - $promise = $this->registry->schemaId( - $subject, - $schema, - ); + $promise = $this->registry->schemaId($subject, $schema); self::assertEquals(2, $promise->wait()); $this->assertRequestCallable($expectedRequest)($container[0]['request']); @@ -100,11 +98,9 @@ public function it_can_get_a_schema_for_id(): void $expectedRequest = schemaRequest(validateSchemaId(1)); $container = []; - $this->registry = new PromisingRegistry($this->clientWithMockResponses($responses, $container)); + $this->registry = new GuzzlePromiseAsyncRegistry($this->clientWithMockResponses($responses, $container)); - $promise = $this->registry->schemaForId( - 1, - ); + $promise = $this->registry->schemaForId(1); self::assertEquals($schema, $promise->wait()); $this->assertRequestCallable($expectedRequest)($container[0]['request']); @@ -126,12 +122,9 @@ public function it_can_get_a_schema_for_subject_and_version(): void $expectedRequest = singleSubjectVersionRequest($subject, validateVersionId($version)); $container = []; - $this->registry = new PromisingRegistry($this->clientWithMockResponses($responses, $container)); + $this->registry = new GuzzlePromiseAsyncRegistry($this->clientWithMockResponses($responses, $container)); - $promise = $this->registry->schemaForSubjectAndVersion( - $subject, - $version, - ); + $promise = $this->registry->schemaForSubjectAndVersion($subject, $version); self::assertEquals($schema, $promise->wait()); $this->assertRequestCallable($expectedRequest)($container[0]['request']); @@ -152,12 +145,9 @@ public function it_can_get_the_schema_version(): void $expectedRequest = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); $container = []; - $this->registry = new PromisingRegistry($this->clientWithMockResponses($responses, $container)); + $this->registry = new GuzzlePromiseAsyncRegistry($this->clientWithMockResponses($responses, $container)); - $promise = $this->registry->schemaVersion( - $subject, - $schema, - ); + $promise = $this->registry->schemaVersion($subject, $schema); self::assertEquals(3, $promise->wait()); $this->assertRequestCallable($expectedRequest)($container[0]['request']); @@ -179,11 +169,9 @@ public function it_can_get_the_latest_version(): void $expectedRequest = singleSubjectVersionRequest($subject, VERSION_LATEST); $container = []; - $this->registry = new PromisingRegistry($this->clientWithMockResponses($responses, $container)); + $this->registry = new GuzzlePromiseAsyncRegistry($this->clientWithMockResponses($responses, $container)); - $promise = $this->registry->latestVersion( - $subject, - ); + $promise = $this->registry->latestVersion($subject); self::assertEquals($schema, $promise->wait()); $this->assertRequestCallable($expectedRequest)($container[0]['request']); @@ -202,7 +190,8 @@ public function it_will_not_throw_but_pass_exceptions(): void sprintf('{"error_code": %d, "message": "test"}', SchemaNotFoundException::ERROR_CODE) ) ]; - $this->registry = new PromisingRegistry($this->clientWithMockResponses($responses)); + + $this->registry = new GuzzlePromiseAsyncRegistry($this->clientWithMockResponses($responses)); /** @var Exception $exception */ $exception = $this->registry->schemaForId(1)->wait(); @@ -213,24 +202,27 @@ public function it_will_not_throw_but_pass_exceptions(): void /** * @param ResponseInterface[] $responses - * @param array $container + * @param array $container * * @return Client */ private function clientWithMockResponses(array $responses, array &$container = []): Client { + $history = Middleware::history($container); + $mockHandler = new MockHandler($responses); $stack = HandlerStack::create($mockHandler); - $stack->push(Middleware::history($container)); + $stack->push($history); return new Client(['handler' => $stack]); } private function assertRequestCallable(RequestInterface $expectedRequest): callable { - return function (RequestInterface $actual) use ($expectedRequest) { + return function (RequestInterface $actual) use ($expectedRequest) { $this->assertEquals($expectedRequest->getUri(), $actual->getUri()); - //$this->assertEquals($expectedRequest->getHeaders(), $actual->getHeaders()); + $this->assertEquals($expectedRequest->getHeader(ACCEPT), $actual->getHeader(ACCEPT)); + $this->assertEquals($expectedRequest->getHeader(CONTENT_TYPE), $actual->getHeader(CONTENT_TYPE)); $this->assertEquals($expectedRequest->getMethod(), $actual->getMethod()); $this->assertEquals($expectedRequest->getBody()->getContents(), $actual->getBody()->getContents()); diff --git a/test/Registry/Psr18SyncRegistryTest.php b/test/Registry/Psr18SyncRegistryTest.php new file mode 100644 index 0000000..c800dc0 --- /dev/null +++ b/test/Registry/Psr18SyncRegistryTest.php @@ -0,0 +1,214 @@ +registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); + + self::assertEquals(3, $this->registry->register($subject, $schema)); + $this->assertRequestCallable($expectedRequest)($container[0]['request']); + } + + /** + * @test + * @throws SchemaRegistryException + * @throws AvroSchemaParseException + */ + public function it_can_get_the_schema_id_for_a_schema_and_subject(): void + { + $responses = [ + new Response(200, [], '{"id": 2}') + ]; + $subject = 'test'; + $schema = AvroSchema::parse('{"type": "string"}'); + $expectedRequest = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + + $container = []; + $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); + + self::assertEquals(2, $this->registry->schemaId($subject, $schema)); + $this->assertRequestCallable($expectedRequest)($container[0]['request']); + } + + /** + * @test + * @throws SchemaRegistryException + * @throws AvroSchemaParseException + */ + public function it_can_get_a_schema_for_id(): void + { + $responses = [ + new Response(200, [], '{"schema": "\"string\""}') + ]; + $schema = AvroSchema::parse('"string"'); + $expectedRequest = schemaRequest(validateSchemaId(1)); + + $container = []; + $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); + + self::assertEquals($schema, $this->registry->schemaForId(1)); + $this->assertRequestCallable($expectedRequest)($container[0]['request']); + } + + /** + * @test + * @throws SchemaRegistryException + * @throws AvroSchemaParseException + */ + public function it_can_get_a_schema_for_subject_and_version(): void + { + $responses = [ + new Response(200, [], '{"schema": "\"string\""}') + ]; + $subject = 'test'; + $version = 2; + $schema = AvroSchema::parse('{"type": "string"}'); + $expectedRequest = singleSubjectVersionRequest($subject, validateVersionId($version)); + + $container = []; + $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); + + self::assertEquals($schema, $this->registry->schemaForSubjectAndVersion($subject, $version)); + $this->assertRequestCallable($expectedRequest)($container[0]['request']); + } + + /** + * @test + * @throws SchemaRegistryException + * @throws AvroSchemaParseException + */ + public function it_can_get_the_schema_version(): void + { + $responses = [ + new Response(200, [], '{"version": 3}') + ]; + $subject = 'test'; + $schema = AvroSchema::parse('{"type": "string"}'); + $expectedRequest = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + + $container = []; + $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); + + self::assertEquals(3, $this->registry->schemaVersion($subject, $schema)); + $this->assertRequestCallable($expectedRequest)($container[0]['request']); + } + + /** + * @test + * @throws SchemaRegistryException + * @throws AvroSchemaParseException + */ + public function it_can_get_the_latest_version(): void + { + $responses = [ + new Response(200, [], '{"schema": "\"string\""}') + ]; + + $subject = 'test'; + $schema = AvroSchema::parse('{"type": "string"}'); + $expectedRequest = singleSubjectVersionRequest($subject, VERSION_LATEST); + + $container = []; + $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); + + self::assertEquals($schema, $this->registry->latestVersion($subject)); + $this->assertRequestCallable($expectedRequest)($container[0]['request']); + } + + /** + * @test + * @throws SchemaRegistryException + */ + public function it_will_throw_exceptions(): void + { + $this->expectException(SchemaNotFoundException::class); + + $responses = [ + new Response( + 404, + [], + sprintf('{"error_code": %d, "message": "test"}', SchemaNotFoundException::ERROR_CODE) + ) + ]; + + $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses)); + + $this->registry->schemaForId(1); + } + + /** + * @param ResponseInterface[] $responses + * @param array $container + * + * @return Client + */ + private function clientWithMockResponses(array $responses, array &$container = []): Client + { + $history = Middleware::history($container); + + $mockHandler = new MockHandler($responses); + $stack = HandlerStack::create($mockHandler); + $stack->push($history); + + return new Client(['handler' => $stack]); + } + + private function assertRequestCallable(RequestInterface $expectedRequest): callable + { + return function (RequestInterface $actual) use ($expectedRequest) { + $this->assertEquals($expectedRequest->getUri(), $actual->getUri()); + $this->assertEquals($expectedRequest->getHeader(ACCEPT), $actual->getHeader(ACCEPT)); + $this->assertEquals($expectedRequest->getHeader(CONTENT_TYPE), $actual->getHeader(CONTENT_TYPE)); + $this->assertEquals($expectedRequest->getMethod(), $actual->getMethod()); + $this->assertEquals($expectedRequest->getBody()->getContents(), $actual->getBody()->getContents()); + + return $actual; + }; + } +} diff --git a/test/Requests/FunctionsTest.php b/test/Requests/FunctionsTest.php index 30f1e55..9e895e7 100644 --- a/test/Requests/FunctionsTest.php +++ b/test/Requests/FunctionsTest.php @@ -1,4 +1,4 @@ - Date: Sat, 3 Oct 2020 00:18:09 +0200 Subject: [PATCH 03/16] [FT] Moved constants and functions into class containers (#48) --- composer.json | 6 +- src/Constants.php | 29 ++ src/Constants/Constants.php | 16 - src/Exception/ExceptionMap.php | 4 +- src/Json.php | 73 +++++ src/Registry/GuzzlePromiseAsyncRegistry.php | 62 +--- src/Registry/Psr18SyncRegistry.php | 35 +-- src/Requests.php | 216 ++++++++++++++ src/Requests/Functions.php | 278 ------------------ test/IntegrationTest.php | 91 +++--- .../GuzzlePromiseAsyncRegistryTest.php | 27 +- test/Registry/Psr18SyncRegistryTest.php | 27 +- .../{FunctionsTest.php => RequestsTest.php} | 176 +++++------ 13 files changed, 488 insertions(+), 552 deletions(-) create mode 100644 src/Constants.php delete mode 100644 src/Constants/Constants.php create mode 100644 src/Json.php create mode 100644 src/Requests.php delete mode 100644 src/Requests/Functions.php rename test/Requests/{FunctionsTest.php => RequestsTest.php} (54%) diff --git a/composer.json b/composer.json index 321702b..853bf01 100644 --- a/composer.json +++ b/composer.json @@ -37,11 +37,7 @@ "autoload": { "psr-4": { "FlixTech\\SchemaRegistryApi\\": "src/" - }, - "files": [ - "src/Requests/Functions.php", - "src/Constants/Constants.php" - ] + } }, "autoload-dev": { "psr-4": { diff --git a/src/Constants.php b/src/Constants.php new file mode 100644 index 0000000..a00992b --- /dev/null +++ b/src/Constants.php @@ -0,0 +1,29 @@ + 'application/vnd.schemaregistry.v1+json']; + public const CONTENT_TYPE_HEADER = [self::CONTENT_TYPE => 'application/vnd.schemaregistry.v1+json']; + public const CONTENT_TYPE = 'Content-Type'; + + private function __construct() + { + } + + private function __clone() + { + } +} diff --git a/src/Constants/Constants.php b/src/Constants/Constants.php deleted file mode 100644 index 5dd2e99..0000000 --- a/src/Constants/Constants.php +++ /dev/null @@ -1,16 +0,0 @@ - 'application/vnd.schemaregistry.v1+json']; -const CONTENT_TYPE_HEADER_KEY = 'Content-Type'; -const CONTENT_TYPE_HEADER = [CONTENT_TYPE_HEADER_KEY => 'application/vnd.schemaregistry.v1+json']; diff --git a/src/Exception/ExceptionMap.php b/src/Exception/ExceptionMap.php index 81aceb4..a84590f 100644 --- a/src/Exception/ExceptionMap.php +++ b/src/Exception/ExceptionMap.php @@ -5,9 +5,9 @@ namespace FlixTech\SchemaRegistryApi\Exception; use Exception; +use FlixTech\SchemaRegistryApi\Json; use Psr\Http\Message\ResponseInterface; use function array_key_exists; -use function FlixTech\SchemaRegistryApi\Requests\jsonDecode; use function sprintf; final class ExceptionMap @@ -89,7 +89,7 @@ public function hasMappableError(ResponseInterface $response): bool private function guardAgainstMissingErrorCode(ResponseInterface $response): array { try { - $decodedBody = jsonDecode((string) $response->getBody()); + $decodedBody = Json::jsonDecode((string)$response->getBody()); if (!is_array($decodedBody) || !array_key_exists(self::ERROR_CODE_FIELD_NAME, $decodedBody)) { throw new RuntimeException( diff --git a/src/Json.php b/src/Json.php new file mode 100644 index 0000000..7e70529 --- /dev/null +++ b/src/Json.php @@ -0,0 +1,73 @@ +isJsonString('$schema must be a valid JSON string'); + + return $schema; + } + + /** + * @param string $jsonString + * @param int $depth + * + * @return mixed + * + * @throws JsonException + */ + public static function jsonDecode(string $jsonString, int $depth = 512) + { + return json_decode($jsonString, true, $depth, JSON_THROW_ON_ERROR); + } + + /** + * @param mixed $data + * + * @return string + * + * @throws JsonException + */ + public static function jsonEncode($data): string + { + return json_encode($data, JSON_THROW_ON_ERROR); + } + + /** + * @param ResponseInterface $response + * + * @return array + */ + public static function decodeResponse(ResponseInterface $response): array + { + $body = (string)$response->getBody(); + + try { + return Json::jsonDecode($body); + } catch (JsonException $e) { + throw new InvalidArgumentException( + sprintf('%s - with content "%s"', $e->getMessage(), $body), + $e->getCode(), + $e + ); + } + } + + private function __clone() + { + } +} diff --git a/src/Registry/GuzzlePromiseAsyncRegistry.php b/src/Registry/GuzzlePromiseAsyncRegistry.php index 470249b..2bf6123 100644 --- a/src/Registry/GuzzlePromiseAsyncRegistry.php +++ b/src/Registry/GuzzlePromiseAsyncRegistry.php @@ -7,23 +7,18 @@ use AvroSchema; use Closure; use FlixTech\SchemaRegistryApi\AsynchronousRegistry; +use FlixTech\SchemaRegistryApi\Constants; use FlixTech\SchemaRegistryApi\Exception\ExceptionMap; use FlixTech\SchemaRegistryApi\Schema\AvroReference; use FlixTech\SchemaRegistryApi\Exception\RuntimeException; use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; +use FlixTech\SchemaRegistryApi\Json; +use FlixTech\SchemaRegistryApi\Requests; use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Promise\PromiseInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use const FlixTech\SchemaRegistryApi\Constants\VERSION_LATEST; -use function FlixTech\SchemaRegistryApi\Requests\checkIfSubjectHasSchemaRegisteredRequest; -use function FlixTech\SchemaRegistryApi\Requests\decodeResponse; -use function FlixTech\SchemaRegistryApi\Requests\registerNewSchemaVersionWithSubjectRequest; -use function FlixTech\SchemaRegistryApi\Requests\schemaRequest; -use function FlixTech\SchemaRegistryApi\Requests\singleSubjectVersionRequest; -use function FlixTech\SchemaRegistryApi\Requests\validateSchemaId; -use function FlixTech\SchemaRegistryApi\Requests\validateVersionId; class GuzzlePromiseAsyncRegistry implements AsynchronousRegistry { @@ -71,10 +66,10 @@ public function __construct(ClientInterface $client) */ public function register(string $subject, AvroSchema $schema, AvroReference ...$references): PromiseInterface { - $request = registerNewSchemaVersionWithSubjectRequest((string) $schema, $subject, ...$references); + $request = Requests::registerNewSchemaVersionWithSubjectRequest((string) $schema, $subject, ...$references); $onFulfilled = function (ResponseInterface $response) { - return $this->getJsonFromResponseBody($response)['id']; + return Json::decodeResponse($response)['id']; }; return $this->makeRequest($request, $onFulfilled); @@ -87,10 +82,10 @@ public function register(string $subject, AvroSchema $schema, AvroReference ...$ */ public function schemaId(string $subject, AvroSchema $schema): PromiseInterface { - $request = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + $request = Requests::checkIfSubjectHasSchemaRegisteredRequest($subject, (string)$schema); $onFulfilled = function (ResponseInterface $response) { - return $this->getJsonFromResponseBody($response)['id']; + return Json::decodeResponse($response)['id']; }; return $this->makeRequest($request, $onFulfilled); @@ -103,11 +98,11 @@ public function schemaId(string $subject, AvroSchema $schema): PromiseInterface */ public function schemaForId(int $schemaId): PromiseInterface { - $request = schemaRequest(validateSchemaId($schemaId)); + $request = Requests::schemaRequest(Requests::validateSchemaId($schemaId)); $onFulfilled = function (ResponseInterface $response) { return AvroSchema::parse( - $this->getJsonFromResponseBody($response)['schema'] + Json::decodeResponse($response)['schema'] ); }; @@ -121,11 +116,11 @@ public function schemaForId(int $schemaId): PromiseInterface */ public function schemaForSubjectAndVersion(string $subject, int $version): PromiseInterface { - $request = singleSubjectVersionRequest($subject, validateVersionId($version)); + $request = Requests::singleSubjectVersionRequest($subject, Requests::validateVersionId($version)); $onFulfilled = function (ResponseInterface $response) { return AvroSchema::parse( - $this->getJsonFromResponseBody($response)['schema'] + Json::decodeResponse($response)['schema'] ); }; @@ -139,10 +134,10 @@ public function schemaForSubjectAndVersion(string $subject, int $version): Promi */ public function schemaVersion(string $subject, AvroSchema $schema): PromiseInterface { - $request = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + $request = Requests::checkIfSubjectHasSchemaRegisteredRequest($subject, (string)$schema); $onFulfilled = function (ResponseInterface $response) { - return $this->getJsonFromResponseBody($response)['version']; + return Json::decodeResponse($response)['version']; }; return $this->makeRequest($request, $onFulfilled); @@ -155,11 +150,11 @@ public function schemaVersion(string $subject, AvroSchema $schema): PromiseInter */ public function latestVersion(string $subject): PromiseInterface { - $request = singleSubjectVersionRequest($subject, VERSION_LATEST); + $request = Requests::singleSubjectVersionRequest($subject, Constants::VERSION_LATEST); $onFulfilled = function (ResponseInterface $response) { return AvroSchema::parse( - $this->getJsonFromResponseBody($response)['schema'] + Json::decodeResponse($response)['schema'] ); }; @@ -178,31 +173,4 @@ private function makeRequest(RequestInterface $request, callable $onFulfilled): ->sendAsync($request) ->then($onFulfilled, $this->rejectedCallback); } - - /** - * @param ResponseInterface $response - * @return array - */ - private function getJsonFromResponseBody(ResponseInterface $response): array - { - $body = (string) $response->getBody(); - - try { - $decoded = \GuzzleHttp\json_decode($body, true); - - if (!is_array($decoded)) { - throw new InvalidArgumentException( - sprintf('response content "%s" is not a valid json object', $body) - ); - } - - return $decoded; - } catch (InvalidArgumentException $e) { - throw new InvalidArgumentException( - sprintf('%s - with content "%s"', $e->getMessage(), $body), - $e->getCode(), - $e - ); - } - } } diff --git a/src/Registry/Psr18SyncRegistry.php b/src/Registry/Psr18SyncRegistry.php index 90c7fbd..674329d 100644 --- a/src/Registry/Psr18SyncRegistry.php +++ b/src/Registry/Psr18SyncRegistry.php @@ -6,23 +6,18 @@ use AvroSchema; use AvroSchemaParseException; +use FlixTech\SchemaRegistryApi\Constants; use FlixTech\SchemaRegistryApi\Exception\ExceptionMap; use FlixTech\SchemaRegistryApi\Exception\InvalidAvroSchemaException; use FlixTech\SchemaRegistryApi\Exception\RuntimeException; use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; +use FlixTech\SchemaRegistryApi\Json; +use FlixTech\SchemaRegistryApi\Requests; use FlixTech\SchemaRegistryApi\SynchronousRegistry; use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use const FlixTech\SchemaRegistryApi\Constants\VERSION_LATEST; -use function FlixTech\SchemaRegistryApi\Requests\checkIfSubjectHasSchemaRegisteredRequest; -use function FlixTech\SchemaRegistryApi\Requests\decodeResponse; -use function FlixTech\SchemaRegistryApi\Requests\registerNewSchemaVersionWithSubjectRequest; -use function FlixTech\SchemaRegistryApi\Requests\schemaRequest; -use function FlixTech\SchemaRegistryApi\Requests\singleSubjectVersionRequest; -use function FlixTech\SchemaRegistryApi\Requests\validateSchemaId; -use function FlixTech\SchemaRegistryApi\Requests\validateVersionId; class Psr18SyncRegistry implements SynchronousRegistry { @@ -44,62 +39,62 @@ public function __construct(ClientInterface $client) public function register(string $subject, AvroSchema $schema): int { - $request = registerNewSchemaVersionWithSubjectRequest((string) $schema, $subject); + $request = Requests::registerNewSchemaVersionWithSubjectRequest((string)$schema, $subject); $response = $this->makeRequest($request); $this->guardAgainstErrorResponse($response); - return decodeResponse($response)['id']; + return Json::decodeResponse($response)['id']; } public function schemaVersion(string $subject, AvroSchema $schema): int { - $request = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + $request = Requests::checkIfSubjectHasSchemaRegisteredRequest($subject, (string)$schema); $response = $this->makeRequest($request); $this->guardAgainstErrorResponse($response); - return decodeResponse($response)['version']; + return Json::decodeResponse($response)['version']; } public function latestVersion(string $subject): AvroSchema { - $request = singleSubjectVersionRequest($subject, VERSION_LATEST); + $request = Requests::singleSubjectVersionRequest($subject, Constants::VERSION_LATEST); $response = $this->makeRequest($request); $this->guardAgainstErrorResponse($response); - return $this->parseAvroSchema(decodeResponse($response)['schema']); + return $this->parseAvroSchema(Json::decodeResponse($response)['schema']); } public function schemaId(string $subject, AvroSchema $schema): int { - $request = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + $request = Requests::checkIfSubjectHasSchemaRegisteredRequest($subject, (string)$schema); $response = $this->makeRequest($request); $this->guardAgainstErrorResponse($response); - return decodeResponse($response)['id']; + return Json::decodeResponse($response)['id']; } public function schemaForId(int $schemaId): AvroSchema { - $request = schemaRequest(validateSchemaId($schemaId)); + $request = Requests::schemaRequest(Requests::validateSchemaId($schemaId)); $response = $this->makeRequest($request); $this->guardAgainstErrorResponse($response); - return $this->parseAvroSchema(decodeResponse($response)['schema']); + return $this->parseAvroSchema(Json::decodeResponse($response)['schema']); } public function schemaForSubjectAndVersion(string $subject, int $version): AvroSchema { - $request = singleSubjectVersionRequest($subject, validateVersionId($version)); + $request = Requests::singleSubjectVersionRequest($subject, Requests::validateVersionId($version)); $response = $this->makeRequest($request); $this->guardAgainstErrorResponse($response); - return $this->parseAvroSchema(decodeResponse($response)['schema']); + return $this->parseAvroSchema(Json::decodeResponse($response)['schema']); } /** diff --git a/src/Requests.php b/src/Requests.php new file mode 100644 index 0000000..fded789 --- /dev/null +++ b/src/Requests.php @@ -0,0 +1,216 @@ + Json::jsonEncode($decoded)]); + } + + public static function registerNewSchemaVersionWithSubjectRequest(string $schema, string $subjectName): RequestInterface + { + return new Request( + 'POST', + Utils::uriFor("/subjects/$subjectName/versions"), + Constants::CONTENT_TYPE_HEADER + Constants::ACCEPT_HEADER, + self::prepareJsonSchemaForTransfer(Json::validateStringAsJson($schema)) + ); + } + + public static function checkSchemaCompatibilityAgainstVersionRequest(string $schema, string $subjectName, string $versionId): RequestInterface + { + return new Request( + 'POST', + Utils::uriFor("/compatibility/subjects/$subjectName/versions/$versionId"), + Constants::CONTENT_TYPE_HEADER + Constants::ACCEPT_HEADER, + self::prepareJsonSchemaForTransfer(Json::validateStringAsJson($schema)) + ); + } + + public static function checkIfSubjectHasSchemaRegisteredRequest(string $subjectName, string $schema): RequestInterface + { + return new Request( + 'POST', + Utils::uriFor("/subjects/$subjectName"), + Constants::CONTENT_TYPE_HEADER + Constants::ACCEPT_HEADER, + self::prepareJsonSchemaForTransfer(Json::validateStringAsJson($schema)) + ); + } + + public static function schemaRequest(string $id): RequestInterface + { + return new Request( + 'GET', + Utils::uriFor("/schemas/ids/$id"), + Constants::ACCEPT_HEADER + ); + } + + public static function defaultCompatibilityLevelRequest(): RequestInterface + { + return new Request( + 'GET', + '/config', + Constants::ACCEPT_HEADER + ); + } + + public static function validateCompatibilityLevel(string $compatibilityVersion): string + { + $compatibilities = [ + Constants::COMPATIBILITY_NONE, + Constants::COMPATIBILITY_BACKWARD, + Constants::COMPATIBILITY_BACKWARD_TRANSITIVE, + Constants::COMPATIBILITY_FORWARD, + Constants::COMPATIBILITY_FORWARD_TRANSITIVE, + Constants::COMPATIBILITY_FULL, + Constants::COMPATIBILITY_FULL_TRANSITIVE, + + ]; + Assert::that($compatibilityVersion)->inArray( + $compatibilities, + '$level must be one of ' . implode(', ', $compatibilities) + ); + + return $compatibilityVersion; + } + + public static function prepareCompatibilityLevelForTransport(string $compatibilityLevel): string + { + return Json::jsonEncode(['compatibility' => $compatibilityLevel]); + } + + public static function changeDefaultCompatibilityLevelRequest(string $level): RequestInterface + { + return new Request( + 'PUT', + '/config', + Constants::ACCEPT_HEADER, + self::prepareCompatibilityLevelForTransport(self::validateCompatibilityLevel($level)) + ); + } + + public static function subjectCompatibilityLevelRequest(string $subjectName): RequestInterface + { + return new Request( + 'GET', + Utils::uriFor("/config/$subjectName"), + Constants::ACCEPT_HEADER + ); + } + + public static function changeSubjectCompatibilityLevelRequest(string $subjectName, string $level): RequestInterface + { + return new Request( + 'PUT', + Utils::uriFor("/config/$subjectName"), + Constants::ACCEPT_HEADER, + self::prepareCompatibilityLevelForTransport(self::validateCompatibilityLevel($level)) + ); + } + + /** + * @param int|string $versionId + * @return string + */ + public static function validateVersionId($versionId): string + { + if (Constants::VERSION_LATEST !== $versionId) { + Assert::that($versionId) + ->integerish('$versionId must be an integer of type int or string') + ->between(1, 2 ** 31 - 1, '$versionId must be between 1 and 2^31 - 1'); + } + + return (string)$versionId; + } + + /** + * @param int|string $schemaId + * @return string + */ + public static function validateSchemaId($schemaId): string + { + Assert::that($schemaId) + ->integerish('$schemaId must be an integer value of type int or string') + ->greaterThan(0, '$schemaId must be greater than 0'); + + return (string)$schemaId; + } + + /** + * @param string $subjectName + * @return RequestInterface + */ + public static function deleteSubjectRequest(string $subjectName): RequestInterface + { + return new Request( + 'DELETE', + Utils::uriFor("/subjects/$subjectName"), + Constants::ACCEPT_HEADER + ); + } + + /** + * @param string $subjectName + * @param string $versionId + * @return RequestInterface + */ + public static function deleteSubjectVersionRequest(string $subjectName, string $versionId): RequestInterface + { + return new Request( + 'DELETE', + Utils::uriFor("/subjects/$subjectName/versions/$versionId"), + Constants::ACCEPT_HEADER + ); + } + + private function __clone() + { + } +} diff --git a/src/Requests/Functions.php b/src/Requests/Functions.php deleted file mode 100644 index a7b9884..0000000 --- a/src/Requests/Functions.php +++ /dev/null @@ -1,278 +0,0 @@ - - */ -function decodeResponse(ResponseInterface $response): array -{ - $body = (string) $response->getBody(); - - try { - return jsonDecode($body); - } catch (JsonException $e) { - throw new InvalidArgumentException( - sprintf('%s - with content "%s"', $e->getMessage(), $body), - $e->getCode(), - $e - ); - } -} - -function allSubjectsRequest(): RequestInterface -{ - return new Request( - 'GET', - 'subjects', - ACCEPT_HEADER - ); -} - -function allSubjectVersionsRequest(string $subjectName): RequestInterface -{ - return new Request( - 'GET', - sprintf('subjects/%s/versions', $subjectName), - ACCEPT_HEADER - ); -} - -function singleSubjectVersionRequest(string $subjectName, string $versionId): RequestInterface -{ - return new Request( - 'GET', - sprintf( - 'subjects/%s/versions/%s', - $subjectName, - $versionId, - ), - ACCEPT_HEADER - ); -} - -function registerNewSchemaVersionWithSubjectRequest(string $schema, string $subjectName, AvroReference ...$references): RequestInterface -{ - return new Request( - 'POST', - sprintf('subjects/%s/versions', $subjectName), - CONTENT_TYPE_HEADER + ACCEPT_HEADER, - prepareJsonSchemaForTransfer(validateSchemaStringAsJson($schema), ...$references) - ); -} - -function checkSchemaCompatibilityAgainstVersionRequest(string $schema, string $subjectName, string $versionId): RequestInterface -{ - return new Request( - 'POST', - sprintf( - 'compatibility/subjects/%s/versions/%s', - $subjectName, - $versionId, - ), - CONTENT_TYPE_HEADER + ACCEPT_HEADER, - prepareJsonSchemaForTransfer(validateSchemaStringAsJson($schema)) - ); -} - -function checkIfSubjectHasSchemaRegisteredRequest(string $subjectName, string $schema): RequestInterface -{ - return new Request( - 'POST', - sprintf('subjects/%s', $subjectName), - CONTENT_TYPE_HEADER + ACCEPT_HEADER, - prepareJsonSchemaForTransfer(validateSchemaStringAsJson($schema)) - ); -} - -function schemaRequest(string $id): RequestInterface -{ - return new Request( - 'GET', - sprintf('schemas/ids/%s', $id), - ACCEPT_HEADER - ); -} - -function defaultCompatibilityLevelRequest(): RequestInterface -{ - return new Request( - 'GET', - 'config', - ACCEPT_HEADER - ); -} - -function changeDefaultCompatibilityLevelRequest(string $level): RequestInterface -{ - return new Request( - 'PUT', - 'config', - ACCEPT_HEADER, - prepareCompatibilityLevelForTransport(validateCompatibilityLevel($level)) - ); -} - -function subjectCompatibilityLevelRequest(string $subjectName): RequestInterface -{ - return new Request( - 'GET', - sprintf('config/%s', $subjectName), - ACCEPT_HEADER - ); -} - -function changeSubjectCompatibilityLevelRequest(string $subjectName, string $level): RequestInterface -{ - return new Request( - 'PUT', - sprintf('config/%s', $subjectName), - ACCEPT_HEADER, - prepareCompatibilityLevelForTransport(validateCompatibilityLevel($level)) - ); -} - -/** - * @param int|string $versionId - * @return string - */ -function validateVersionId($versionId): string -{ - if (VERSION_LATEST !== $versionId) { - Assert::that($versionId) - ->integerish('$versionId must be an integer of type int or string') - ->between(1, 2 ** 31 - 1, '$versionId must be between 1 and 2^31 - 1'); - } - - return (string) $versionId; -} - -function validateSchemaStringAsJson(string $schema): string -{ - Assert::that($schema)->isJsonString('$schema must be a valid JSON string'); - - return $schema; -} - -function prepareJsonSchemaForTransfer(string $schema, AvroReference ...$references): string -{ - $return = [ - 'schema' => $schema - ]; - - return !$references - ? \GuzzleHttp\json_encode($return) - : \GuzzleHttp\json_encode(array_merge($return, ['references' => $references])); -} - -function validateCompatibilityLevel(string $compatibilityVersion): string -{ - $compatibilities = [ - COMPATIBILITY_NONE, - COMPATIBILITY_BACKWARD, - COMPATIBILITY_BACKWARD_TRANSITIVE, - COMPATIBILITY_FORWARD, - COMPATIBILITY_FORWARD_TRANSITIVE, - COMPATIBILITY_FULL, - COMPATIBILITY_FULL_TRANSITIVE, - - ]; - Assert::that($compatibilityVersion)->inArray( - $compatibilities, - '$level must be one of ' . implode(', ', $compatibilities) - ); - - return $compatibilityVersion; -} - -function prepareCompatibilityLevelForTransport(string $compatibilityLevel): string -{ - return \GuzzleHttp\json_encode(['compatibility' => $compatibilityLevel]); -} - -/** - * @param int|string $schemaId - * @return string - */ -function validateSchemaId($schemaId): string -{ - Assert::that($schemaId) - ->integerish('$schemaId must be an integer value of type int or string') - ->greaterThan(0, '$schemaId must be greater than 0'); - - return (string) $schemaId; -} - -/** - * @param string $subjectName - * @return RequestInterface - */ -function deleteSubjectRequest(string $subjectName): RequestInterface -{ - return new Request( - 'DELETE', - sprintf('subjects/%s', $subjectName), - ACCEPT_HEADER - ); -} - -/** - * @param string $subjectName - * @param string $versionId - * @return RequestInterface - */ -function deleteSubjectVersionRequest(string $subjectName, string $versionId): RequestInterface -{ - return new Request( - 'DELETE', - sprintf('subjects/%s/versions/%s', $subjectName, $versionId), - ACCEPT_HEADER - ); -} diff --git a/test/IntegrationTest.php b/test/IntegrationTest.php index 421ea06..ff7a339 100644 --- a/test/IntegrationTest.php +++ b/test/IntegrationTest.php @@ -4,6 +4,7 @@ namespace FlixTech\SchemaRegistryApi\Test; +use FlixTech\SchemaRegistryApi\Constants; use FlixTech\SchemaRegistryApi\Exception\ExceptionMap; use FlixTech\SchemaRegistryApi\Exception\IncompatibleAvroSchemaException; use FlixTech\SchemaRegistryApi\Exception\InvalidAvroSchemaException; @@ -11,26 +12,14 @@ use FlixTech\SchemaRegistryApi\Exception\SchemaNotFoundException; use FlixTech\SchemaRegistryApi\Exception\SubjectNotFoundException; use FlixTech\SchemaRegistryApi\Exception\VersionNotFoundException; +use FlixTech\SchemaRegistryApi\Json; +use FlixTech\SchemaRegistryApi\Requests; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Psr7\Utils; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; -use const FlixTech\SchemaRegistryApi\Constants\COMPATIBILITY_BACKWARD; -use const FlixTech\SchemaRegistryApi\Constants\COMPATIBILITY_FORWARD; -use const FlixTech\SchemaRegistryApi\Constants\COMPATIBILITY_FULL; -use const FlixTech\SchemaRegistryApi\Constants\VERSION_LATEST; -use function FlixTech\SchemaRegistryApi\Requests\allSubjectsRequest; -use function FlixTech\SchemaRegistryApi\Requests\allSubjectVersionsRequest; -use function FlixTech\SchemaRegistryApi\Requests\changeDefaultCompatibilityLevelRequest; -use function FlixTech\SchemaRegistryApi\Requests\changeSubjectCompatibilityLevelRequest; -use function FlixTech\SchemaRegistryApi\Requests\checkIfSubjectHasSchemaRegisteredRequest; -use function FlixTech\SchemaRegistryApi\Requests\checkSchemaCompatibilityAgainstVersionRequest; -use function FlixTech\SchemaRegistryApi\Requests\defaultCompatibilityLevelRequest; -use function FlixTech\SchemaRegistryApi\Requests\registerNewSchemaVersionWithSubjectRequest; -use function FlixTech\SchemaRegistryApi\Requests\schemaRequest; -use function FlixTech\SchemaRegistryApi\Requests\singleSubjectVersionRequest; -use function FlixTech\SchemaRegistryApi\Requests\subjectCompatibilityLevelRequest; /** * @group integration @@ -118,36 +107,36 @@ protected function setUp(): void public function managing_subjects_and_versions(): void { $this->client - ->sendAsync(allSubjectsRequest()) + ->sendAsync(Requests::allSubjectsRequest()) ->then( function (ResponseInterface $request) { - $this->assertEmpty(\GuzzleHttp\json_decode($request->getBody()->getContents(), true)); + $this->assertEmpty(Json::jsonDecode($request->getBody()->getContents())); } )->wait(); $this->client - ->sendAsync(registerNewSchemaVersionWithSubjectRequest($this->baseSchema, self::SUBJECT_NAME)) + ->sendAsync(Requests::registerNewSchemaVersionWithSubjectRequest($this->baseSchema, self::SUBJECT_NAME)) ->then( function (ResponseInterface $request) { - $this->assertEquals(1, \GuzzleHttp\json_decode($request->getBody()->getContents(), true)['id']); + $this->assertEquals(1, Json::jsonDecode($request->getBody()->getContents())['id']); } )->wait(); $this->client - ->sendAsync(schemaRequest('1')) + ->sendAsync(Requests::schemaRequest('1')) ->then( function (ResponseInterface $request) { - $decodedBody = \GuzzleHttp\json_decode($request->getBody()->getContents(), true); + $decodedBody = Json::jsonDecode($request->getBody()->getContents()); $this->assertJsonStringEqualsJsonString($this->baseSchema, $decodedBody['schema']); } )->wait(); $this->client - ->sendAsync(checkIfSubjectHasSchemaRegisteredRequest(self::SUBJECT_NAME, $this->baseSchema)) + ->sendAsync(Requests::checkIfSubjectHasSchemaRegisteredRequest(self::SUBJECT_NAME, $this->baseSchema)) ->then( function (ResponseInterface $request) { - $decodedBody = \GuzzleHttp\json_decode($request->getBody()->getContents(), true); + $decodedBody = Json::jsonDecode($request->getBody()->getContents()); $this->assertEquals(1, $decodedBody['id']); $this->assertEquals(1, $decodedBody['version']); @@ -157,10 +146,10 @@ function (ResponseInterface $request) { )->wait(); $this->client - ->sendAsync(singleSubjectVersionRequest(self::SUBJECT_NAME, VERSION_LATEST)) + ->sendAsync(Requests::singleSubjectVersionRequest(self::SUBJECT_NAME, Constants::VERSION_LATEST)) ->then( function (ResponseInterface $request) { - $decodedBody = \GuzzleHttp\json_decode($request->getBody()->getContents(), true); + $decodedBody = Json::jsonDecode($request->getBody()->getContents()); $this->assertEquals(self::SUBJECT_NAME, $decodedBody['subject']); $this->assertEquals(1, $decodedBody['version']); @@ -170,23 +159,23 @@ function (ResponseInterface $request) { )->wait(); $this->client - ->sendAsync(checkSchemaCompatibilityAgainstVersionRequest( + ->sendAsync(Requests::checkSchemaCompatibilityAgainstVersionRequest( $this->compatibleSchemaEvolution, self::SUBJECT_NAME, - VERSION_LATEST + Constants::VERSION_LATEST ))->then( function (ResponseInterface $request) { - $decodedBody = \GuzzleHttp\json_decode($request->getBody()->getContents(), true); + $decodedBody = Json::jsonDecode($request->getBody()->getContents()); $this->assertTrue($decodedBody['is_compatible']); } )->wait(); $this->client - ->sendAsync(checkSchemaCompatibilityAgainstVersionRequest( + ->sendAsync(Requests::checkSchemaCompatibilityAgainstVersionRequest( $this->incompatibleSchemaEvolution, self::SUBJECT_NAME, - VERSION_LATEST + Constants::VERSION_LATEST ))->otherwise( function (RequestException $exception) { $this->assertInstanceOf( @@ -197,7 +186,7 @@ function (RequestException $exception) { )->wait(); $this->client - ->sendAsync(registerNewSchemaVersionWithSubjectRequest($this->invalidSchema, self::SUBJECT_NAME)) + ->sendAsync(Requests::registerNewSchemaVersionWithSubjectRequest($this->invalidSchema, self::SUBJECT_NAME)) ->otherwise( function (RequestException $exception) { $this->assertInstanceOf( @@ -208,7 +197,7 @@ function (RequestException $exception) { )->wait(); $this->client - ->sendAsync(singleSubjectVersionRequest('INVALID', VERSION_LATEST)) + ->sendAsync(Requests::singleSubjectVersionRequest('INVALID', Constants::VERSION_LATEST)) ->otherwise( function (RequestException $exception) { $this->assertInstanceOf( @@ -219,7 +208,7 @@ function (RequestException $exception) { )->wait(); $this->client - ->sendAsync(singleSubjectVersionRequest(self::SUBJECT_NAME, 'INVALID')) + ->sendAsync(Requests::singleSubjectVersionRequest(self::SUBJECT_NAME, 'INVALID')) ->otherwise( function (RequestException $exception) { $this->assertInstanceOf( @@ -230,7 +219,7 @@ function (RequestException $exception) { )->wait(); $this->client - ->sendAsync(singleSubjectVersionRequest(self::SUBJECT_NAME, '5')) + ->sendAsync(Requests::singleSubjectVersionRequest(self::SUBJECT_NAME, '5')) ->otherwise( function (RequestException $exception) { $this->assertInstanceOf( @@ -241,7 +230,7 @@ function (RequestException $exception) { )->wait(); $this->client - ->sendAsync(schemaRequest('6')) + ->sendAsync(Requests::schemaRequest('6')) ->otherwise( function (RequestException $exception) { $this->assertInstanceOf( @@ -252,18 +241,18 @@ function (RequestException $exception) { )->wait(); $this->client - ->sendAsync(registerNewSchemaVersionWithSubjectRequest($this->compatibleSchemaEvolution, self::SUBJECT_NAME)) + ->sendAsync(Requests::registerNewSchemaVersionWithSubjectRequest($this->compatibleSchemaEvolution, self::SUBJECT_NAME)) ->then( function (ResponseInterface $request) { - $this->assertEquals(2, \GuzzleHttp\json_decode($request->getBody()->getContents(), true)['id']); + $this->assertEquals(2, Json::jsonDecode($request->getBody()->getContents())['id']); } )->wait(); $this->client - ->sendAsync(allSubjectVersionsRequest(self::SUBJECT_NAME)) + ->sendAsync(Requests::allSubjectVersionsRequest(self::SUBJECT_NAME)) ->then( function (ResponseInterface $request) { - $this->assertEquals([1, 2], \GuzzleHttp\json_decode($request->getBody()->getContents(), true)); + $this->assertEquals([1, 2], Json::jsonDecode($request->getBody()->getContents())); } )->wait(); } @@ -274,52 +263,52 @@ function (ResponseInterface $request) { public function managing_compatibility_levels(): void { $this->client - ->sendAsync(defaultCompatibilityLevelRequest()) + ->sendAsync(Requests::defaultCompatibilityLevelRequest()) ->then( function (ResponseInterface $request) { - $decodedBody = \GuzzleHttp\json_decode($request->getBody()->getContents(), true); + $decodedBody = Json::jsonDecode($request->getBody()->getContents()); $this->assertEquals( - COMPATIBILITY_BACKWARD, + Constants::COMPATIBILITY_BACKWARD, $decodedBody['compatibilityLevel'] ); } )->wait(); $this->client - ->sendAsync(changeDefaultCompatibilityLevelRequest(COMPATIBILITY_FULL)) + ->sendAsync(Requests::changeDefaultCompatibilityLevelRequest(Constants::COMPATIBILITY_FULL)) ->then( function (ResponseInterface $request) { - $decodedBody = \GuzzleHttp\json_decode($request->getBody()->getContents(), true); + $decodedBody = Json::jsonDecode($request->getBody()->getContents()); $this->assertEquals( - COMPATIBILITY_FULL, + Constants::COMPATIBILITY_FULL, $decodedBody['compatibility'] ); } )->wait(); $this->client - ->sendAsync(changeSubjectCompatibilityLevelRequest(self::SUBJECT_NAME, COMPATIBILITY_FORWARD)) + ->sendAsync(Requests::changeSubjectCompatibilityLevelRequest(self::SUBJECT_NAME, Constants::COMPATIBILITY_FORWARD)) ->then( function (ResponseInterface $request) { - $decodedBody = \GuzzleHttp\json_decode($request->getBody()->getContents(), true); + $decodedBody = Json::jsonDecode($request->getBody()->getContents()); $this->assertEquals( - COMPATIBILITY_FORWARD, + Constants::COMPATIBILITY_FORWARD, $decodedBody['compatibility'] ); } )->wait(); $this->client - ->sendAsync(subjectCompatibilityLevelRequest(self::SUBJECT_NAME)) + ->sendAsync(Requests::subjectCompatibilityLevelRequest(self::SUBJECT_NAME)) ->then( function (ResponseInterface $request) { - $decodedBody = \GuzzleHttp\json_decode($request->getBody()->getContents(), true); + $decodedBody = Json::jsonDecode($request->getBody()->getContents()); $this->assertEquals( - COMPATIBILITY_FORWARD, + Constants::COMPATIBILITY_FORWARD, $decodedBody['compatibilityLevel'] ); } diff --git a/test/Registry/GuzzlePromiseAsyncRegistryTest.php b/test/Registry/GuzzlePromiseAsyncRegistryTest.php index 0b5ac43..287b49a 100644 --- a/test/Registry/GuzzlePromiseAsyncRegistryTest.php +++ b/test/Registry/GuzzlePromiseAsyncRegistryTest.php @@ -7,9 +7,11 @@ use AvroSchema; use AvroSchemaParseException; use Exception; +use FlixTech\SchemaRegistryApi\Constants; use FlixTech\SchemaRegistryApi\Exception\SchemaNotFoundException; use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; use FlixTech\SchemaRegistryApi\Registry\GuzzlePromiseAsyncRegistry; +use FlixTech\SchemaRegistryApi\Requests; use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; @@ -18,15 +20,6 @@ use PHPUnit\Framework\TestCase; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use const FlixTech\SchemaRegistryApi\Constants\ACCEPT; -use const FlixTech\SchemaRegistryApi\Constants\CONTENT_TYPE; -use const FlixTech\SchemaRegistryApi\Constants\VERSION_LATEST; -use function FlixTech\SchemaRegistryApi\Requests\checkIfSubjectHasSchemaRegisteredRequest; -use function FlixTech\SchemaRegistryApi\Requests\registerNewSchemaVersionWithSubjectRequest; -use function FlixTech\SchemaRegistryApi\Requests\schemaRequest; -use function FlixTech\SchemaRegistryApi\Requests\singleSubjectVersionRequest; -use function FlixTech\SchemaRegistryApi\Requests\validateSchemaId; -use function FlixTech\SchemaRegistryApi\Requests\validateVersionId; class GuzzlePromiseAsyncRegistryTest extends TestCase { @@ -48,7 +41,7 @@ public function it_should_register_schemas(): void ]; $subject = 'test'; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = registerNewSchemaVersionWithSubjectRequest((string) $schema, $subject); + $expectedRequest = Requests::registerNewSchemaVersionWithSubjectRequest((string)$schema, $subject); $container = []; $client = $this->clientWithMockResponses($responses, $container); @@ -73,7 +66,7 @@ public function it_can_get_the_schema_id_for_a_schema_and_subject(): void ]; $subject = 'test'; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + $expectedRequest = Requests::checkIfSubjectHasSchemaRegisteredRequest($subject, (string)$schema); $container = []; $this->registry = new GuzzlePromiseAsyncRegistry($this->clientWithMockResponses($responses, $container)); @@ -95,7 +88,7 @@ public function it_can_get_a_schema_for_id(): void new Response(200, [], '{"schema": "\"string\""}') ]; $schema = AvroSchema::parse('"string"'); - $expectedRequest = schemaRequest(validateSchemaId(1)); + $expectedRequest = Requests::schemaRequest(Requests::validateSchemaId(1)); $container = []; $this->registry = new GuzzlePromiseAsyncRegistry($this->clientWithMockResponses($responses, $container)); @@ -119,7 +112,7 @@ public function it_can_get_a_schema_for_subject_and_version(): void $subject = 'test'; $version = 2; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = singleSubjectVersionRequest($subject, validateVersionId($version)); + $expectedRequest = Requests::singleSubjectVersionRequest($subject, Requests::validateVersionId($version)); $container = []; $this->registry = new GuzzlePromiseAsyncRegistry($this->clientWithMockResponses($responses, $container)); @@ -142,7 +135,7 @@ public function it_can_get_the_schema_version(): void ]; $subject = 'test'; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + $expectedRequest = Requests::checkIfSubjectHasSchemaRegisteredRequest($subject, (string)$schema); $container = []; $this->registry = new GuzzlePromiseAsyncRegistry($this->clientWithMockResponses($responses, $container)); @@ -166,7 +159,7 @@ public function it_can_get_the_latest_version(): void $subject = 'test'; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = singleSubjectVersionRequest($subject, VERSION_LATEST); + $expectedRequest = Requests::singleSubjectVersionRequest($subject, Constants::VERSION_LATEST); $container = []; $this->registry = new GuzzlePromiseAsyncRegistry($this->clientWithMockResponses($responses, $container)); @@ -221,8 +214,8 @@ private function assertRequestCallable(RequestInterface $expectedRequest): calla { return function (RequestInterface $actual) use ($expectedRequest) { $this->assertEquals($expectedRequest->getUri(), $actual->getUri()); - $this->assertEquals($expectedRequest->getHeader(ACCEPT), $actual->getHeader(ACCEPT)); - $this->assertEquals($expectedRequest->getHeader(CONTENT_TYPE), $actual->getHeader(CONTENT_TYPE)); + $this->assertEquals($expectedRequest->getHeader(Constants::ACCEPT), $actual->getHeader(Constants::ACCEPT)); + $this->assertEquals($expectedRequest->getHeader(Constants::CONTENT_TYPE), $actual->getHeader(Constants::CONTENT_TYPE)); $this->assertEquals($expectedRequest->getMethod(), $actual->getMethod()); $this->assertEquals($expectedRequest->getBody()->getContents(), $actual->getBody()->getContents()); diff --git a/test/Registry/Psr18SyncRegistryTest.php b/test/Registry/Psr18SyncRegistryTest.php index c800dc0..ceb63c7 100644 --- a/test/Registry/Psr18SyncRegistryTest.php +++ b/test/Registry/Psr18SyncRegistryTest.php @@ -6,9 +6,11 @@ use AvroSchema; use AvroSchemaParseException; +use FlixTech\SchemaRegistryApi\Constants; use FlixTech\SchemaRegistryApi\Exception\SchemaNotFoundException; use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; use FlixTech\SchemaRegistryApi\Registry\Psr18SyncRegistry; +use FlixTech\SchemaRegistryApi\Requests; use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; @@ -17,15 +19,6 @@ use PHPUnit\Framework\TestCase; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use const FlixTech\SchemaRegistryApi\Constants\ACCEPT; -use const FlixTech\SchemaRegistryApi\Constants\CONTENT_TYPE; -use const FlixTech\SchemaRegistryApi\Constants\VERSION_LATEST; -use function FlixTech\SchemaRegistryApi\Requests\checkIfSubjectHasSchemaRegisteredRequest; -use function FlixTech\SchemaRegistryApi\Requests\registerNewSchemaVersionWithSubjectRequest; -use function FlixTech\SchemaRegistryApi\Requests\schemaRequest; -use function FlixTech\SchemaRegistryApi\Requests\singleSubjectVersionRequest; -use function FlixTech\SchemaRegistryApi\Requests\validateSchemaId; -use function FlixTech\SchemaRegistryApi\Requests\validateVersionId; class Psr18SyncRegistryTest extends TestCase { @@ -46,7 +39,7 @@ public function it_should_register_schemas(): void ]; $subject = 'test'; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = registerNewSchemaVersionWithSubjectRequest((string) $schema, $subject); + $expectedRequest = Requests::registerNewSchemaVersionWithSubjectRequest((string)$schema, $subject); $container = []; $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); @@ -67,7 +60,7 @@ public function it_can_get_the_schema_id_for_a_schema_and_subject(): void ]; $subject = 'test'; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + $expectedRequest = Requests::checkIfSubjectHasSchemaRegisteredRequest($subject, (string)$schema); $container = []; $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); @@ -87,7 +80,7 @@ public function it_can_get_a_schema_for_id(): void new Response(200, [], '{"schema": "\"string\""}') ]; $schema = AvroSchema::parse('"string"'); - $expectedRequest = schemaRequest(validateSchemaId(1)); + $expectedRequest = Requests::schemaRequest(Requests::validateSchemaId(1)); $container = []; $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); @@ -109,7 +102,7 @@ public function it_can_get_a_schema_for_subject_and_version(): void $subject = 'test'; $version = 2; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = singleSubjectVersionRequest($subject, validateVersionId($version)); + $expectedRequest = Requests::singleSubjectVersionRequest($subject, Requests::validateVersionId($version)); $container = []; $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); @@ -130,7 +123,7 @@ public function it_can_get_the_schema_version(): void ]; $subject = 'test'; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = checkIfSubjectHasSchemaRegisteredRequest($subject, (string) $schema); + $expectedRequest = Requests::checkIfSubjectHasSchemaRegisteredRequest($subject, (string)$schema); $container = []; $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); @@ -152,7 +145,7 @@ public function it_can_get_the_latest_version(): void $subject = 'test'; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = singleSubjectVersionRequest($subject, VERSION_LATEST); + $expectedRequest = Requests::singleSubjectVersionRequest($subject, Constants::VERSION_LATEST); $container = []; $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); @@ -203,8 +196,8 @@ private function assertRequestCallable(RequestInterface $expectedRequest): calla { return function (RequestInterface $actual) use ($expectedRequest) { $this->assertEquals($expectedRequest->getUri(), $actual->getUri()); - $this->assertEquals($expectedRequest->getHeader(ACCEPT), $actual->getHeader(ACCEPT)); - $this->assertEquals($expectedRequest->getHeader(CONTENT_TYPE), $actual->getHeader(CONTENT_TYPE)); + $this->assertEquals($expectedRequest->getHeader(Constants::ACCEPT), $actual->getHeader(Constants::ACCEPT)); + $this->assertEquals($expectedRequest->getHeader(Constants::CONTENT_TYPE), $actual->getHeader(Constants::CONTENT_TYPE)); $this->assertEquals($expectedRequest->getMethod(), $actual->getMethod()); $this->assertEquals($expectedRequest->getBody()->getContents(), $actual->getBody()->getContents()); diff --git a/test/Requests/FunctionsTest.php b/test/Requests/RequestsTest.php similarity index 54% rename from test/Requests/FunctionsTest.php rename to test/Requests/RequestsTest.php index 9e895e7..0225c5d 100644 --- a/test/Requests/FunctionsTest.php +++ b/test/Requests/RequestsTest.php @@ -4,55 +4,27 @@ namespace FlixTech\SchemaRegistryApi\Test\Requests; +use FlixTech\SchemaRegistryApi\Constants; +use FlixTech\SchemaRegistryApi\Json; +use FlixTech\SchemaRegistryApi\Requests; use FlixTech\SchemaRegistryApi\Schema\AvroName; use FlixTech\SchemaRegistryApi\Schema\AvroReference; use Generator; use InvalidArgumentException; use PHPUnit\Framework\TestCase; -use const FlixTech\SchemaRegistryApi\Constants\ACCEPT_HEADER; -use const FlixTech\SchemaRegistryApi\Constants\ACCEPT_HEADER_KEY; -use const FlixTech\SchemaRegistryApi\Constants\COMPATIBILITY_BACKWARD; -use const FlixTech\SchemaRegistryApi\Constants\COMPATIBILITY_BACKWARD_TRANSITIVE; -use const FlixTech\SchemaRegistryApi\Constants\COMPATIBILITY_FORWARD; -use const FlixTech\SchemaRegistryApi\Constants\COMPATIBILITY_FORWARD_TRANSITIVE; -use const FlixTech\SchemaRegistryApi\Constants\COMPATIBILITY_FULL; -use const FlixTech\SchemaRegistryApi\Constants\COMPATIBILITY_FULL_TRANSITIVE; -use const FlixTech\SchemaRegistryApi\Constants\COMPATIBILITY_NONE; -use const FlixTech\SchemaRegistryApi\Constants\CONTENT_TYPE_HEADER; -use const FlixTech\SchemaRegistryApi\Constants\CONTENT_TYPE_HEADER_KEY; -use const FlixTech\SchemaRegistryApi\Constants\VERSION_LATEST; -use function FlixTech\SchemaRegistryApi\Requests\allSubjectsRequest; -use function FlixTech\SchemaRegistryApi\Requests\allSubjectVersionsRequest; -use function FlixTech\SchemaRegistryApi\Requests\changeDefaultCompatibilityLevelRequest; -use function FlixTech\SchemaRegistryApi\Requests\changeSubjectCompatibilityLevelRequest; -use function FlixTech\SchemaRegistryApi\Requests\checkIfSubjectHasSchemaRegisteredRequest; -use function FlixTech\SchemaRegistryApi\Requests\checkSchemaCompatibilityAgainstVersionRequest; -use function FlixTech\SchemaRegistryApi\Requests\defaultCompatibilityLevelRequest; -use function FlixTech\SchemaRegistryApi\Requests\deleteSubjectRequest; -use function FlixTech\SchemaRegistryApi\Requests\deleteSubjectVersionRequest; -use function FlixTech\SchemaRegistryApi\Requests\prepareCompatibilityLevelForTransport; -use function FlixTech\SchemaRegistryApi\Requests\prepareJsonSchemaForTransfer; -use function FlixTech\SchemaRegistryApi\Requests\registerNewSchemaVersionWithSubjectRequest; -use function FlixTech\SchemaRegistryApi\Requests\schemaRequest; -use function FlixTech\SchemaRegistryApi\Requests\singleSubjectVersionRequest; -use function FlixTech\SchemaRegistryApi\Requests\subjectCompatibilityLevelRequest; -use function FlixTech\SchemaRegistryApi\Requests\validateCompatibilityLevel; -use function FlixTech\SchemaRegistryApi\Requests\validateSchemaId; -use function FlixTech\SchemaRegistryApi\Requests\validateSchemaStringAsJson; -use function FlixTech\SchemaRegistryApi\Requests\validateVersionId; - -class FunctionsTest extends TestCase + +class RequestsTest extends TestCase { /** * @test */ public function it_should_produce_a_Request_to_get_all_subjects(): void { - $request = allSubjectsRequest(); + $request = Requests::allSubjectsRequest(); self::assertEquals('GET', $request->getMethod()); self::assertEquals('subjects', $request->getUri()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } /** @@ -60,11 +32,11 @@ public function it_should_produce_a_Request_to_get_all_subjects(): void */ public function it_should_produce_a_Request_to_get_all_subject_versions(): void { - $request = allSubjectVersionsRequest('test'); + $request = Requests::allSubjectVersionsRequest('test'); self::assertEquals('GET', $request->getMethod()); self::assertEquals('subjects/test/versions', $request->getUri()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } /** @@ -72,11 +44,11 @@ public function it_should_produce_a_Request_to_get_all_subject_versions(): void */ public function it_should_produce_a_Request_to_get_a_specific_subject_version(): void { - $request = singleSubjectVersionRequest('test', '3'); + $request = Requests::singleSubjectVersionRequest('test', '3'); self::assertEquals('GET', $request->getMethod()); self::assertEquals('subjects/test/versions/3', $request->getUri()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } /** @@ -89,12 +61,14 @@ public function it_should_produce_a_Request_to_get_a_specific_subject_version(): */ public function it_should_produce_a_request_to_register_a_new_schema_version(string $initialSchema, string $finalSchema, array $references): void { - $request = registerNewSchemaVersionWithSubjectRequest($initialSchema, 'test', ...$references); + $request = Requests::registerNewSchemaVersionWithSubjectRequest($initialSchema, 'test', ...$references); self::assertEquals('POST', $request->getMethod()); self::assertEquals('subjects/test/versions', $request->getUri()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); - self::assertEquals(CONTENT_TYPE_HEADER[CONTENT_TYPE_HEADER_KEY], $request->getHeader(CONTENT_TYPE_HEADER_KEY)[0]); + self::assertEquals( + [Constants::CONTENT_TYPE => [Constants::CONTENT_TYPE_HEADER[Constants::CONTENT_TYPE]]] + [Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], + $request->getHeaders() + ); self::assertJsonStringEqualsJsonString($finalSchema, $request->getBody()->getContents()); } @@ -136,17 +110,19 @@ public static function dataForRegisteringSchemas(): Generator { */ public function it_should_produce_a_request_to_check_schema_compatibility_against_a_subject_version(): void { - $request = checkSchemaCompatibilityAgainstVersionRequest( + $request = Requests::checkSchemaCompatibilityAgainstVersionRequest( '{"type":"test"}', 'test', - VERSION_LATEST + Constants::VERSION_LATEST ); self::assertEquals('POST', $request->getMethod()); self::assertEquals('compatibility/subjects/test/versions/latest', $request->getUri()); self::assertEquals('{"schema":"{\"type\":\"test\"}"}', $request->getBody()->getContents()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); - self::assertEquals(CONTENT_TYPE_HEADER[CONTENT_TYPE_HEADER_KEY], $request->getHeader(CONTENT_TYPE_HEADER_KEY)[0]); + self::assertEquals( + [Constants::CONTENT_TYPE => [Constants::CONTENT_TYPE_HEADER[Constants::CONTENT_TYPE]]] + [Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], + $request->getHeaders() + ); } /** @@ -154,13 +130,15 @@ public function it_should_produce_a_request_to_check_schema_compatibility_agains */ public function it_should_produce_a_request_to_check_if_a_subject_already_has_a_schema(): void { - $request = checkIfSubjectHasSchemaRegisteredRequest('test', '{"type":"test"}'); + $request = Requests::checkIfSubjectHasSchemaRegisteredRequest('test', '{"type":"test"}'); self::assertEquals('POST', $request->getMethod()); self::assertEquals('subjects/test', $request->getUri()); self::assertEquals('{"schema":"{\"type\":\"test\"}"}', $request->getBody()->getContents()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); - self::assertEquals(CONTENT_TYPE_HEADER[CONTENT_TYPE_HEADER_KEY], $request->getHeader(CONTENT_TYPE_HEADER_KEY)[0]); + self::assertEquals( + [Constants::CONTENT_TYPE => [Constants::CONTENT_TYPE_HEADER[Constants::CONTENT_TYPE]]] + [Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], + $request->getHeaders() + ); } /** @@ -168,11 +146,11 @@ public function it_should_produce_a_request_to_check_if_a_subject_already_has_a_ */ public function it_should_produce_a_request_to_get_a_specific_schema_by_id(): void { - $request = schemaRequest('3'); + $request = Requests::schemaRequest('3'); self::assertEquals('GET', $request->getMethod()); self::assertEquals('schemas/ids/3', $request->getUri()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } /** @@ -180,11 +158,11 @@ public function it_should_produce_a_request_to_get_a_specific_schema_by_id(): vo */ public function it_should_produce_a_request_to_get_the_global_compatibility_level(): void { - $request = defaultCompatibilityLevelRequest(); + $request = Requests::defaultCompatibilityLevelRequest(); self::assertEquals('GET', $request->getMethod()); self::assertEquals('config', $request->getUri()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } /** @@ -192,12 +170,12 @@ public function it_should_produce_a_request_to_get_the_global_compatibility_leve */ public function it_should_produce_a_request_to_change_the_global_compatibility_level(): void { - $request = changeDefaultCompatibilityLevelRequest(COMPATIBILITY_FULL); + $request = Requests::changeDefaultCompatibilityLevelRequest(Constants::COMPATIBILITY_FULL); self::assertEquals('PUT', $request->getMethod()); self::assertEquals('config', $request->getUri()); self::assertEquals('{"compatibility":"FULL"}', $request->getBody()->getContents()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } /** @@ -205,11 +183,11 @@ public function it_should_produce_a_request_to_change_the_global_compatibility_l */ public function it_should_produce_a_request_to_get_the_subject_compatibility_level(): void { - $request = subjectCompatibilityLevelRequest('test'); + $request = Requests::subjectCompatibilityLevelRequest('test'); self::assertEquals('GET', $request->getMethod()); self::assertEquals('config/test', $request->getUri()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } /** @@ -217,12 +195,12 @@ public function it_should_produce_a_request_to_get_the_subject_compatibility_lev */ public function it_should_produce_a_request_to_change_the_subject_compatibility_level(): void { - $request = changeSubjectCompatibilityLevelRequest('test', COMPATIBILITY_FORWARD); + $request = Requests::changeSubjectCompatibilityLevelRequest('test', Constants::COMPATIBILITY_FORWARD); self::assertEquals('PUT', $request->getMethod()); self::assertEquals('config/test', $request->getUri()); self::assertEquals('{"compatibility":"FORWARD"}', $request->getBody()->getContents()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } /** @@ -233,9 +211,9 @@ public function it_should_validate_a_JSON_schema_string(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$schema must be a valid JSON string'); - self::assertJsonStringEqualsJsonString('{"type":"test"}', validateSchemaStringAsJson('{"type":"test"}')); + self::assertJsonStringEqualsJsonString('{"type":"test"}', Json::validateStringAsJson('{"type":"test"}')); - validateSchemaStringAsJson('INVALID'); + Json::validateStringAsJson('INVALID'); } /** @@ -245,12 +223,12 @@ public function it_should_prepare_a_JSON_schema_for_transfer(): void { self::assertJsonStringEqualsJsonString( '{"schema":"{\"type\":\"string\"}"}', - prepareJsonSchemaForTransfer('{"type":"string"}') + Requests::prepareJsonSchemaForTransfer('{"type":"string"}') ); self::assertJsonStringEqualsJsonString( '{"schema":"{\"type\": \"string\"}"}', - prepareJsonSchemaForTransfer('{"schema":"{\"type\": \"string\"}"}') + Requests::prepareJsonSchemaForTransfer('{"schema":"{\"type\": \"string\"}"}') ); } @@ -263,35 +241,35 @@ public function it_should_validate_a_compatibility_level_string(): void $this->expectExceptionMessage('$level must be one of NONE, BACKWARD, BACKWARD_TRANSITIVE, FORWARD, FORWARD_TRANSITIVE, FULL, FULL_TRANSITIVE'); self::assertEquals( - COMPATIBILITY_NONE, - validateCompatibilityLevel(COMPATIBILITY_NONE) + Constants::COMPATIBILITY_NONE, + Requests::validateCompatibilityLevel(Constants::COMPATIBILITY_NONE) ); self::assertEquals( - COMPATIBILITY_FULL, - validateCompatibilityLevel(COMPATIBILITY_FULL) + Constants::COMPATIBILITY_FULL, + Requests::validateCompatibilityLevel(Constants::COMPATIBILITY_FULL) ); self::assertEquals( - COMPATIBILITY_FULL_TRANSITIVE, - validateCompatibilityLevel(COMPATIBILITY_FULL_TRANSITIVE) + Constants::COMPATIBILITY_FULL_TRANSITIVE, + Requests::validateCompatibilityLevel(Constants::COMPATIBILITY_FULL_TRANSITIVE) ); self::assertEquals( - COMPATIBILITY_BACKWARD, - validateCompatibilityLevel(COMPATIBILITY_BACKWARD) + Constants::COMPATIBILITY_BACKWARD, + Requests::validateCompatibilityLevel(Constants::COMPATIBILITY_BACKWARD) ); self::assertEquals( - COMPATIBILITY_BACKWARD_TRANSITIVE, - validateCompatibilityLevel(COMPATIBILITY_BACKWARD_TRANSITIVE) + Constants::COMPATIBILITY_BACKWARD_TRANSITIVE, + Requests::validateCompatibilityLevel(Constants::COMPATIBILITY_BACKWARD_TRANSITIVE) ); self::assertEquals( - COMPATIBILITY_FORWARD, - validateCompatibilityLevel(COMPATIBILITY_FORWARD) + Constants::COMPATIBILITY_FORWARD, + Requests::validateCompatibilityLevel(Constants::COMPATIBILITY_FORWARD) ); self::assertEquals( - COMPATIBILITY_FORWARD_TRANSITIVE, - validateCompatibilityLevel(COMPATIBILITY_FORWARD_TRANSITIVE) + Constants::COMPATIBILITY_FORWARD_TRANSITIVE, + Requests::validateCompatibilityLevel(Constants::COMPATIBILITY_FORWARD_TRANSITIVE) ); - validateCompatibilityLevel('INVALID'); + Requests::validateCompatibilityLevel('INVALID'); } /** @@ -301,31 +279,31 @@ public function it_should_prepare_compatibility_string_for_transport(): void { self::assertEquals( '{"compatibility":"NONE"}', - prepareCompatibilityLevelForTransport(COMPATIBILITY_NONE) + Requests::prepareCompatibilityLevelForTransport(Constants::COMPATIBILITY_NONE) ); self::assertEquals( '{"compatibility":"BACKWARD"}', - prepareCompatibilityLevelForTransport(COMPATIBILITY_BACKWARD) + Requests::prepareCompatibilityLevelForTransport(Constants::COMPATIBILITY_BACKWARD) ); self::assertEquals( '{"compatibility":"BACKWARD_TRANSITIVE"}', - prepareCompatibilityLevelForTransport(COMPATIBILITY_BACKWARD_TRANSITIVE) + Requests::prepareCompatibilityLevelForTransport(Constants::COMPATIBILITY_BACKWARD_TRANSITIVE) ); self::assertEquals( '{"compatibility":"FORWARD"}', - prepareCompatibilityLevelForTransport(COMPATIBILITY_FORWARD) + Requests::prepareCompatibilityLevelForTransport(Constants::COMPATIBILITY_FORWARD) ); self::assertEquals( '{"compatibility":"FORWARD_TRANSITIVE"}', - prepareCompatibilityLevelForTransport(COMPATIBILITY_FORWARD_TRANSITIVE) + Requests::prepareCompatibilityLevelForTransport(Constants::COMPATIBILITY_FORWARD_TRANSITIVE) ); self::assertEquals( '{"compatibility":"FULL"}', - prepareCompatibilityLevelForTransport(COMPATIBILITY_FULL) + Requests::prepareCompatibilityLevelForTransport(Constants::COMPATIBILITY_FULL) ); self::assertEquals( '{"compatibility":"FULL_TRANSITIVE"}', - prepareCompatibilityLevelForTransport(COMPATIBILITY_FULL_TRANSITIVE) + Requests::prepareCompatibilityLevelForTransport(Constants::COMPATIBILITY_FULL_TRANSITIVE) ); } @@ -337,7 +315,7 @@ public function it_should_validate_version_id_type(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$versionId must be an integer of type int or string'); - validateVersionId([3]); + Requests::validateVersionId([3]); } /** @@ -348,7 +326,7 @@ public function it_should_validate_version_id_overflow(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$versionId must be between 1 and 2^31 - 1'); - validateVersionId(2 ** 31); + Requests::validateVersionId(2 ** 31); } /** @@ -359,7 +337,7 @@ public function it_should_validate_version_id_less_than_one(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$versionId must be between 1 and 2^31 - 1'); - validateVersionId(0); + Requests::validateVersionId(0); } /** @@ -367,9 +345,9 @@ public function it_should_validate_version_id_less_than_one(): void */ public function it_should_validate_valid_version_id(): void { - self::assertSame(VERSION_LATEST, validateVersionId(VERSION_LATEST)); - self::assertSame('3', validateVersionId(3)); - self::assertSame('3', validateVersionId('3')); + self::assertSame(Constants::VERSION_LATEST, Requests::validateVersionId(Constants::VERSION_LATEST)); + self::assertSame('3', Requests::validateVersionId(3)); + self::assertSame('3', Requests::validateVersionId('3')); } /** @@ -377,8 +355,8 @@ public function it_should_validate_valid_version_id(): void */ public function it_should_validate_valid_schema_ids(): void { - self::assertSame('3', validateSchemaId(3)); - self::assertSame('3', validateSchemaId('3')); + self::assertSame('3', Requests::validateSchemaId(3)); + self::assertSame('3', Requests::validateSchemaId('3')); } /** @@ -386,11 +364,11 @@ public function it_should_validate_valid_schema_ids(): void */ public function it_should_produce_a_valid_subject_deletion_request(): void { - $request = deleteSubjectRequest('test'); + $request = Requests::deleteSubjectRequest('test'); self::assertEquals('DELETE', $request->getMethod()); self::assertEquals('subjects/test', $request->getUri()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } /** @@ -398,16 +376,16 @@ public function it_should_produce_a_valid_subject_deletion_request(): void */ public function it_should_produce_a_valid_subject_version_deletion_request(): void { - $request = deleteSubjectVersionRequest('test', VERSION_LATEST); + $request = Requests::deleteSubjectVersionRequest('test', Constants::VERSION_LATEST); self::assertEquals('DELETE', $request->getMethod()); self::assertEquals('subjects/test/versions/latest', $request->getUri()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); - $request = deleteSubjectVersionRequest('test', '5'); + $request = Requests::deleteSubjectVersionRequest('test', '5'); self::assertEquals('DELETE', $request->getMethod()); self::assertEquals('subjects/test/versions/5', $request->getUri()); - self::assertEquals(ACCEPT_HEADER[ACCEPT_HEADER_KEY], $request->getHeader(ACCEPT_HEADER_KEY)[0]); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } } From 5f1ac52794252c544f0d2cdecfba3bb57441545e Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Sat, 3 Oct 2020 10:53:15 +0200 Subject: [PATCH 04/16] [FIX] Don't remove but deprecate Functions and Constants (#49) --- .php-cs-fixer.dist.php | 1 + composer.json | 6 +- src/Constants/Constants.php | 63 ++++ src/Registry/Cache/SimpleCacheAdapter.php | 4 +- src/Requests/Functions.php | 400 ++++++++++++++++++++++ test/Exception/ExceptionMapTest.php | 3 +- test/Requests/FunctionsTest.php | 391 +++++++++++++++++++++ test/{Requests => }/RequestsTest.php | 4 +- 8 files changed, 865 insertions(+), 7 deletions(-) create mode 100644 src/Constants/Constants.php create mode 100644 src/Requests/Functions.php create mode 100644 test/Requests/FunctionsTest.php rename test/{Requests => }/RequestsTest.php (99%) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index eba70ff..34efddf 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -9,6 +9,7 @@ return (new PhpCsFixer\Config()) ->setRules( [ + '@PSR2' => true, 'no_unused_imports' => true, 'ordered_imports' => [ 'sort_algorithm' => 'alpha', diff --git a/composer.json b/composer.json index 853bf01..321702b 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,11 @@ "autoload": { "psr-4": { "FlixTech\\SchemaRegistryApi\\": "src/" - } + }, + "files": [ + "src/Requests/Functions.php", + "src/Constants/Constants.php" + ] }, "autoload-dev": { "psr-4": { diff --git a/src/Constants/Constants.php b/src/Constants/Constants.php new file mode 100644 index 0000000..c6ae24c --- /dev/null +++ b/src/Constants/Constants.php @@ -0,0 +1,63 @@ + 'application/vnd.schemaregistry.v1+json']; + +/** + * @deprecated Use \FlixTech\SchemaRegistryApi\Constants::CONTENT_TYPE instead + */ +const CONTENT_TYPE = 'Content-Type'; + +/** + * @deprecated Use \FlixTech\SchemaRegistryApi\Constants::CONTENT_TYPE_HEADER instead + */ +const CONTENT_TYPE_HEADER = [CONTENT_TYPE => 'application/vnd.schemaregistry.v1+json']; diff --git a/src/Registry/Cache/SimpleCacheAdapter.php b/src/Registry/Cache/SimpleCacheAdapter.php index 3bb217c..a81024f 100644 --- a/src/Registry/Cache/SimpleCacheAdapter.php +++ b/src/Registry/Cache/SimpleCacheAdapter.php @@ -76,11 +76,11 @@ public function getWithId(int $schemaId): ?AvroSchema public function getIdWithHash(string $hash): ?int { $rawId = $this->cache->get($hash); - + if (null === $rawId) { return null; } - + return (int) $rawId; } diff --git a/src/Requests/Functions.php b/src/Requests/Functions.php new file mode 100644 index 0000000..3428c11 --- /dev/null +++ b/src/Requests/Functions.php @@ -0,0 +1,400 @@ + + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Json::decodeResponse instead + */ +function decodeResponse(ResponseInterface $response): array +{ + $body = (string)$response->getBody(); + + try { + return jsonDecode($body); + } catch (JsonException $e) { + throw new InvalidArgumentException( + sprintf('%s - with content "%s"', $e->getMessage(), $body), + $e->getCode(), + $e + ); + } +} + +/** + * @return RequestInterface + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::allSubjectsRequest instead + */ +function allSubjectsRequest(): RequestInterface +{ + return new Request( + 'GET', + '/subjects', + ACCEPT_HEADER + ); +} + +/** + * @param string $subjectName + * + * @return RequestInterface + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::allSubjectVersionsRequest instead + */ +function allSubjectVersionsRequest(string $subjectName): RequestInterface +{ + return new Request( + 'GET', + Utils::uriFor("/subjects/$subjectName/versions"), + ACCEPT_HEADER + ); +} + +/** + * @param string $subjectName + * @param string $versionId + * + * @return RequestInterface + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::singleSubjectVersionRequest instead + */ +function singleSubjectVersionRequest(string $subjectName, string $versionId): RequestInterface +{ + return new Request( + 'GET', + Utils::uriFor("/subjects/$subjectName/versions/$versionId"), + ACCEPT_HEADER + ); +} + +/** + * @param string $schema + * @param string $subjectName + * + * @return RequestInterface + * + * @throws JsonException + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::registerNewSchemaVersionWithSubjectRequest instead + */ +function registerNewSchemaVersionWithSubjectRequest(string $schema, string $subjectName): RequestInterface +{ + return new Request( + 'POST', + Utils::uriFor("/subjects/$subjectName/versions"), + CONTENT_TYPE_HEADER + ACCEPT_HEADER, + prepareJsonSchemaForTransfer(validateSchemaStringAsJson($schema)) + ); +} + +/** + * @param string $schema + * @param string $subjectName + * @param string $versionId + * + * @return RequestInterface + * + * @throws JsonException + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::checkSchemaCompatibilityAgainstVersionRequest instead + */ +function checkSchemaCompatibilityAgainstVersionRequest(string $schema, string $subjectName, string $versionId): RequestInterface +{ + return new Request( + 'POST', + Utils::uriFor("/compatibility/subjects/$subjectName/versions/$versionId"), + CONTENT_TYPE_HEADER + ACCEPT_HEADER, + prepareJsonSchemaForTransfer(validateSchemaStringAsJson($schema)) + ); +} + +/** + * @param string $subjectName + * @param string $schema + * + * @return RequestInterface + * + * @throws JsonException + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::checkIfSubjectHasSchemaRegisteredRequest instead + */ +function checkIfSubjectHasSchemaRegisteredRequest(string $subjectName, string $schema): RequestInterface +{ + return new Request( + 'POST', + Utils::uriFor("/subjects/$subjectName"), + CONTENT_TYPE_HEADER + ACCEPT_HEADER, + prepareJsonSchemaForTransfer(validateSchemaStringAsJson($schema)) + ); +} + +/** + * @param string $id + * @return RequestInterface + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::schemaRequest instead + */ +function schemaRequest(string $id): RequestInterface +{ + return new Request( + 'GET', + Utils::uriFor("/schemas/ids/$id"), + ACCEPT_HEADER + ); +} + +/** + * @return RequestInterface + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::defaultCompatibilityLevelRequest instead + */ +function defaultCompatibilityLevelRequest(): RequestInterface +{ + return new Request( + 'GET', + '/config', + ACCEPT_HEADER + ); +} + +/** + * @param string $level + * + * @return RequestInterface + * + * @throws JsonException + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::changeDefaultCompatibilityLevelRequest instead + */ +function changeDefaultCompatibilityLevelRequest(string $level): RequestInterface +{ + return new Request( + 'PUT', + '/config', + ACCEPT_HEADER, + prepareCompatibilityLevelForTransport(validateCompatibilityLevel($level)) + ); +} + +/** + * @param string $subjectName + * + * @return RequestInterface + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::subjectCompatibilityLevelRequest instead + */ +function subjectCompatibilityLevelRequest(string $subjectName): RequestInterface +{ + return new Request( + 'GET', + Utils::uriFor("/config/$subjectName"), + ACCEPT_HEADER + ); +} + +/** + * @param string $subjectName + * @param string $level + * + * @return RequestInterface + * + * @throws JsonException + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::changeSubjectCompatibilityLevelRequest instead + */ +function changeSubjectCompatibilityLevelRequest(string $subjectName, string $level): RequestInterface +{ + return new Request( + 'PUT', + Utils::uriFor("/config/$subjectName"), + ACCEPT_HEADER, + prepareCompatibilityLevelForTransport(validateCompatibilityLevel($level)) + ); +} + +/** + * @param int|string $versionId + * + * @return string + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::validateVersionId instead + */ +function validateVersionId($versionId): string +{ + if (VERSION_LATEST !== $versionId) { + Assert::that($versionId) + ->integerish('$versionId must be an integer of type int or string') + ->between(1, 2 ** 31 - 1, '$versionId must be between 1 and 2^31 - 1'); + } + + return (string)$versionId; +} + +/** + * @param string $schema + * + * @return string + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::validateSchemaStringAsJson instead + */ +function validateSchemaStringAsJson(string $schema): string +{ + Assert::that($schema)->isJsonString('$schema must be a valid JSON string'); + + return $schema; +} + +/** + * @param string $schema + * + * @return string + * + * @throws JsonException + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::prepareJsonSchemaForTransfer instead + */ +function prepareJsonSchemaForTransfer(string $schema): string +{ + $decoded = jsonDecode($schema); + + if (is_array($decoded) && array_key_exists('schema', $decoded)) { + return jsonEncode($decoded); + } + + return jsonEncode(['schema' => jsonEncode($decoded)]); +} + +/** + * @param string $compatibilityVersion + * + * @return string + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::validateCompatibilityLevel instead + */ +function validateCompatibilityLevel(string $compatibilityVersion): string +{ + $compatibilities = [ + COMPATIBILITY_NONE, + COMPATIBILITY_BACKWARD, + COMPATIBILITY_BACKWARD_TRANSITIVE, + COMPATIBILITY_FORWARD, + COMPATIBILITY_FORWARD_TRANSITIVE, + COMPATIBILITY_FULL, + COMPATIBILITY_FULL_TRANSITIVE, + + ]; + Assert::that($compatibilityVersion)->inArray( + $compatibilities, + '$level must be one of ' . implode(', ', $compatibilities) + ); + + return $compatibilityVersion; +} + +/** + * @param string $compatibilityLevel + * + * @return string + * + * @throws JsonException + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::prepareCompatibilityLevelForTransport instead + */ +function prepareCompatibilityLevelForTransport(string $compatibilityLevel): string +{ + return jsonEncode(['compatibility' => $compatibilityLevel]); +} + +/** + * @param int|string $schemaId + * @return string + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::validateSchemaId instead + */ +function validateSchemaId($schemaId): string +{ + Assert::that($schemaId) + ->integerish('$schemaId must be an integer value of type int or string') + ->greaterThan(0, '$schemaId must be greater than 0'); + + return (string)$schemaId; +} + +/** + * @param string $subjectName + * + * @return RequestInterface + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::deleteSubjectRequest instead + */ +function deleteSubjectRequest(string $subjectName): RequestInterface +{ + return new Request( + 'DELETE', + Utils::uriFor("/subjects/$subjectName"), + ACCEPT_HEADER + ); +} + +/** + * @param string $subjectName + * @param string $versionId + * + * @return RequestInterface + * + * @deprecated Use \FlixTech\SchemaRegistryApi\Requests::deleteSubjectVersionRequest instead + */ +function deleteSubjectVersionRequest(string $subjectName, string $versionId): RequestInterface +{ + return new Request( + 'DELETE', + Utils::uriFor("/subjects/$subjectName/versions/$versionId"), + ACCEPT_HEADER + ); +} diff --git a/test/Exception/ExceptionMapTest.php b/test/Exception/ExceptionMapTest.php index 1de8877..23d7b92 100644 --- a/test/Exception/ExceptionMapTest.php +++ b/test/Exception/ExceptionMapTest.php @@ -281,8 +281,7 @@ private function assertSchemaRegistryException( string $expectedMessage, int $errorCode, SchemaRegistryException $exception - ): void - { + ): void { self::assertInstanceOf($exceptionClass, $exception); self::assertEquals($errorCode, $exception->getCode()); self::assertEquals($expectedMessage, $exception->getMessage()); diff --git a/test/Requests/FunctionsTest.php b/test/Requests/FunctionsTest.php new file mode 100644 index 0000000..ca0fe7c --- /dev/null +++ b/test/Requests/FunctionsTest.php @@ -0,0 +1,391 @@ +getMethod()); + self::assertEquals('/subjects', $request->getUri()); + self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); + } + + /** + * @test + */ + public function it_should_produce_a_Request_to_get_all_subject_versions(): void + { + $request = allSubjectVersionsRequest('test'); + + self::assertEquals('GET', $request->getMethod()); + self::assertEquals('/subjects/test/versions', $request->getUri()); + self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); + } + + /** + * @test + */ + public function it_should_produce_a_Request_to_get_a_specific_subject_version(): void + { + $request = singleSubjectVersionRequest('test', '3'); + + self::assertEquals('GET', $request->getMethod()); + self::assertEquals('/subjects/test/versions/3', $request->getUri()); + self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); + } + + /** + * @test + */ + public function it_should_produce_a_request_to_register_a_new_schema_version(): void + { + $request = registerNewSchemaVersionWithSubjectRequest('{"type": "string"}', 'test'); + + self::assertEquals('POST', $request->getMethod()); + self::assertEquals('/subjects/test/versions', $request->getUri()); + self::assertEquals( + [CONTENT_TYPE => [CONTENT_TYPE_HEADER[CONTENT_TYPE]]] + [ACCEPT => [ACCEPT_HEADER[ACCEPT]]], + $request->getHeaders() + ); + self::assertEquals('{"schema":"{\"type\":\"string\"}"}', $request->getBody()->getContents()); + + $request = registerNewSchemaVersionWithSubjectRequest('{"schema": "{\"type\": \"string\"}"}', 'test'); + + self::assertEquals('POST', $request->getMethod()); + self::assertEquals('/subjects/test/versions', $request->getUri()); + self::assertEquals( + [CONTENT_TYPE => [CONTENT_TYPE_HEADER[CONTENT_TYPE]]] + [ACCEPT => [ACCEPT_HEADER[ACCEPT]]], + $request->getHeaders() + ); + self::assertEquals('{"schema":"{\"type\": \"string\"}"}', $request->getBody()->getContents()); + } + + /** + * @test + */ + public function it_should_produce_a_request_to_check_schema_compatibility_against_a_subject_version(): void + { + $request = checkSchemaCompatibilityAgainstVersionRequest( + '{"type":"test"}', + 'test', + VERSION_LATEST + ); + + self::assertEquals('POST', $request->getMethod()); + self::assertEquals('/compatibility/subjects/test/versions/latest', $request->getUri()); + self::assertEquals('{"schema":"{\"type\":\"test\"}"}', $request->getBody()->getContents()); + self::assertEquals( + [CONTENT_TYPE => [CONTENT_TYPE_HEADER[CONTENT_TYPE]]] + [ACCEPT => [ACCEPT_HEADER[ACCEPT]]], + $request->getHeaders() + ); + } + + /** + * @test + */ + public function it_should_produce_a_request_to_check_if_a_subject_already_has_a_schema(): void + { + $request = checkIfSubjectHasSchemaRegisteredRequest('test', '{"type":"test"}'); + + self::assertEquals('POST', $request->getMethod()); + self::assertEquals('/subjects/test', $request->getUri()); + self::assertEquals('{"schema":"{\"type\":\"test\"}"}', $request->getBody()->getContents()); + self::assertEquals( + [CONTENT_TYPE => [CONTENT_TYPE_HEADER[CONTENT_TYPE]]] + [ACCEPT => [ACCEPT_HEADER[ACCEPT]]], + $request->getHeaders() + ); + } + + /** + * @test + */ + public function it_should_produce_a_request_to_get_a_specific_schema_by_id(): void + { + $request = schemaRequest('3'); + + self::assertEquals('GET', $request->getMethod()); + self::assertEquals('/schemas/ids/3', $request->getUri()); + self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); + } + + /** + * @test + */ + public function it_should_produce_a_request_to_get_the_global_compatibility_level(): void + { + $request = defaultCompatibilityLevelRequest(); + + self::assertEquals('GET', $request->getMethod()); + self::assertEquals('/config', $request->getUri()); + self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); + } + + /** + * @test + */ + public function it_should_produce_a_request_to_change_the_global_compatibility_level(): void + { + $request = changeDefaultCompatibilityLevelRequest(COMPATIBILITY_FULL); + + self::assertEquals('PUT', $request->getMethod()); + self::assertEquals('/config', $request->getUri()); + self::assertEquals('{"compatibility":"FULL"}', $request->getBody()->getContents()); + self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); + } + + /** + * @test + */ + public function it_should_produce_a_request_to_get_the_subject_compatibility_level(): void + { + $request = subjectCompatibilityLevelRequest('test'); + + self::assertEquals('GET', $request->getMethod()); + self::assertEquals('/config/test', $request->getUri()); + self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); + } + + /** + * @test + */ + public function it_should_produce_a_request_to_change_the_subject_compatibility_level(): void + { + $request = changeSubjectCompatibilityLevelRequest('test', COMPATIBILITY_FORWARD); + + self::assertEquals('PUT', $request->getMethod()); + self::assertEquals('/config/test', $request->getUri()); + self::assertEquals('{"compatibility":"FORWARD"}', $request->getBody()->getContents()); + self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); + } + + /** + * @test + */ + public function it_should_validate_a_JSON_schema_string(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$schema must be a valid JSON string'); + + self::assertJsonStringEqualsJsonString('{"type":"test"}', validateSchemaStringAsJson('{"type":"test"}')); + + validateSchemaStringAsJson('INVALID'); + } + + /** + * @test + */ + public function it_should_prepare_a_JSON_schema_for_transfer(): void + { + self::assertJsonStringEqualsJsonString( + '{"schema":"{\"type\":\"string\"}"}', + prepareJsonSchemaForTransfer('{"type": "string"}') + ); + + self::assertJsonStringEqualsJsonString( + '{"schema":"{\"type\": \"string\"}"}', + prepareJsonSchemaForTransfer('{"schema":"{\"type\": \"string\"}"}') + ); + } + + /** + * @test + */ + public function it_should_validate_a_compatibility_level_string(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$level must be one of NONE, BACKWARD, BACKWARD_TRANSITIVE, FORWARD, FORWARD_TRANSITIVE, FULL, FULL_TRANSITIVE'); + + self::assertEquals( + COMPATIBILITY_NONE, + validateCompatibilityLevel(COMPATIBILITY_NONE) + ); + self::assertEquals( + COMPATIBILITY_FULL, + validateCompatibilityLevel(COMPATIBILITY_FULL) + ); + self::assertEquals( + COMPATIBILITY_FULL_TRANSITIVE, + validateCompatibilityLevel(COMPATIBILITY_FULL_TRANSITIVE) + ); + self::assertEquals( + COMPATIBILITY_BACKWARD, + validateCompatibilityLevel(COMPATIBILITY_BACKWARD) + ); + self::assertEquals( + COMPATIBILITY_BACKWARD_TRANSITIVE, + validateCompatibilityLevel(COMPATIBILITY_BACKWARD_TRANSITIVE) + ); + self::assertEquals( + COMPATIBILITY_FORWARD, + validateCompatibilityLevel(COMPATIBILITY_FORWARD) + ); + self::assertEquals( + COMPATIBILITY_FORWARD_TRANSITIVE, + validateCompatibilityLevel(COMPATIBILITY_FORWARD_TRANSITIVE) + ); + + validateCompatibilityLevel('INVALID'); + } + + /** + * @test + */ + public function it_should_prepare_compatibility_string_for_transport(): void + { + self::assertEquals( + '{"compatibility":"NONE"}', + prepareCompatibilityLevelForTransport(COMPATIBILITY_NONE) + ); + self::assertEquals( + '{"compatibility":"BACKWARD"}', + prepareCompatibilityLevelForTransport(COMPATIBILITY_BACKWARD) + ); + self::assertEquals( + '{"compatibility":"BACKWARD_TRANSITIVE"}', + prepareCompatibilityLevelForTransport(COMPATIBILITY_BACKWARD_TRANSITIVE) + ); + self::assertEquals( + '{"compatibility":"FORWARD"}', + prepareCompatibilityLevelForTransport(COMPATIBILITY_FORWARD) + ); + self::assertEquals( + '{"compatibility":"FORWARD_TRANSITIVE"}', + prepareCompatibilityLevelForTransport(COMPATIBILITY_FORWARD_TRANSITIVE) + ); + self::assertEquals( + '{"compatibility":"FULL"}', + prepareCompatibilityLevelForTransport(COMPATIBILITY_FULL) + ); + self::assertEquals( + '{"compatibility":"FULL_TRANSITIVE"}', + prepareCompatibilityLevelForTransport(COMPATIBILITY_FULL_TRANSITIVE) + ); + } + + /** + * @test + */ + public function it_should_validate_version_id_type(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$versionId must be an integer of type int or string'); + + validateVersionId([3]); + } + + /** + * @test + */ + public function it_should_validate_version_id_overflow(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$versionId must be between 1 and 2^31 - 1'); + + validateVersionId(2 ** 31); + } + + /** + * @test + */ + public function it_should_validate_version_id_less_than_one(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$versionId must be between 1 and 2^31 - 1'); + + validateVersionId(0); + } + + /** + * @test + */ + public function it_should_validate_valid_version_id(): void + { + self::assertSame(VERSION_LATEST, validateVersionId(VERSION_LATEST)); + self::assertSame('3', validateVersionId(3)); + self::assertSame('3', validateVersionId('3')); + } + + /** + * @test + */ + public function it_should_validate_valid_schema_ids(): void + { + self::assertSame('3', validateSchemaId(3)); + self::assertSame('3', validateSchemaId('3')); + } + + /** + * @test + */ + public function it_should_produce_a_valid_subject_deletion_request(): void + { + $request = deleteSubjectRequest('test'); + + self::assertEquals('DELETE', $request->getMethod()); + self::assertEquals('/subjects/test', $request->getUri()); + self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); + } + + /** + * @test + */ + public function it_should_produce_a_valid_subject_version_deletion_request(): void + { + $request = deleteSubjectVersionRequest('test', VERSION_LATEST); + + self::assertEquals('DELETE', $request->getMethod()); + self::assertEquals('/subjects/test/versions/latest', $request->getUri()); + self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); + + $request = deleteSubjectVersionRequest('test', '5'); + + self::assertEquals('DELETE', $request->getMethod()); + self::assertEquals('/subjects/test/versions/5', $request->getUri()); + self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); + } +} diff --git a/test/Requests/RequestsTest.php b/test/RequestsTest.php similarity index 99% rename from test/Requests/RequestsTest.php rename to test/RequestsTest.php index 0225c5d..9cafc5f 100644 --- a/test/Requests/RequestsTest.php +++ b/test/RequestsTest.php @@ -1,8 +1,8 @@ - Date: Sun, 4 Oct 2020 07:06:51 +0200 Subject: [PATCH 05/16] Adding `permanent` flag to `deleteSubject` Request --- src/Requests.php | 7 +++++-- test/RequestsTest.php | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Requests.php b/src/Requests.php index fded789..6611eb5 100644 --- a/src/Requests.php +++ b/src/Requests.php @@ -185,13 +185,16 @@ public static function validateSchemaId($schemaId): string /** * @param string $subjectName + * @param bool $permanent * @return RequestInterface */ - public static function deleteSubjectRequest(string $subjectName): RequestInterface + public static function deleteSubjectRequest(string $subjectName, bool $permanent): RequestInterface { + $query = $permanent ? "true" : "false"; + return new Request( 'DELETE', - Utils::uriFor("/subjects/$subjectName"), + Utils::uriFor("/subjects/$subjectName?permanent=$query"), Constants::ACCEPT_HEADER ); } diff --git a/test/RequestsTest.php b/test/RequestsTest.php index 9cafc5f..0951ae2 100644 --- a/test/RequestsTest.php +++ b/test/RequestsTest.php @@ -364,10 +364,10 @@ public function it_should_validate_valid_schema_ids(): void */ public function it_should_produce_a_valid_subject_deletion_request(): void { - $request = Requests::deleteSubjectRequest('test'); + $request = Requests::deleteSubjectRequest('test', false); self::assertEquals('DELETE', $request->getMethod()); - self::assertEquals('subjects/test', $request->getUri()); + self::assertEquals('subjects/test?permanent=false', $request->getUri()); self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } From 662a53fb8b32ec73797e56c710f38c49588be57f Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Sun, 4 Oct 2020 07:11:34 +0200 Subject: [PATCH 06/16] Adding `permanent` flag to `deleteSubjectVersionRequest` Request --- src/Requests.php | 7 +++++-- test/RequestsTest.php | 18 +++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Requests.php b/src/Requests.php index 6611eb5..0a7a84c 100644 --- a/src/Requests.php +++ b/src/Requests.php @@ -202,13 +202,16 @@ public static function deleteSubjectRequest(string $subjectName, bool $permanent /** * @param string $subjectName * @param string $versionId + * @param bool $permanent * @return RequestInterface */ - public static function deleteSubjectVersionRequest(string $subjectName, string $versionId): RequestInterface + public static function deleteSubjectVersionRequest(string $subjectName, string $versionId, bool $permanent = false): RequestInterface { + $query = $permanent ? "true" : "false"; + return new Request( 'DELETE', - Utils::uriFor("/subjects/$subjectName/versions/$versionId"), + Utils::uriFor("/subjects/$subjectName/versions/$versionId?permanent=$query"), Constants::ACCEPT_HEADER ); } diff --git a/test/RequestsTest.php b/test/RequestsTest.php index 0951ae2..f6549ca 100644 --- a/test/RequestsTest.php +++ b/test/RequestsTest.php @@ -376,16 +376,28 @@ public function it_should_produce_a_valid_subject_deletion_request(): void */ public function it_should_produce_a_valid_subject_version_deletion_request(): void { + $request = Requests::deleteSubjectVersionRequest('test', Constants::VERSION_LATEST, false); + + self::assertEquals('DELETE', $request->getMethod()); + self::assertEquals('/subjects/test/versions/latest?permanent=false', $request->getUri()); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); + $request = Requests::deleteSubjectVersionRequest('test', Constants::VERSION_LATEST); self::assertEquals('DELETE', $request->getMethod()); - self::assertEquals('subjects/test/versions/latest', $request->getUri()); + self::assertEquals('subjects/test/versions/latest?permanent=false', $request->getUri()); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); + + $request = Requests::deleteSubjectVersionRequest('test', '5', false); + + self::assertEquals('DELETE', $request->getMethod()); + self::assertEquals('/subjects/test/versions/5?permanent=false', $request->getUri()); self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); - $request = Requests::deleteSubjectVersionRequest('test', '5'); + $request = Requests::deleteSubjectVersionRequest('test', '5', true); self::assertEquals('DELETE', $request->getMethod()); - self::assertEquals('subjects/test/versions/5', $request->getUri()); + self::assertEquals('subjects/test/versions/5?permanent=true', $request->getUri()); self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } } From 3edad9c7bda9fbefc79a7af63e71b425629c6cfd Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Sun, 4 Oct 2020 07:13:43 +0200 Subject: [PATCH 07/16] Add default value to permanent flag in `deleteSubjectRequest` --- src/Requests.php | 2 +- test/RequestsTest.php | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Requests.php b/src/Requests.php index 0a7a84c..ad40674 100644 --- a/src/Requests.php +++ b/src/Requests.php @@ -188,7 +188,7 @@ public static function validateSchemaId($schemaId): string * @param bool $permanent * @return RequestInterface */ - public static function deleteSubjectRequest(string $subjectName, bool $permanent): RequestInterface + public static function deleteSubjectRequest(string $subjectName, bool $permanent = false): RequestInterface { $query = $permanent ? "true" : "false"; diff --git a/test/RequestsTest.php b/test/RequestsTest.php index f6549ca..5b44a9b 100644 --- a/test/RequestsTest.php +++ b/test/RequestsTest.php @@ -364,11 +364,23 @@ public function it_should_validate_valid_schema_ids(): void */ public function it_should_produce_a_valid_subject_deletion_request(): void { + $request = Requests::deleteSubjectRequest('test'); + + self::assertEquals('DELETE', $request->getMethod()); + self::assertEquals('/subjects/test?permanent=false', $request->getUri()); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); + $request = Requests::deleteSubjectRequest('test', false); self::assertEquals('DELETE', $request->getMethod()); self::assertEquals('subjects/test?permanent=false', $request->getUri()); self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); + + $request = Requests::deleteSubjectRequest('test', true); + + self::assertEquals('DELETE', $request->getMethod()); + self::assertEquals('/subjects/test?permanent=true', $request->getUri()); + self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } /** From ce605ab92952be0aa24e5927344553b4201b0593 Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Sun, 4 Oct 2020 08:31:18 +0200 Subject: [PATCH 08/16] Rename `Json::jsonDecode` to `Json::decode` --- src/Exception/ExceptionMap.php | 2 +- src/Json.php | 4 ++-- src/Requests.php | 2 +- test/IntegrationTest.php | 24 ++++++++++++------------ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Exception/ExceptionMap.php b/src/Exception/ExceptionMap.php index a84590f..1bc6dec 100644 --- a/src/Exception/ExceptionMap.php +++ b/src/Exception/ExceptionMap.php @@ -89,7 +89,7 @@ public function hasMappableError(ResponseInterface $response): bool private function guardAgainstMissingErrorCode(ResponseInterface $response): array { try { - $decodedBody = Json::jsonDecode((string)$response->getBody()); + $decodedBody = Json::decode((string)$response->getBody()); if (!is_array($decodedBody) || !array_key_exists(self::ERROR_CODE_FIELD_NAME, $decodedBody)) { throw new RuntimeException( diff --git a/src/Json.php b/src/Json.php index 7e70529..c107b11 100644 --- a/src/Json.php +++ b/src/Json.php @@ -30,7 +30,7 @@ public static function validateStringAsJson(string $schema): string * * @throws JsonException */ - public static function jsonDecode(string $jsonString, int $depth = 512) + public static function decode(string $jsonString, int $depth = 512) { return json_decode($jsonString, true, $depth, JSON_THROW_ON_ERROR); } @@ -57,7 +57,7 @@ public static function decodeResponse(ResponseInterface $response): array $body = (string)$response->getBody(); try { - return Json::jsonDecode($body); + return Json::decode($body); } catch (JsonException $e) { throw new InvalidArgumentException( sprintf('%s - with content "%s"', $e->getMessage(), $body), diff --git a/src/Requests.php b/src/Requests.php index ad40674..d5f5bb8 100644 --- a/src/Requests.php +++ b/src/Requests.php @@ -44,7 +44,7 @@ public static function singleSubjectVersionRequest(string $subjectName, string $ public static function prepareJsonSchemaForTransfer(string $schema): string { - $decoded = Json::jsonDecode($schema); + $decoded = Json::decode($schema); if (is_array($decoded) && array_key_exists('schema', $decoded)) { return Json::jsonEncode($decoded); diff --git a/test/IntegrationTest.php b/test/IntegrationTest.php index ff7a339..a3027b0 100644 --- a/test/IntegrationTest.php +++ b/test/IntegrationTest.php @@ -110,7 +110,7 @@ public function managing_subjects_and_versions(): void ->sendAsync(Requests::allSubjectsRequest()) ->then( function (ResponseInterface $request) { - $this->assertEmpty(Json::jsonDecode($request->getBody()->getContents())); + $this->assertEmpty(Json::decode($request->getBody()->getContents())); } )->wait(); @@ -118,7 +118,7 @@ function (ResponseInterface $request) { ->sendAsync(Requests::registerNewSchemaVersionWithSubjectRequest($this->baseSchema, self::SUBJECT_NAME)) ->then( function (ResponseInterface $request) { - $this->assertEquals(1, Json::jsonDecode($request->getBody()->getContents())['id']); + $this->assertEquals(1, Json::decode($request->getBody()->getContents())['id']); } )->wait(); @@ -126,7 +126,7 @@ function (ResponseInterface $request) { ->sendAsync(Requests::schemaRequest('1')) ->then( function (ResponseInterface $request) { - $decodedBody = Json::jsonDecode($request->getBody()->getContents()); + $decodedBody = Json::decode($request->getBody()->getContents()); $this->assertJsonStringEqualsJsonString($this->baseSchema, $decodedBody['schema']); } @@ -136,7 +136,7 @@ function (ResponseInterface $request) { ->sendAsync(Requests::checkIfSubjectHasSchemaRegisteredRequest(self::SUBJECT_NAME, $this->baseSchema)) ->then( function (ResponseInterface $request) { - $decodedBody = Json::jsonDecode($request->getBody()->getContents()); + $decodedBody = Json::decode($request->getBody()->getContents()); $this->assertEquals(1, $decodedBody['id']); $this->assertEquals(1, $decodedBody['version']); @@ -149,7 +149,7 @@ function (ResponseInterface $request) { ->sendAsync(Requests::singleSubjectVersionRequest(self::SUBJECT_NAME, Constants::VERSION_LATEST)) ->then( function (ResponseInterface $request) { - $decodedBody = Json::jsonDecode($request->getBody()->getContents()); + $decodedBody = Json::decode($request->getBody()->getContents()); $this->assertEquals(self::SUBJECT_NAME, $decodedBody['subject']); $this->assertEquals(1, $decodedBody['version']); @@ -165,7 +165,7 @@ function (ResponseInterface $request) { Constants::VERSION_LATEST ))->then( function (ResponseInterface $request) { - $decodedBody = Json::jsonDecode($request->getBody()->getContents()); + $decodedBody = Json::decode($request->getBody()->getContents()); $this->assertTrue($decodedBody['is_compatible']); } @@ -244,7 +244,7 @@ function (RequestException $exception) { ->sendAsync(Requests::registerNewSchemaVersionWithSubjectRequest($this->compatibleSchemaEvolution, self::SUBJECT_NAME)) ->then( function (ResponseInterface $request) { - $this->assertEquals(2, Json::jsonDecode($request->getBody()->getContents())['id']); + $this->assertEquals(2, Json::decode($request->getBody()->getContents())['id']); } )->wait(); @@ -252,7 +252,7 @@ function (ResponseInterface $request) { ->sendAsync(Requests::allSubjectVersionsRequest(self::SUBJECT_NAME)) ->then( function (ResponseInterface $request) { - $this->assertEquals([1, 2], Json::jsonDecode($request->getBody()->getContents())); + $this->assertEquals([1, 2], Json::decode($request->getBody()->getContents())); } )->wait(); } @@ -266,7 +266,7 @@ public function managing_compatibility_levels(): void ->sendAsync(Requests::defaultCompatibilityLevelRequest()) ->then( function (ResponseInterface $request) { - $decodedBody = Json::jsonDecode($request->getBody()->getContents()); + $decodedBody = Json::decode($request->getBody()->getContents()); $this->assertEquals( Constants::COMPATIBILITY_BACKWARD, @@ -279,7 +279,7 @@ function (ResponseInterface $request) { ->sendAsync(Requests::changeDefaultCompatibilityLevelRequest(Constants::COMPATIBILITY_FULL)) ->then( function (ResponseInterface $request) { - $decodedBody = Json::jsonDecode($request->getBody()->getContents()); + $decodedBody = Json::decode($request->getBody()->getContents()); $this->assertEquals( Constants::COMPATIBILITY_FULL, @@ -292,7 +292,7 @@ function (ResponseInterface $request) { ->sendAsync(Requests::changeSubjectCompatibilityLevelRequest(self::SUBJECT_NAME, Constants::COMPATIBILITY_FORWARD)) ->then( function (ResponseInterface $request) { - $decodedBody = Json::jsonDecode($request->getBody()->getContents()); + $decodedBody = Json::decode($request->getBody()->getContents()); $this->assertEquals( Constants::COMPATIBILITY_FORWARD, @@ -305,7 +305,7 @@ function (ResponseInterface $request) { ->sendAsync(Requests::subjectCompatibilityLevelRequest(self::SUBJECT_NAME)) ->then( function (ResponseInterface $request) { - $decodedBody = Json::jsonDecode($request->getBody()->getContents()); + $decodedBody = Json::decode($request->getBody()->getContents()); $this->assertEquals( Constants::COMPATIBILITY_FORWARD, From 7d756192469dac237b01f8cf00296c1479ba59e2 Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Sun, 4 Oct 2020 08:32:16 +0200 Subject: [PATCH 09/16] Rename `Json::jsonEncode` to `Json::encode` --- src/Json.php | 2 +- src/Requests.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Json.php b/src/Json.php index c107b11..1c35d32 100644 --- a/src/Json.php +++ b/src/Json.php @@ -42,7 +42,7 @@ public static function decode(string $jsonString, int $depth = 512) * * @throws JsonException */ - public static function jsonEncode($data): string + public static function encode($data): string { return json_encode($data, JSON_THROW_ON_ERROR); } diff --git a/src/Requests.php b/src/Requests.php index d5f5bb8..4b0a8a1 100644 --- a/src/Requests.php +++ b/src/Requests.php @@ -47,10 +47,10 @@ public static function prepareJsonSchemaForTransfer(string $schema): string $decoded = Json::decode($schema); if (is_array($decoded) && array_key_exists('schema', $decoded)) { - return Json::jsonEncode($decoded); + return Json::encode($decoded); } - return Json::jsonEncode(['schema' => Json::jsonEncode($decoded)]); + return Json::encode(['schema' => Json::encode($decoded)]); } public static function registerNewSchemaVersionWithSubjectRequest(string $schema, string $subjectName): RequestInterface @@ -123,7 +123,7 @@ public static function validateCompatibilityLevel(string $compatibilityVersion): public static function prepareCompatibilityLevelForTransport(string $compatibilityLevel): string { - return Json::jsonEncode(['compatibility' => $compatibilityLevel]); + return Json::encode(['compatibility' => $compatibilityLevel]); } public static function changeDefaultCompatibilityLevelRequest(string $level): RequestInterface From 0452e6cd7e6d4899c12fa5852f4beb3e423a43fd Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Sun, 4 Oct 2020 10:44:33 +0200 Subject: [PATCH 10/16] Initial `SchemaType` implementations --- src/Constants.php | 3 ++ src/Schemas/AvroSchemaType.php | 39 ++++++++++++++++++ src/Schemas/JsonSchemaType.php | 39 ++++++++++++++++++ src/Schemas/ProtobufSchemaType.php | 39 ++++++++++++++++++ src/Schemas/SchemaType.php | 18 +++++++++ src/Schemas/ValueObject.php | 19 +++++++++ test/Schemas/SchemaTypeTest.php | 63 ++++++++++++++++++++++++++++++ 7 files changed, 220 insertions(+) create mode 100644 src/Schemas/AvroSchemaType.php create mode 100644 src/Schemas/JsonSchemaType.php create mode 100644 src/Schemas/ProtobufSchemaType.php create mode 100644 src/Schemas/SchemaType.php create mode 100644 src/Schemas/ValueObject.php create mode 100644 test/Schemas/SchemaTypeTest.php diff --git a/src/Constants.php b/src/Constants.php index a00992b..a9842bb 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -18,6 +18,9 @@ final class Constants public const ACCEPT_HEADER = [self::ACCEPT => 'application/vnd.schemaregistry.v1+json']; public const CONTENT_TYPE_HEADER = [self::CONTENT_TYPE => 'application/vnd.schemaregistry.v1+json']; public const CONTENT_TYPE = 'Content-Type'; + public const AVRO_TYPE = 'AVRO'; + public const JSON_TYPE = 'JSON'; + public const PROTOBUF_TYPE = 'PROTOBUF'; private function __construct() { diff --git a/src/Schemas/AvroSchemaType.php b/src/Schemas/AvroSchemaType.php new file mode 100644 index 0000000..fbe2104 --- /dev/null +++ b/src/Schemas/AvroSchemaType.php @@ -0,0 +1,39 @@ + + */ +final class AvroSchemaType extends ValueObject implements SchemaType +{ + /** + * @var AvroSchemaType + */ + private static $instance; + + public static function instance(): AvroSchemaType + { + if (self::$instance !== null) { + return self::$instance; + } + + self::$instance = new self(); + + return self::$instance; + } + + public function value(): string + { + return Constants::AVRO_TYPE; + } + + public function __toString(): string + { + return $this->value(); + } +} diff --git a/src/Schemas/JsonSchemaType.php b/src/Schemas/JsonSchemaType.php new file mode 100644 index 0000000..8f27113 --- /dev/null +++ b/src/Schemas/JsonSchemaType.php @@ -0,0 +1,39 @@ + + */ +final class JsonSchemaType extends ValueObject implements SchemaType +{ + /** + * @var JsonSchemaType + */ + private static $instance; + + public static function instance(): JsonSchemaType + { + if (self::$instance !== null) { + return self::$instance; + } + + self::$instance = new self(); + + return self::$instance; + } + + public function value(): string + { + return Constants::JSON_TYPE; + } + + public function __toString(): string + { + return $this->value(); + } +} diff --git a/src/Schemas/ProtobufSchemaType.php b/src/Schemas/ProtobufSchemaType.php new file mode 100644 index 0000000..389bd70 --- /dev/null +++ b/src/Schemas/ProtobufSchemaType.php @@ -0,0 +1,39 @@ + + */ +final class ProtobufSchemaType extends ValueObject implements SchemaType +{ + /** + * @var ProtobufSchemaType + */ + private static $instance; + + public static function instance(): ProtobufSchemaType + { + if (self::$instance !== null) { + return self::$instance; + } + + self::$instance = new self(); + + return self::$instance; + } + + public function value(): string + { + return Constants::PROTOBUF_TYPE; + } + + public function __toString(): string + { + return $this->value(); + } +} diff --git a/src/Schemas/SchemaType.php b/src/Schemas/SchemaType.php new file mode 100644 index 0000000..5fe834c --- /dev/null +++ b/src/Schemas/SchemaType.php @@ -0,0 +1,18 @@ + $className + * @param string $expected + */ + public function type_should_match_the_value(string $className, string $expected): void + { + self::assertEquals($expected, $className::instance()->value()); + self::assertEquals($expected, (string)$className::instance()); + } + + /** + * @test + * @dataProvider provideSchemaTypes + * + * @phpstan-template template T of SchemaType + * @param string $className + * @phpstan-param class-string $className + */ + public function types_cannot_be_cloned(string $className): void + { + $this->expectException(Error::class); + $result = clone $className::instance(); + } + + public function provideSchemaTypes(): Generator + { + yield 'AvroSchemaType' => [ + AvroSchemaType::class, + Constants::AVRO_TYPE, + ]; + + yield 'JsonSchemaType' => [ + JsonSchemaType::class, + Constants::JSON_TYPE, + ]; + + yield 'ProtobufSchemaType' => [ + ProtobufSchemaType::class, + Constants::PROTOBUF_TYPE, + ]; + } +} From db440b90c82e7b0eddcdc60397d132284f4bc6c4 Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Fri, 16 Oct 2020 10:13:16 +0200 Subject: [PATCH 11/16] Renaming `ValueObject` to `SchemaTypes` --- src/Schemas/AvroSchemaType.php | 2 +- src/Schemas/JsonSchemaType.php | 2 +- src/Schemas/ProtobufSchemaType.php | 2 +- src/Schemas/SchemaTypes.php | 34 ++++++++++++++++++++++++++++++ src/Schemas/ValueObject.php | 19 ----------------- 5 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 src/Schemas/SchemaTypes.php delete mode 100644 src/Schemas/ValueObject.php diff --git a/src/Schemas/AvroSchemaType.php b/src/Schemas/AvroSchemaType.php index fbe2104..785ccc1 100644 --- a/src/Schemas/AvroSchemaType.php +++ b/src/Schemas/AvroSchemaType.php @@ -9,7 +9,7 @@ /** * @implements SchemaType */ -final class AvroSchemaType extends ValueObject implements SchemaType +final class AvroSchemaType extends SchemaTypes implements SchemaType { /** * @var AvroSchemaType diff --git a/src/Schemas/JsonSchemaType.php b/src/Schemas/JsonSchemaType.php index 8f27113..ae6bc07 100644 --- a/src/Schemas/JsonSchemaType.php +++ b/src/Schemas/JsonSchemaType.php @@ -9,7 +9,7 @@ /** * @implements SchemaType */ -final class JsonSchemaType extends ValueObject implements SchemaType +final class JsonSchemaType extends SchemaTypes implements SchemaType { /** * @var JsonSchemaType diff --git a/src/Schemas/ProtobufSchemaType.php b/src/Schemas/ProtobufSchemaType.php index 389bd70..d7420fd 100644 --- a/src/Schemas/ProtobufSchemaType.php +++ b/src/Schemas/ProtobufSchemaType.php @@ -9,7 +9,7 @@ /** * @implements SchemaType */ -final class ProtobufSchemaType extends ValueObject implements SchemaType +final class ProtobufSchemaType extends SchemaTypes implements SchemaType { /** * @var ProtobufSchemaType diff --git a/src/Schemas/SchemaTypes.php b/src/Schemas/SchemaTypes.php new file mode 100644 index 0000000..75b4abe --- /dev/null +++ b/src/Schemas/SchemaTypes.php @@ -0,0 +1,34 @@ + Date: Mon, 15 Nov 2021 21:14:27 +0100 Subject: [PATCH 12/16] Fixing test suite after rebase massacre --- src/Exception/ExceptionMap.php | 7 ++- src/Json.php | 4 +- src/Registry/Decorators/CachingDecorator.php | 3 +- src/Registry/GuzzlePromiseAsyncRegistry.php | 2 +- src/Registry/Psr18SyncRegistry.php | 5 ++- src/Requests.php | 45 ++++++++++---------- src/Requests/Functions.php | 30 ++++++------- test/IntegrationTest.php | 1 - test/Requests/FunctionsTest.php | 35 +++++++-------- test/RequestsTest.php | 16 +++---- test/Schema/AvroNameTest.php | 6 ++- test/Schema/AvroReferenceTest.php | 7 +-- 12 files changed, 80 insertions(+), 81 deletions(-) diff --git a/src/Exception/ExceptionMap.php b/src/Exception/ExceptionMap.php index 1bc6dec..da2e431 100644 --- a/src/Exception/ExceptionMap.php +++ b/src/Exception/ExceptionMap.php @@ -39,7 +39,10 @@ private function __construct() { $factoryFn = static function (string $exceptionClass): callable { return static function (int $errorCode, string $errorMessage) use ($exceptionClass): SchemaRegistryException { - return new $exceptionClass($errorMessage, $errorCode); + /** @var SchemaRegistryException $e */ + $e = new $exceptionClass($errorMessage, $errorCode); + + return $e; }; }; @@ -84,7 +87,7 @@ public function hasMappableError(ResponseInterface $response): bool /** * @param ResponseInterface $response - * @return array + * @return array */ private function guardAgainstMissingErrorCode(ResponseInterface $response): array { diff --git a/src/Json.php b/src/Json.php index 1c35d32..2e7c233 100644 --- a/src/Json.php +++ b/src/Json.php @@ -24,9 +24,9 @@ public static function validateStringAsJson(string $schema): string /** * @param string $jsonString - * @param int $depth + * @param int<1, max> $depth * - * @return mixed + * @return mixed|array * * @throws JsonException */ diff --git a/src/Registry/Decorators/CachingDecorator.php b/src/Registry/Decorators/CachingDecorator.php index 9cdfe78..77d56de 100644 --- a/src/Registry/Decorators/CachingDecorator.php +++ b/src/Registry/Decorators/CachingDecorator.php @@ -7,8 +7,9 @@ use AvroSchema; use Exception; use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; -use FlixTech\SchemaRegistryApi\Schema\AvroReference; +use FlixTech\SchemaRegistryApi\Registry; use FlixTech\SchemaRegistryApi\Registry\Cache\CacheAdapter; +use FlixTech\SchemaRegistryApi\Schema\AvroReference; use GuzzleHttp\Promise\PromiseInterface; use function call_user_func; diff --git a/src/Registry/GuzzlePromiseAsyncRegistry.php b/src/Registry/GuzzlePromiseAsyncRegistry.php index 2bf6123..38f66ed 100644 --- a/src/Registry/GuzzlePromiseAsyncRegistry.php +++ b/src/Registry/GuzzlePromiseAsyncRegistry.php @@ -9,11 +9,11 @@ use FlixTech\SchemaRegistryApi\AsynchronousRegistry; use FlixTech\SchemaRegistryApi\Constants; use FlixTech\SchemaRegistryApi\Exception\ExceptionMap; -use FlixTech\SchemaRegistryApi\Schema\AvroReference; use FlixTech\SchemaRegistryApi\Exception\RuntimeException; use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; use FlixTech\SchemaRegistryApi\Json; use FlixTech\SchemaRegistryApi\Requests; +use FlixTech\SchemaRegistryApi\Schema\AvroReference; use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Promise\PromiseInterface; diff --git a/src/Registry/Psr18SyncRegistry.php b/src/Registry/Psr18SyncRegistry.php index 674329d..039e997 100644 --- a/src/Registry/Psr18SyncRegistry.php +++ b/src/Registry/Psr18SyncRegistry.php @@ -13,6 +13,7 @@ use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; use FlixTech\SchemaRegistryApi\Json; use FlixTech\SchemaRegistryApi\Requests; +use FlixTech\SchemaRegistryApi\Schema\AvroReference; use FlixTech\SchemaRegistryApi\SynchronousRegistry; use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Client\ClientInterface; @@ -37,9 +38,9 @@ public function __construct(ClientInterface $client) $this->map = ExceptionMap::instance(); } - public function register(string $subject, AvroSchema $schema): int + public function register(string $subject, AvroSchema $schema, AvroReference ...$references): int { - $request = Requests::registerNewSchemaVersionWithSubjectRequest((string)$schema, $subject); + $request = Requests::registerNewSchemaVersionWithSubjectRequest((string)$schema, $subject, ...$references); $response = $this->makeRequest($request); $this->guardAgainstErrorResponse($response); diff --git a/src/Requests.php b/src/Requests.php index 4b0a8a1..6c3beb3 100644 --- a/src/Requests.php +++ b/src/Requests.php @@ -5,6 +5,7 @@ namespace FlixTech\SchemaRegistryApi; use Assert\Assert; +use FlixTech\SchemaRegistryApi\Schema\AvroReference; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Utils; use Psr\Http\Message\RequestInterface; @@ -19,7 +20,7 @@ public static function allSubjectsRequest(): RequestInterface { return new Request( 'GET', - '/subjects', + 'subjects', Constants::ACCEPT_HEADER ); } @@ -28,7 +29,7 @@ public static function allSubjectVersionsRequest(string $subjectName): RequestIn { return new Request( 'GET', - Utils::uriFor("/subjects/$subjectName/versions"), + Utils::uriFor("subjects/$subjectName/versions"), Constants::ACCEPT_HEADER ); } @@ -37,29 +38,29 @@ public static function singleSubjectVersionRequest(string $subjectName, string $ { return new Request( 'GET', - Utils::uriFor("/subjects/$subjectName/versions/$versionId"), + Utils::uriFor("subjects/$subjectName/versions/$versionId"), Constants::ACCEPT_HEADER ); } - public static function prepareJsonSchemaForTransfer(string $schema): string + public static function prepareJsonSchemaForTransfer(string $schema, AvroReference ...$references): string { - $decoded = Json::decode($schema); - - if (is_array($decoded) && array_key_exists('schema', $decoded)) { - return Json::encode($decoded); - } + $return = [ + 'schema' => $schema + ]; - return Json::encode(['schema' => Json::encode($decoded)]); + return !$references + ? Json::encode($return) + : Json::encode(array_merge($return, ['references' => $references])); } - public static function registerNewSchemaVersionWithSubjectRequest(string $schema, string $subjectName): RequestInterface + public static function registerNewSchemaVersionWithSubjectRequest(string $schema, string $subjectName, AvroReference ...$references): RequestInterface { return new Request( 'POST', - Utils::uriFor("/subjects/$subjectName/versions"), + Utils::uriFor("subjects/$subjectName/versions"), Constants::CONTENT_TYPE_HEADER + Constants::ACCEPT_HEADER, - self::prepareJsonSchemaForTransfer(Json::validateStringAsJson($schema)) + self::prepareJsonSchemaForTransfer(Json::validateStringAsJson($schema), ...$references), ); } @@ -67,7 +68,7 @@ public static function checkSchemaCompatibilityAgainstVersionRequest(string $sch { return new Request( 'POST', - Utils::uriFor("/compatibility/subjects/$subjectName/versions/$versionId"), + Utils::uriFor("compatibility/subjects/$subjectName/versions/$versionId"), Constants::CONTENT_TYPE_HEADER + Constants::ACCEPT_HEADER, self::prepareJsonSchemaForTransfer(Json::validateStringAsJson($schema)) ); @@ -77,7 +78,7 @@ public static function checkIfSubjectHasSchemaRegisteredRequest(string $subjectN { return new Request( 'POST', - Utils::uriFor("/subjects/$subjectName"), + Utils::uriFor("subjects/$subjectName"), Constants::CONTENT_TYPE_HEADER + Constants::ACCEPT_HEADER, self::prepareJsonSchemaForTransfer(Json::validateStringAsJson($schema)) ); @@ -87,7 +88,7 @@ public static function schemaRequest(string $id): RequestInterface { return new Request( 'GET', - Utils::uriFor("/schemas/ids/$id"), + Utils::uriFor("schemas/ids/$id"), Constants::ACCEPT_HEADER ); } @@ -96,7 +97,7 @@ public static function defaultCompatibilityLevelRequest(): RequestInterface { return new Request( 'GET', - '/config', + 'config', Constants::ACCEPT_HEADER ); } @@ -130,7 +131,7 @@ public static function changeDefaultCompatibilityLevelRequest(string $level): Re { return new Request( 'PUT', - '/config', + 'config', Constants::ACCEPT_HEADER, self::prepareCompatibilityLevelForTransport(self::validateCompatibilityLevel($level)) ); @@ -140,7 +141,7 @@ public static function subjectCompatibilityLevelRequest(string $subjectName): Re { return new Request( 'GET', - Utils::uriFor("/config/$subjectName"), + Utils::uriFor("config/$subjectName"), Constants::ACCEPT_HEADER ); } @@ -149,7 +150,7 @@ public static function changeSubjectCompatibilityLevelRequest(string $subjectNam { return new Request( 'PUT', - Utils::uriFor("/config/$subjectName"), + Utils::uriFor("config/$subjectName"), Constants::ACCEPT_HEADER, self::prepareCompatibilityLevelForTransport(self::validateCompatibilityLevel($level)) ); @@ -194,7 +195,7 @@ public static function deleteSubjectRequest(string $subjectName, bool $permanent return new Request( 'DELETE', - Utils::uriFor("/subjects/$subjectName?permanent=$query"), + Utils::uriFor("subjects/$subjectName?permanent=$query"), Constants::ACCEPT_HEADER ); } @@ -211,7 +212,7 @@ public static function deleteSubjectVersionRequest(string $subjectName, string $ return new Request( 'DELETE', - Utils::uriFor("/subjects/$subjectName/versions/$versionId?permanent=$query"), + Utils::uriFor("subjects/$subjectName/versions/$versionId?permanent=$query"), Constants::ACCEPT_HEADER ); } diff --git a/src/Requests/Functions.php b/src/Requests/Functions.php index 3428c11..078e718 100644 --- a/src/Requests/Functions.php +++ b/src/Requests/Functions.php @@ -4,7 +4,6 @@ use Assert\Assert; use GuzzleHttp\Psr7\Request; -use GuzzleHttp\Psr7\Utils; use InvalidArgumentException; use JsonException; use Psr\Http\Message\RequestInterface; @@ -21,10 +20,11 @@ use const FlixTech\SchemaRegistryApi\Constants\VERSION_LATEST; use function implode; use function json_decode; +use function sprintf; /** * @param string $jsonString - * @param int $depth + * @param int<1, max> $depth * * @return mixed * @@ -82,7 +82,7 @@ function allSubjectsRequest(): RequestInterface { return new Request( 'GET', - '/subjects', + 'subjects', ACCEPT_HEADER ); } @@ -98,7 +98,7 @@ function allSubjectVersionsRequest(string $subjectName): RequestInterface { return new Request( 'GET', - Utils::uriFor("/subjects/$subjectName/versions"), + sprintf("subjects/%s/versions", $subjectName), ACCEPT_HEADER ); } @@ -115,7 +115,7 @@ function singleSubjectVersionRequest(string $subjectName, string $versionId): Re { return new Request( 'GET', - Utils::uriFor("/subjects/$subjectName/versions/$versionId"), + sprintf("subjects/%s/versions/%s", $subjectName, $versionId), ACCEPT_HEADER ); } @@ -133,7 +133,7 @@ function registerNewSchemaVersionWithSubjectRequest(string $schema, string $subj { return new Request( 'POST', - Utils::uriFor("/subjects/$subjectName/versions"), + sprintf("subjects/%s/versions", $subjectName), CONTENT_TYPE_HEADER + ACCEPT_HEADER, prepareJsonSchemaForTransfer(validateSchemaStringAsJson($schema)) ); @@ -153,7 +153,7 @@ function checkSchemaCompatibilityAgainstVersionRequest(string $schema, string $s { return new Request( 'POST', - Utils::uriFor("/compatibility/subjects/$subjectName/versions/$versionId"), + sprintf("compatibility/subjects/%s/versions/%s", $subjectName, $versionId), CONTENT_TYPE_HEADER + ACCEPT_HEADER, prepareJsonSchemaForTransfer(validateSchemaStringAsJson($schema)) ); @@ -172,7 +172,7 @@ function checkIfSubjectHasSchemaRegisteredRequest(string $subjectName, string $s { return new Request( 'POST', - Utils::uriFor("/subjects/$subjectName"), + sprintf("subjects/%s", $subjectName), CONTENT_TYPE_HEADER + ACCEPT_HEADER, prepareJsonSchemaForTransfer(validateSchemaStringAsJson($schema)) ); @@ -188,7 +188,7 @@ function schemaRequest(string $id): RequestInterface { return new Request( 'GET', - Utils::uriFor("/schemas/ids/$id"), + sprintf("schemas/ids/%s", $id), ACCEPT_HEADER ); } @@ -202,7 +202,7 @@ function defaultCompatibilityLevelRequest(): RequestInterface { return new Request( 'GET', - '/config', + 'config', ACCEPT_HEADER ); } @@ -219,7 +219,7 @@ function changeDefaultCompatibilityLevelRequest(string $level): RequestInterface { return new Request( 'PUT', - '/config', + 'config', ACCEPT_HEADER, prepareCompatibilityLevelForTransport(validateCompatibilityLevel($level)) ); @@ -236,7 +236,7 @@ function subjectCompatibilityLevelRequest(string $subjectName): RequestInterface { return new Request( 'GET', - Utils::uriFor("/config/$subjectName"), + sprintf("config/%s", $subjectName), ACCEPT_HEADER ); } @@ -254,7 +254,7 @@ function changeSubjectCompatibilityLevelRequest(string $subjectName, string $lev { return new Request( 'PUT', - Utils::uriFor("/config/$subjectName"), + sprintf("config/%s", $subjectName), ACCEPT_HEADER, prepareCompatibilityLevelForTransport(validateCompatibilityLevel($level)) ); @@ -377,7 +377,7 @@ function deleteSubjectRequest(string $subjectName): RequestInterface { return new Request( 'DELETE', - Utils::uriFor("/subjects/$subjectName"), + sprintf("subjects/%s", $subjectName), ACCEPT_HEADER ); } @@ -394,7 +394,7 @@ function deleteSubjectVersionRequest(string $subjectName, string $versionId): Re { return new Request( 'DELETE', - Utils::uriFor("/subjects/$subjectName/versions/$versionId"), + sprintf("subjects/%s/versions/%s", $subjectName, $versionId), ACCEPT_HEADER ); } diff --git a/test/IntegrationTest.php b/test/IntegrationTest.php index a3027b0..0e440b2 100644 --- a/test/IntegrationTest.php +++ b/test/IntegrationTest.php @@ -17,7 +17,6 @@ use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Psr7\Utils; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; diff --git a/test/Requests/FunctionsTest.php b/test/Requests/FunctionsTest.php index ca0fe7c..14ced3c 100644 --- a/test/Requests/FunctionsTest.php +++ b/test/Requests/FunctionsTest.php @@ -51,7 +51,7 @@ public function it_should_produce_a_Request_to_get_all_subjects(): void $request = allSubjectsRequest(); self::assertEquals('GET', $request->getMethod()); - self::assertEquals('/subjects', $request->getUri()); + self::assertEquals('subjects', $request->getUri()); self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); } @@ -63,7 +63,7 @@ public function it_should_produce_a_Request_to_get_all_subject_versions(): void $request = allSubjectVersionsRequest('test'); self::assertEquals('GET', $request->getMethod()); - self::assertEquals('/subjects/test/versions', $request->getUri()); + self::assertEquals('subjects/test/versions', $request->getUri()); self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); } @@ -75,7 +75,7 @@ public function it_should_produce_a_Request_to_get_a_specific_subject_version(): $request = singleSubjectVersionRequest('test', '3'); self::assertEquals('GET', $request->getMethod()); - self::assertEquals('/subjects/test/versions/3', $request->getUri()); + self::assertEquals('subjects/test/versions/3', $request->getUri()); self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); } @@ -87,7 +87,7 @@ public function it_should_produce_a_request_to_register_a_new_schema_version(): $request = registerNewSchemaVersionWithSubjectRequest('{"type": "string"}', 'test'); self::assertEquals('POST', $request->getMethod()); - self::assertEquals('/subjects/test/versions', $request->getUri()); + self::assertEquals('subjects/test/versions', $request->getUri()); self::assertEquals( [CONTENT_TYPE => [CONTENT_TYPE_HEADER[CONTENT_TYPE]]] + [ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders() @@ -97,7 +97,7 @@ public function it_should_produce_a_request_to_register_a_new_schema_version(): $request = registerNewSchemaVersionWithSubjectRequest('{"schema": "{\"type\": \"string\"}"}', 'test'); self::assertEquals('POST', $request->getMethod()); - self::assertEquals('/subjects/test/versions', $request->getUri()); + self::assertEquals('subjects/test/versions', $request->getUri()); self::assertEquals( [CONTENT_TYPE => [CONTENT_TYPE_HEADER[CONTENT_TYPE]]] + [ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders() @@ -117,7 +117,7 @@ public function it_should_produce_a_request_to_check_schema_compatibility_agains ); self::assertEquals('POST', $request->getMethod()); - self::assertEquals('/compatibility/subjects/test/versions/latest', $request->getUri()); + self::assertEquals('compatibility/subjects/test/versions/latest', $request->getUri()); self::assertEquals('{"schema":"{\"type\":\"test\"}"}', $request->getBody()->getContents()); self::assertEquals( [CONTENT_TYPE => [CONTENT_TYPE_HEADER[CONTENT_TYPE]]] + [ACCEPT => [ACCEPT_HEADER[ACCEPT]]], @@ -133,7 +133,7 @@ public function it_should_produce_a_request_to_check_if_a_subject_already_has_a_ $request = checkIfSubjectHasSchemaRegisteredRequest('test', '{"type":"test"}'); self::assertEquals('POST', $request->getMethod()); - self::assertEquals('/subjects/test', $request->getUri()); + self::assertEquals('subjects/test', $request->getUri()); self::assertEquals('{"schema":"{\"type\":\"test\"}"}', $request->getBody()->getContents()); self::assertEquals( [CONTENT_TYPE => [CONTENT_TYPE_HEADER[CONTENT_TYPE]]] + [ACCEPT => [ACCEPT_HEADER[ACCEPT]]], @@ -149,7 +149,7 @@ public function it_should_produce_a_request_to_get_a_specific_schema_by_id(): vo $request = schemaRequest('3'); self::assertEquals('GET', $request->getMethod()); - self::assertEquals('/schemas/ids/3', $request->getUri()); + self::assertEquals('schemas/ids/3', $request->getUri()); self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); } @@ -161,7 +161,7 @@ public function it_should_produce_a_request_to_get_the_global_compatibility_leve $request = defaultCompatibilityLevelRequest(); self::assertEquals('GET', $request->getMethod()); - self::assertEquals('/config', $request->getUri()); + self::assertEquals('config', $request->getUri()); self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); } @@ -173,7 +173,7 @@ public function it_should_produce_a_request_to_change_the_global_compatibility_l $request = changeDefaultCompatibilityLevelRequest(COMPATIBILITY_FULL); self::assertEquals('PUT', $request->getMethod()); - self::assertEquals('/config', $request->getUri()); + self::assertEquals('config', $request->getUri()); self::assertEquals('{"compatibility":"FULL"}', $request->getBody()->getContents()); self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); } @@ -186,7 +186,7 @@ public function it_should_produce_a_request_to_get_the_subject_compatibility_lev $request = subjectCompatibilityLevelRequest('test'); self::assertEquals('GET', $request->getMethod()); - self::assertEquals('/config/test', $request->getUri()); + self::assertEquals('config/test', $request->getUri()); self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); } @@ -198,7 +198,7 @@ public function it_should_produce_a_request_to_change_the_subject_compatibility_ $request = changeSubjectCompatibilityLevelRequest('test', COMPATIBILITY_FORWARD); self::assertEquals('PUT', $request->getMethod()); - self::assertEquals('/config/test', $request->getUri()); + self::assertEquals('config/test', $request->getUri()); self::assertEquals('{"compatibility":"FORWARD"}', $request->getBody()->getContents()); self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); } @@ -225,11 +225,6 @@ public function it_should_prepare_a_JSON_schema_for_transfer(): void '{"schema":"{\"type\":\"string\"}"}', prepareJsonSchemaForTransfer('{"type": "string"}') ); - - self::assertJsonStringEqualsJsonString( - '{"schema":"{\"type\": \"string\"}"}', - prepareJsonSchemaForTransfer('{"schema":"{\"type\": \"string\"}"}') - ); } /** @@ -367,7 +362,7 @@ public function it_should_produce_a_valid_subject_deletion_request(): void $request = deleteSubjectRequest('test'); self::assertEquals('DELETE', $request->getMethod()); - self::assertEquals('/subjects/test', $request->getUri()); + self::assertEquals('subjects/test', $request->getUri()); self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); } @@ -379,13 +374,13 @@ public function it_should_produce_a_valid_subject_version_deletion_request(): vo $request = deleteSubjectVersionRequest('test', VERSION_LATEST); self::assertEquals('DELETE', $request->getMethod()); - self::assertEquals('/subjects/test/versions/latest', $request->getUri()); + self::assertEquals('subjects/test/versions/latest', $request->getUri()); self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); $request = deleteSubjectVersionRequest('test', '5'); self::assertEquals('DELETE', $request->getMethod()); - self::assertEquals('/subjects/test/versions/5', $request->getUri()); + self::assertEquals('subjects/test/versions/5', $request->getUri()); self::assertEquals([ACCEPT => [ACCEPT_HEADER[ACCEPT]]], $request->getHeaders()); } } diff --git a/test/RequestsTest.php b/test/RequestsTest.php index 5b44a9b..0b41d6a 100644 --- a/test/RequestsTest.php +++ b/test/RequestsTest.php @@ -72,7 +72,8 @@ public function it_should_produce_a_request_to_register_a_new_schema_version(str self::assertJsonStringEqualsJsonString($finalSchema, $request->getBody()->getContents()); } - public static function dataForRegisteringSchemas(): Generator { + public static function dataForRegisteringSchemas(): Generator + { yield 'Schema without schema key' => [ '{"type":"string"}', '{"schema":"{\"type\":\"string\"}"}', @@ -225,11 +226,6 @@ public function it_should_prepare_a_JSON_schema_for_transfer(): void '{"schema":"{\"type\":\"string\"}"}', Requests::prepareJsonSchemaForTransfer('{"type":"string"}') ); - - self::assertJsonStringEqualsJsonString( - '{"schema":"{\"type\": \"string\"}"}', - Requests::prepareJsonSchemaForTransfer('{"schema":"{\"type\": \"string\"}"}') - ); } /** @@ -367,7 +363,7 @@ public function it_should_produce_a_valid_subject_deletion_request(): void $request = Requests::deleteSubjectRequest('test'); self::assertEquals('DELETE', $request->getMethod()); - self::assertEquals('/subjects/test?permanent=false', $request->getUri()); + self::assertEquals('subjects/test?permanent=false', $request->getUri()); self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); $request = Requests::deleteSubjectRequest('test', false); @@ -379,7 +375,7 @@ public function it_should_produce_a_valid_subject_deletion_request(): void $request = Requests::deleteSubjectRequest('test', true); self::assertEquals('DELETE', $request->getMethod()); - self::assertEquals('/subjects/test?permanent=true', $request->getUri()); + self::assertEquals('subjects/test?permanent=true', $request->getUri()); self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); } @@ -391,7 +387,7 @@ public function it_should_produce_a_valid_subject_version_deletion_request(): vo $request = Requests::deleteSubjectVersionRequest('test', Constants::VERSION_LATEST, false); self::assertEquals('DELETE', $request->getMethod()); - self::assertEquals('/subjects/test/versions/latest?permanent=false', $request->getUri()); + self::assertEquals('subjects/test/versions/latest?permanent=false', $request->getUri()); self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); $request = Requests::deleteSubjectVersionRequest('test', Constants::VERSION_LATEST); @@ -403,7 +399,7 @@ public function it_should_produce_a_valid_subject_version_deletion_request(): vo $request = Requests::deleteSubjectVersionRequest('test', '5', false); self::assertEquals('DELETE', $request->getMethod()); - self::assertEquals('/subjects/test/versions/5?permanent=false', $request->getUri()); + self::assertEquals('subjects/test/versions/5?permanent=false', $request->getUri()); self::assertEquals([Constants::ACCEPT => [Constants::ACCEPT_HEADER[Constants::ACCEPT]]], $request->getHeaders()); $request = Requests::deleteSubjectVersionRequest('test', '5', true); diff --git a/test/Schema/AvroNameTest.php b/test/Schema/AvroNameTest.php index d75d926..1ca73d4 100644 --- a/test/Schema/AvroNameTest.php +++ b/test/Schema/AvroNameTest.php @@ -15,7 +15,8 @@ class AvroNameTest extends TestCase * @dataProvider avroReferences * @test */ - public function it_should_only_be_constructable_from_a_valid_Avro_reference(string $fullName, bool $isValid): void { + public function it_should_only_be_constructable_from_a_valid_Avro_reference(string $fullName, bool $isValid): void + { if (!$isValid) { $this->expectException(InvalidArgumentException::class); } @@ -23,7 +24,8 @@ public function it_should_only_be_constructable_from_a_valid_Avro_reference(stri $this->assertSame((string) new AvroName($fullName), $fullName); } - public static function avroReferences(): Generator { + public static function avroReferences(): Generator + { yield 'Valid root name' => ['test', true]; yield 'Valid full name' => ['test.example', true]; yield 'Empty full name' => ['', false]; diff --git a/test/Schema/AvroReferenceTest.php b/test/Schema/AvroReferenceTest.php index c3fe852..98950e1 100644 --- a/test/Schema/AvroReferenceTest.php +++ b/test/Schema/AvroReferenceTest.php @@ -21,7 +21,8 @@ class AvroReferenceTest extends TestCase * @param bool $isValid * @param string $expectedJson */ - public function it_should_be_constructable(string $avroName, string $subject, $version, bool $isValid, ?string $expectedJson): void { + public function it_should_be_constructable(string $avroName, string $subject, $version, bool $isValid, ?string $expectedJson): void + { if (!$isValid) { $this->expectException(InvalidArgumentException::class); } @@ -32,7 +33,8 @@ public function it_should_be_constructable(string $avroName, string $subject, $v ); } - public static function references(): Generator { + public static function references(): Generator + { yield 'Valid version with latest' => [ 'test.example.MyRecord', 'example-value', @@ -68,6 +70,5 @@ public static function references(): Generator { false, null, ]; - } } From 8b4b753bc6c81ac73013c602efec9388c9d52cfe Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Mon, 15 Nov 2021 21:26:09 +0100 Subject: [PATCH 13/16] Add missing invoke to ExceptionMap.php --- src/Exception/ExceptionMap.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Exception/ExceptionMap.php b/src/Exception/ExceptionMap.php index da2e431..2d4638e 100644 --- a/src/Exception/ExceptionMap.php +++ b/src/Exception/ExceptionMap.php @@ -60,6 +60,12 @@ private function __construct() ]; } + public function __invoke(ResponseInterface $response): SchemaRegistryException + { + return $this->exceptionFor($response); + } + + /** * Maps a ResponseInterface to the internal SchemaRegistryException types. * From a628fba21da0aaf0da88cf7ae4c6f952b960eacd Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Tue, 16 Nov 2021 11:43:53 +0100 Subject: [PATCH 14/16] Adding support for AvroReference in new registries --- src/Exception/ExceptionMap.php | 124 ++++++++---------- src/Registry/GuzzlePromiseAsyncRegistry.php | 23 +--- src/Registry/Psr18SyncRegistry.php | 14 +- src/Requests.php | 3 +- test/Exception/ExceptionMapTest.php | 52 ++------ .../GuzzlePromiseAsyncRegistryTest.php | 7 +- test/Registry/Psr18SyncRegistryTest.php | 7 +- test/Schema/AvroReferenceTest.php | 2 +- 8 files changed, 93 insertions(+), 139 deletions(-) diff --git a/src/Exception/ExceptionMap.php b/src/Exception/ExceptionMap.php index 2d4638e..6a40097 100644 --- a/src/Exception/ExceptionMap.php +++ b/src/Exception/ExceptionMap.php @@ -4,8 +4,8 @@ namespace FlixTech\SchemaRegistryApi\Exception; -use Exception; use FlixTech\SchemaRegistryApi\Json; +use GuzzleHttp\Exception\RequestException; use Psr\Http\Message\ResponseInterface; use function array_key_exists; use function sprintf; @@ -30,104 +30,90 @@ public static function instance(): ExceptionMap return self::$instance; } - /** - * @var array - */ - private $map; - private function __construct() { - $factoryFn = static function (string $exceptionClass): callable { - return static function (int $errorCode, string $errorMessage) use ($exceptionClass): SchemaRegistryException { - /** @var SchemaRegistryException $e */ - $e = new $exceptionClass($errorMessage, $errorCode); - - return $e; - }; - }; - - $this->map = [ - IncompatibleAvroSchemaException::errorCode() => $factoryFn(IncompatibleAvroSchemaException::class), - BackendDataStoreException::errorCode() => $factoryFn(BackendDataStoreException::class), - OperationTimedOutException::errorCode() => $factoryFn(OperationTimedOutException::class), - MasterProxyException::errorCode() => $factoryFn(MasterProxyException::class), - InvalidVersionException::errorCode() => $factoryFn(InvalidVersionException::class), - InvalidAvroSchemaException::errorCode() => $factoryFn(InvalidAvroSchemaException::class), - SchemaNotFoundException::errorCode() => $factoryFn(SchemaNotFoundException::class), - SubjectNotFoundException::errorCode() => $factoryFn(SubjectNotFoundException::class), - VersionNotFoundException::errorCode() => $factoryFn(VersionNotFoundException::class), - InvalidCompatibilityLevelException::errorCode() => $factoryFn(InvalidCompatibilityLevelException::class), - ]; - } - - public function __invoke(ResponseInterface $response): SchemaRegistryException - { - return $this->exceptionFor($response); } - /** - * Maps a ResponseInterface to the internal SchemaRegistryException types. + * Maps a RequestException to the internal SchemaRegistryException types. * * @param ResponseInterface $response * * @return SchemaRegistryException - * - * @throws RuntimeException */ - public function exceptionFor(ResponseInterface $response): SchemaRegistryException + public function __invoke(ResponseInterface $response): SchemaRegistryException { - $decodedBody = $this->guardAgainstMissingErrorCode($response); + $this->guardAgainstValidHTPPCode($response); + + $decodedBody = Json::decodeResponse($response); + $this->guardAgainstMissingErrorCode($decodedBody); $errorCode = $decodedBody[self::ERROR_CODE_FIELD_NAME]; - $errorMessage = $decodedBody[self::ERROR_MESSAGE_FIELD_NAME] ?? "Unknown Error"; + $errorMessage = $decodedBody[self::ERROR_MESSAGE_FIELD_NAME]; return $this->mapErrorCodeToException($errorCode, $errorMessage); } - public function hasMappableError(ResponseInterface $response): bool + public function isHttpError(ResponseInterface $response): bool { - $statusCode = $response->getStatusCode(); - - return $statusCode >= 400 && $statusCode < 600; + return $response->getStatusCode() >= 400 && $response->getStatusCode() < 600; } /** - * @param ResponseInterface $response - * @return array + * @param array $decodedBody */ - private function guardAgainstMissingErrorCode(ResponseInterface $response): array + private function guardAgainstMissingErrorCode(array $decodedBody): void { - try { - $decodedBody = Json::decode((string)$response->getBody()); - - if (!is_array($decodedBody) || !array_key_exists(self::ERROR_CODE_FIELD_NAME, $decodedBody)) { - throw new RuntimeException( - sprintf( - 'Invalid message body received - cannot find "error_code" field in response body "%s"', - (string) $response->getBody() - ) - ); - } - - return $decodedBody; - } catch (Exception $e) { + if (!is_array($decodedBody) || !array_key_exists(self::ERROR_CODE_FIELD_NAME, $decodedBody)) { throw new RuntimeException( - sprintf( - 'Invalid message body received - cannot find "error_code" field in response body "%s"', - (string) $response->getBody() - ), - $e->getCode(), - $e + 'Invalid message body received - cannot find "error_code" field in response body.' ); } } private function mapErrorCodeToException(int $errorCode, string $errorMessage): SchemaRegistryException { - if (!array_key_exists($errorCode, $this->map)) { - throw new RuntimeException(sprintf('Unknown error code "%d"', $errorCode)); + switch ($errorCode) { + case IncompatibleAvroSchemaException::errorCode(): + return new IncompatibleAvroSchemaException($errorMessage, $errorCode); + + case BackendDataStoreException::errorCode(): + return new BackendDataStoreException($errorMessage, $errorCode); + + case OperationTimedOutException::errorCode(): + return new OperationTimedOutException($errorMessage, $errorCode); + + case MasterProxyException::errorCode(): + return new MasterProxyException($errorMessage, $errorCode); + + case InvalidVersionException::errorCode(): + return new InvalidVersionException($errorMessage, $errorCode); + + case InvalidAvroSchemaException::errorCode(): + return new InvalidAvroSchemaException($errorMessage, $errorCode); + + case SchemaNotFoundException::errorCode(): + return new SchemaNotFoundException($errorMessage, $errorCode); + + case SubjectNotFoundException::errorCode(): + return new SubjectNotFoundException($errorMessage, $errorCode); + + case VersionNotFoundException::errorCode(): + return new VersionNotFoundException($errorMessage, $errorCode); + + case InvalidCompatibilityLevelException::errorCode(): + return new InvalidCompatibilityLevelException($errorMessage, $errorCode); + + default: + throw new RuntimeException(sprintf('Unknown error code "%d"', $errorCode)); } + } - return $this->map[$errorCode]($errorCode, $errorMessage); + private function guardAgainstValidHTPPCode(ResponseInterface $response): void + { + if (!$this->isHttpError($response)) { + throw new RuntimeException( + sprintf('Cannot process response without invalid HTTP code %d', $response->getStatusCode()), + ); + } } } diff --git a/src/Registry/GuzzlePromiseAsyncRegistry.php b/src/Registry/GuzzlePromiseAsyncRegistry.php index 38f66ed..900b519 100644 --- a/src/Registry/GuzzlePromiseAsyncRegistry.php +++ b/src/Registry/GuzzlePromiseAsyncRegistry.php @@ -37,31 +37,18 @@ public function __construct(ClientInterface $client) $this->client = $client; $exceptionMap = ExceptionMap::instance(); - $responseExistenceGuard = static function (RequestException $exception): ResponseInterface { - $response = $exception->getResponse(); - - if (!$response) { - throw new RuntimeException( - "RequestException does not provide a response to inspect.", - $exception->getCode(), - $exception - ); + $this->rejectedCallback = static function (RequestException $exception) use ($exceptionMap): SchemaRegistryException { + if (!$exception->hasResponse()) { + throw new RuntimeException('RequestException has no response to inspect', RuntimeException::errorCode(), $exception); } - return $response; - }; - - $this->rejectedCallback = static function (RequestException $exception) use ( - $exceptionMap, - $responseExistenceGuard - ): SchemaRegistryException { - return $exceptionMap->exceptionFor($responseExistenceGuard($exception)); + return $exceptionMap($exception->getResponse()); /** @phpstan-ignore-line */ }; } /** * {@inheritdoc} - * + *x * @throws RuntimeException */ public function register(string $subject, AvroSchema $schema, AvroReference ...$references): PromiseInterface diff --git a/src/Registry/Psr18SyncRegistry.php b/src/Registry/Psr18SyncRegistry.php index 039e997..5edd18d 100644 --- a/src/Registry/Psr18SyncRegistry.php +++ b/src/Registry/Psr18SyncRegistry.php @@ -27,15 +27,9 @@ class Psr18SyncRegistry implements SynchronousRegistry */ private $client; - /** - * @var ExceptionMap - */ - private $map; - public function __construct(ClientInterface $client) { $this->client = $client; - $this->map = ExceptionMap::instance(); } public function register(string $subject, AvroSchema $schema, AvroReference ...$references): int @@ -105,9 +99,13 @@ public function schemaForSubjectAndVersion(string $subject, int $version): AvroS */ private function guardAgainstErrorResponse(ResponseInterface $response): void { - if ($this->map->hasMappableError($response)) { - throw $this->map->exceptionFor($response); + $map = ExceptionMap::instance(); + + if (!$map->isHttpError($response)) { + return; } + + throw $map($response); } private function makeRequest(RequestInterface $request): ResponseInterface diff --git a/src/Requests.php b/src/Requests.php index 6c3beb3..a1a5774 100644 --- a/src/Requests.php +++ b/src/Requests.php @@ -9,6 +9,7 @@ use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Utils; use Psr\Http\Message\RequestInterface; +use function count; final class Requests { @@ -49,7 +50,7 @@ public static function prepareJsonSchemaForTransfer(string $schema, AvroReferenc 'schema' => $schema ]; - return !$references + return count($references) === 0 ? Json::encode($return) : Json::encode(array_merge($return, ['references' => $references])); } diff --git a/test/Exception/ExceptionMapTest.php b/test/Exception/ExceptionMapTest.php index 23d7b92..2fc9ab1 100644 --- a/test/Exception/ExceptionMapTest.php +++ b/test/Exception/ExceptionMapTest.php @@ -17,7 +17,8 @@ use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; use FlixTech\SchemaRegistryApi\Exception\SubjectNotFoundException; use FlixTech\SchemaRegistryApi\Exception\VersionNotFoundException; -use Generator; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use LogicException; use PHPUnit\Framework\TestCase; @@ -34,7 +35,7 @@ public function it_should_handle_InvalidAvroSchema_code(): void InvalidAvroSchemaException::class, 'Invalid Avro schema', 42201, - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 422, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -53,7 +54,7 @@ public function it_should_handle_IncompatibleAvroSchema_code(): void IncompatibleAvroSchemaException::class, 'Incompatible Avro schema', 409, - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 409, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -72,7 +73,7 @@ public function it_should_handle_BackendDataStore_code(): void BackendDataStoreException::class, 'Error in the backend datastore', 50001, - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 500, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -91,7 +92,7 @@ public function it_should_handle_InvalidCompatibilityLevel_code(): void InvalidCompatibilityLevelException::class, 'Invalid compatibility level', 42203, - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 422, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -110,7 +111,7 @@ public function it_should_handle_InvalidVersion_code(): void InvalidVersionException::class, 'Invalid version', 42202, - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 422, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -129,7 +130,7 @@ public function it_should_handle_MasterProxy_code(): void MasterProxyException::class, 'Error while forwarding the request to the master', 50003, - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 500, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -148,7 +149,7 @@ public function it_should_handle_OperationTimedOut_code(): void OperationTimedOutException::class, 'Operation timed out', 50002, - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 500, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -167,7 +168,7 @@ public function it_should_handle_SchemaNotFound_code(): void SchemaNotFoundException::class, 'Schema not found', 40403, - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 404, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -186,7 +187,7 @@ public function it_should_handle_SubjectNotFound_code(): void SubjectNotFoundException::class, 'Subject not found', 40401, - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 404, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -205,7 +206,7 @@ public function it_should_handle_VersionNotFound_code(): void VersionNotFoundException::class, 'Version not found', 40402, - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 404, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -215,24 +216,6 @@ public function it_should_handle_VersionNotFound_code(): void ); } - /** - * @test - * @dataProvider statusCodeProvider - * @param int $statusCode - * @param bool $expected - */ - public function it_should_be_able_to_determine_if_an_exception_should_be_thrown(int $statusCode, bool $expected): void - { - $actual = (ExceptionMap::instance())->hasMappableError( - new Response( - $statusCode, - ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], - '{"error_code":40402,"message": "Version not found"}' - ) - ); - self::assertEquals($expected, $actual); - } - /** * @test */ @@ -250,7 +233,7 @@ public function it_should_not_process_exceptions_with_missing_error_codes(): voi $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Invalid message body received - cannot find "error_code" field in response body'); - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 404, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -267,7 +250,7 @@ public function it_should_not_process_unknown_error_codes(): void $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Unknown error code "99999"'); - (ExceptionMap::instance())->exceptionFor( + (ExceptionMap::instance())( new Response( 404, ['Content-Type' => 'application/vnd.schemaregistry.v1+json'], @@ -286,13 +269,6 @@ private function assertSchemaRegistryException( self::assertEquals($errorCode, $exception->getCode()); self::assertEquals($expectedMessage, $exception->getMessage()); } - - public function statusCodeProvider(): ?Generator - { - yield 'Valid 400' => [400, true]; - yield 'Valid 599' => [599, true]; - yield 'Invalid 399' => [399, false]; - } } class InvalidNewSchemaRegistryException extends AbstractSchemaRegistryException diff --git a/test/Registry/GuzzlePromiseAsyncRegistryTest.php b/test/Registry/GuzzlePromiseAsyncRegistryTest.php index 287b49a..5c2014b 100644 --- a/test/Registry/GuzzlePromiseAsyncRegistryTest.php +++ b/test/Registry/GuzzlePromiseAsyncRegistryTest.php @@ -12,6 +12,8 @@ use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; use FlixTech\SchemaRegistryApi\Registry\GuzzlePromiseAsyncRegistry; use FlixTech\SchemaRegistryApi\Requests; +use FlixTech\SchemaRegistryApi\Schema\AvroName; +use FlixTech\SchemaRegistryApi\Schema\AvroReference; use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; @@ -41,14 +43,15 @@ public function it_should_register_schemas(): void ]; $subject = 'test'; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = Requests::registerNewSchemaVersionWithSubjectRequest((string)$schema, $subject); + $references = new AvroReference(new AvroName('test.name'), 'example-value', Constants::VERSION_LATEST); + $expectedRequest = Requests::registerNewSchemaVersionWithSubjectRequest((string)$schema, $subject, $references); $container = []; $client = $this->clientWithMockResponses($responses, $container); $this->registry = new GuzzlePromiseAsyncRegistry($client); - $promise = $this->registry->register($subject, $schema); + $promise = $this->registry->register($subject, $schema, $references); self::assertEquals(3, $promise->wait()); $this->assertRequestCallable($expectedRequest)($container[0]['request']); diff --git a/test/Registry/Psr18SyncRegistryTest.php b/test/Registry/Psr18SyncRegistryTest.php index ceb63c7..67a88a0 100644 --- a/test/Registry/Psr18SyncRegistryTest.php +++ b/test/Registry/Psr18SyncRegistryTest.php @@ -11,6 +11,8 @@ use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; use FlixTech\SchemaRegistryApi\Registry\Psr18SyncRegistry; use FlixTech\SchemaRegistryApi\Requests; +use FlixTech\SchemaRegistryApi\Schema\AvroName; +use FlixTech\SchemaRegistryApi\Schema\AvroReference; use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; @@ -39,12 +41,13 @@ public function it_should_register_schemas(): void ]; $subject = 'test'; $schema = AvroSchema::parse('{"type": "string"}'); - $expectedRequest = Requests::registerNewSchemaVersionWithSubjectRequest((string)$schema, $subject); + $references = new AvroReference(new AvroName('test.name'), 'example-value', Constants::VERSION_LATEST); + $expectedRequest = Requests::registerNewSchemaVersionWithSubjectRequest((string)$schema, $subject, $references); $container = []; $this->registry = new Psr18SyncRegistry($this->clientWithMockResponses($responses, $container)); - self::assertEquals(3, $this->registry->register($subject, $schema)); + self::assertEquals(3, $this->registry->register($subject, $schema, $references)); $this->assertRequestCallable($expectedRequest)($container[0]['request']); } diff --git a/test/Schema/AvroReferenceTest.php b/test/Schema/AvroReferenceTest.php index 98950e1..951c01d 100644 --- a/test/Schema/AvroReferenceTest.php +++ b/test/Schema/AvroReferenceTest.php @@ -19,7 +19,7 @@ class AvroReferenceTest extends TestCase * @param string $subject * @param string|int $version * @param bool $isValid - * @param string $expectedJson + * @param ?string $expectedJson */ public function it_should_be_constructable(string $avroName, string $subject, $version, bool $isValid, ?string $expectedJson): void { From 7f5a217f30153e37c40e35ddd73484e27f5a344f Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Tue, 16 Nov 2021 11:50:23 +0100 Subject: [PATCH 15/16] Fixing IntegrationTest.php --- test/IntegrationTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/IntegrationTest.php b/test/IntegrationTest.php index 0e440b2..0b59c01 100644 --- a/test/IntegrationTest.php +++ b/test/IntegrationTest.php @@ -179,7 +179,7 @@ function (ResponseInterface $request) { function (RequestException $exception) { $this->assertInstanceOf( IncompatibleAvroSchemaException::class, - (ExceptionMap::instance())($exception) + (ExceptionMap::instance())($exception->getResponse()) /** @phpstan-ignore-line */ ); } )->wait(); @@ -190,7 +190,7 @@ function (RequestException $exception) { function (RequestException $exception) { $this->assertInstanceOf( InvalidAvroSchemaException::class, - (ExceptionMap::instance())($exception) + (ExceptionMap::instance())($exception->getResponse()) /** @phpstan-ignore-line */ ); } )->wait(); @@ -201,7 +201,7 @@ function (RequestException $exception) { function (RequestException $exception) { $this->assertInstanceOf( SubjectNotFoundException::class, - (ExceptionMap::instance())($exception) + (ExceptionMap::instance())($exception->getResponse()) /** @phpstan-ignore-line */ ); } )->wait(); @@ -212,7 +212,7 @@ function (RequestException $exception) { function (RequestException $exception) { $this->assertInstanceOf( InvalidVersionException::class, - (ExceptionMap::instance())($exception) + (ExceptionMap::instance())($exception->getResponse()) /** @phpstan-ignore-line */ ); } )->wait(); @@ -223,7 +223,7 @@ function (RequestException $exception) { function (RequestException $exception) { $this->assertInstanceOf( VersionNotFoundException::class, - (ExceptionMap::instance())($exception) + (ExceptionMap::instance())($exception->getResponse()) /** @phpstan-ignore-line */ ); } )->wait(); @@ -234,7 +234,7 @@ function (RequestException $exception) { function (RequestException $exception) { $this->assertInstanceOf( SchemaNotFoundException::class, - (ExceptionMap::instance())($exception) + (ExceptionMap::instance())($exception->getResponse()) /** @phpstan-ignore-line */ ); } )->wait(); From 3ce999651e45daf81211184ffae739869a99aa11 Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Tue, 16 Nov 2021 11:54:12 +0100 Subject: [PATCH 16/16] Fixing CS --- test/Exception/ExceptionMapTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/Exception/ExceptionMapTest.php b/test/Exception/ExceptionMapTest.php index 2fc9ab1..067862f 100644 --- a/test/Exception/ExceptionMapTest.php +++ b/test/Exception/ExceptionMapTest.php @@ -17,8 +17,6 @@ use FlixTech\SchemaRegistryApi\Exception\SchemaRegistryException; use FlixTech\SchemaRegistryApi\Exception\SubjectNotFoundException; use FlixTech\SchemaRegistryApi\Exception\VersionNotFoundException; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use LogicException; use PHPUnit\Framework\TestCase;