Skip to content

Commit

Permalink
Merge branch 'release/1.0.0-beta.5' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
lindyhopchris committed Jul 10, 2021
2 parents fbb12bd + b95d1a6 commit 5dbb358
Show file tree
Hide file tree
Showing 21 changed files with 762 additions and 33 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ jobs:
build:

runs-on: ubuntu-latest

strategy:
fail-fast: true
matrix:
php: ['7.4', '8.0']
laravel: ['^8.0']
laravel: ['^8.30']

steps:
- name: Checkout Code
Expand All @@ -38,6 +38,6 @@ jobs:
timeout_minutes: 5
max_attempts: 5
command: composer install --no-suggest --prefer-dist -n -o

- name: Execute tests
run: vendor/bin/phpunit
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,45 @@
All notable changes to this project will be documented in this file. This project adheres to
[Semantic Versioning](http://semver.org/) and [this changelog format](http://keepachangelog.com/).

## [1.0.0-beta.5] - 2021-07-10

### Added

- The authorizer now has separate `showRelated()` and `showRelationship()` methods. Previously both these controller
actions were authorized via the single `showRelationship()` method. Adding the new `showRelated` method means
developers can now implement separate authorization logic for these two actions if desired. Our default implementation
remains unchanged - both are authorized using the `view<RelationshipName>` method on the relevant policy.
- The request class now has a `isCreatingOrUpdating()` helper method to determine whether the request is to create or
updated a resource.
- Add stop on first failure to all validators in the resource request class.
- [#85](https://github.com/laravel-json-api/laravel/issues/85) When running an application with debug mode turned on,
the default JSON:API error object for an exception will now contain detailed exception information, including the
stack trace, in the object's `meta` member.
- [#103](https://github.com/laravel-json-api/laravel/issues/103) Can now fully customise attribute serialization to JSON
using the `extractUsing()` callback. This receives the model, column name and value. This is useful if the developer
needs to control the serialization of a few fields on their schema. However, the recommendation is to use a resource
class for complete control over the serialization of a model to a JSON:API resource.

### Changed

- Minimum Laravel version is now `8.30`. This change was required to use the `$stopOnFirstFailure` property on Laravel's
`FormRequest` class.
- Schema classes no longer automatically sort their fields by name when iterating over them. This change was made to
give the developer full control over the order of fields (particularly as this order affects the order in which fields
are listed when serialized to a JSON:API resource). Developers can list fields in name order if that is the preferred
order.
- Removed the `LaravelJsonApi\Spec\UnexpectedDocumentException` which was thrown if there was a failure when decoding
request JSON content before parsing it for compliance with the JSON:API specification. A `JsonApiException` will now
be thrown instead.

### Fixed

- [#101](https://github.com/laravel-json-api/laravel/issues/101) Ensure controller create action always returns a
response that will result in a `201 Created` response.
- [#102](https://github.com/laravel-json-api/laravel/issues/102) The attach and detach to-many relationship controller
actions now correctly resolve the collection query class using the relation's inverse resource type. Previously they
were incorrectly using the primary resource type to resolve the query class.

## [1.0.0-beta.4] - 2021-06-02

### Fixed
Expand Down
14 changes: 7 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@
"require": {
"php": "^7.4|^8.0",
"ext-json": "*",
"laravel-json-api/core": "^1.0.0-beta.4",
"laravel-json-api/eloquent": "^1.0.0-beta.5",
"laravel-json-api/core": "^1.0.0-beta.5",
"laravel-json-api/eloquent": "^1.0.0-beta.6",
"laravel-json-api/encoder-neomerx": "^1.0.0-beta.1",
"laravel-json-api/exceptions": "^1.0.0-beta.2",
"laravel-json-api/spec": "^1.0.0-beta.1",
"laravel-json-api/exceptions": "^1.0.0-beta.4",
"laravel-json-api/spec": "^1.0.0-beta.2",
"laravel-json-api/validation": "^1.0.0-beta.2",
"laravel/framework": "^8.0"
"laravel/framework": "^8.30"
},
"require-dev": {
"laravel-json-api/hashids": "^1.0.0-beta.2",
"laravel-json-api/hashids": "^1.0.0-beta.3",
"laravel-json-api/testing": "^1.0.0-beta.1",
"orchestra/testbench": "^6.9",
"phpunit/phpunit": "^9.5"
Expand Down Expand Up @@ -66,7 +66,7 @@
]
}
},
"minimum-stability": "dev",
"minimum-stability": "stable",
"prefer-stable": true,
"config": {
"sort-packages": true
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Controllers/Actions/AttachRelationship.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public function attachRelationship(Route $route, StoreContract $store)
$resourceType = $route->resourceType()
);

$query = ResourceQuery::queryMany($resourceType);
$query = ResourceQuery::queryMany($relation->inverse());

$model = $route->model();
$response = null;
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Controllers/Actions/DetachRelationship.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public function detachRelationship(Route $route, StoreContract $store)
$resourceType = $route->resourceType()
);

$query = ResourceQuery::queryMany($resourceType);
$query = ResourceQuery::queryMany($relation->inverse());

$model = $route->model();
$response = null;
Expand Down
4 changes: 3 additions & 1 deletion src/Http/Controllers/Actions/Store.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public function store(Route $route, StoreContract $store)
$response = $this->saved($model, $request, $query);
}

return $response ?: DataResponse::make($model)->withQueryParameters($query);
return $response ?? DataResponse::make($model)
->withQueryParameters($query)
->didCreate();
}
}
35 changes: 33 additions & 2 deletions src/Http/Requests/FormRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Http\FormRequest as BaseFormRequest;
use Illuminate\Support\Str;
use LaravelJsonApi\Contracts\Schema\Schema;
use LaravelJsonApi\Core\JsonApiService;
use LaravelJsonApi\Validation\Factory as ValidationFactory;
Expand Down Expand Up @@ -85,13 +86,23 @@ public function isViewingOne(): bool
}

