8000 Merge branch 'feature/content-negotiation' into async-with-cn · BrianLangevin/laravel-json-api@4ee80e4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4ee80e4

Browse files
committed
Merge branch 'feature/content-negotiation' into async-with-cn
2 parents 9e0b61f + e9c949f commit 4ee80e4

File tree

9 files changed

+91
-51
lines changed

9 files changed

+91
-51
lines changed

src/Api/Api.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use CloudCreativity\LaravelJsonApi\Contracts\Resolver\ResolverInterface;
2626
use CloudCreativity\LaravelJsonApi\Contracts\Store\StoreInterface;
2727
use CloudCreativity\LaravelJsonApi\Contracts\Validators\ValidatorFactoryInterface;
28+
use CloudCreativity\LaravelJsonApi\Exceptions\RuntimeException;
2829
use CloudCreativity\LaravelJsonApi\Factories\Factory;
2930
use CloudCreativity\LaravelJsonApi\Http\Responses\Responses;
3031
use CloudCreativity\LaravelJsonApi\Resolver\AggregateResolver;
@@ -272,11 +273,13 @@ public function getCodecs()
272273
}
273274

274275
/**
276+
* Get the default API codec.
277+
*
275278
* @return Codec
276279
*/
277280
public function getDefaultCodec()
278281
{
279-
return $this->codecs->find(MediaTypeInterface::JSON_API_MEDIA_TYPE) ?: $this->codecs->first();
282+
return $this->codecs->find(MediaTypeInterface::JSON_API_MEDIA_TYPE) ?: Codec::jsonApi();
280283
}
281284

282285
/**

src/Api/Codec.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ class Codec
2121
*/
2222
private $options;
2323

24+
/**
25+
* Create a codec for the JSON API media type.
26+
*
27+
* @param int $options
28+
* @param string|null $urlPrefix
29+
* @param int $depth
30+
* @return Codec
31+
*/
32+
public static function jsonApi(int $options = 0, string $urlPrefix = null, int $depth = 512): self
33+
{
34+
return self::encoder(MediaTypeInterface::JSON_API_MEDIA_TYPE, $options, $urlPrefix, $depth);
35+
}
36+
2437
/**
2538
* Create a codec that will encode JSON API content.
2639
*

src/Exceptions/HandlesErrors.php

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818

1919
namespace CloudCreativity\LaravelJsonApi\Exceptions;
2020

21-
use CloudCreativity\LaravelJsonApi\Http\Responses\ErrorResponse;
22-
use CloudCreativity\LaravelJsonApi\Services\JsonApiService;
2321
use CloudCreativity\LaravelJsonApi\Utils\Helpers;
2422
use Exception;
2523
use Illuminate\Http\Request;
@@ -37,25 +35,16 @@ trait HandlesErrors
3735
* Does the HTTP request require a JSON API error response?
3836
*
3937
* This method determines if we need to render a JSON API error response
40-
* for the provided exception. We need to do this if:
41-
*
42-
* - The client has requested JSON API via its Accept header; or
43-
* - The application is handling a request to a JSON API endpoint.
38+
* for the client. We need to do this if the client has requested JSON
39+
* API via its Accept header.
4440
*
4541
* @param Request $request
4642
* @param Exception $e
4743
* @return bool
4844
*/
4945
public function isJsonApi($request, Exception $e)
5046
{
51-
if (Helpers::wantsJsonApi($request)) {
52-
return true;
53-
}
54-
55-
/** @var JsonApiService $service */
56-
$service = app(JsonApiService::class);
57-
58-
return !is_null($service->requestApi());
47+
return Helpers::wantsJsonApi($request);
5948
}
6049

6150
/**
@@ -65,15 +54,7 @@ public function isJsonApi($request, Exception $e)
6554
*/
6655
public function renderJsonApi($request, Exception $e)
6756
{
68-
/** @var ErrorResponse $response */
69-
$response = app('json-api.exceptions')->parse($e);
70-
71-
/** Client does not accept a JSON API response. */
72-
if (Response::HTTP_NOT_ACCEPTABLE === $response->getHttpCode()) {
73-
return response('', Response::HTTP_NOT_ACCEPTABLE);
74-
}
75-
76-
return json_api()->response()->errors($response);
57+
return json_api()->response()->exception($e);
7758
}
7859

