Skip to content

Commit ccbd9c1

Browse files
committed
Merge branch 'release/0.4.2'
2 parents a27c864 + 0f8e143 commit ccbd9c1

File tree

7 files changed

+337
-10
lines changed

7 files changed

+337
-10
lines changed

CHANGELOG.md

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

5+
## [0.4.2] - 2016-08-11
6+
7+
### Added
8+
9+
- Can now register a resource type with the resource registrar without providing a controller name. The controller
10+
will default to the studley case of the resource type with `Controller` appended - e.g. `posts` becomes
11+
`PostsController`.
12+
- New `InteractsWithResources` test helper. This extends `MakesJsonApiRequests` and adds in helpers for using
13+
when a test case is testing a single resource type.
14+
515
## [0.4.1] - 2016-07-27
616

717
### Added

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"require": {
2525
"php": ">=5.5.0",
2626
"laravel/framework": "^5.1",
27-
"cloudcreativity/json-api": "^0.5.0",
27+
"cloudcreativity/json-api": "^0.5.2",
2828
"symfony/psr-http-message-bridge": "^0.2.0",
2929
"zendframework/zend-diactoros": "^1.3"
3030
},

src/Routing/ResourceRegistrar.php

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

2121
use CloudCreativity\LaravelJsonApi\Document\GeneratesRouteNames;
2222
use Illuminate\Contracts\Routing\Registrar;
23+
use Illuminate\Support\Str;
2324

2425
/**
2526
* Class ResourceRegistrar
@@ -48,13 +49,17 @@ public function __construct(Registrar $router)
4849
}
4950

5051
/**
51-
* @param $resourceType
52-
* @param $controller
52+
* Register routes for the supplied resource type
53+
*
54+
* @param string $resourceType
55+
* @param string|null $controller
5356
* @param array $options
5457
* @return void
5558
*/
56-
public function resource($resourceType, $controller, array $options = [])
59+
public function resource($resourceType, $controller = null, array $options = [])
5760
{
61+
$controller = $controller ?: $this->controllerFor($resourceType);
62+
5863
$this->registerIndex($resourceType, $controller);
5964
$this->registerResource($resourceType, $controller);
6065
$this->registerRelatedResource($resourceType, $controller);
@@ -181,4 +186,12 @@ protected function relationshipUri($resourceType)
181186
);
182187
}
183188

189+
/**
190+
* @param $resourceType
191+
* @return string
192+
*/
193+
protected function controllerFor($resourceType)
194+
{
195+
return Str::studly($resourceType) . 'Controller';
196+
}
184197
}

src/Services/JsonApiService.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ public function __construct(ResourceRegistrar $registrar)
5555
* Register a resource type with the router.
5656
*
5757
* @param string $resourceType
58-
* @param string $controller
58+
* @param string|null $controller
5959
* @param array $options
6060
*/
61-
public function resource($resourceType, $controller, array $options = [])
61+
public function resource($resourceType, $controller = null, array $options = [])
6262
{
6363
$this->registrar->resource($resourceType, $controller, $options);
6464
}
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2016 Cloud Creativity Limited
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
namespace CloudCreativity\LaravelJsonApi\Testing;
20+
21+
use CloudCreativity\JsonApi\Testing\ResourcesTester;
22+
use CloudCreativity\JsonApi\Testing\ResourceTester;
23+
use Illuminate\Contracts\Routing\UrlRoutable;
24+
use Illuminate\Http\Response;
25+
use Illuminate\Support\Collection;
26+
use InvalidArgumentException;
27+
use Neomerx\JsonApi\Contracts\Document\DocumentInterface as Keys;
28+
use Neomerx\JsonApi\Contracts\Http\Headers\MediaTypeInterface;
29+
use Neomerx\JsonApi\Contracts\Http\Query\QueryParametersParserInterface as Params;
30+
31+
/**
32+
* Class InteractsWithResources
33+
* @package CloudCreativity\LaravelJsonApi
34+
*
35+
* This trait MUST be used on a class that uses this trait:
36+
* Illuminate\Foundation\Testing\Concerns\MakesHttpRequests
37+
*/
38+
trait InteractsWithResources
39+
{
40+
41+
use MakesJsonApiRequests;
42+
43+
/**
44+
* Get the resource type that this test case is testing.
45+
*
46+
* @return string
47+
*/
48+
abstract protected function getResourceType();
49+
50+
/**
51+
* Get the route prefix that should be added to the resource type to create the route name.
52+
*
53+
* Test case classes should overload this method if they are registering resource types
54+
* under a group with a route name. E.g. if your resource `posts` is registered under a route
55+
* group name alias of `api::` then this method needs to return `api::` as the route name
56+
* for the `posts` resource will be `api::posts`.
57+
*
58+
* @return string|null
59+
*/
60+
protected function getRoutePrefix()
61+
{
62+
return null;
63+
}
64+
65+
/**
66+
* @param array $params
67+
* @param array $headers
68+
* @return $this
69+
*/
70+
protected function doSearch(array $params = [], array $headers = [])
71+
{
72+
$params = $this->addDefaultRouteParams($params);
73+
$route = $this->resolveRouteName();
74+
$uri = $this->linkTo()->index($route, $params);
75+
76+
return $this->jsonApi('GET', $uri, [], $headers);
77+
}
78+
79+
/**
80+
* @param array|Collection $ids
81+
* the ids - may contain UrlRoutable objects (includes Models)
82+
* @param array $params
83+
* @param array $headers
84+
* @return $this
85+
*/
86+
protected function doSearchById($ids, array $params = [], array $headers = [])
87+
{
88+
if (!isset($params[Params::PARAM_FILTER])) {
89+
$params[Params::PARAM_FILTER] = [];
90+
}
91+
92+
$params[Params::PARAM_FILTER][Keys::KEYWORD_ID] = $this->normalizeIds($ids);
93+
94+
return $this->doSearch($params, $headers);
95+
}
96+
97+
/**
98+
* @param array $data
99+
* @param array $params
100+
* @param array $headers
101+
* @return $this
102+
*/
103+
protected function doCreate(array $data, array $params = [], array $headers = [])
104+
{
105+
$params = $this->addDefaultRouteParams($params);
106+
$route = $this->resolveRouteName();
107+
$uri = $this->linkTo()->index($route, $params);
108+
109+
return $this->jsonApi('POST', $uri, ['data' => $data], $headers);
110+
}
111+
112+
/**
113+
* @param mixed $resourceId
114+
* @param array $params
115+
* @param array $headers
116+
* @return $this
117+
*/
118+
protected function doRead($resourceId, array $params = [], array $headers = [])
119+
{
120+
$params = $this->addDefaultRouteParams($params);
121+
$route = $this->resolveRouteName();
122+
$uri = $this->linkTo()->resource($route, $resourceId, $params);
123+
124+
return $this->jsonApi('GET', $uri, $headers);
125+
}
126+
127+
/**
128+
* @param array $data
129+
* @param array $params
130+
* @param array $headers
131+
* @return $this
132+
*/
133+
protected function doUpdate(array $data, array $params = [], array $headers = [])
134+
{
135+
$id = isset($data[Keys::KEYWORD_ID]) ? $data[Keys::KEYWORD_ID] : null;
136+
137+
if (empty($id)) {
138+
throw new InvalidArgumentException('Expecting data to contain a resource id');
139+
}
140+
141+
$params = $this->addDefaultRouteParams($params);
142+
$route = $this->resolveRouteName();
143+
$uri = $this->linkTo()->resource($route, $id, $params);
144+
145+
return $this->jsonApi('PATCH', $uri, ['data' => $data], $headers);
146+
}
147+
148+
/**
149+
* @param $resourceId
150+
* @param array $params
151+
* @param array $headers
152+
* @return $this
153+
*/
154+
protected function doDelete($resourceId, array $params = [], array $headers = [])
155+
{
156+
$params = $this->addDefaultRouteParams($params);
157+
$route = $this->resolveRouteName();
158+
$uri = $this->linkTo()->resource($route, $resourceId, $params);
159+
160+
return $this->jsonApi('DELETE', $uri, [], $headers);
161+
}
162+
163+
/**
164+
* Assert that a search response is a collection only containing the expected resource type.
165+
*
166+
* @param string $contentType
167+
* @return ResourcesTester
168+
*/
169+
protected function assertSearchResponse($contentType = MediaTypeInterface::JSON_API_MEDIA_TYPE)
170+
{
171+
$this->assertJsonApiResponse(Response::HTTP_OK, $contentType);
172+
173+
return $this
174+
->seeDocument()
175+
->assertResourceCollection()
176+
->assertTypes($this->getResourceType());
177+
}
178+
179+
/**
180+
* Assert that a search response contains a singleton resource with the expected id.
181+
*
182+
* @param string|int|UrlRoutable $expectedId
183+
* @param string $contentType
184+
* @return ResourceTester
185+
* @todo needs to support `null` responses
186+
*/
187+
protected function assertSearchOneResponse($expectedId, $contentType = MediaTypeInterface::JSON_API_MEDIA_TYPE)
188+
{
189+
if ($expectedId instanceof UrlRoutable) {
190+
$expectedId = $expectedId->getRouteKey();
191+
}
192+
193+
$this->assertJsonApiResponse(Response::HTTP_OK, $contentType);
194+
195+
return $this
196+
->seeDocument()
197+
->assertResource()
198+
->assertIs($this->getResourceType(), $expectedId);
199+
}
200+
201+
/**
202+
* Assert that the response to a search by id(s) request contains the expected ids.
203+
*
204+
* @param array|Collection $expectedIds
205+
* the ids - may contain UrlRoutable objects (e.g. Models)
206+
* @param string $contentType
207+
* @return ResourcesTester
208+
*/
209+
protected function assertSearchByIdResponse($expectedIds, $contentType = MediaTypeInterface::JSON_API_MEDIA_TYPE)
210+
{
211+
$this->assertJsonApiResponse(Response::HTTP_OK, $contentType);
212+
213+
return $this
214+
->seeDocument()
215+
->assertResourceCollection()
216+
->assertContainsOnly([
217+
$this->getResourceType() => $this->normalizeIds($expectedIds),
218+
]);
219+
}
220+
221+
/**
222+
* @return string
223+
*/
224+
protected function resolveRouteName()
225+
{
226+
return $this->getRoutePrefix() . $this->getResourceType();
227+
}
228+
229+
/**
230+
* Add default parameters to those provided to a `do*` method.
231+
*
232+
* Classes can override this method if they need to add any default parameters for constructing
233+
* the route link.
234+
*
235+
* @param array $params
236+
* @return array
237+
*/
238+
protected function addDefaultRouteParams(array $params)
239+
{
240+
return $params;
241+
}
242+
243+
/**
244+
* Normalize ids for a find many request
245+
*
246+
* @param array|Collection $ids
247+
* @return array
248+
*/
249+
protected function normalizeIds($ids)
250+
{
251+
$ids = new Collection($ids);
252+
253+
return $ids->map(function ($id) {
254+
return ($id instanceof UrlRoutable) ? $id->getRouteKey() : $id;
255+
})->all();
256+
}
257+
}

src/Testing/MakesJsonApiRequests.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use CloudCreativity\JsonApi\Testing\DocumentTester;
2222
use CloudCreativity\JsonApi\Testing\ErrorsTester;
2323
use CloudCreativity\LaravelJsonApi\Document\GeneratesLinks;
24+
use Illuminate\Contracts\Routing\UrlRoutable;
2425
use Illuminate\Http\Response;
2526
use Neomerx\JsonApi\Contracts\Document\DocumentInterface as Keys;
2627
use Neomerx\JsonApi\Contracts\Document\LinkInterface;
@@ -83,7 +84,7 @@ protected function assertJsonApiResponse(
8384
* Assert response is a JSON API resource index response.
8485
*
8586
* @param string|string[] $resourceType
86-
* @param string|int|null $resourceId
87+
* @param string|int|UrlRoutable|null $resourceId
8788
* if a singular resource is expected, the id of the singular resource.
8889
* @param string $contentType
8990
* @return $this
@@ -93,6 +94,10 @@ protected function assertIndexResponse(
9394
$resourceId = null,
9495
$contentType = MediaTypeInterface::JSON_API_MEDIA_TYPE
9596
) {
97+
if ($resourceId instanceof UrlRoutable) {
98+
$resourceId = $resourceId->getRouteKey();
99+
}
100+
96101
$this->assertJsonApiResponse(Response::HTTP_OK, $contentType);
97102

98103
if (!$resourceId) {
@@ -229,6 +234,10 @@ protected function assertHasOneRelationshipResponse(
229234
*/
230235
protected function seeStatusCode($expected)
231236
{
237+
if (!$this->response) {
238+
PHPUnit::fail('No response - have you made a call to the application?');
239+
}
240+
232241
$actual = $this->response->getStatusCode();
233242
$message = "Expected status code {$expected}, got {$actual}";
234243
$content = (array) json_decode((string) $this->response->getContent(), true);

0 commit comments

Comments
 (0)