Problem/Motivation

After Wim's fix in #3035045: Make EntityToJsonApi use subrequests so that it never breaks again, we found that the method used to derive the route doesn't account for routes that are overridden by jsonapi_extras - i.e. a route that in plain JSONAPI would be described as jsonapi.node--article.individual may be overridden to be jsonapi.article.individual, and only the first route definition is considered inside EntityToJsonApi.

Proposed resolution

The service, as part of jsonapi_extras, should be aware of the overridden route. I have a quick patch that replaces the existing logic to load the appropriate resourceType from the resourceTypeRepository, this works in our situation, but further thought may be required so that this fits all use cases (i.e. is the overridden resourceType always going to be the component of the route?).

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

logickal created an issue. See original summary.

logickal’s picture

Status: Needs review » Needs work

The last submitted patch, 2: jsonapi_extras-entitytojsonapi_route_issue-3049221-2.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

logickal’s picture

Just adding another note based on a rabbit hole we went down this afternoon - It would seem that if you have a cron job that is processing queues that expect to be able to use the service, there is no session as part of the subrequest so the SessionHandler::write() method throws an exception as it tries to get the uid from the non-existent session. I'll continue chasing that down and also address the test failure on this issue tomorrow.

Edit - this MAY be a result of Drupal 8.6.14, but I don't have any solid evidence of that as of yet.

logickal’s picture

Attaching a patch that modifies the service to take in the session service and use that if it can't pick one up from the current request. This is definitely WIP just to fix our immediate issue and doesn't yet address the test failures, so leaving this as Needs Work.

e0ipso’s picture

@logickal any chance you can give this a try before starting to fix tests?

I feel this will deserve dedicated testing. Maybe normalizing a user with and without the proper permissions?

logickal’s picture

Thanks @e0ipso - that is much cleaner than my rush-job patch. :D We are testing it now, but looks good so far. When I have a few more cycles over the next day or two I will start taking a stab at the tests.

Wim Leers’s picture

Status: Needs work » Needs review
  1. +++ b/src/EntityToJsonApi.php
    @@ -22,14 +25,44 @@ class EntityToJsonApi {
    +    $this->session = $request->hasPreviousSession()
    +      ? $request->getSession()
    +      : $session;
    
    @@ -42,15 +75,25 @@ class EntityToJsonApi {
    +    if ($this->session) {
    +      $request->setSession($this->session);
    +    }
    

    Session handling is complex. I can't vouch for this being correct. It'd require digging very deep in Drupal and Symfony internals.

    Given that it seems to be working for both @logickal and @e0ipso (and testbot), I think this is probably fine.

  2. +++ b/src/EntityToJsonApi.php
    @@ -42,15 +75,25 @@ class EntityToJsonApi {
    -    $jsonapi_url = Url::fromRoute($route_name, ['entity' => $entity->uuid()])->toString(TRUE)->getGeneratedUrl();
    ...
    +    $jsonapi_url = Url::fromRoute($route_name, ['entity' => $entity->uuid()])
    +      ->toString(TRUE)
    +      ->getGeneratedUrl();
    

    Unnecessary change, was confused by this (wondering: "huh, what is different?!").

  3. +++ b/src/EntityToJsonApi.php
    @@ -42,15 +75,25 @@ class EntityToJsonApi {
    +    $resource_type_name = $this->resourceTypeRepository
    +      ->get($entity->getEntityTypeId(), $entity->bundle())
    +      ->getTypeName();
    +    $route_name = sprintf('jsonapi.%s.individual', $resource_type_name);
    

    Ah, so this is what we were missing :)

Wim Leers’s picture

(Marked as Needs review so that automated testing kicks in; then we'd at least know that our current tests still pass.)

e0ipso’s picture

Thanks for taking a look Wim!

Unnecessary change, was confused by this (wondering: "huh, what is different?!").

Well, it was running beyond 80 characters. The linter took care of it.


Even if the session handling is right I don't think we can ensure authentication is properly propagated to the HTTP subrequests. What if we are authenticating the master via an HTTP header, or a qs parameter, or some obscure auth provider? Will subrequests inherity the "current user" properly?

Wim Leers’s picture

What if we are authenticating the master via an HTTP header, or a qs parameter, or some obscure auth provider?

That's what \Symfony\Component\HttpFoundation\Request::create()'s $server parameter is for (PHP $_SERVER contains HTTP request headers — yes this is weird, this is PHP's legacy sadly). CommentController's subrequest handling does do this.

So it seems that I introduced two bugs in #3035045: Make EntityToJsonApi use subrequests so that it never breaks again:

  1. not respecting resource type name aliasing (that's what the issue title says)
  2. not passing on all information from the master request to the subrequest

WRT point 2, I did that kind of intentionally. I've always have been and still am concerned about using this service to generate information for anonymous consumption while in an authenticated user's context, because that would leak information only accessible to the authenticated user to the general public. But clearly I have to admit defeat here since people do want to use it in this way. OTOH this means I think this can never ever get into Drupal core; it's too easy and hence too dangerous to get wrong.

So… ported that same logic from CommentController's subrequest handling. Patch still needs to be cleaned up, but this gets the gist across.

Wim Leers’s picture

I think that with #11's interdiff, all of the things in #8.1 can be removed.

Status: Needs review » Needs work

The last submitted patch, 11: 3049221-11.patch, failed testing. View results

e0ipso’s picture

  1. +++ b/src/EntityToJsonApi.php
    @@ -42,15 +76,26 @@ class EntityToJsonApi {
    +    $query = $request->query->all();
    

    This is probably going to cause an error if the request calling this service contains a non-jsonapi approved qs parameter.

  2. +++ b/src/EntityToJsonApi.php
    @@ -42,15 +76,26 @@ class EntityToJsonApi {
    +    $request = Request::create($jsonapi_url, 'GET', $query, $request->cookies->all(), [], $request->server->all());
    

    :clap:

    also, how is $_SERVER still a thing?! Oh… PHP.

  3. +++ b/src/EntityToJsonApi.php
    @@ -42,15 +76,26 @@ class EntityToJsonApi {
    +      $request->setSession($this->session);
    

    Now that we are passing cookies. I think that cookie-less sessions that are relevant can be considered an edge case. If so, this makes me a bit nervous to have it here.

Wim Leers’s picture

  1. Yep. I think we can choose to not retain the master request's query string TBH; authentication would nearly always happen through request headers. We can leave supporting that as a future enhancement.
  2. :)
  3. See #12 :)
e0ipso’s picture

Status: Needs work » Needs review
FileSize
2.89 KB

  • e0ipso committed b016004 on 8.x-3.x
    Issue #3049221 by e0ipso, logickal, Wim Leers: EntityToJsonapi service...
e0ipso’s picture

Status: Needs review » Fixed

Status: Fixed » Closed (fixed)

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