Commit 970a679e authored by Mateu Aguiló Bosch's avatar Mateu Aguiló Bosch

Support more complex trees

parent 8a63860c
......@@ -3,8 +3,8 @@
namespace Drupal\subrequests\Blueprint;
use Drupal\Component\Serialization\Json;
use Rs\Json\Pointer;
use Rs\Json\Pointer\NonexistentValueReferencedException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
......@@ -147,43 +147,28 @@ class RequestTree {
*/
public function dereference(array $responses) {
$this->requests = array_map(function (Request $request) use ($responses) {
$subrequest_id = $request->attributes->get(static::SUBREQUEST_ID);
// Detect {{/foo#/bar}}
$pattern = '/\{\{\/([^\{\}]+)@(\/[^\{\}]+)\}\}/';
$id = $request->attributes->get(static::SUBREQUEST_ID);
$parent_id = $request->attributes->get(static::SUBREQUEST_PARENT_ID);
// Allow replacement tokens in:
// 1. The body.
// 2. The path.
// 3. The query string values.
$matches = [];
if (preg_match($pattern, $request->getContent(), $matches)) {
// Do the magic.
$matches;
$content = $request->getContent();
$changes = static::replaceAllOccurrences($responses, $content);
$uri = $request->getUri();
$changes += static::replaceAllOccurrences($responses, $uri);
foreach ($request->query as $key => $value) {
$changes += static::replaceAllOccurrences($responses, $value);
$request->query->set($key, $value);
}
$matches = [];
if (preg_match($pattern, $request->getRequestUri(), $matches)) {
$replacement = static::findReplacement($responses, $matches[1], $matches[2]);
$new_uri = preg_replace($pattern, $replacement, $request->getRequestUri());
$request->server->set('REQUEST_URI', $new_uri);
// If there is anything to update.
if ($changes) {
// We need to duplicate the request to force recomputing the internal
// caches.
$request = Request::create(
$new_uri,
$request->getMethod(),
(array) $request->query->getIterator(),
(array) $request->cookies->getIterator(),
(array) $request->files->getIterator(),
(array) $request->server->getIterator(),
$request->getContent()
);
$request->headers->set('Content-ID', sprintf('<%s>', $subrequest_id));
}
foreach ($request->query as $key => $value) {
$matches = [];
if (preg_match($pattern, $request->getUri(), $matches)) {
// Do the magic.
$matches;
}
$request = static::cloneRequest($request, $uri, $content, $id, $parent_id);
}
return $request;
}, $this->getRequests());
}
......@@ -213,15 +198,108 @@ class RequestTree {
return TRUE;
}
protected static function findReplacement($responses, $id, $json_pointer_path) {
/** @var \Symfony\Component\HttpFoundation\Response $response */
$response = array_filter($responses, function (Response $response) use ($id) {
/**
* Do in-place replacements for an input string containing replacement tokens.
*
* @param array $responses
* The pool of responses where to find the replacement data.
* @param string $input
* The string containing the token to replace. It's passed by reference and
* modified if necessary.
*
* @return int
* The number of replacements made.
*/
public static function replaceAllOccurrences(array $responses, &$input) {
// Detect {{/foo#/bar}}
$pattern = '/\{\{\/([^\{\}]+)@(\/[^\{\}]+)\}\}/';
$matches = [];
if (!preg_match_all($pattern, $input, $matches)) {
return 0;
}
for ($index = 0; $index < count($matches[1]); $index++) {
$replacement = static::findReplacement(
$responses,
$matches[1][$index],
$matches[2][$index]
);
$pattern = sprintf('/%s/', preg_quote($matches[0][$index], '/'));
$input = preg_replace($pattern, $replacement, $input);
}
return $index;
}
/**
* Find a replacement in the responses for the JSON pointer.
*
* @param \Symfony\Component\HttpFoundation\Response[] $responses
* The array of responses to look data into.
* @param string $id
* The response ID to extract data from.
* @param $json_pointer_path
* The JSON pointer path of the data to extract.
*
* @throws \Rs\Json\Pointer\NonexistentValueReferencedException
* When the referenced response was not found.
*
* @return mixed
* The contents of the pointed JSON property.
*/
protected static function findReplacement(array $responses, $id, $json_pointer_path) {
$found = array_filter($responses, function (Response $response) use ($id) {
return $response->headers->get('Content-ID') === sprintf('<%s>', $id);
})[0];
});
$response = reset($found);
if (!$response instanceof Response) {
throw new NonexistentValueReferencedException('Response is still not ready.');
}
// Find the data in the response output.
$pointer = new Pointer($response->getContent());
return $pointer->get($json_pointer_path);
}
/**
* Clones a request and modifies certain parameters.
*
* We need to do this to reset some of the internal request caches. There may
* be a better way of doing this, but I could not find it in the time that I
* expected.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The original request.
* @param $uri
* The (potentially) new URI.
* @param $content
* The (potentially) new body content.
* @param $id
* The subrequest id.
* @param $parent_id
* The subrequest id of the parent.
*
* @return \Symfony\Component\HttpFoundation\Request
* The cloned request.
*/
protected static function cloneRequest(Request $request, $uri, $content, $id, $parent_id) {
$request->server->set('REQUEST_URI', $uri);
$sub_tree = $request->attributes->get(static::SUBREQUEST_TREE);
$session = $request->getSession();
$new_request = Request::create(
$uri,
$request->getMethod(),
(array) $request->query->getIterator(),
(array) $request->cookies->getIterator(),
(array) $request->files->getIterator(),
(array) $request->server->getIterator(),
$content
);
$new_request->headers->set('Content-ID', sprintf('<%s>', $id));
$new_request->attributes->set(static::SUBREQUEST_PARENT_ID, $parent_id);
$new_request->attributes->set(static::SUBREQUEST_ID, $id);
$new_request->attributes->set(static::SUBREQUEST_TREE, $sub_tree);
$new_request->setSession($session);
return $new_request;
}
}
......@@ -54,10 +54,13 @@ class JsonBlueprintDenormalizer implements DenormalizerInterface, SerializerAwar
// sub-tree to the root.
// TODO: If a tree hangs from a parent that is not attached to the root, then this process may fail.
foreach ($requests_per_parent as $parent_id => $children_requests) {
$parent_request = $root_tree->getDescendant($parent_id);
$parent_requests = array_filter($requests, function (Request $request) use ($parent_id) {
return $request->attributes->get(RequestTree::SUBREQUEST_ID) == $parent_id;
});
$parent_request = reset($parent_requests);
$parent_request->attributes->set(
RequestTree::SUBREQUEST_TREE,
new RequestTree($children_requests)
new RequestTree($children_requests, $parent_id)
);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment