Skip to content
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

Relationship route error when relationship is marked as hidden #105

Closed
bbprojectnet opened this issue Jul 14, 2021 · 4 comments
Closed

Relationship route error when relationship is marked as hidden #105

bbprojectnet opened this issue Jul 14, 2021 · 4 comments
Labels
bug Something isn't working
Milestone

Comments

@bbprojectnet
Copy link

bbprojectnet commented Jul 14, 2021

Steps to reproduce:

  • define route relationship, example:
		$server
			->resource('declarations', \App\Http\Controllers\Api\V1\DeclarationController::class)
			->only('store', 'show', 'update', 'delete')
			->relationships(function (Relationships $relationships) {
				$relationships->hasOne('patient')->only('related', 'show');
				$relationships->hasOne('signature');
			});
  • mark relationship in schema as hidden:
			HashId::make()->alreadyHashed(),
			DateTime::make('createdAt')->sortable()->readOnly(),
			DateTime::make('updatedAt')->sortable()->readOnly(),

			BelongsTo::make('patient')->readOnly()->hidden(),
			BelongsTo::make('signature')->type('attachments'),
  • try to access /api/resource/id/relation:

in my case: {{url}}/api/declarations/{{declaration_id}}/patient

Stack trace:

[2021-07-14 05:42:05] local.ERROR: Unexpected relationship patient on resource declarations. {"userId":10,"exception":"[object] (LogicException(code: 0): Unexpected relationship patient on resource declarations. at vendor\\laravel-json-api\\core\\src\\Core\\Resources\\JsonApiResource.php:267)
[stacktrace]
#0 vendor\\laravel-json-api\\core\\src\\Core\\Responses\\Internal\\RelatedResourceResponse.php(108): LaravelJsonApi\\Core\\Resources\\JsonApiResource->relationship('patient')
#1 vendor\\laravel-json-api\\core\\src\\Core\\Responses\\Internal\\RelatedResourceResponse.php(97): LaravelJsonApi\\Core\\Responses\\Internal\\RelatedResourceResponse->metaForRelationship()
#2 vendor\\laravel-json-api\\core\\src\\Core\\Responses\\Internal\\RelatedResourceResponse.php(81): LaravelJsonApi\\Core\\Responses\\Internal\\RelatedResourceResponse->allMeta()
#3 vendor\\laravel-json-api\\core\\src\\Core\\Responses\\RelatedResponse.php(110): LaravelJsonApi\\Core\\Responses\\Internal\\RelatedResourceResponse->toResponse(Object(Illuminate\\Http\\Request))
#4 vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(776): LaravelJsonApi\\Core\\Responses\\RelatedResponse->toResponse(Object(Illuminate\\Http\\Request))
#5 vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(763): Illuminate\\Routing\\Router::toResponse(Object(Illuminate\\Http\\Request), Object(LaravelJsonApi\\Core\\Responses\\RelatedResponse))
#6 vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(695): Illuminate\\Routing\\Router->prepareResponse(Object(Illuminate\\Http\\Request), Object(LaravelJsonApi\\Core\\Responses\\RelatedResponse))
#7 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(128): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#8 vendor\\laravel-json-api\\laravel\\src\\Http\\Middleware\\BootJsonApi.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#9 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): LaravelJsonApi\\Laravel\\Http\\Middleware\\BootJsonApi->handle(Object(Illuminate\\Http\\Request), Object(Closure), 'v1')
#10 app\\Http\\Middleware\\ProfileJsonResponse.php(22): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#11 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): App\\Http\\Middleware\\ProfileJsonResponse->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#12 vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\ThrottleRequests.php(127): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#13 vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\ThrottleRequests.php(103): Illuminate\\Routing\\Middleware\\ThrottleRequests->handleRequest(Object(Illuminate\\Http\\Request), Object(Closure), Array)
#14 vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\ThrottleRequests.php(55): Illuminate\\Routing\\Middleware\\ThrottleRequests->handleRequestUsingNamedLimiter(Object(Illuminate\\Http\\Request), Object(Closure), 'api', Object(Closure))
#15 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Routing\\Middleware\\ThrottleRequests->handle(Object(Illuminate\\Http\\Request), Object(Closure), 'api')
#16 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#17 vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(697): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#18 vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(672): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#19 vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(636): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#20 vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(625): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#21 vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(166): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#22 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(128): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#23 vendor\\barryvdh\\laravel-debugbar\\src\\Middleware\\InjectDebugbar.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#24 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Barryvdh\\Debugbar\\Middleware\\InjectDebugbar->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#25 vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#26 vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#27 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#28 vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#29 vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#30 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#31 vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#32 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#33 vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#34 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#35 vendor\\fruitcake\\laravel-cors\\src\\HandleCors.php(52): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#36 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Fruitcake\\Cors\\HandleCors->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#37 vendor\\fideloper\\proxy\\src\\TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#38 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Fideloper\\Proxy\\TrustProxies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#39 vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#40 vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(141): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#41 vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(110): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#42 public\\index.php(52): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#43 {main}
"} 
@lindyhopchris
Copy link
Contributor

Looking at this, even if we fix it at the point this error is occurring, there's a chance it might also then fail further down the encoding stack.

Primarily here:
https://github.com/laravel-json-api/encoder-neomerx/blob/f41bb36f2fa32e35f41116425a402eaf88abec7d/src/Schema/Schema.php#L182

And then again here:
https://github.com/laravel-json-api/encoder-neomerx/blob/f41bb36f2fa32e35f41116425a402eaf88abec7d/src/Schema/Schema.php#L202

Though the code-path for that might be for relationship identifiers, rather than related resource. However the problem is the same - as the relationship field is hidden, we cannot acquire the information from the resource.

The problem with the encoder package is the return type isn't nullable - you have to return a link. This presents a significant challenge for this scenario.

Workaround:

The work-around is to not hide the relationship and have it as a links-only relationship. I.e. the relationship is not hidden, but uses the cannotEagerLoad() method to ensure it can never have data. This means the relationship still appears in the resource but only has the links member. This is semantically-correct - i.e. it exposes the links for the relationship so the client can programmatically obtain the relationship value if needed.

This actually explains why I've never encountered this problem... because I always use links-only relationships rather than hiding relationships.

@lindyhopchris
Copy link
Contributor

Potential Solution

Potentially the $resource->relationship($name) method could return a relationship object even if the relationship is hidden. I.e. we use $resource->relationships() when encoding, so perhaps the $resource->relationship() method could be used to obtain relationship information even if the relationship is hidden. The resource can do this because it has access to the underlying schema.

This would be preferrable to exposing the schema, which is not desirable because the use of the schema by the resource is an internal implementation detail. I.e. calling code shouldn't expect the resource to have the schema.

@lindyhopchris lindyhopchris added the bug Something isn't working label Jul 16, 2021
@lindyhopchris
Copy link
Contributor

Actually, #111 would be a good solution for this - though we'd have to make the relationship() method on the resource class return a nullable type to fix. Worth investigating.

@lindyhopchris
Copy link
Contributor

Closing as will be in the 1.1 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants