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' => '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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy