Skip to content

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

Closed
wants to merge 50 commits into from
Closed

Add JSON API basic support #1036

wants to merge 50 commits into from

Conversation

hectorh30
Copy link

@hectorh30 hectorh30 commented Apr 7, 2017

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR TODO

Add support for the JSON API format: http://jsonapi.org/format/

TODOs:

  • Add data key support
  • Add PATCH support to updates
  • Add basic error serialization support
  • Add basic pagination support
  • Add basic ordering support
  • Add basic filtering support
  • Fix PATCH support on main api interface
  • Add support for GETting many-to-many relationships
  • Add support for POSTing and PATCHing many-to-many relationships
  • Enable the PATCH operation only if JSONAPI support is enabled.
  • Improve test code coverage.

Other desirable features:
Add suport for sparse fieldsets (similar to #1062)
Add basic include support
Improve POST support by adding the Location header according to spec
Improve linking support

Looking to contribute to #785.
Also related to #147 and #759 because of the PATCH HTTP method support.

@dunglas
Copy link
Member

dunglas commented Apr 7, 2017

Great!

@hectorh30
Copy link
Author

hectorh30 commented Apr 8, 2017

What is api_sub_level flag used for? I'm worndering if it is to be considered in the JSON API normalizers, but can't find anything on the docs or the related PR (#616) about it

@hectorh30 hectorh30 changed the title Feature/add jsonapi update Add JSON API basic support Apr 8, 2017
@dunglas
Copy link
Member

dunglas commented Apr 8, 2017

It's an internal marker allowing a normalizer to build a context for serializing a subresource itself instead of relying on the trait.

@hectorh30
Copy link
Author

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 @type: hydra:Operation. Hope thats not too bad.

@soyuka soyuka added this to the 2.1.0 milestone Apr 12, 2017
@Simperfit
Copy link
Contributor

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
Copy link
Member

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
Copy link
Member

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'))) {
Copy link
Member

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');
Copy link
Member

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
Copy link
Member

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)
Copy link
Member

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
Copy link
Member

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
Copy link
Member

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)
Copy link
Member

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.
Copy link
Member

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?

Copy link
Author

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.

Copy link
Author

@hectorh30 hectorh30 left a 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)
Copy link
Author

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);
Copy link
Author

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'];
Copy link
Author

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'])) {
Copy link
Author

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) {
Copy link
Author

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\'',
Copy link
Author

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']
Copy link
Author

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.
Copy link
Author

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)
Copy link
Author

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__));
Copy link
Member

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.

Copy link
Member

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.

Copy link
Member

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! 😛

Copy link
Contributor

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);
Copy link
Member

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,
Copy link
Member

@meyerbaptiste meyerbaptiste Apr 25, 2017

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),

Copy link
Author

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
Copy link
Member

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
Copy link
Member

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

@StephenOTT
Copy link

How close is this work?

Is there major hurdles to overcome?

@hectorh30
Copy link
Author

Work on this was followed up in #1175. Please have a look there.

I'm closing this in favor of that PR.

@hectorh30 hectorh30 closed this Jul 14, 2017
meyerbaptiste added a commit to meyerbaptiste/core that referenced this pull request Sep 18, 2017
meyerbaptiste added a commit to meyerbaptiste/core that referenced this pull request Sep 18, 2017
meyerbaptiste added a commit to meyerbaptiste/core that referenced this pull request Oct 6, 2017
meyerbaptiste added a commit to meyerbaptiste/core that referenced this pull request Oct 12, 2017
meyerbaptiste added a commit to meyerbaptiste/core that referenced this pull request Oct 12, 2017
meyerbaptiste added a commit to meyerbaptiste/core that referenced this pull request Oct 13, 2017
meyerbaptiste added a commit to meyerbaptiste/core that referenced this pull request Oct 13, 2017
cr3a7ure added a commit to cr3a7ure/core that referenced this pull request Oct 27, 2017
* 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
hoangnd25 pushed a commit to hoangnd25/core that referenced this pull request Feb 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants