Skip to content

Commit

Permalink
fix: ensure non-existing relation inclusion does not break requests
Browse files Browse the repository at this point in the history
In case relation has a wildcard whitelisting and request contains a non-existing relation inclusion
  • Loading branch information
alexzarbn committed Oct 27, 2022
1 parent 4156974 commit f4ad23e
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 9 deletions.
30 changes: 22 additions & 8 deletions src/Drivers/Standard/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -339,14 +339,17 @@ public function getQualifiedFieldName(string $field): string
* @param string $relation
* @return string
*/
public function getRelationModelClass(string $relation): string
public function getRelationModelClass(string $relation): ?string
{
$relations = collect(explode('.', $relation));
$relation = $relations[0];

$resourceModel = (new $this->resourceModelClass)->$relation()->getModel();
$resourceModel = (new $this->resourceModelClass);

foreach ($relations as $nestedRelation) {
if (!method_exists($resourceModel, $nestedRelation)) {
return null;
}

foreach ($relations->skip(1) as $nestedRelation) {
$resourceModel = $resourceModel->$nestedRelation()->getModel();
}

Expand Down Expand Up @@ -545,12 +548,16 @@ public function applyAggregatesToQuery($query, Request $request, array $aggregat
);
}

if (!$relationModelClass = $this->getRelationModelClass($aggregateDescriptor['relation'])) {
continue;
}

$query->withAggregate([
$aggregateDescriptor['relation'] => function (Builder $aggregateQuery) use (
$aggregateDescriptor,
$request
$request,
$relationModelClass
) {
$relationModelClass = $this->getRelationModelClass($aggregateDescriptor['relation']);
$relationQueryBuilder = $this->clone($relationModelClass);

$relationQueryBuilder->applyFiltersToQuery(
Expand Down Expand Up @@ -590,9 +597,16 @@ public function applyIncludesToQuery($query, Request $request, array $includeDes
}

foreach ($includeDescriptors as $includeDescriptor) {
if (!$relationModelClass = $this->getRelationModelClass($includeDescriptor['relation'])) {
continue;
}

$query->with([
$includeDescriptor['relation'] => function (Relation $includeQuery) use ($includeDescriptor, $request) {
$relationModelClass = $this->getRelationModelClass($includeDescriptor['relation']);
$includeDescriptor['relation'] => function (Relation $includeQuery) use (
$includeDescriptor,
$request,
$relationModelClass
) {
$relationQueryBuilder = $this->clone($relationModelClass);

$relationQueryBuilder->applyFiltersToQuery(
Expand Down
12 changes: 12 additions & 0 deletions tests/Feature/StandardShowOperationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,16 @@ public function getting_a_single_resource_with_nested_included_relation(): void

$this->assertResourceShown($response, $post->fresh('user.roles')->toArray());
}

/** @test */
public function getting_a_single_resource_with_nested_non_existing_included_relation(): void
{
$post = factory(Post::class)->create(['user_id' => factory(User::class)->create()->id]);

Gate::policy(Post::class, GreenPolicy::class);

$response = $this->get("/api/posts/{$post->id}?include=image.non-existing-relation");

$this->assertResourceShown($response, $post->fresh()->toArray());
}
}
2 changes: 1 addition & 1 deletion tests/Fixtures/app/Http/Controllers/PostsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,6 @@ public function exposedScopes(): array
*/
public function includes(): array
{
return ['user', 'user.roles'];
return ['user', 'user.roles', 'image.*'];
}
}

0 comments on commit f4ad23e

Please sign in to comment.