Comments

gabesullice created an issue. See original summary.

gabesullice’s picture

gabesullice’s picture

Status: Active » Needs review
Related issues: +#2937961: ResourceType should provide related ResourceTypes
StatusFileSize
new18.12 KB
new44.32 KB

This patch also depends on #2937961: ResourceType should provide related ResourceTypes

This was a bit of a deep dive. Since we now have a solid way to get relationship data from a resource type, it no longer makes sense to let the relationship routes to be as dynamic (and therefore complex) as they are.

This patch defines a route for every valid relationship and removes the related route parameter.

Status: Needs review » Needs work

The last submitted patch, 3: 2939722-3.patch, failed testing. View results

wim leers’s picture

This was a bit of a deep dive. Since we now have a solid way to get relationship data from a resource type, it no longer makes sense to let the relationship routes to be as dynamic (and therefore complex) as they are.

This patch defines a route for every valid relationship and removes the related route parameter.

That is a very big change, which means we'll definitely need sign-off from @e0ipso.

Too late ATM to start reviewing a >40K patch. Will do so tomorrow.

gabesullice’s picture

That is a very big change, which means we'll definitely need sign-off from @e0ipso.

💯%

gabesullice’s picture

StatusFileSize
new12.04 KB
new30.18 KB

Rerolled since #2937961: ResourceType should provide related ResourceTypes landed. Which knocks about 10k off the patch :)

To give a little more credence to this change I'll point out some of my reasoning.

  1. +++ b/src/Controller/EntityResource.php
    @@ -425,60 +423,10 @@ class EntityResource {
    -      !$field_list instanceof EntityReferenceFieldItemListInterface
    

    One step toward support for relationship fields which aren't direct entity reference derivatives.

  2. +++ b/src/Controller/EntityResource.php
    @@ -425,60 +423,10 @@ class EntityResource {
    -      throw new HttpException(500, 'Invalid internal structure for relationship field list.');
    

    The client can no longer cause an Internal Server Error.

  3. +++ b/src/Controller/EntityResource.php
    @@ -425,60 +423,10 @@ class EntityResource {
    -      throw new NotFoundHttpException(sprintf('The relationship %s is not present in this resource.', $related_field));
    ...
    -        throw new NotFoundHttpException(sprintf(
    

    We no longer need to do our own 404 checking.

  4. +++ b/src/Controller/EntityResource.php
    @@ -425,60 +423,10 @@ class EntityResource {
    -    $is_multiple = $field_list
    -      ->getDataDefinition()
    -      ->getFieldStorageDefinition()
    -      ->isMultiple();
    

    Less code duplication.

  5. +++ b/src/Controller/EntityResource.php
    @@ -425,60 +423,10 @@ class EntityResource {
    -      /** @var \Drupal\Core\Entity\EntityInterface $referenced_entity */
    -      $referenced_entity = $field_list->entity;
    -      $referenced_resource_type = $this->resourceTypeRepository
    -        ->get(
    -          $referenced_entity->getEntityTypeId(),
    -          $referenced_entity->bundle()
    -        );
    -      if (!$referenced_resource_type) {
    -        throw new NotFoundHttpException(sprintf(
    -          'Unable to find the resource for "%s:%s" on the related field "%s".',
    -          $referenced_entity->getEntityTypeId(),
    -          $referenced_entity->bundle(),
    -          $related_field
    -        ));
    -      }
    

    This code doesn't work in a world with internal resources. It will incorrectly respond with a 404 if the first relationship is internal, but other relationships are public.

gabesullice’s picture

Status: Needs work » Needs review

The last submitted patch, 7: 2939722-7.tests_only.patch, failed testing. View results

gabesullice’s picture

Assigned: gabesullice » Unassigned
gabesullice’s picture

e0ipso’s picture

Title: Do not provide routes for internal resource types. » Do not provide routes for internal resource types

This in #3

This was a bit of a deep dive. Since we now have a solid way to get relationship data from a resource type, it no longer makes sense to let the relationship routes to be as dynamic (and therefore complex) as they are.

and this in #5

That is a very big change, which means we'll definitely need sign-off from @e0ipso.

Make me wonder if we want to take a different approach to the patch: split in two to get the MVP in (Do not provide routes for internal resource types), and then add a follow up for the maintainability bit (Define related and relationship routes statically).

Thoughts?

wim leers’s picture

I agree that @e0ipso's proposal would make this much easier to review and commit. But I suspect that the "no routes for internal resource types" bit becomes much simpler if "define related and relationship routes" lands first. So the latter may need to be a blocker rather than a follow-up.

gabesullice’s picture

StatusFileSize
new19.3 KB

Make me wonder if we want to take a different approach to the patch: split in two to get the MVP... Thoughts?

Well, #7 is already finished and passing tests/ready for review. So "MVP" is already met/exceeded. I understand that it may be harder to review though. So, I've made a patch that just removes support for internal resource/relationship routes and also doesn't create base individual/collection routes.

So the latter may need to be a blocker rather than a follow-up.

Once you decide to just define all routes statically, it becomes so trivial to remove the relationship routes in a follow-up that it might just be too much process and you end up with #7.

The code below is what the follow-up would be:

+++ b/src/Routing/Routes.php
@@ -140,30 +150,39 @@ class Routes implements ContainerInjectionInterface {
+      $relationships = $this->getRoutableRelationships($resource_type);
+      foreach ($relationships as $field => $relatable_resource_types) {
+        if (static::hasExternalResourceTypes($relatable_resource_types)) {

@@ -184,4 +203,40 @@ class Routes implements ContainerInjectionInterface {
+  protected static function getRoutableRelationships(ResourceType $resource_type) {
+    return array_filter($resource_type->getRelatableResourceTypes(), function ($field) use ($resource_type) {
+      return $resource_type->isFieldEnabled($field);
+    }, ARRAY_FILTER_USE_KEY);
+  }
...
+  protected static function hasExternalResourceTypes(array $resource_types) {
+    foreach ($resource_types as $resource_type) {
+      if (!$resource_type->isInternal()) {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }

We have two patches ready for review:

  1. #7 which only "provides" truly accessible results. We won't need a follow-up.
  2. The attached patch does not defined static relationship routes and checks them dynamically. It does still need to modify route generation for individual/collection routes in Routes.php though. We'll need to remove most of the tests introduced by this patch when/if we do the "static routing" follow-up.

My personal preference is #7. I can see both sides of the argument WRT a more difficult review process though, so I'll leave it up to @e0ipso/@WimLeers.

gabesullice’s picture

StatusFileSize
new29.95 KB

Here's a re-roll of #7.

wim leers’s picture

Status: Needs review » Needs work

So @e0ipso was right: statically defined routes (well, explicit routes, really) can be a follow-up.

I think #14 is much easier to understand and hence land. The explicit routes portion can then be a pure refactoring-for-maintainability-reasons follow-up. This issue is in fact the necessary task, the bugfix. The follow-up isn't.

Understanding/analysis:

  1. +++ b/src/Controller/EntityResource.php
    @@ -453,30 +455,6 @@ class EntityResource {
    -      if (!$referenced_resource_type) {
    -        throw new NotFoundHttpException(sprintf(
    
    @@ -842,15 +820,20 @@ class EntityResource {
    -  protected function isRelationshipField($entity_field) {
    ...
    +  protected function isRelationshipField(FieldItemListInterface $entity_field) {
    

    👍 All this logic now can elegantly live in this helper method.

  2. +++ b/src/Controller/EntryPoint.php
    @@ -70,7 +70,12 @@ class EntryPoint extends ControllerBase {
    +      $resources = array_filter($this->resourceTypeRepository->all(), function ($resource) {
    +        return !$resource->isInternal();
    +      });
    +
    
    +++ b/src/Routing/Routes.php
    @@ -100,6 +100,10 @@ class Routes implements ContainerInjectionInterface {
    +      if ($resource_type->isInternal()) {
    +        continue;
    +      }
    +
    

    👍 These are the essential changes: they solve the problem described in the title.

  3. --- a/tests/src/Unit/Routing/RoutesTest.php
    +++ b/tests/src/Unit/Routing/RoutesTest.php
    

    👍 This is testing the essential changes.

Remarks:

  1. +++ b/tests/src/Kernel/Controller/EntityResourceTest.php
    @@ -854,7 +947,7 @@ class EntityResourceTest extends JsonapiKernelTestBase {
        * @return \Drupal\jsonapi\Controller\EntityResource
        *   The resource.
        */
    -  protected function buildEntityResource($entity_type_id, $bundle) {
    +  protected function buildEntityResource($entity_type_id, $bundle, $relatable_resource_types = [], $internal = FALSE) {
    

    Docblock not updated.

  2. +++ b/tests/src/Kernel/Controller/EntityResourceTest.php
    @@ -872,8 +965,11 @@ class EntityResourceTest extends JsonapiKernelTestBase {
    +    $resource_type = new ResourceType($entity_type_id, $bundle, NULL, $internal);
    +    $resource_type->setRelatableResourceTypes($relatable_resource_types);
    

    If we're changing this ::buildEntityResource() helper anyway, shouldn't we just let it receive a single ResourceType parameter, i.e. basically receiving this $resource_type object that we're manually constructing here?

    Nah, never mind, that is again expanding scope. That'd be another nice small refactoring issue that could go in easily later.

  3. +++ b/tests/src/Kernel/Controller/EntryPointTest.php
    @@ -4,6 +4,8 @@ namespace Drupal\Tests\jsonapi\Kernel\Controller;
    @@ -27,24 +29,72 @@ class EntryPointTest extends JsonapiKernelTestBase {
    

    Are the changes to this test actually necessary?

    It's effectively changing this from a kernel test to a unit test. I think the whole point was that this was testing what a particular set of modules was resulting in?

wim leers’s picture

Also, now that #2932035: ResourceTypes should be internal when EntityType::isInternal is TRUE is in, this should be adding an integration test similar to core's \Drupal\Tests\content_moderation\Kernel\ContentModerationStateResourceTest.

gabesullice’s picture

Status: Needs work » Needs review
StatusFileSize
new18.12 KB
new22.8 KB
new3.91 KB

Rerolled and integration test done 💥

The last submitted patch, 18: 2939722-18.tests_only.patch, failed testing. View results

wim leers’s picture

Status: Needs review » Needs work
  1. This fixed #16.1, but not #16.3?
  2. +++ b/tests/src/Functional/InternalEntitiesTest.php
    @@ -66,16 +73,49 @@ class InternalEntitiesTest extends BrowserTestBase {
    +      $this->assertEquals(
    

    Nit: Can be assertSame().

  3. +++ b/tests/src/Kernel/Controller/EntityResourceTest.php
    @@ -943,6 +943,10 @@ class EntityResourceTest extends JsonapiKernelTestBase {
    +   * @param array $relatable_resource_types
    

    Nit: Should get a more specific typehint than array.

gabesullice’s picture

Status: Needs work » Needs review
StatusFileSize
new1.43 KB
new22.81 KB

#16.2 was a scope expansion that you wanted to do later no?

#16.3 Quickly, yes. We should not be adding invalid links to the entry point, that really breaks the point of HATEOAS. For that reason, I think we need to be testing that we are adding the proper links there. Maybe I'm missing your point though?

Edit: looks like you edited a bit of the review

wim leers’s picture

Sorry, for #20.3, I meant ResourceTypeInterface[].

#16.3 Quickly, yes. We should not be adding invalid links to the entry point, that really breaks the point of HATEOAS. For that reason, I think we need to be testing that we are adding the proper links there. Maybe I'm missing your point though?

I think it'd be better to keep it as a functional test and instead add the jsonapi_entity_test module that you added in #2939705: Do not include internal resource types under `include`. Then we're not testing a mocked world, but the real world.

EDIT: Because that module includes an entity type that is internal and should therefore NOT show up in the entry point!

gabesullice’s picture

StatusFileSize
new5.03 KB
new20.32 KB

Ah, yeah, now that we have an integration test, that's much better.

wim leers’s picture

Title: Do not provide routes for internal resource types » [PP-1] Do not provide routes for internal resource types
Status: Needs review » Postponed

#2939705: Do not include internal resource types under `include` was reverted because it broke JSON API on Drupal 8.6, I reverted it, so this is now blocked again.

wim leers’s picture

Assigned: Unassigned » e0ipso
Status: Postponed » Needs review

#23 looks great!

Let's hear what @e0ipso thinks.

e0ipso’s picture

Status: Needs review » Reviewed & tested by the community

This looks good to me 🎊.


+++ b/tests/src/Kernel/Controller/EntryPointTest.php
@@ -29,72 +27,24 @@ class EntryPointTest extends JsonapiKernelTestBase {
-   * Provides test cases to test entry point route generation.
...
-   * Ensures that internal resources are not present in the entry point.

Nit: Now that the tests have been collapsed to a single test, let's add these comments inline.

wim leers’s picture

Title: [PP-1] Do not provide routes for internal resource types » Do not provide routes for internal resource types

#2939705: Do not include internal resource types under `include` was recommitted. Having @e0ipso RTBC this is wonderful :) Now retesting…

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 23: 2939722-23.patch, failed testing. View results

wim leers’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new20.28 KB

That didn't work — patch no longer applies obviously.

This is a rebase automatically performed by git.

  • Wim Leers committed 29655ae on 8.x-1.x
    Issue #2939722 by gabesullice, Wim Leers, e0ipso: Do not provide routes...
wim leers’s picture

Status: Reviewed & tested by the community » Fixed

Green! Two unused statements and @e0ipso's remark in #26, but I can fix those upon commit :)

Except … those lines @e0ipso pointed out don't exist in the patch! So rather than waiting for Mateu to clarify, I'm deferring that to a follow-up if necessary. Because more likely than not, this was something on Mateu's local instance.

e0ipso’s picture

Ah, that's totally fine. What I meant was to add the intention of those comments in the new relevant parts of the code. I seem to recall a similar code was elsewhere. I don't think this deserves a single more second of consideration, let's move on to important things.

🎉❗️

  • Wim Leers committed 26b5b79 on 8.x-1.x
    Issue #2939722 by gabesullice, Wim Leers, e0ipso: Do not provide routes...
wim leers’s picture

Guess what, I failed to commit the commit interdiff from #29 😂😅

#32: happy to still do that though :) But the second comment you mention actually already exists at \Drupal\Tests\jsonapi\Functional\InternalEntitiesTest::testEntryPoint()!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.