Skip to content

Commit c6ee8d4

Browse files
author
Lee Hicks
authored
DF-1301 #close Response caching using URL path and parameters as key
2 parents aaf2a53 + e2c53c2 commit c6ee8d4

3 files changed

Lines changed: 139 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

55
## [Unreleased]
6+
### Added
7+
- DF-1301 Response caching using URL path and parameters as key
8+
69
## [0.8.1] - 2018-01-25
710
### Added
811
- DF-1293 Added implements_access_list config option for overriding swagger def for role service access

src/Models/ScriptConfig.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use DreamFactory\Core\Exceptions\ServiceUnavailableException;
66
use DreamFactory\Core\Models\BaseServiceConfigModel;
7+
use DreamFactory\Core\Models\ServiceCacheConfig;
78
use Illuminate\Database\Query\Builder;
89

910
/**
@@ -52,6 +53,19 @@ public static function getType()
5253
return '';
5354
}
5455

56+
/**
57+
* {@inheritdoc}
58+
*/
59+
public static function getConfig($id, $local_config = null, $protect = true)
60+
{
61+
$config = parent::getConfig($id, $local_config, $protect);
62+
63+
$cacheConfig = ServiceCacheConfig::whereServiceId($id)->first();
64+
$config = array_merge($config, ($cacheConfig ? $cacheConfig->toArray() : []));
65+
66+
return $config;
67+
}
68+
5569
/**
5670
* {@inheritdoc}
5771
*/
@@ -71,9 +85,32 @@ public static function setConfig($id, $config, $local_config = null)
7185
}
7286
}
7387

88+
ServiceCacheConfig::setConfig($id, $config, $local_config);
89+
7490
return parent::setConfig($id, $config, $local_config);
7591
}
7692

93+
/**
94+
* {@inheritdoc}
95+
*/
96+
public static function storeConfig($id, $config)
97+
{
98+
ServiceCacheConfig::storeConfig($id, $config);
99+
100+
parent::storeConfig($id, $config);
101+
}
102+
103+
/**
104+
* {@inheritdoc}
105+
*/
106+
public static function getConfigSchema()
107+
{
108+
$schema = parent::getConfigSchema();
109+
$schema = array_merge($schema, ServiceCacheConfig::getConfigSchema());
110+
111+
return $schema;
112+
}
113+
77114
/**
78115
* @param array $schema
79116
*/

src/Services/Script.php

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace DreamFactory\Core\Script\Services;
44

5+
use Config;
56
use DreamFactory\Core\Contracts\HttpStatusCodeInterface;
67
use DreamFactory\Core\Contracts\ServiceResponseInterface;
78
use DreamFactory\Core\Enums\ApiOptions;
@@ -12,6 +13,7 @@
1213
use DreamFactory\Core\Services\BaseRestService;
1314
use DreamFactory\Core\Utility\ResourcesWrapper;
1415
use DreamFactory\Core\Utility\ResponseFactory;
16+
use DreamFactory\Core\Utility\Session;
1517
use Illuminate\Foundation\Bus\DispatchesJobs;
1618
use Log;
1719

@@ -39,6 +41,10 @@ class Script extends BaseRestService
3941
* @var array $scriptConfig Configuration for the engine for this particular script
4042
*/
4143
protected $scriptConfig;
44+
/**
45+
* @type bool
46+
*/
47+
protected $cacheEnabled = false;
4248
/**
4349
* @var boolean $queued Configuration for the engine for this particular script
4450
*/
@@ -77,6 +83,9 @@ public function __construct($settings = [])
7783
$this->scriptConfig = [];
7884
}
7985

86+
$this->cacheEnabled = array_get_bool($this->config, 'cache_enabled');
87+
$this->cacheTTL = intval(array_get($this->config, 'cache_ttl', Config::get('df.default_cache_ttl')));
88+
8089
$this->implementsAccessList = array_get_bool($this->config, 'implements_access_list');
8190
}
8291

@@ -177,14 +186,79 @@ public function getAccessList()
177186
return array_values(array_unique($list));
178187
}
179188

