8000 [Feature] Allow decoder wildcard media parameters (#294) · GIANTCRAB/laravel-json-api@ec903d1 · GitHub
[go: up one dir, main page]

Skip to content

Commit ec903d1

Browse files
[Feature] Allow decoder wildcard media parameters (cloudcreativity#294)
Allow wildcard media type parameters for decoders. Closes cloudcreativity#265
1 parent b0ac46b commit ec903d1

File tree

5 files changed

+55
-8
lines changed

5 files changed

+55
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ are not meant to be extended.
2828
Also added PHP 7 type-hinting to all methods in the abstract class.
2929

3030
### Fixed
31+
- [#265](https://github.com/cloudcreativity/laravel-json-api/issues/265)
32+
Allow wildcard media type parameters when matching decoders. This enables support for media types that
33+
include random parameter values, primarily the `multipart/form-data` type that has a `boundary` parameter
34+
that is unpredictable.
3135
- [#280](https://github.com/cloudcreativity/laravel-json-api/issues/280)
3236
Validation error objects for relationship objects now have correct source pointers.
3337
- [#284](https://github.com/cloudcreativity/laravel-json-api/issues/284)

docs/features/media-types.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,7 @@ class ContentNegotiator extends BaseContentNegotiator
589589
{
590590
protected $decoding = [
591591
'multipart/form-data' => \App\JsonApi\MultipartDecoder::class,
592+
'multipart/form-data; boundary=*' => \App\JsonApi\MultipartDecoder::class,
592593
];
593594
}
594595
```
@@ -630,11 +631,12 @@ class ContentNegotiator extends BaseContentNegotiator
630631
*/
631632
protected function decodingsForResource(?Avatar $avatar): DecodingList
632633
{
633-
$multiPart = Decoding::create('multipart/form-data', new MultipartDecoder());
634+
$decoder = new MultipartDecoder();
634635

635636
return $this
636637
->decodingMediaTypes()
637-
->when(is_null($avatar), $multiPart);
638+
->when(is_null($avatar), Decoding::create('multipart/form-data', $decoder))
639+
->when(is_null($avatar), Decoding::create('multipart/form-data; boundary=*', $decoder));
638640
}
639641

640642
}

src/Codec/Decoding.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use CloudCreativity\LaravelJsonApi\Contracts\Decoder\DecoderInterface;
2121
use CloudCreativity\LaravelJsonApi\Decoder\JsonApiDecoder;
2222
use CloudCreativity\LaravelJsonApi\Exceptions\RuntimeException;
23+
use Illuminate\Support\Collection;
2324
use Neomerx\JsonApi\Contracts\Http\Headers\MediaTypeInterface;
2425
use Neomerx\JsonApi\Http\Headers\MediaType;
2526

@@ -145,1 8000 0 +146,37 @@ public function isNotJsonApi(): bool
145146
/**
146147
* @param MediaTypeInterface $mediaType
147148
* @return bool
149+
* @todo normalization will not be necessary for neomerx/json-api:^3.0
150+
* @see https://github.com/neomerx/json-api/issues/221
148151
*/
149152
public function equalsTo(MediaTypeInterface $mediaType): bool
150153
{
151-
return $this->mediaType->equalsTo($mediaType);
154+
return $this->normalize($this->mediaType)->equalsTo(
155+
$this->normalize($mediaType)
156+
);
157+
}
158+
159+
/**
160+
* @return array
161+
*/
162+
private function getWildCardParameters(): array
163+
{
164+
return collect((array) $this->mediaType->getParameters())->filter(function ($value) {
165+
return '*' === $value;
166+
})->keys()->all();
167+
}
168+
169+
/**
170+
* @param MediaTypeInterface $mediaType
171+
* @return MediaTypeInterface
172+
*/
173+
private function normalize(MediaTypeInterface $mediaType): MediaTypeInterface
174+
{
175+
$params = collect((array) $mediaType->getParameters())->forget(
176+
$this->getWildCardParameters()
177+
)->all();
178+
179+
return new MediaType($mediaType->getType(), $mediaType->getSubType(), $params ?: null);
152180
}
153181

154182
}

tests/dummy/app/JsonApi/Avatars/ContentNegotiator.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,10 @@ protected function encodingsForOne(?Avatar $avatar): EncodingList
4646
*/
4747
protected function decodingsForResource(?Avatar $avatar): DecodingList
4848
{
49-
$multiPart = Decoding::create('multipart/form-data', new FileDecoder());
50-
5149
return $this
5250
->decodingMediaTypes()
53-
->when(is_null($avatar), $multiPart);
51+
->when(is_null($avatar), Decoding::create('multipart/form-data', new FileDecoder()))
52+
->when(is_null($avatar), Decoding::create('multipart/form-data; boundary=*', new FileDecoder()));
5453
}
5554

5655
}

tests/dummy/tests/Feature/Avatars/CreateTest.php

Lines changed: 16 additions & 2 deletions

< 4CD7 td data-grid-cell-id="diff-17b42312ec83876147b463ca48f2b52566222db3476cc5dd282ed6e5f43b3273-48-62-0" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-additionNum-bgColor, var(--diffBlob-addition-bgColor-num));text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative left-side">
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,26 @@
2626
class CreateTest extends TestCase
2727
{
2828
29+
/**
30+
* @return array
31+
*/
32+
public function multipartProvider(): array
33+
{
34+
return [
35+
['multipart/form-data'],
36+
['multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'],
37+
];
38+
}
39+
2940
/**
3041
* Test that a user can upload an avatar to the API using a standard
3142
* HTML form post. This means our API must allow a non-JSON API content media type
3243
* when creating the resource.
44+
*
45+
* @param string $contentType
46+
* @dataProvider multipartProvider
3347
*/
34-
public function test(): void
48+
public function test(string $contentType): void
3549
{
3650
$user = factory(User::class)->create();
3751
$file = UploadedFile::fake()->create('avatar.jpg');
@@ -45,7 +59,7 @@ public function test(): void
4559
$response = $this->actingAs($user, 'api')->post(
4660
'/api/v1/avatars?include=user',
4761
['avatar' => $file],
48-
['Content-Type' => 'multipart/form-data', 'Content-Length' => '1']
62+
['Content-Type' => $contentType, 'Content-Length' => '1']
4963
);
5064

5165
$id = $response
0