7960
}

src/Factories/Factory.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,12 @@ public function createResourceProvider($fqn)
290290
*/
291291
public function createResponseFactory(Api $api)
292292
{
293-
return new Responses($this, $api, $this->container->make('json-api.request'));
293+
return new Responses(
294+
$this,
295+
$api,
296+
$this->container->make('json-api.request'),
297+
$this->container->make('json-api.exceptions')
298+
);
294299
}
295300

296301
/**

src/Http/Responses/Responses.php

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
use CloudCreativity\LaravelJsonApi\Api\Api;
2222
use CloudCreativity\LaravelJsonApi\Api\Codec;
23+
use CloudCreativity\LaravelJsonApi\Contracts\Exceptions\ExceptionParserInterface;
2324
use CloudCreativity\LaravelJsonApi\Contracts\Http\Responses\ErrorResponseInterface;
2425
use CloudCreativity\LaravelJsonApi\Contracts\Pagination\PageInterface;
2526
use CloudCreativity\LaravelJsonApi\Contracts\Queue\AsynchronousProcess;
@@ -57,6 +58,11 @@ class Responses extends BaseResponses
5758
*/
5859
private $jsonApiRequest;
5960

61+
/**
62+
* @var ExceptionParserInterface
63+
*/
64+
private $exceptions;
65+
6066
/**
6167
* @var Codec|null
6268
*/
@@ -74,12 +80,18 @@ class Responses extends BaseResponses
7480
* @param Api $api
7581
* the API that is sending the responses.
7682
* @param JsonApiRequest $request
83+
* @param $exceptions
7784
*/
78-
public function __construct(Factory $factory, Api $api, JsonApiRequest $request)
79-
{
85+
public function __construct(
86+
Factory $factory,
87+
Api $api,
88+
JsonApiRequest $request,
89+
ExceptionParserInterface $exceptions
90+
) {
8091
$this->factory = $factory;
8192
$this->api = $api;
8293
$this->jsonApiRequest = $request;
94+
$this->exceptions = $exceptions;
8395
}
8496

8597
/**
@@ -385,6 +397,24 @@ public function errors($errors, $defaultStatusCode = null, array $headers = [])
385397
);
386398
}
387399

400+
/**
401+
* Render an exception that has arisen from the exception handler.
402+
*
403+
* @param \Exception $ex
404+
* @return mixed
405+
*/
406+
public function exception(\Exception $ex)
407+
{
408+
/** If the current codec cannot encode JSON API, we need to reset it. */
409+
if ($this->getCodec()->willNotEncode()) {
410+
$this->codec = $this->api->getDefaultCodec();
411+
}
412+
413+
return $this->getErrorResponse(
414+
$this->exceptions->parse($ex)
415+
);
416+
}
417+
388418
/**
389419
* @param ErrorInterface|ErrorInterface[]|ErrorCollection|ErrorResponseInterface $errors
390420
* @param int $statusCode
@@ -393,9 +423,6 @@ public function errors($errors, $defaultStatusCode = null, array $headers = [])
393423
*/
394424
public function getErrorResponse($errors, $statusCode = self::HTTP_BAD_REQUEST, array $headers = [])
395425
{
396-
/** If the error occurred while we were encoding, the encoder needs to be reset. */
397-
$this->resetEncoder();
398-
399426
if ($errors instanceof ErrorResponseInterface) {
400427
$statusCode = $errors->getHttpCode();
401428
$headers = $errors->getHeaders();
@@ -535,17 +562,6 @@ protected function isAsync($data)
535562
return $data instanceof AsynchronousProcess;
536563
}
537564

538-
/**
539-
* Reset the encoder.
540-
*
541-
* @return void
542-
*/
543-
protected function resetEncoder()
544-
{
545-
$this->getEncoder()->withLinks([])->withMeta(null);
546-
}
547-
548-
549565
/**
550566
* @param PageInterface $page
551567
* @param $meta

tests/dummy/app/Http/Controllers/AvatarsController.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,16 @@ class AvatarsController extends JsonApiController
1818
*/
1919
protected function reading(Avatar $avatar, ValidatedRequest $request): ?StreamedResponse
2020
{
21-
if ($request->getCodec()->is($avatar->media_type)) {
22-
return Storage::disk('local')->download($avatar->path);
21+
if (!$request->getCodec()->is($avatar->media_type)) {
22+
return null;
2323
}
2424

25-
return null;
25+
abort_unless(
26+
Storage::disk('local')->exists($avatar->path),
27+
404,
28+
'The image file does not exist.'
29+
);
30+
31+
return Storage::disk('local')->download($avatar->path);
2632
}
2733
}

tests/dummy/tests/Feature/Avatars/ReadTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,22 @@ public function testDownload(): void
3737
->assertHeader('Content-Type', $avatar->media_type);
3838
}
3939

40+
/**
41+
* If the avatar model exists, but the file doesn't, we need to get an error back. As
42+
* we have not requests JSON API, this should be the standard Laravel error i.e.
43+
* `text/html`.
44+
*/
45+
public function testDownloadFileDoesNotExist(): void
46+
{
47+
$path = 'avatars/does-not-exist.jpg';
48+
$avatar = factory(Avatar::class)->create(compact('path'));
49+
50+
$this->withAcceptMediaType('image/*')
51+
->doRead($avatar)
52+
->assertStatus(404)
53+
->assertHeader('Content-Type', 'text/html; charset=UTF-8');
54+
}
55+
4056
/**
4157
* Test that we can include the user in the response.
4258
*/

tests/lib/Integration/ErrorsTest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,10 @@ public function invalidDocumentProvider()
9898
public function testDocumentRequired($content, $method = 'POST')
9999
{
100100
if ('POST' === $method) {
101-
$uri = $this->api()->url()->create('posts');
101+
$uri = $this->apiUrl()->getResourceTypeUrl('posts');
102102
} else {
103103
$model = factory(Post::class)->create();
104-
$uri = $this->api()->url()->read('posts', $model);
104+
$uri = $this->apiUrl()->getResourceUrl('posts', $model);
105105
}
106106

107107
$expected = [
@@ -155,7 +155,7 @@ public function testIgnoresData($content, $method = 'GET')
155155
*/
156156
public function testCustomDocumentRequired()
157157
{
158-
$uri = $this->api()->url()->create('posts');
158+
$uri = $this->apiUrl()->getResourceTypeUrl('posts');
159159
$expected = $this->withCustomError(DocumentRequiredException::class);
160160

161161
$this->doInvalidRequest($uri, '')
@@ -367,11 +367,11 @@ private function withCustomError($key)
367367
*/
368368
private function doInvalidRequest($uri, $content, $method = 'POST')
369369
{
370-
$headers = [
370+
$headers = $this->transformHeadersToServerVars([
371371
'CONTENT_LENGTH' => mb_strlen($content, '8bit'),
372372
'CONTENT_TYPE' => 'application/vnd.api+json',
373373
'Accept' =>  B980 9;application/vnd.api+json',
374-
];
374+
]);
375375

376376
return $this->call($method, $uri, [], [], [], $headers, $content);
377377
}

tests/lib/Integration/Validation/Spec/TestCase.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ protected function doInvalidRequest($uri, $content, $method = 'POST')
3535
$content = json_encode($content);
3636
}
3737

38-
$headers = [
38+
$headers = $this->transformHeadersToServerVars([
3939
'CONTENT_LENGTH' => mb_strlen($content, '8bit'),
4040
'CONTENT_TYPE' => 'application/vnd.api+json',
4141
'Accept' => 'application/vnd.api+json',
42-
];
42+
]);
4343

4444
return $this->call($method, $uri, [], [], [], $headers, $content);
4545
}

0 commit comments

Comments
 (0)
0