Skip to content

Commit f534fb2

Browse files
committed
Merge branch 'release/1.0.0-alpha.4' into main
2 parents 19699dd + 407bbb7 commit f534fb2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1112
-216
lines changed

CHANGELOG.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,69 @@
33
All notable changes to this project will be documented in this file. This project adheres to
44
[Semantic Versioning](http://semver.org/) and [this changelog format](http://keepachangelog.com/).
55

6+
## [1.0.0-alpha.4] - 2021-02-27
7+
8+
### Added
9+
10+
- Added missing `jsonapi:authorizer` generator command.
11+
- The Eloquent schema now has `indexQuery` and `relatableQuery` methods. These allow filtering for authorization
12+
purposes when a list of resources is being retrieved. For instance, it could filter those queries so that only models
13+
belonging to the authenticated user are returned.
14+
- [#23](https://github.com/laravel-json-api/laravel/issues/23) The resource request class now does not need to exist for
15+
the destroy controller action. Previously the implementation was expecting the resource request class to exist, even
16+
though delete validation was optional.
17+
- [#24](https://github.com/laravel-json-api/laravel/issues/24) Controller actions will now stop executing and return a
18+
response if one is returned by the *before* action hooks: i.e. `searching`, `reading`, `saving`, `creating`,
19+
`updating`, `deleting`, `readingRelated<Name>`, `reading<Name>`, `updating<Name>`, `attaching<Name>` and
20+
`detaching<Name>`.
21+
- [#37](https://github.com/laravel-json-api/laravel/issues/37) Can now use constructor dependency injection in `Server`
22+
classes.
23+
- [#40](https://github.com/laravel-json-api/laravel/issues/40) There is now a new `MetaResponse` class that can be used
24+
when returning meta-only responses. In addition, response classes have been updated to add a `withServer` method. This
25+
can be used to specify the named server the response should use to encode the JSON:API document. This has to be used
26+
when returning responses from routes that have not run the JSON:API middleware (i.e. there is no default server
27+
available via the service container).
28+
- [#9](https://github.com/laravel-json-api/laravel/issues/9) The Laravel route registrar is now passed through to
29+
the `resources`, `relationships` and `actions` callbacks as the second function argument.
30+
- [#36](https://github.com/laravel-json-api/laravel/issues/36) Eloquent schemas now support complex singular filter
31+
logic, via the `Schema::isSingular()` method.
32+
- [#33](https://github.com/laravel-json-api/laravel/issues/33) Specification compliance will now reject an incorrect
33+
resource type in a relationship. For example, if a relationship expects `tags` but the client sends `posts`, the
34+
request will be rejected with an error message that `posts` are not supported.
35+
36+
### Changed
37+
38+
- [#22](https://github.com/laravel-json-api/laravel/issues/22) **BREAKING** The `index` and `store` methods on the
39+
authorizer contract now receive the model class as their second argument. This is useful for authorizers that are used
40+
for multiple resource types.
41+
- **BREAKING** When querying or modifying models via the schema repository or store, calls to `using()` must be replaced
42+
with `withRequest()`. This change was made to make it clearer that the request class can be passed into query
43+
builders.
44+
- [#28](https://github.com/laravel-json-api/laravel/issues/28) The sparse field sets validation rule will now reject
45+
with a specific message identifying any resource types in the parameter that do not exist.
46+
- [#35](https://github.com/laravel-json-api/laravel/issues/35) The `Relation::type()` method must now be used when
47+
setting the inverse resource type for the relation.
48+
49+
### Fixed
50+
51+
- Optional parameters to generator commands that require values now work correctly. Previously these were incorrectly
52+
set up as optional parameters that expected no values.
53+
- [#25](https://github.com/laravel-json-api/laravel/issues/25) The encoder now correctly handles conditional fields when
54+
iterating over a resource's relationships.
55+
- [#26](https://github.com/laravel-json-api/laravel/issues/26) Fix parsing the `fields` query parameter to field set
56+
value objects.
57+
- [#34](https://github.com/laravel-json-api/laravel/issues/34) Do not require server option when generating a generic
58+
authorizer with multiple servers present.
59+
- [#29](https://github.com/laravel-json-api/laravel/issues/29) Do not reject delete requests without a `Content-Type`
60+
header.
61+
- [#11](https://github.com/laravel-json-api/laravel/issues/11) Fixed iterating over an empty *to-many* generator twice
62+
in the underlying compound document encoder.
63+
64+
### Deprecated
65+
66+
- The `Relation::inverseType()` method is deprecated and will be removed in `1.0-stable`. Use `Relation::type()`
67+
instead.
68+
669
## [1.0.0-alpha.3] - 2021-02-09
770

871
### Added

composer.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
"require": {
2626
"php": "^7.4|^8.0",
2727
"ext-json": "*",
28-
"laravel-json-api/core": "^1.0.0-alpha.3",
29-
"laravel-json-api/eloquent": "^1.0.0-alpha.3",
30-
"laravel-json-api/encoder-neomerx": "^1.0.0-alpha.3",
28+
"laravel-json-api/core": "^1.0.0-alpha.4",
29+
"laravel-json-api/eloquent": "^1.0.0-alpha.4",
30+
"laravel-json-api/encoder-neomerx": "^1.0.0-alpha.4",
3131
"laravel-json-api/exceptions": "^1.0.0-alpha.2",
32-
"laravel-json-api/spec": "^1.0.0-alpha.3",
33-
"laravel-json-api/validation": "^1.0.0-alpha.3",
32+
"laravel-json-api/spec": "^1.0.0-alpha.4",
33+
"laravel-json-api/validation": "^1.0.0-alpha.4",
3434
"laravel/framework": "^8.0"
3535
},
3636
"require-dev": {
@@ -65,7 +65,7 @@
6565
]
6666
}
6767
},
68-
"minimum-stability": "stable",
68+
"minimum-stability": "dev",
6969
"prefer-stable": true,
7070
"config": {
7171
"sort-packages": true

phpunit.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
<testsuite name="Integration">
2727
<directory suffix="Test.php">./tests/lib/Integration/</directory>
2828
</testsuite>
29-
<testsuite name="Acceptance">
29+
<testsuite name="Acceptance">
30+
<directory suffix="Test.php">./tests/lib/Acceptance/</directory>
31+
</testsuite>
32+
<testsuite name="Dummy">
3033
<directory suffix="Test.php">./tests/dummy/tests/</directory>
3134
</testsuite>
3235
</testsuites>

src/Console/GeneratorCommand.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ abstract class GeneratorCommand extends BaseGeneratorCommand
4040
*/
4141
public function handle()
4242
{
43-
if (!$server = $this->getServerInput()) {
43+
$server = $this->getServerInput();
44+
45+
if ($this->doesRequireServer() && empty($server)) {
4446
$this->error('You must use the server option when you have more than one API.');
4547
return 1;
4648
}
4749

48-
if (is_null($this->getServerNamespace($server))) {
50+
if (!empty($server) && is_null($this->getServerNamespace($server))) {
4951
$this->error("Server {$server} does not exist in your jsonapi.servers configuration.");
5052
return 1;
5153
}
@@ -136,6 +138,18 @@ protected function getClassType(): string
136138
return $this->classType;
137139
}
138140

141+
/**
142+
* Does the generator require a server to be specified?
143+
*
144+
* Child classes can overload this method if a server is not required.
145+
*
146+
* @return bool
147+
*/
148+
protected function doesRequireServer(): bool
149+
{
150+
return true;
151+
}
152+
139153
/**
140154
* Get the console command arguments.
141155
*

src/Console/MakeAuthorizer.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
/*
3+
* Copyright 2021 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace LaravelJsonApi\Laravel\Console;
21+
22+
use Symfony\Component\Console\Input\InputOption;
23+
24+
class MakeAuthorizer extends GeneratorCommand
25+
{
26+
27+
/**
28+
* @var string
29+
*/
30+
protected $name = 'jsonapi:authorizer';
31+
32+
/**
33+
* @var string
34+
*/
35+
protected $description = 'Create a new JSON:API authorizer.';
36+
37+
/**
38+
* @var string
39+
*/
40+
protected $type = 'JSON:API authorizer';
41+
42+
/**
43+
* @var string
44+
*/
45+
protected $classType = 'Authorizer';
46+
47+
/**
48+
* @inheritDoc
49+
*/
50+
protected function getStub()
51+
{
52+
return $this->resolveStubPath('authorizer.stub');
53+
}
54+
55+
/**
56+
* @param string $rootNamespace
57+
* @return string
58+
*/
59+
protected function getDefaultNamespace($rootNamespace)
60+
{
61+
if ($this->option('resource')) {
62+
return parent::getDefaultNamespace($rootNamespace);
63+
}
64+
65+
$jsonApi = trim(config('jsonapi.namespace') ?: 'JsonApi', '\\');
66+
67+
return $rootNamespace . '\\' . $jsonApi . '\\' . 'Authorizers';
68+
}
69+
70+
/**
71+
* @inheritDoc
72+
*/
73+
protected function doesRequireServer(): bool
74+
{
75+
return true === $this->option('resource');
76+
}
77+
78+
/**
79+
* @inheritDoc
80+
*/
81+
protected function getOptions()
82+
{
83+
return [
84+
['force', null, InputOption::VALUE_NONE, 'Create the class even if the schema already exists.'],
85+
['resource', 'r', InputOption::VALUE_NONE, 'Whether the authorizer is for a specific resource type.'],
86+
['server', 's', InputOption::VALUE_REQUIRED, 'The JSON:API server the schema exists in.'],
87+
];
88+
}
89+
90+
}

src/Console/MakeQuery.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ protected function getOptions()
9191
['collection', 'c', InputOption::VALUE_NONE, 'Create a query collection class.'],
9292
['both', 'b', InputOption::VALUE_NONE, 'Create a query and a query collection class.'],
9393
['force', null, InputOption::VALUE_NONE, 'Create the class even if the query already exists'],
94-
['server', 's', InputOption::VALUE_NONE, 'The JSON:API server the query exists in.'],
94+
['server', 's', InputOption::VALUE_REQUIRED, 'The JSON:API server the query exists in.'],
9595
];
9696
}
9797

src/Console/MakeRequest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ protected function getOptions()
5959
{
6060
return [
6161
['force', null, InputOption::VALUE_NONE, 'Create the class even if the request already exists'],
62-
['server', 's', InputOption::VALUE_NONE, 'The JSON:API server the request exists in.'],
62+
['server', 's', InputOption::VALUE_REQUIRED, 'The JSON:API server the request exists in.'],
6363
];
6464
}
6565

src/Console/MakeRequests.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ protected function getOptions()
7878
{
7979
return [
8080
['force', null, InputOption::VALUE_NONE, 'Create the request classes even if they already exists'],
81-
['server', 's', InputOption::VALUE_NONE, 'The JSON:API server the requests exists in.'],
81+
['server', 's', InputOption::VALUE_REQUIRED, 'The JSON:API server the requests exists in.'],
8282
];
8383
}
8484
}

src/Console/MakeResource.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ protected function getOptions()
5959
{
6060
return [
6161
['force', null, InputOption::VALUE_NONE, 'Create the class even if the resource already exists'],
62-
['server', 's', InputOption::VALUE_NONE, 'The JSON:API server the resource exists in.'],
62+
['server', 's', InputOption::VALUE_REQUIRED, 'The JSON:API server the resource exists in.'],
6363
];
6464
}
6565

src/Console/MakeSchema.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ protected function getOptions()
104104
{
105105
return [
106106
['force', null, InputOption::VALUE_NONE, 'Create the class even if the schema already exists'],
107-
['model', 'm', InputOption::VALUE_NONE, 'The model that the schema applies to.'],
108-
['server', 's', InputOption::VALUE_NONE, 'The JSON:API server the schema exists in.'],
107+
['model', 'm', InputOption::VALUE_REQUIRED, 'The model that the schema applies to.'],
108+
['server', 's', InputOption::VALUE_REQUIRED, 'The JSON:API server the schema exists in.'],
109109
];
110110
}
111111

src/Console/MakeServer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ protected function getOptions()
129129
{
130130
return [
131131
['force', null, InputOption::VALUE_NONE, 'Create the class even if the server already exists'],
132-
['uri', null, InputOption::VALUE_NONE, 'The base URI of the server.'],
132+
['uri', null, InputOption::VALUE_REQUIRED, 'The base URI of the server.'],
133133
];
134134
}
135135

src/Http/Controllers/Actions/AttachRelationship.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,21 @@ public function attachRelationship(Route $route, StoreContract $store)
5555
$query = ResourceQuery::queryMany($resourceType);
5656

5757
$model = $route->model();
58+
$response = null;
5859

5960
if (method_exists($this, $hook = 'attaching' . Str::classify($fieldName))) {
60-
$this->{$hook}($model, $request, $query);
61+
$response = $this->{$hook}($model, $request, $query);
62+
}
63+
64+
if ($response) {
65+
return $response;
6166
}
6267

6368
$result = $store
6469
->modifyToMany($resourceType, $model, $fieldName)
65-
->using($query)
70+
->withRequest($query)
6671
->attach($request->validatedForRelation());
6772

68-
$response = null;
69-
7073
if (method_exists($this, $hook = 'attached' . Str::classify($fieldName))) {
7174
$response = $this->{$hook}($model, $result, $request, $query);
7275
}

src/Http/Controllers/Actions/Destroy.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919

2020
namespace LaravelJsonApi\Laravel\Http\Controllers\Actions;
2121

22+
use Illuminate\Auth\Access\AuthorizationException;
23+
use Illuminate\Auth\AuthenticationException;
2224
use Illuminate\Contracts\Support\Responsable;
2325
use Illuminate\Http\Response;
26+
use Illuminate\Support\Facades\Auth;
2427
use LaravelJsonApi\Contracts\Routing\Route;
2528
use LaravelJsonApi\Contracts\Store\Store as StoreContract;
2629
use LaravelJsonApi\Laravel\Http\Requests\ResourceRequest;
@@ -34,26 +37,47 @@ trait Destroy
3437
* @param Route $route
3538
* @param StoreContract $store
3639
* @return Response|Responsable
40+
* @throws AuthenticationException|AuthorizationException
3741
*/
3842
public function destroy(Route $route, StoreContract $store)
3943
{
40-
$request = ResourceRequest::forResource(
44+
$request = ResourceRequest::forResourceIfExists(
4145
$resourceType = $route->resourceType()
4246
);
4347

4448
$model = $route->model();
4549

50+
/**
51+
* The resource request class is optional for deleting,
52+
* as delete validation is optional. However, if we do not have
53+
* a resource request then the action will not have been authorized.
54+
* So we need to trigger authorization in this case.
55+
*/
56+
if (!$request) {
57+
$check = $route->authorizer()->destroy(
58+
$request = \request(),
59+
$model,
60+
);
61+
62+
throw_if(false === $check && Auth::guest(), new AuthenticationException());
63+
throw_if(false === $check, new AuthorizationException());
64+
}
65+
66+
$response = null;
67+
4668
if (method_exists($this, 'deleting')) {
47-
$this->deleting($model, $request);
69+
$response = $this->deleting($model, $request);
70+
}
71+
72+
if ($response) {
73+
return $response;
4874
}
4975

5076
$store->delete(
5177
$resourceType,
5278
$route->modelOrResourceId()
5379
);
5480

55-
$response = null;
56-
5781
if (method_exists($this, 'deleted')) {
5882
$response = $this->deleted($model, $request);
5983
}

0 commit comments

Comments
 (0)