8000 Added flysystem renderer · CakeDC/cakephp-api@f947d43 · GitHub
[go: up one dir, main page]

Skip to content

Commit f947d43

Browse files
rochamarcelosteinkel
authored andcommitted
Added flysystem renderer
1 parent 152e3db commit f947d43

File tree

4 files changed

+373
-1
lines changed

4 files changed

+373
-1
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"cakedc/users": "^7.0"
3333
},
3434
"require-dev": {
35-
"phpunit/phpunit": "<6.0"
35+
"phpunit/phpunit": "<6.0",
36+
"league/flysystem-vfs": "^1.0"
3637
},
3738
"autoload": {
3839
"psr-4": {

config/api.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@
7676
'scopes' => ['api'],
7777
'levels' => ['error', 'info'],
7878
'file' => 'api.log',
79+
],
80+
'Flysystem' => [
81+
'expire' => '+1 day'
7982
]
8083
]
8184
];
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
/**
3+
* Copyright 2016 - 2018, Cake Development Corporation (http://cakedc.com)
4+
*
5+
* Licensed under The MIT License
6+
* Redistributions of files must retain the above copyright notice.
7+
*
8+
* @copyright Copyright 2016 - 2018, Cake Development Corporation (http://cakedc.com)
9+
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
10+
*/
11+
12+
namespace CakeDC\Api\Service\Renderer;
13+
14+
use Cake\Core\Configure;
15+
use CakeDC\Api\Service\Action\Result;
16+
use Cake\Http\Response;
17+
use Cake\Log\LogTrait;
18+
use Exception;
19+
use League\Flysystem\File;
20+
use League\Flysystem\FileNotFoundException;
21+
use League\Flysystem\Filesystem;
22+
use Zend\Diactoros\Stream;
23+
24+
/**
25+
* Class FlysystemRenderer to render file using Flysystem library
26+
*/
27+
class FlysystemRenderer extends FileRenderer
28+
{
29+
use LogTrait;
30+
/**
31+
* Builds the HTTP response.
32+
*
33+
* @param Result $result The result object returned by the Service.
34+
* @return bool
35+
*/
36+
public function response(Result $result = null)
37+
{
38+
$data = $result->getData();
39+
try {
40+
$file = $this->getFile($data['filesystem'], $data['path']);
41+
42+
$this->_service->setResponse(
43+
$this->deliverAsset($this->_service->getResponse(), $file)
44+
);
45+
} catch (FileNotFoundException $e) {
46+
$response = $this->_service->getResponse()
47+
->withStatus(404);
48+
49+
$this->_service->setResponse($response);
50+
}
51+
52+
return true;
53+
}
54+
55+
/**
56+
* Get flystem file object
57+
*
58+
* @param Filesystem $filesystem custom filesystem
59+
* @param string $path of file at filesystem
60+
* @return File
61+
*/
62+
protected function getFile(Filesystem $filesystem, string $path): File
63+
{
64+
return $filesystem->get($path);
65+
}
66+
67+
/**
68+
* Deliver the asset stream in body
69+
*
70+
* @param Response $response service response
71+
* @param File $file file object
72+
* @return Response
73+
*/
74+
public function deliverAsset(Response $response, File $file) : Response
75+
{
76+
$contentType = $file->getType();
77+
$modified = $file->getTimestamp();
78+
$expire = strtotime(Configure::write('Api.Flysystem.expire'));
79+
$maxAge = $expire - time();
80+
$stream = new Stream($file->readStream());
81+
82+
return $response->withBody($stream)
83+
// Content
84+
->withHeader('Content-Type', $contentType)
85+
// Cache
86+
->withHeader('Cache-Control', 'public,max-age=' . $maxAge)
87+
->withHeader('Date', gmdate('D, j M Y G:i:s \G\M\T', time()))
88+
->withHeader('Last-Modified', gmdate('D, j M Y G:i:s \G\M\T', $modified))
89+
->withHeader('Expires', gmdate('D, j M Y G:i:s \G\M\T', $expire));
90+
}
91+
92+
/**
93+
* Handle error setting a response status
94+
*
95+
* @param Exception $exception thrown at service or action
96+
*
97+
* @return void
98+
*/
99+
public function error(Exception $exception)
100+
{
101+
$code = $exception->getCode();
102+
$response = $this->_service->getResponse()
103+
->withStatus($code ? $code : 500);
104+
105+
$this->log($exception);
106+
107+
$this->_service->setResponse($response);
108+
}
109+
}
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
<?php
2+
/**
3+
* Copyright 2016 - 2018, Cake Development Corporation (http://cakedc.com)
4+
*
5+
* Licensed under The MIT License
6+
* Redistributions of files must retain the above copyright notice.
7+
*
8+
* @copyright Copyright 2016 - 2018, Cake Development Corporation (http://cakedc.com)
9+
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
10+
*/
11+
12+
namespace CakeDC\Api\Test\TestCase\Service\Renderer;
13+
14+
use CakeDC\Api\Exception\UnauthenticatedException;
15+
use CakeDC\Api\Service\Action\Result;
16+
use CakeDC\Api\Service\FallbackService;
17+
use CakeDC\Api\Service\Renderer\FlysystemRenderer;
18+
use CakeDC\Api\Service\Service;
19+
use CakeDC\Api\TestSuite\TestCase;
20+
use CakeDC\Api\Test\ConfigTrait;
21+
use CakeDC\Api\Test\FixturesTrait;
22+
use Cake\Core\Configure;
23+
use League\Flysystem\Filesystem;
24+
use League\Flysystem\Vfs\VfsAdapter;
25+
use VirtualFileSystem\FileSystem as Vfs;
26+
27+
class FlysystemRendererTest extends TestCase
28+
{
29+
30+
use ConfigTrait;
31+
use FixturesTrait;
32+
33+
/**
34+
* @var Service
35+
*/
36+
public $Service;
37+
38+
/**
39+
* setUp method
40+
*
41+
* @return void
42+
*/
43+
public function setUp()
44+
{
45+
parent::setUp();
46+
47+
$this->_initializeRequest();
48+
}
49+
50+
/**
51+
* tearDown method
52+
*
53+
* @return void
54+
*/
55+
public function tearDown()
56+
{
57+
unset($this->Action);
58+
parent::tearDown();
59+
}
60+
61+
/**
62+
* Test initialize
63+
*
64+
* @return void
65+
*/
66+
public function testRendererInitializeByClassName()
67+
{
68+
$response = $this
69+
->getMockBuilder('Cake\Http\Response')
70+
->setMethods(['withStatus', 'withType', 'withStringBody'])
71+
->getMock();
72+
73+
$this->_initializeRequest([], 'GET', ['response' => $response]);
74+
$serviceOptions = [
75+
'version' => null,
76+
'request' => $this->request,
77+
'response' => $response,
78+
'rendererClass' => 'CakeDC/Api.Flysystem'
79+
];
80+
$this->Service = new FallbackService($serviceOptions);
81+
$renderer = $this->Service->getRenderer();
82+
$this->assertTrue($renderer instanceof FlysystemRenderer);
83+
}
84+
85+
/**
86+
* Test render response
87+
*
88+
* @return void
89+
*/
90+
public function testRendererSuccess()
91+
{
92+
Configure::write('debug', 0);
93+
$response = $this
94+
->getMockBuilder('Cake\Http\Response')
95+
->setMethods(['withStatus', 'withType', 'withStringBody'])
96+
->getMock();
97+
98+
$this->_initializeRequest([], 'GET', ['response' => $response]);
99+
$serviceOptions = [
100+
'version' => null,
101+
'request' => $this->request,
102+
'response' => $response,
103+
'rendererClass' => 'CakeDC/Api.Flysystem'
104+
];
105+
$this->Service = new FallbackService($serviceOptions);
106+
107+
$result = new Result();
108+
$statusCode = 200;
109+
$result->setCode($statusCode);
110+
111+
$vfs = new Vfs();
112+
$path = "/my-file.zip";
113+
$vfs->createFile($path, 'content-for-download');
114+
115+
$filesystem = new Filesystem(new VfsAdapter($vfs));
116+
$data = compact('filesystem', 'path');
117+
$result->setData($data);
118+
$renderer = $this->Service->getRenderer();
119+
120+
$renderer->response($result);
121+
122+
$headers = $this->Service->getResponse()->getHeaders();
123+
$this->assertEquals(
124+
['file'],
125+
$headers['Content-Type']
126+
);
127+
$this->assertArrayHasKey('Cache-Control', $headers);
128+
$this->assertArrayHasKey('Date', $headers);
129+
$this->assertArrayHasKey('Last-Modified', $headers);
130+
$this->assertArrayHasKey('Expires', $headers);
131+
}
132+
133+
/**
134+
* Test render response
135+
*
136+
* @return void
137+
*/
138+
public function testRendereFileNotFound()
139+
{
140+
Configure::write('debug', 0);
141+
$response = $this
142+
->getMockBuilder('Cake\Http\Response')
143+
->setMethods(['withStatus', 'withType', 'withStringBody'])
144+
->getMock();
145+
146+
$this->_initializeRequest([], 'GET', ['respo F438 nse' => $response]);
147+
$serviceOptions = [
148+
'version' => null,
149+
'request' => $this->request,
150+
'response' => $response,
151+
'rendererClass' => 'CakeDC/Api.Flysystem'
152+
];
153+
$this->Service = new FallbackService($serviceOptions);
154+
155+
$result = new Result();
156+
$statusCode = 200;
157+
$result->setCode($statusCode);
158+
159+
$vfs = new Vfs();
160+
$path = "/my-file-not-found.zip";
161+
162+
$filesystem = new Filesystem(new VfsAdapter($vfs));
163+
$data = compact('filesystem', 'path');
164+
$result->setData($data);
165+
$renderer = $this->Service->getRenderer();
166+
$response->expects($this->once())
167+
->method('withStatus')
168+
->with(404)
169+
->will($this->returnValue($response));
170+
$response->expects($this->never())
171+
->method('withType');
172+
$response->expects($this->never())
173+
->method('withStringBody');
174+
175+
$renderer->response($result);
176+
177+
$headers = $this->Service->getResponse()->getHeaders();
178+
$this->assertEquals(
179+
['text/html; charset=UTF-8'],
180+
$headers['Content-Type']
181+
);
182+
$this->assertEmpty((string)$this->Service->getResponse()->getBody());
183+
}
184+
185+
/**
186+
* Test render error
187+
*
188+
* @return void
189+
*/
190+
public function testRendererError()
191+
{
192+
$response = $this
193+
->getMockBuilder('Cake\Http\Response')
194+
->setMethods(['withStatus', 'withType', 'withStringBody'])
195+
->getMock();
196+
197+
$this->_initializeRequest([], 'GET', ['response' => $response]);
198+
$serviceOptions = [
199+
'version' => null,
200+
'request' => $this->request,
201+
'response' => $response,
202+
'rendererClass' => 'CakeDC/Api.Flysystem'
203+
];
204+
$this->Service = new FallbackService($serviceOptions);
205+
206+
Configure::write('debug', 0);
207+
$error = new UnauthenticatedException();
208+
$renderer = $this->Service->getRenderer();
209+
210+
$response->expects($this->once())
211+
->method('withStatus')
212+
->with(401)
213+
->will($this->returnValue($response));
214+
$response->expects($this->never())
215+
->method('withType');
216+
$response->expects($this->never())
217+
->method('withStringBody');
218+
219+
220+
$renderer->error($error);
221+
}
222+
223+
/**
224+
* Test render error
225+
*
226+
* @return void
227+
*/
228+
public function testRendererErrorEmptyExceptionCode()
229+
{
230+
$response = $this
231+
->getMockBuilder('Cake\Http\Response')
232+
->setMethods(['withStatus', 'withType', 'withStringBody'])
233+
->getMock();
234+
235+
$this->_initializeRequest([], 'GET', ['response' => $response]);
236+
$serviceOptions = [
237+
'version' => null,
238+
'request' => $this->request,
239+
'response' => $response,
240+
'rendererClass' => 'CakeDC/Api.Flysystem'
241+
];
242+
$this->Service = new FallbackService($serviceOptions);
243+
244+
Configure::write('debug', 0);
245+
$error = new \Exception();
246+
$renderer = $this->Service->getRenderer();
247+
248+
$response->expects($this->once())
249+
->method('withStatus')
250+
->with(500)
251+
->will($this->returnValue($response));
252+
$response->expects($this->never())
253+
->method('withType');
254+
$response->expects($this->never())
255+
->method('withStringBody');
256+
257+
$renderer->error($error);
258+
}
259+
}

0 commit comments

Comments
 (0)
0