/**
* Is this a request to view resources in a relationship (Read related/relationship actions.)
* Is this a request to view related resources in a relationship? (Show-related action.)
*
* @return bool
*/
public function isViewingRelated(): bool
{
return $this->isMethod('GET') && $this->isRelationship() && !$this->urlHasRelationships();
}

/**
* Is this a request to view resource identifiers in a relationship? (Show-relationship action.)
*
* @return bool
*/
public function isViewingRelationship(): bool
{
return $this->isMethod('GET') && $this->isRelationship();
return $this->isMethod('GET') && $this->isRelationship() && $this->urlHasRelationships();
}

/**
Expand All @@ -114,6 +125,16 @@ public function isUpdating(): bool
return $this->isMethod('PATCH') && $this->isNotRelationship();
}

/**
* Is this a request to create or update a resource?
*
* @return bool
*/
public function isCreatingOrUpdating(): bool
{
return $this->isCreating() || $this->isUpdating();
}

/**
* Is this a request to replace a resource relationship?
*
Expand Down Expand Up @@ -320,4 +341,14 @@ private function doesntHaveResourceId(): bool
{
return !$this->hasResourceId();
}

/**
* Does the URL contain the keyword "relationships".
*
* @return bool
*/
private function urlHasRelationships(): bool
{
return Str::of($this->url())->contains('relationships');
}
}
8 changes: 8 additions & 0 deletions src/Http/Requests/ResourceQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ public function authorizeResource(Authorizer $authorizer): bool
return $authorizer->show($this, $this->modelOrFail());
}

if ($this->isViewingRelated()) {
return $authorizer->showRelated(
$this,
$this->modelOrFail(),
$this->jsonApi()->route()->fieldName(),
);
}

