Skip to content

Commit df9a3e4

Browse files
committed
Refactoring and improving test coverage.
Signed-off-by: Jason Lewis <[email protected]>
1 parent da298c1 commit df9a3e4

File tree

5 files changed

+254
-101
lines changed

5 files changed

+254
-101
lines changed

src/Dingo/Api/Dispatcher.php

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -169,18 +169,6 @@ public function patch($uri, $parameters = [])
169169
return $this->queueRequest('patch', $uri, $parameters);
170170
}
171171

172-
/**
173-
* Perform API HEAD request.
174-
*
175-
* @param string $uri
176-
* @param array $parameters
177-
* @return mixed
178-
*/
179-
public function head($uri, $parameters = [])
180-
{
181-
return $this->queueRequest('head', $uri, $parameters);
182-
}
183-
184172
/**
185173
* Perform API DELETE request.
186174
*
@@ -274,8 +262,6 @@ protected function dispatch(InternalRequest $request)
274262
{
275263
try
276264
{
277-
$this->router->enableApiRouting();
278-
279265
$response = $this->router->dispatch($request);
280266

281267
if ( ! $response->isOk())

src/Dingo/Api/Routing/ApiCollection.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public function matches($request)
5656
{
5757
return true;
5858
}
59-
elseif ($this->option('prefix') and starts_with($request->getPathInfo(), "/{$this->option('prefix')}"))
59+
elseif ($this->option('prefix') and starts_with($request->getPathInfo(), trim($this->option('prefix'), '/')))
6060
{
6161
return true;
6262
}

src/Dingo/Api/Routing/Router.php

Lines changed: 33 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,6 @@ class Router extends \Illuminate\Routing\Router {
2020
*/
2121
protected $apiCollections = [];
2222

23-
/**
24-
* Indicates if newly added routes are API routes.
25-
*
26-
* @var bool
27-
*/
28-
protected $apiRouting = false;
29-
3023
/**
3124
* The default API version.
3225
*
@@ -48,6 +41,13 @@ class Router extends \Illuminate\Routing\Router {
4841
*/
4942
protected $exceptionHandler;
5043