189+
protected function buildRequestCacheKey()
190+
{
191+
// build cache_key
192+
$cacheKey = $this->action;
193+
$resource = array_map('rawurlencode', $this->resourceArray);
194+
if ($resource) {
195+
$cacheKey .= ':' . implode('.', $resource);
196+
}
197+
198+
$cacheQuery = '';
199+
// Using raw query string here to allow for multiple parameters with the same key name.
200+
// The laravel Request object or PHP global array $_GET doesn't allow that.
201+
$requestQuery = explode('&', array_get($_SERVER, 'QUERY_STRING'));
202+
203+
// If request is coming from a scripted service then $_SERVER['QUERY_STRING'] will be blank.
204+
// Therefore need to check the Request object for parameters.
205+
foreach ($this->request->getParameters() as $pk => $pv) {
206+
if (is_array($pv)) {
207+
foreach ($pv as $ipk => $ipv) {
208+
$param = $pk . '[' . $ipk . ']=' . $ipv;
209+
if (!in_array($param, $requestQuery)) {
210+
$requestQuery[] = $param;
211+
}
212+
}
213+
} else {
214+
$param = $pk . '=' . $pv;
215+
if (!in_array($param, $requestQuery)) {
216+
$requestQuery[] = $param;
217+
}
218+
}
219+
}
220+
221+
foreach ($requestQuery as $q) {
222+
$pairs = explode('=', $q);
223+
$name = trim(array_get($pairs, 0));
224+
$value = trim(array_get($pairs, 1));
225+
static::parseParameter($cacheQuery, $name, $value);
226+
}
227+
228+
if (!empty($cacheQuery)) {
229+
$cacheKey .= ':' . $cacheQuery;
230+
}
231+
232+
return sha1($cacheKey); // the key may contain confidential info
233+
}
234+
235+
protected static function parseParameter(&$key, $name, $value)
236+
{
237+
if ('_' !== $name) { // this value included with jQuery calls should not be considered
238+
if (is_array($value)) {
239+
foreach ($value as $sub => $subValue) {
240+
static::parseParameter($key, $name . '[' . $sub . ']', $subValue);
241+
}
242+
} else {
243+
Session::replaceLookups($value, true);
244+
$part = $name;
245+
if (!empty($value)) {
246+
$part .= '=' . $value;
247+
}
248+
if (!empty($key)) {
249+
$key .= '&';
250+
}
251+
$key .= $part;
252+
}
253+
}
254+
}
255+
180256
/**
181257
* @return bool|mixed
182-
* @throws
183-
* @throws \DreamFactory\Core\Script\Exceptions\ScriptException
184-
* @throws \DreamFactory\Core\Exceptions\BadRequestException
185258
* @throws \DreamFactory\Core\Exceptions\InternalServerErrorException
186259
* @throws \DreamFactory\Core\Exceptions\RestException
187260
* @throws \DreamFactory\Core\Exceptions\ServiceUnavailableException
261+
* @throws \DreamFactory\Core\Script\Exceptions\ScriptException
188262
*/
189263
protected function processRequest()
190264
{
@@ -208,6 +282,19 @@ protected function processRequest()
208282

209283
$data = $this->getRequestData();
210284

285+
$cacheKey = null;
286+
if ($this->cacheEnabled) {
287+
switch ($this->action) {
288+
case Verbs::GET:
289+
// build cache_key
290+
$cacheKey = $this->buildRequestCacheKey();
291+
if (null !== $result = $this->getFromCache($cacheKey)) {
292+
return $result;
293+
}
294+
break;
295+
}
296+
}
297+
211298
$logOutput = $this->request->getParameterAsBool('log_output', true);
212299
$result = $this->handleScript('service.' . $this->name, $this->getScriptContent(), $this->engineType,
213300
$this->scriptConfig, $data, $logOutput);
@@ -222,11 +309,17 @@ protected function processRequest()
222309
$status = array_get($result, 'status_code', HttpStatusCodeInterface::HTTP_OK);
223310
$headers = (array)array_get($result, 'headers');
224311

225-
return ResponseFactory::create($content, $contentType, $status, $headers);
312+
$result = ResponseFactory::create($content, $contentType, $status, $headers);
313+
} else {
314+
// otherwise assume raw content
315+
$result = ResponseFactory::create($result);
316+
}
317+
318+
if ($cacheKey) {
319+
$this->addToCache($cacheKey, $result);
226320
}
227321

228-
// otherwise assume raw content
229-
return ResponseFactory::create($result);
322+
return $result;
230323
}
231324

232325
protected function getEventName()

0 commit comments

Comments
 (0)