if ($this->isViewingRelationship()) {
return $authorizer->showRelationship(
$this,
Expand Down
10 changes: 4 additions & 6 deletions src/Http/Requests/ResourceRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
use LaravelJsonApi\Core\Support\Str;
use LaravelJsonApi\Spec\RelationBuilder;
use LaravelJsonApi\Spec\ResourceBuilder;
use LaravelJsonApi\Spec\UnexpectedDocumentException;
use LogicException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
Expand Down Expand Up @@ -238,7 +237,7 @@ public function validatedForRelation()
*/
protected function prepareForValidation()
{
if ($this->isCreating() || $this->isUpdating()) {
if ($this->isCreatingOrUpdating()) {
$this->assertSupportedMediaType();
$this->validateResourceDocument();
} else if ($this->isModifyingRelationship()) {
Expand Down Expand Up @@ -316,7 +315,7 @@ protected function createRelationshipValidator(ValidationFactory $factory): Vali
$this->relationshipRules(),
$this->messages(),
$this->attributes()
);
)->stopOnFirstFailure($this->stopOnFirstFailure);
}

/**
Expand All @@ -338,7 +337,7 @@ protected function createDeleteValidator(ValidationFactory $factory): Validator
$this->attributes(),
method_exists($this, 'deleteAttributes') ? $this->deleteAttributes() : []
)
);
)->stopOnFirstFailure($this->stopOnFirstFailure);
}

/**
Expand Down Expand Up @@ -487,7 +486,7 @@ private function relationshipRules(): array
* Validate the JSON API document for a resource request.
*
* @return void
* @throws HttpExceptionInterface
* @throws JsonApiException
*/
private function validateResourceDocument(): void
{
Expand All @@ -510,7 +509,6 @@ private function validateResourceDocument(): void
* Validate the JSON API document for a modify relationship request.
*
* @return void
* @throws UnexpectedDocumentException
* @throws JsonApiException
*/
private function validateRelationshipDocument(): void
Expand Down
42 changes: 42 additions & 0 deletions tests/dummy/app/Http/Controllers/Api/V1/UserController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
/*
* Copyright 2021 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Responses\DataResponse;
use LaravelJsonApi\Laravel\Http\Controllers\Actions;

class UserController extends Controller
{

use Actions\FetchOne;

/**
* Return the current user.
*
* @param Request $request
* @return DataResponse
*/
public function me(Request $request): DataResponse
{
return DataResponse::make($request->user());
}
}
2 changes: 1 addition & 1 deletion tests/dummy/app/JsonApi/V1/Comments/CommentSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class CommentSchema extends Schema
public function fields(): array
{
return [
HashId::make()->alreadyHashed(),
HashId::make()->alreadyHashed()->withLength(10),
Str::make('content'),
DateTime::make('createdAt')->sortable()->readOnly(),
BelongsTo::make('post'),
Expand Down
7 changes: 6 additions & 1 deletion tests/dummy/app/JsonApi/V1/Posts/PostSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,18 @@ class PostSchema extends Schema
*/
protected int $maxDepth = 3;

/**
* @var string
*/
protected $defaultSort = '-createdAt';

/**
* @inheritDoc
*/
public function fields(): array
{
return [
HashId::make()->alreadyHashed(),
HashId::make()->alreadyHashed()->withLength(10),
BelongsTo::make('author')->type('users')->readOnly(),
HasMany::make('comments')->readOnly(),
Str::make('content'),
Expand Down
2 changes: 1 addition & 1 deletion tests/dummy/app/JsonApi/V1/Tags/TagSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class TagSchema extends Schema
public function fields(): array
{
return [
HashId::make()->alreadyHashed(),
HashId::make()->alreadyHashed()->withLength(10),
DateTime::make('createdAt')->sortable()->readOnly(),
Str::make('name')->sortable(),
BelongsToMany::make('posts')
Expand Down
2 changes: 1 addition & 1 deletion tests/dummy/app/JsonApi/V1/Users/UserSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class UserSchema extends Schema
public function fields(): array
{
return [
HashId::make()->alreadyHashed(),
HashId::make()->alreadyHashed()->withLength(10),
DateTime::make('createdAt')->readOnly(),
Str::make('name'),
DateTime::make('updatedAt')->readOnly(),
Expand Down
38 changes: 38 additions & 0 deletions tests/dummy/app/Policies/UserPolicy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
/*
* Copyright 2021 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace App\Policies;

use App\Models\User;

class UserPolicy
{

/**
* Determine if the user can view the other user.
*
* @param User $user
* @param User $other
* @return bool
*/
public function view(User $user, User $other): bool
{
return true;
}
}
Loading

0 comments on commit 5dbb358

Please sign in to comment.