44+
/**
45+
* Array of cached API requests.
46+
*
47+
* @var array
48+
*/
49+
protected $cachedApiRequests = [];
50+
5151
/**
5252
* Register an API group.
5353
*
@@ -62,21 +62,19 @@ public function api($options, Closure $callback)
6262
throw new BadMethodCallException('Unable to register API without an API version.');
6363
}
6464

65-
$this->enableApiRouting();
66-
6765
// Once we have the version from the options we'll check to see if an API
6866
// collection already exists for this verison. If it doesn't then we'll
6967
// create a new collection.
7068
$version = $options['version'];
7169

70+
$options['api'] = true;
71+
7272
if ( ! isset($this->apiCollections[$version]))
7373
{
7474
$this->apiCollections[$version] = $this->newApiCollection($version, array_except($options, ['version']));
7575
}
7676

7777
$this->group($options, $callback);
78-
79-
$this->disableApiRouting();
8078
}
8179

8280
/**
@@ -107,36 +105,23 @@ public function dispatch(Request $request)
107105
// If an exception is caught and we are currently routing an API request then
108106
// we'll handle this exception by building a new response from it. This
109107
// allows the API to gracefully handle its own exceptions.
110-
if (($this->routingForApi() or $this->requestTargettingApi($request)) and ! $request instanceof InternalRequest)
108+
if ($this->requestTargettingApi($request) and ! $request instanceof InternalRequest)
111109
{
112110
$response = $this->handleException($exception);
113111
}
114112

115113
// If the request was an internal request then we will rethrow the exception
116114
// so that developers can easily catch them and adjust ther esponse
117-
// themselves. We also disable API routing so that the parent
118-
// request isn't treated as an API request.
115+
// themselves.
119116
else
120117
{
121-
$this->disableApiRouting();
122-
123118
throw $exception;
124119
}
125120
}
126121

127-
if ($this->routingForApi() or $this->requestTargettingApi($request))
122+
if ($this->requestTargettingApi($request))
128123
{
129124
$response = Response::makeFromExisting($response)->morph();
130-
131-
// If the request that was dispatched is an internal request then we need to
132-
// disable the API routing so that the parent request is not treated in
133-
// the same way. This prevents it from generating an Api Response for
134-
// the parent request. Another internal request will still result
135-
// in API routing being enabled.
136-
if ($request instanceof InternalRequest)
137-
{
138-
$this->disableApiRouting();
139-
}
140125
}
141126

142127
return $response;
@@ -186,7 +171,7 @@ protected function addRoute($methods, $uri, $action)
186171
{
187172
$route = $this->createRoute($methods, $uri, $action);
188173

189-
if ($this->routingForApi())
174+
if ($this->routingForApi($route))
190175
{
191176
$route->before('api');
192177

@@ -226,7 +211,7 @@ protected function createRoute($methods, $uri, $action)
226211
// see if the controller is one of the API controllers. If it is then we
227212
// need to check if the method is protected and get any scopes
228213
// associated with the method.
229-
if ($this->routingForApi() and $this->routingToController($action))
214+
if ($this->routingForApi($route) and $this->routingToController($action))
230215
{
231216
list ($class, $method) = explode('@', $route->getActionName());
232217

@@ -317,7 +302,7 @@ protected function controllerMethodProtected($route, $controller, $method)
317302
*/
318303
protected function findRoute($request)
319304
{
320-
if ($this->routingForApi() or $this->requestTargettingApi($request))
305+
if ($this->requestTargettingApi($request))
321306
{
322307
$version = $this->getRequestVersion($request);
323308

@@ -345,12 +330,22 @@ protected function findRoute($request)
345330
*/
346331
public function requestTargettingApi($request)
347332
{
348-
if (empty($this->apiCollections)) return false;
333+
if (empty($this->apiCollections))
334+
{
335+
return false;
336+
}
349337

350-
return array_first($this->apiCollections, function($key, $value) use ($request)
338+
if (in_array($request, $this->cachedApiRequests))
351339
{
352-
return $value->matches($request);
340+
return true;
341+
}
342+
343+
$collection = array_first($this->apiCollections, function($key, $collection) use ($request)
344+
{
345+
return $collection->matches($request);
353346
}, false);
347+
348+
return $collection instanceof ApiCollection;
354349
}
355350

356351
/**
@@ -388,37 +383,16 @@ public function getApiCollection($version)
388383
}
389384

390385
/**
391-
* Determine if the current request is an API request.
386+
* Determine if a route is an API route.
392387
*
388+
* @param \Illuminate\Routing\Route
393389
* @return bool
394390
*/
395-
public function routingForApi()
396-
{
397-
return $this->apiRouting;
398-
}
399-
400-
/**
401-
* Enable API request.
402-
*
403-
* @return \Dingo\Api\Routing\Router
404-
*/
405-
public function enableApiRouting()
391+
public function routingForApi($route)
406392
{
407-
$this->apiRouting = true;
408-
409-
return $this;
410-
}
411-
412-
/**
413-
* Disable API request.
414-
*
415-
* @return \Dingo\Api\Routing\Router
416-
*/
417-
public function disableApiRouting()
418-
{
419-
$this->apiRouting = false;
393+
$action = $route->getAction();
420394

421-
return $this;
395+
return isset($action['api']) and $action['api'] === true;
422396
}
423397

424398
/**

tests/DispatcherTest.php

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ class DispatcherTest extends PHPUnit_Framework_TestCase {
88
public function setUp()
99
{
1010
$this->request = new Illuminate\Http\Request;
11-
$this->router = m::mock('Dingo\Api\Routing\Router');
11+
$this->router = new Dingo\Api\Routing\Router(new Illuminate\Events\Dispatcher);
12+
$this->router->setDefaultApiVersion('v1');
13+
$this->router->setApiVendor('test');
1214
$this->auth = m::mock('Dingo\Api\Authentication');
1315
$this->dispatcher = new Dingo\Api\Dispatcher($this->request, $this->router, $this->auth);
1416
}
@@ -22,46 +24,57 @@ public function tearDown()
2224

2325
public function testInternalRequests()
2426
{
25-
$this->router->shouldReceive('dispatch')->andReturn(new Dingo\Api\Http\Response('test', 200));
27+
$this->router->api(['version' => 'v1'], function()
28+
{
29+
$this->router->get('test', function(){ return 'test'; });
30+
$this->router->post('test', function(){ return 'test'; });
31+
$this->router->put('test', function(){ return 'test'; });
32+
$this->router->patch('test', function(){ return 'test'; });
33+
$this->router->delete('test', function(){ return 'test'; });
34+
});
2635

27-
$this->router->shouldReceive('getDefaultApiVersion')->times(6)->andReturn('v1');
28-
$this->router->shouldReceive('getApiVendor')->times(6)->andReturn('testing');
29-
$this->router->shouldReceive('getApiCollection')->times(6)->with('v1')->andReturn(new Dingo\Api\Routing\ApiCollection('v1', ['prefix' => 'api']));
30-
$this->router->shouldReceive('enableApiRouting')->times(6);
31-
32-
$this->auth->shouldReceive('setUser')->times(6)->with(null);
36+
$this->auth->shouldReceive('setUser')->times(5)->with(null);
3337

3438
$this->assertEquals('test', $this->dispatcher->get('test'));
3539
$this->assertEquals('test', $this->dispatcher->post('test'));
3640
$this->assertEquals('test', $this->dispatcher->put('test'));
3741
$this->assertEquals('test', $this->dispatcher->patch('test'));
38-
$this->assertEquals('test', $this->dispatcher->head('test'));
3942
$this->assertEquals('test', $this->dispatcher->delete('test'));
4043
}
4144

4245

4346
public function testInternalRequestWithVersionAndParameters()
4447
{
45-
$this->router->shouldReceive('dispatch')->andReturn(new Dingo\Api\Http\Response('test', 200));
46-
47-
$this->router->shouldReceive('getApiVendor')->once()->andReturn('testing');
48-
$this->router->shouldReceive('getApiCollection')->once()->with('v1')->andReturn(new Dingo\Api\Routing\ApiCollection('v1', ['prefix' => 'api']));
49-
$this->router->shouldReceive('enableApiRouting')->once();
48+
$this->router->api(['version' => 'v1'], function()
49+
{
50+
$this->router->get('test', function(){ return 'test'; });
51+
});
5052

5153
$this->auth->shouldReceive('setUser')->once()->with(null);
5254

5355
$this->assertEquals('test', $this->dispatcher->version('v1')->with(['foo' => 'bar'])->get('test'));
5456
}
5557

5658

57-
public function testInternalRequestWithPrefixAndDomain()
59+
public function testInternalRequestWithPrefix()
5860
{
59-
$this->router->shouldReceive('dispatch')->andReturn(new Dingo\Api\Http\Response('test', 200));
61+
$this->router->api(['version' => 'v1', 'prefix' => 'baz'], function()
62+
{
63+
$this->router->get('test', function(){ return 'test'; });
64+
});
65+
66+
$this->auth->shouldReceive('setUser')->once()->with(null);
67+
68+
$this->assertEquals('test', $this->dispatcher->get('test'));
69+
}
70+
6071

61-
$this->router->shouldReceive('getDefaultApiVersion')->once()->andReturn('v1');
62-
$this->router->shouldReceive('getApiVendor')->once()->andReturn('testing');
63-
$this->router->shouldReceive('getApiCollection')->once()->with('v1')->andReturn(new Dingo\Api\Routing\ApiCollection('v1', ['domain' => 'testing']));
64-
$this->router->shouldReceive('enableApiRouting')->once();
72+
public function testInternalRequestWithDomain()
73+
{
74+
$this->router->api(['version' => 'v1', 'domain' => 'foo.bar'], function()
75+
{
76+
$this->router->get('test', function(){ return 'test'; });
77+
});
6578

6679
$this->auth->shouldReceive('setUser')->once()->with(null);
6780

@@ -74,17 +87,50 @@ public function testInternalRequestWithPrefixAndDomain()
7487
*/
7588
public function testInternalRequestThrowsException()
7689
{
77-
$this->router->shouldReceive('dispatch')->andReturn(new Dingo\Api\Http\Response('test', 500));
90+
$this->router->api(['version' => 'v1'], function() {});
91+
92+
$this->auth->shouldReceive('setUser')->once()->with(null);
93+
94+
$this->dispatcher->get('test');
95+
}
7896

79-
$this->router->shouldReceive('getDefaultApiVersion')->once()->andReturn('v1');
80-
$this->router->shouldReceive('getApiVendor')->once()->andReturn('testing');
81-
$this->router->shouldReceive('getApiCollection')->once()->with('v1')->andReturn(new Dingo\Api\Routing\ApiCollection('v1', ['domain' => 'testing']));
82-
$this->router->shouldReceive('enableApiRouting')->once();
97+
98+
/**
99+
* @expectedException Symfony\Component\HttpKernel\Exception\HttpException
100+
*/
101+
public function testInternalRequestThrowsExceptionWhenResponseIsNotOkay()
102+
{
103+
$this->router->api(['version' => 'v1'], function()
104+
{
105+
$this->router->get('test', function()
106+
{
107+
return new Illuminate\Http\Response('test', 401);
108+
});
109+
});
83110

84111
$this->auth->shouldReceive('setUser')->once()->with(null);
85112

86113
$this->dispatcher->get('test');
87114
}
115+
116+
117+
/**
118+
* @expectedException \RuntimeException
119+
*/
120+
public function testPretendingToBeUserWithInvalidParameterThrowsException()
121+
{
122+
$this->dispatcher->be('foo');
123+
}
124+
125+
126+
public function testPretendingToBeUserSetsUserOnAuthentication()
127+
{
128+
$user = m::mock('Illuminate\Database\Eloquent\Model');
129+
130+
$this->auth->shouldReceive('setUser')->once()->with($user);
131+
132+
$this->dispatcher->be($user);
133+
}
88134

89135

90136
}

0 commit comments

Comments
 (0)