-
-
Notifications
You must be signed in to change notification settings - Fork 966
fix(jsonapi): add missing "included" schema parts #6277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
e39f8df
fix(jsonapi): add missing "included" schema parts
GwendolenLynch 16150ea
fix(test): test correct format
GwendolenLynch 6eacdcc
chore(jsonschema): refactor definition name logic
GwendolenLynch d2bbc26
remove useless comment
soyuka c7e89d6
remove empty line
soyuka c7f7de1
add on invalid
soyuka File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| <?php | ||
|
|
||
| /* | ||
| * This file is part of the API Platform project. | ||
| * | ||
| * (c) Kévin Dunglas <dunglas@gmail.com> | ||
| * | ||
| * For the full copyright and license information, please view the LICENSE | ||
| * file that was distributed with this source code. | ||
| */ | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace ApiPlatform\JsonSchema; | ||
|
|
||
| use ApiPlatform\Metadata\Operation; | ||
| use ApiPlatform\Metadata\Util\ResourceClassInfoTrait; | ||
| use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; | ||
|
|
||
| final class DefinitionNameFactory implements DefinitionNameFactoryInterface | ||
| { | ||
| use ResourceClassInfoTrait; | ||
|
|
||
| public function __construct(private ?array $distinctFormats) | ||
| { | ||
| } | ||
|
|
||
| public function create(string $className, string $format = 'json', ?string $inputOrOutputClass = null, ?Operation $operation = null, array $serializerContext = []): string | ||
| { | ||
| if ($operation) { | ||
| $prefix = $operation->getShortName(); | ||
| } | ||
|
|
||
| if (!isset($prefix)) { | ||
| $prefix = (new \ReflectionClass($className))->getShortName(); | ||
| } | ||
|
|
||
| if (null !== $inputOrOutputClass && $className !== $inputOrOutputClass) { | ||
| $parts = explode('\\', $inputOrOutputClass); | ||
| $shortName = end($parts); | ||
| $prefix .= '.'.$shortName; | ||
| } | ||
|
|
||
| if ('json' !== $format && ($this->distinctFormats[$format] ?? false)) { | ||
| // JSON is the default, and so isn't included in the definition name | ||
| $prefix .= '.'.$format; | ||
| } | ||
|
|
||
| $definitionName = $serializerContext[SchemaFactory::OPENAPI_DEFINITION_NAME] ?? null; | ||
| if ($definitionName) { | ||
| $name = sprintf('%s-%s', $prefix, $definitionName); | ||
| } else { | ||
| $groups = (array) ($serializerContext[AbstractNormalizer::GROUPS] ?? []); | ||
| $name = $groups ? sprintf('%s-%s', $prefix, implode('_', $groups)) : $prefix; | ||
| } | ||
|
|
||
| return $this->encodeDefinitionName($name); | ||
| } | ||
|
|
||
| private function encodeDefinitionName(string $name): string | ||
| { | ||
| return preg_replace('/[^a-zA-Z0-9.\-_]/', '.', $name); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| <?php | ||
|
|
||
| /* | ||
| * This file is part of the API Platform project. | ||
| * | ||
| * (c) Kévin Dunglas <dunglas@gmail.com> | ||
| * | ||
| * For the full copyright and license information, please view the LICENSE | ||
| * file that was distributed with this source code. | ||
| */ | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace ApiPlatform\JsonSchema; | ||
|
|
||
| use ApiPlatform\Metadata\Operation; | ||
|
|
||
| /** | ||
| * Factory for creating definition names for resources in a JSON Schema document. | ||
| * | ||
| * @author Gwendolen Lynch <gwendolen.lynch@gmail.com> | ||
| */ | ||
| interface DefinitionNameFactoryInterface | ||
|
GwendolenLynch marked this conversation as resolved.
|
||
| { | ||
| /** | ||
| * Creates a resource definition name. | ||
| * | ||
| * @param class-string $className | ||
| * | ||
| * @return string the definition name | ||
| */ | ||
| public function create(string $className, string $format = 'json', ?string $inputOrOutputClass = null, ?Operation $operation = null, array $serializerContext = []): string; | ||
|
GwendolenLynch marked this conversation as resolved.
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| <?php | ||
|
|
||
| /* | ||
| * This file is part of the API Platform project. | ||
| * | ||
| * (c) Kévin Dunglas <dunglas@gmail.com> | ||
| * | ||
| * For the full copyright and license information, please view the LICENSE | ||
| * file that was distributed with this source code. | ||
| */ | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace ApiPlatform\JsonSchema; | ||
|
|
||
| use ApiPlatform\Metadata\CollectionOperationInterface; | ||
| use ApiPlatform\Metadata\Exception\OperationNotFoundException; | ||
| use ApiPlatform\Metadata\HttpOperation; | ||
| use ApiPlatform\Metadata\Operation; | ||
| use ApiPlatform\Metadata\Resource\ResourceMetadataCollection; | ||
| use ApiPlatform\Metadata\Util\ResourceClassInfoTrait; | ||
|
|
||
| /** | ||
| * @internal | ||
| */ | ||
| trait ResourceMetadataTrait | ||
| { | ||
| use ResourceClassInfoTrait; | ||
|
|
||
| private function findOutputClass(string $className, string $type, Operation $operation, ?array $serializerContext): ?string | ||
| { | ||
| $inputOrOutput = ['class' => $className]; | ||
| $inputOrOutput = Schema::TYPE_OUTPUT === $type ? ($operation->getOutput() ?? $inputOrOutput) : ($operation->getInput() ?? $inputOrOutput); | ||
| $forceSubschema = $serializerContext[SchemaFactory::FORCE_SUBSCHEMA] ?? false; | ||
|
|
||
| return $forceSubschema ? ($inputOrOutput['class'] ?? $inputOrOutput->class ?? $operation->getClass()) : ($inputOrOutput['class'] ?? $inputOrOutput->class ?? null); | ||
|
GwendolenLynch marked this conversation as resolved.
|
||
| } | ||
|
|
||
| private function findOperation(string $className, string $type, ?Operation $operation, ?array $serializerContext): Operation | ||
| { | ||
| if (null === $operation) { | ||
| if (null === $this->resourceMetadataFactory) { | ||
| return new HttpOperation(); | ||
| } | ||
| $resourceMetadataCollection = $this->resourceMetadataFactory->create($className); | ||
|
GwendolenLynch marked this conversation as resolved.
|
||
|
|
||
| try { | ||
| $operation = $resourceMetadataCollection->getOperation(); | ||
| } catch (OperationNotFoundException $e) { | ||
| $operation = new HttpOperation(); | ||
| } | ||
| $forceSubschema = $serializerContext[SchemaFactory::FORCE_SUBSCHEMA] ?? false; | ||
| if ($operation->getShortName() === $this->getShortClassName($className) && $forceSubschema) { | ||
| $operation = new HttpOperation(); | ||
| } | ||
|
|
||
| return $this->findOperationForType($resourceMetadataCollection, $type, $operation); | ||
| } | ||
|
|
||
| // The best here is to use an Operation when calling `buildSchema`, we try to do a smart guess otherwise | ||
| if ($this->resourceMetadataFactory && !$operation->getClass()) { | ||
| $resourceMetadataCollection = $this->resourceMetadataFactory->create($className); | ||
|
GwendolenLynch marked this conversation as resolved.
|
||
|
|
||
| if ($operation->getName()) { | ||
| return $resourceMetadataCollection->getOperation($operation->getName()); | ||
| } | ||
|
|
||
| return $this->findOperationForType($resourceMetadataCollection, $type, $operation); | ||
| } | ||
|
|
||
| return $operation; | ||
| } | ||
|
|
||
| private function findOperationForType(ResourceMetadataCollection $resourceMetadataCollection, string $type, Operation $operation): Operation | ||
| { | ||
| // Find the operation and use the first one that matches criterias | ||
| foreach ($resourceMetadataCollection as $resourceMetadata) { | ||
| foreach ($resourceMetadata->getOperations() ?? [] as $op) { | ||
| if ($operation instanceof CollectionOperationInterface && $op instanceof CollectionOperationInterface) { | ||
| $operation = $op; | ||
| break 2; | ||
| } | ||
|
|
||
| if (Schema::TYPE_INPUT === $type && \in_array($op->getMethod(), ['POST', 'PATCH', 'PUT'], true)) { | ||
| $operation = $op; | ||
| break 2; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return $operation; | ||
| } | ||
|
|
||
| private function getSerializerContext(Operation $operation, string $type = Schema::TYPE_OUTPUT): array | ||
| { | ||
| return Schema::TYPE_OUTPUT === $type ? ($operation->getNormalizationContext() ?? []) : ($operation->getDenormalizationContext() ?? []); | ||
| } | ||
|
|
||
| private function getShortClassName(string $fullyQualifiedName): string | ||
| { | ||
| $parts = explode('\\', $fullyQualifiedName); | ||
| return end($parts); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why would you reset the context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was hoping you'd bring this up 👍
I haven't mapped it out fully yet, but the JSON:API serializer seems to mostly ignore normalization groups (c.f.
includequery parameter.So if it is not a
jsonapiformat, just pass it on as-is, else generate the schema without groups. Hadn't gotten past that so far. I got distracted with enums.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you mention the json:api specification in a comment (e.g: the JSON:API serializer ignores normalization groups and uses an
includeparameter)?side note I've come accross:
core/src/OpenApi/Factory/OpenApiFactory.php
Line 402 in 93f8b5f
Looks like we don't send the context from the openapi factory but I'm not sure why.