-
-
Notifications
You must be signed in to change notification settings - Fork 906
Add JSON API basic support #1036
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
Add JSON API basic support #1036
Conversation
Great! |
What is |
It's an internal marker allowing a normalizer to build a context for serializing a subresource itself instead of relying on the trait. |
Is it ok to make PATCH universally available or should it be available only if using JSON API format? Right now I went for the former. In order to get tests working (and without fully knowing about the Hydra spec) right now I added a PATCH operation with |
Thanks for contributing to this ! didn't have much time to go forward. Ill try to review it when it will be ready. I think it's ok to have patch support, @dunglas ? |
* Flattens possible 'page' array query parameter into dot-separated values to avoid | ||
* conflicts with Doctrine\Orm\Extension\PaginationExtension. | ||
* | ||
* See: http://jsonapi.org/format/#fetching-pagination |
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.
You can use @see
.
* Flattens possible 'filter' array query parameter into first-level query parameters | ||
* to be processed by api-platform. | ||
* | ||
* See: http://jsonapi.org/format/#fetching-filtering and http://jsonapi.org/recommendations/#filtering |
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.
Same here
} | ||
|
||
// If page query parameter is not defined or is not an array, never mind | ||
if (!$request->query->get('filter') || !is_array($request->query->get('filter'))) { |
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.
This one will be faster: (less function call, the better):
$filter = $request->query->get('filter');
if (null === $filter || !is_array($filter)) {
return;
}
} | ||
|
||
// Otherwise, flatten into dot-separated values | ||
$pageParameters = $request->query->get('filter'); |
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.
And you'll be able to reuse the $filter
variable here.
final class TransformSortingParametersListener | ||
{ | ||
/** | ||
* @var string Keyword used to retrieve the value |
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.
You can remove this docblock (it is useless)
@@ -417,7 +489,7 @@ protected function getAttributeValue($object, $attribute, $format = null, array | |||
* | |||
* @return string|array | |||
*/ | |||
private function normalizeRelation(PropertyMetadata $propertyMetadata, $relatedObject, string $resourceClass, string $format = null, array $context) | |||
protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relatedObject, string $resourceClass, string $format = null, array $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 would prefer to don't propagate this data structure outside of the JSONAPI component. Can't you transform this array in IRI before passing it to the abstract normalizer?
class CamelCaseToDashedCaseNameConverter implements NameConverterInterface | ||
{ | ||
/** | ||
* @var array|null |
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.
Useless
private $attributes; | ||
|
||
/** | ||
* @var bool |
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.
Useless
* @param null|array $attributes The list of attributes to rename or null for all attributes | ||
* @param bool $lowerCamelCase Use lowerCamelCase style | ||
*/ | ||
public function __construct(array $attributes = null, $lowerCamelCase = true) |
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.
bool $lowerCamelCase
/** | ||
* CamelCase to dashed name converter. | ||
* | ||
* Based on Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter. |
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't we use it directly instead of copying the class?
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.
No because that one does not allow to use underscores instead of dashes.
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.
Thanks a lot. I fixed most of the stuff.
Still pending: enable PATCH only if using JSON API.
@@ -417,7 +489,7 @@ protected function getAttributeValue($object, $attribute, $format = null, array | |||
* | |||
* @return string|array | |||
*/ | |||
private function normalizeRelation(PropertyMetadata $propertyMetadata, $relatedObject, string $resourceClass, string $format = null, array $context) | |||
protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relatedObject, string $resourceClass, string $format = null, array $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.
Ok. Approaching another way.
$isValidJsonapi = 'response.json is valid JSON API.' === $validationResponse; | ||
|
||
if (!$isValidJsonapi) { | ||
unlink($fileName); |
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.
Yep, good catch. Thanks.
); | ||
} | ||
|
||
$normalizedItem = $normalizedItem['data']; |
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.
Sure. I agree. WDYT about $normalizedItemData = $normalizedItem['data'];
?
|
||
$relationships = []; | ||
|
||
if (isset($normalizedItem['relationships'])) { |
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.
Yeah, no point. Sorry about that.
$fieldName | ||
); | ||
|
||
if ($this->nameConverter) { |
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.
Sure, I took Symfony code this as a reference, but see no problem.
foreach ($attributeValue as $attributeValueElement) { | ||
if (!isset($attributeValueElement['data'])) { | ||
throw new RuntimeException(sprintf( | ||
'Expected \'data\' attribute in collection for attribute \'%s\'', |
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.
Would it be ok to specify the attribute? Something like 'The JSON API attribute \'%s\' must contain a "data" key.'
@@ -46,7 +46,7 @@ public function create(string $resourceClass): ResourceMetadata | |||
|
|||
if (null === $resourceMetadata->getItemOperations()) { | |||
$resourceMetadata = $resourceMetadata->withItemOperations($this->createOperations( | |||
$isAbstract ? ['GET', 'DELETE'] : ['GET', 'PUT', 'DELETE'] | |||
$isAbstract ? ['GET', 'DELETE'] : ['GET', 'PUT', 'DELETE', 'PATCH'] |
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.
Sure. I added it to the PR description.
/** | ||
* CamelCase to dashed name converter. | ||
* | ||
* Based on Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter. |
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.
No because that one does not allow to use underscores instead of dashes.
@@ -417,7 +489,7 @@ protected function getAttributeValue($object, $attribute, $format = null, array | |||
* | |||
* @return string|array | |||
*/ | |||
private function normalizeRelation(PropertyMetadata $propertyMetadata, $relatedObject, string $resourceClass, string $format = null, array $context) | |||
protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relatedObject, string $resourceClass, string $format = null, array $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.
Rolled back the changes on the visilibity of both methods, but got to copy some code to the JsonApi ItemNormalizer, hope thats ok.
{ | ||
$fileName = $this->iSaveTheResponse(); | ||
|
||
$validationResponse = exec(sprintf('cd %s && jsonapi-validator -f response.json', __DIR__)); |
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 repost my comment from #785 (comment) here, I'm really not a fan of this.
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 agree. It should be done in Travis directly. No need to run the command from PHP.
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.
@dunglas, my suggestion was to use \Sanpi\Behatch\Json\JsonInspector::validate()
and not to run any command in Travis or PHP! 😛
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.
Yep I agree with @meyerbaptiste didn't had the time to do it but we should do that
$trace = $object->getTrace(); | ||
} | ||
|
||
$message = $object->getErrorMessage($object, $context, $this->debug); |
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.
It's $this
instead of $object
here. BTW, this var is useless.
|
||
$data = [ | ||
'title' => $context['title'] ?? 'An error occurred', | ||
'description' => $message ?? (string) $object, |
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.
(string) $object
is not correct here because if $object
var is a FlattenException
instance then you will get a fatal error because this class cannot be converted to string.
BTW, ErrorNormalizerTrait::getErrorMessage()
always returns a string, so the null coalescing operator is useless here:
'description' => $this->getErrorMessage($object, $context, $this->debug),
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.
You're totally right. Thanks a lot. I just pushed the fix.
@@ -5,11 +5,12 @@ Feature: NelmioApiDoc integration | |||
|
|||
Scenario: Create a user | |||
When I send a "GET" request to "/nelmioapidoc" | |||
Then the response status code should be 200 | |||
And the response status code should be 200 |
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 this change?
|
||
@createSchema | ||
@dropSchema | ||
Scenario: Correctly serialize a collection |
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.
Your scenario isn't correct. You have to divide it into several scenarios, with one for each operation. Each scenario consist of a list of steps: Givens, Whens, Thens. In your scenario here, there is only a list of Whens, no Thens, it's a little insane.
If you want to know more about this, I invite you to follow this link: http://docs.behat.org/en/latest/user_guide.html
You can also take inspiration from what has been done for Hydra: https://github.com/api-platform/core/tree/master/features/hydra
How close is this work? Is there major hurdles to overcome? |
Work on this was followed up in #1175. Please have a look there. I'm closing this in favor of that PR. |
* GraphQL Query support (api-platform#1358) * Fix missing service when no Varnish URL is defined * [PropertyFilter] Fix whitelist comparison (api-platform#1379) * Remove wrong doc * Swagger subcollection documentation issue (api-platform#1395) * Make the requirements configurable Closes api-platform#1401 * Provide a better exception message and type Getting "Not found" on a route where you are getting an object can get really confusing. * Bump branch alias to 2.2.x-dev 2.1.x-dev is already taken by the 2.1 branch, and this should represent the next minor version anyway. * Fix tests * Reuse PriorityTaggedServiceTrait from symfony * Improve payload support and remove duplicate code in ConstraintViolationListNormalizer (api-platform#1416) * Filter Annotation implementation Parent class filters (needs test) Support for nested properties Tests Fix some comments and remove id=>id in compiler pass * Throw on abstract data providers / filters * Remove an unused var * Remove useless badges * Enable the coverage * Fix some quality issues * Add job to test upstream libs deprecations If api-platform uses upstream libs in a deprecated way, we should be aware of it. * Add job to test upstream libs deprecations If api-platform uses upstream libs in a deprecated way, we should be aware of it. * Fix missing cache tag on empty collections * Allow plain IDs with `allow_plain_identifiers` * Fix indentation for GraphQL features. * Add JSON API basic support (api-platform#785, api-platform#1036, api-platform#1175) * Clean Behat tests * Fix tags addition with an empty value * Document swagger-specific description options … and where to find them should there be newer ones. * Fix PHPUnit tests * Fix missing return statement * Support & compatibility for PHP7.2 * Add feature to update swagger context for properties * Generator compat improvements (api-platform#1429) * Add support for resource names without namespace * [SF 4.0] Make actions explicitly public * Allow phpdocumentor/reflection-docblock 4 * fix hydra documentation normalizer with subresources * Create a base collection normalizer * Fix request auto-runner * Update the changelog * Update changelog
Add support for the JSON API format: http://jsonapi.org/format/
TODOs:
data
key supportOther desirable features:
Add suport for sparse fieldsets (similar to #1062)
Add basic include support
Improve POST support by adding the
Location
header according to specImprove linking support
Looking to contribute to #785.
Also related to #147 and #759 because of the PATCH HTTP method support.