diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bed8493..84c5bae 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,43 +2,48 @@ name: Tests on: push: - branches: [ main, develop, 4.x ] + branches: + - main + - develop + - 4.x pull_request: - branches: [ main, develop, 4.x ] + branches: + - main + - develop + - 4.x jobs: build: - runs-on: ubuntu-latest strategy: fail-fast: true matrix: - php: [8.2, 8.3, 8.4] - laravel: [11] + php: [ 8.2, 8.3, 8.4 ] + laravel: [ 11, 12 ] steps: - - name: Checkout Code - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: dom, curl, libxml, mbstring, zip - tools: composer:v2 - coverage: none - ini-values: error_reporting=E_ALL, zend.assertions=1 - - - name: Set Laravel Version - run: composer require "illuminate/contracts:^${{ matrix.laravel }}" --no-update - - - name: Install dependencies - uses: nick-fields/retry@v2 - with: - timeout_minutes: 5 - max_attempts: 5 - command: composer update --prefer-dist --no-interaction --no-progress - - - name: Execute tests - run: vendor/bin/phpunit + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip + tools: composer:v2 + coverage: none + ini-values: error_reporting=E_ALL, zend.assertions=1 + + - name: Set Laravel Version + run: composer require "illuminate/contracts:^${{ matrix.laravel }}" --no-update + + - name: Install dependencies + uses: nick-fields/retry@v2 + with: + timeout_minutes: 5 + max_attempts: 5 + command: composer update --prefer-dist --no-interaction --no-progress + + - name: Execute tests + run: vendor/bin/phpunit diff --git a/CHANGELOG.md b/CHANGELOG.md index 3587901..9b0c791 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,38 @@ All notable changes to this project will be documented in this file. This projec ## Unreleased +## [5.2.0] - 2025-02-24 + +### Added + +- Package now supports both Laravel 11 and 12. + +## [5.1.0] - 2025-01-23 + +### Added + +- [#24](https://github.com/laravel-json-api/core/pull/24) Add `header` property to error source object. + +## [5.0.2] - 2025-01-11 + +### Fixed + +- [#23](https://github.com/laravel-json-api/core/pull/23) Ensure relationship meta is included in paginated relationship + responses. + +## [5.0.1] - 2024-11-30 + +### Fixed + +- Removed more PHP 8.4 deprecation notices. + +## [5.0.0] - 2024-11-29 + +### Added + +- **BREAKING**: [#21](https://github.com/laravel-json-api/core/pull/21) The `Authorizer` contract now allows all methods + to return a `bool` or an Illuminate authorization response. + ## [4.3.2] - 2024-11-30 ### Fixed diff --git a/composer.json b/composer.json index cad7a51..bfbf592 100644 --- a/composer.json +++ b/composer.json @@ -25,9 +25,10 @@ "require": { "php": "^8.2", "ext-json": "*", - "illuminate/contracts": "^11.0", - "illuminate/http": "^11.0", - "illuminate/support": "^11.0" + "illuminate/auth": "^11.0|^12.0", + "illuminate/contracts": "^11.0|^12.0", + "illuminate/http": "^11.0|^12.0", + "illuminate/support": "^11.0|^12.0" }, "require-dev": { "phpunit/phpunit": "^10.5" @@ -45,7 +46,7 @@ }, "extra": { "branch-alias": { - "dev-4.x": "4.x-dev" + "dev-develop": "5.x-dev" } }, "minimum-stability": "stable", diff --git a/src/Contracts/Auth/Authorizer.php b/src/Contracts/Auth/Authorizer.php index 8c83b4d..2378e30 100644 --- a/src/Contracts/Auth/Authorizer.php +++ b/src/Contracts/Auth/Authorizer.php @@ -11,6 +11,7 @@ namespace LaravelJsonApi\Contracts\Auth; +use Illuminate\Auth\Access\Response; use Illuminate\Http\Request; interface Authorizer @@ -20,45 +21,45 @@ interface Authorizer * * @param Request $request * @param string $modelClass - * @return bool + * @return bool|Response */ - public function index(Request $request, string $modelClass): bool; + public function index(Request $request, string $modelClass): bool|Response; /** * Authorize the store controller action. * * @param Request $request * @param string $modelClass - * @return bool + * @return bool|Response */ - public function store(Request $request, string $modelClass): bool; + public function store(Request $request, string $modelClass): bool|Response; /** * Authorize the show controller action. * * @param Request $request * @param object $model - * @return bool + * @return bool|Response */ - public function show(Request $request, object $model): bool; + public function show(Request $request, object $model): bool|Response; /** * Authorize the update controller action. * * @param object $model * @param Request $request - * @return bool + * @return bool|Response */ - public function update(Request $request, object $model): bool; + public function update(Request $request, object $model): bool|Response; /** * Authorize the destroy controller action. * * @param Request $request * @param object $model - * @return bool + * @return bool|Response */ - public function destroy(Request $request, object $model): bool; + public function destroy(Request $request, object $model): bool|Response; /** * Authorize the show-related controller action. @@ -66,9 +67,9 @@ public function destroy(Request $request, object $model): bool; * @param Request $request * @param object $model * @param string $fieldName - * @return bool + * @return bool|Response */ - public function showRelated(Request $request, object $model, string $fieldName): bool; + public function showRelated(Request $request, object $model, string $fieldName): bool|Response; /** * Authorize the show-relationship controller action. @@ -76,9 +77,9 @@ public function showRelated(Request $request, object $model, string $fieldName): * @param Request $request * @param object $model * @param string $fieldName - * @return bool + * @return bool|Response */ - public function showRelationship(Request $request, object $model, string $fieldName): bool; + public function showRelationship(Request $request, object $model, string $fieldName): bool|Response; /** * Authorize the update-relationship controller action. @@ -86,9 +87,9 @@ public function showRelationship(Request $request, object $model, string $fieldN * @param Request $request * @param object $model * @param string $fieldName - * @return bool + * @return bool|Response */ - public function updateRelationship(Request $request, object $model, string $fieldName): bool; + public function updateRelationship(Request $request, object $model, string $fieldName): bool|Response; /** * Authorize the attach-relationship controller action. @@ -96,9 +97,9 @@ public function updateRelationship(Request $request, object $model, string $fiel * @param Request $request * @param object $model * @param string $fieldName - * @return bool + * @return bool|Response */ - public function attachRelationship(Request $request, object $model, string $fieldName): bool; + public function attachRelationship(Request $request, object $model, string $fieldName): bool|Response; /** * Authorize the detach-relationship controller action. @@ -106,7 +107,7 @@ public function attachRelationship(Request $request, object $model, string $fiel * @param Request $request * @param object $model * @param string $fieldName - * @return bool + * @return bool|Response */ - public function detachRelationship(Request $request, object $model, string $fieldName): bool; + public function detachRelationship(Request $request, object $model, string $fieldName): bool|Response; } diff --git a/src/Core/Auth/Authorizer.php b/src/Core/Auth/Authorizer.php index 31b4a8d..2347752 100644 --- a/src/Core/Auth/Authorizer.php +++ b/src/Core/Auth/Authorizer.php @@ -12,6 +12,7 @@ namespace LaravelJsonApi\Core\Auth; use Illuminate\Contracts\Auth\Access\Gate; +use Illuminate\Auth\Access\Response; use Illuminate\Http\Request; use LaravelJsonApi\Contracts\Auth\Authorizer as AuthorizerContract; use LaravelJsonApi\Contracts\Schema\Schema; @@ -47,10 +48,10 @@ public function __construct(Gate $gate, JsonApiService $service) /** * @inheritDoc */ - public function index(Request $request, string $modelClass): bool + public function index(Request $request, string $modelClass): bool|Response { if ($this->mustAuthorize()) { - return $this->gate->check( + return $this->gate->inspect( 'viewAny', $modelClass ); @@ -62,10 +63,10 @@ public function index(Request $request, string $modelClass): bool /** * @inheritDoc */ - public function store(Request $request, string $modelClass): bool + public function store(Request $request, string $modelClass): bool|Response { if ($this->mustAuthorize()) { - return $this->gate->check( + return $this->gate->inspect( 'create', $modelClass ); @@ -77,10 +78,10 @@ public function store(Request $request, string $modelClass): bool /** * @inheritDoc */ - public function show(Request $request, object $model): bool + public function show(Request $request, object $model): bool|Response { if ($this->mustAuthorize()) { - return $this->gate->check( + return $this->gate->inspect( 'view', $model ); @@ -92,10 +93,10 @@ public function show(Request $request, object $model): bool /** * @inheritDoc */ - public function update(Request $request, object $model): bool + public function update(Request $request, object $model): bool|Response { if ($this->mustAuthorize()) { - return $this->gate->check( + return $this->gate->inspect( 'update', $model ); @@ -107,10 +108,10 @@ public function update(Request $request, object $model): bool /** * @inheritDoc */ - public function destroy(Request $request, object $model): bool + public function destroy(Request $request, object $model): bool|Response { if ($this->mustAuthorize()) { - return $this->gate->check( + return $this->gate->inspect( 'delete', $model ); @@ -122,10 +123,10 @@ public function destroy(Request $request, object $model): bool /** * @inheritDoc */ - public function showRelated(Request $request, object $model, string $fieldName): bool + public function showRelated(Request $request, object $model, string $fieldName): bool|Response { if ($this->mustAuthorize()) { - return $this->gate->check( + return $this->gate->inspect( 'view' . Str::classify($fieldName), $model ); @@ -137,7 +138,7 @@ public function showRelated(Request $request, object $model, string $fieldName): /** * @inheritDoc */ - public function showRelationship(Request $request, object $model, string $fieldName): bool + public function showRelationship(Request $request, object $model, string $fieldName): bool|Response { return $this->showRelated($request, $model, $fieldName); } @@ -145,10 +146,10 @@ public function showRelationship(Request $request, object $model, string $fieldN /** * @inheritDoc */ - public function updateRelationship(Request $request, object $model, string $fieldName): bool + public function updateRelationship(Request $request, object $model, string $fieldName): bool|Response { if ($this->mustAuthorize()) { - return $this->gate->check( + return $this->gate->inspect( 'update' . Str::classify($fieldName), [$model, $this->createRelation($request, $fieldName)] ); @@ -160,10 +161,10 @@ public function updateRelationship(Request $request, object $model, string $fiel /** * @inheritDoc */ - public function attachRelationship(Request $request, object $model, string $fieldName): bool + public function attachRelationship(Request $request, object $model, string $fieldName): bool|Response { if ($this->mustAuthorize()) { - return $this->gate->check( + return $this->gate->inspect( 'attach' . Str::classify($fieldName), [$model, $this->createRelation($request, $fieldName)] ); @@ -175,10 +176,10 @@ public function attachRelationship(Request $request, object $model, string $fiel /** * @inheritDoc */ - public function detachRelationship(Request $request, object $model, string $fieldName): bool + public function detachRelationship(Request $request, object $model, string $fieldName): bool|Response { if ($this->mustAuthorize()) { - return $this->gate->check( + return $this->gate->inspect( 'detach' . Str::classify($fieldName), [$model, $this->createRelation($request, $fieldName)] ); diff --git a/src/Core/Document/ErrorSource.php b/src/Core/Document/ErrorSource.php index 0b84555..13ae81c 100644 --- a/src/Core/Document/ErrorSource.php +++ b/src/Core/Document/ErrorSource.php @@ -31,6 +31,11 @@ class ErrorSource implements Serializable */ private ?string $parameter; + /** + * @var string|null + */ + private ?string $header; + /** * @param ErrorSource|array|null $value * @return ErrorSource @@ -60,7 +65,8 @@ public static function fromArray(array $source): self { return new self( $source['pointer'] ?? null, - $source['parameter'] ?? null + $source['parameter'] ?? null, + $source['header'] ?? null, ); } @@ -69,11 +75,13 @@ public static function fromArray(array $source): self * * @param string|null $pointer * @param string|null $parameter + * @param string|null $header */ - public function __construct(?string $pointer = null, ?string $parameter = null) + public function __construct(?string $pointer = null, ?string $parameter = null, ?string $header = null) { $this->pointer = $pointer; $this->parameter = $parameter; + $this->header = $header; } /** @@ -149,12 +157,47 @@ public function withoutParameter(): self return $this; } + /** + * A string indicating which request header caused the error. + * + * @return string|null + */ + public function header(): ?string + { + return $this->header; + } + + /** + * Add a string indicating which request header caused the error. + * + * @param string|null $header + * @return $this + */ + public function setHeader(?string $header): self + { + $this->header = $header; + + return $this; + } + + /** + * Remove the source header. + * + * @return $this + */ + public function withoutHeader(): self + { + $this->header = null; + + return $this; + } + /** * @return bool */ public function isEmpty(): bool { - return empty($this->pointer) && empty($this->parameter); + return empty($this->pointer) && empty($this->parameter) && empty($this->header); } /** @@ -173,6 +216,7 @@ public function toArray() return array_filter([ 'parameter' => $this->parameter, 'pointer' => $this->pointer, + 'header' => $this->header, ]); } diff --git a/src/Core/Responses/Concerns/HasRelationship.php b/src/Core/Responses/Concerns/HasRelationship.php index cf7f3eb..333ed10 100644 --- a/src/Core/Responses/Concerns/HasRelationship.php +++ b/src/Core/Responses/Concerns/HasRelationship.php @@ -48,7 +48,7 @@ private function allMeta(): ?Hash * * @return array|null */ - private function metaForRelationship(): ?array + protected function metaForRelationship(): ?array { if ($this->hasRelationMeta && $relation = $this->relation()) { return $relation->meta(); diff --git a/src/Core/Responses/Internal/PaginatedIdentifierResponse.php b/src/Core/Responses/Internal/PaginatedIdentifierResponse.php index 619bf1c..df6f47f 100644 --- a/src/Core/Responses/Internal/PaginatedIdentifierResponse.php +++ b/src/Core/Responses/Internal/PaginatedIdentifierResponse.php @@ -43,6 +43,8 @@ public function __construct(JsonApiResource $resource, string $fieldName, Page $ public function meta(): Hash { return Hash::cast($this->page->meta())->merge( + Hash::cast(parent::metaForRelationship()) + )->merge( parent::meta() ); } diff --git a/src/Core/Responses/Internal/PaginatedRelatedResourceResponse.php b/src/Core/Responses/Internal/PaginatedRelatedResourceResponse.php index 3391687..d7a9ea8 100644 --- a/src/Core/Responses/Internal/PaginatedRelatedResourceResponse.php +++ b/src/Core/Responses/Internal/PaginatedRelatedResourceResponse.php @@ -43,6 +43,8 @@ public function __construct(JsonApiResource $resource, string $fieldName, Page $ public function meta(): Hash { return Hash::cast($this->page->meta())->merge( + Hash::cast(parent::metaForRelationship()) + )->merge( parent::meta() ); }