Unverified Commit 45ed3e84 authored by Mateu Aguiló Bosch's avatar Mateu Aguiló Bosch
Browse files

feat(Blueprint): Allow multiple dependencies

parent 235bc9fb
......@@ -97,10 +97,10 @@ properties:
* `body`: the serialized content of the body for the subrequest.
* `headers`: an object of key value pairs. Each key **MUST** be interpreted as
a header name for the subrequest, and the values as the header value.
* `waitFor`: contains the request ID from another request. Indicates that the
current subrequest depends on the other subrequest. When this property is
present, the that particular subrequest cannot be processed until the
referenced request has generated a response.
* `waitFor`: contains the array of request IDs from another request. Indicates
that the current subrequest depends on the other subrequest. When this
property is present, the that particular subrequest cannot be processed
until the referenced request has generated a response.
### Sequential requests
Many times it is necessary to use the information of previous requests in order
......@@ -166,7 +166,7 @@ the request blueprint to express dependencies.
},
{
"requestId": "req-2",
"waitFor": "req-1",
"waitFor": ["req-1"],
"uri": "/menus/{{req-1.body@$.rels.meny.id}}",
"action": "view",
"headers": {
......@@ -175,7 +175,7 @@ the request blueprint to express dependencies.
},
{
"requestId": "req-3",
"waitFor": "req-2",
"waitFor": ["req-2"],
"uri": "/menus/{{req-1.body@$.rels.meny.id}}/courses/{{req-2.body@$.mainCourse.id}}",
"action": "view",
"headers": {
......
......@@ -61,10 +61,14 @@
},
"waitFor": {
"title": "Parent ID",
"description": "ID of another request that is a dependeny for this one.",
"type": "string"
"description": "ID of other requests that this request depends on.",
"type": "array",
"items": {
"type": "string",
"description": "ID of another request that is a dependency for this one."
}
}
}
}
}
}
\ No newline at end of file
}
......@@ -4,12 +4,10 @@ namespace Drupal\subrequests\Normalizer;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Uuid\Php;
use Drupal\subrequests\Blueprint\RequestTree;
use Drupal\subrequests\Subrequest;
use Drupal\subrequests\SubrequestsTree;
use JsonSchema\Validator;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\SerializerAwareInterface;
......@@ -121,7 +119,7 @@ class JsonBlueprintDenormalizer implements DenormalizerInterface, SerializerAwar
$raw_item['body'] = Json::decode($raw_item['body']);
}
$raw_item['headers'] = !empty($raw_item['headers']) ? $raw_item['headers'] : [];
$raw_item['waitFor'] = !empty($raw_item['waitFor']) ? $raw_item['waitFor'] : '<ROOT>';
$raw_item['waitFor'] = !empty($raw_item['waitFor']) ? $raw_item['waitFor'] : ['<ROOT>'];
$raw_item['_resolved'] = FALSE;
return $raw_item;
......@@ -175,19 +173,16 @@ class JsonBlueprintDenormalizer implements DenormalizerInterface, SerializerAwar
public function buildExecutionSequence(array $parsed) {
$sequence = new SubrequestsTree();
$rooted_reqs = array_filter($parsed, function (Subrequest $item) {
return $item->waitFor === '<ROOT>';
return $item->waitFor === ['<ROOT>'];
});
$sequence->stack($rooted_reqs);
$subreqs_with_unresolved_deps = array_values(
array_filter($parsed, function (Subrequest $item) {
return $item->waitFor !== '<ROOT>';
return $item->waitFor !== ['<ROOT>'];
})
);
$dependency_is_resolved = function (Subrequest $item) use ($sequence) {
$parent = array_filter($sequence->getLowestLevel(), function (Subrequest $dep) use ($item) {
return $dep->requestId === $item->waitFor;
});
return !empty($parent);
return empty(array_diff($item->waitFor, $sequence->allIds()));
};
while (count($subreqs_with_unresolved_deps)) {
$no_deps = array_filter($subreqs_with_unresolved_deps, $dependency_is_resolved);
......
......@@ -29,9 +29,9 @@ class Subrequest {
public $headers;
/**
* The parent subrequest.
* The parent subrequests.
*
* @var string
* @var string[]
*/
public $waitFor;
......
......@@ -67,4 +67,22 @@ class SubrequestsTree extends \ArrayObject {
$this->masterRequest = $request;
}
/**
* Gets all the subrequest IDs.
*
* @return \Drupal\subrequests\Subrequest[]
* All the subrequests in all levels.
*/
public function allIds() {
$subrequests = [];
foreach ($this as $item) {
$subrequests = array_merge($subrequests, array_values($item));
}
$all_request_ids = array_map(function (Subrequest $subrequest) {
return $subrequest->requestId;
}, $subrequests);
array_unshift($all_request_ids, '<ROOT>');
return array_unique($all_request_ids);
}
}
......@@ -35,7 +35,7 @@ class JsonPathReplacerTest extends UnitTestCase {
'headers' => [],
'_resolved' => FALSE,
'body' => ['answer' => '{{foo.body@$.stuff}}'],
'waitFor' => 'foo',
'waitFor' => ['foo'],
]);
$batch[] = new Subrequest([
'uri' => '/dolor/{{foo.body@$.stuff}}',
......@@ -44,7 +44,7 @@ class JsonPathReplacerTest extends UnitTestCase {
'headers' => [],
'_resolved' => FALSE,
'body' => 'bar',
'waitFor' => 'foo',
'waitFor' => ['foo'],
]);
$response = Response::create('{"things":["what","keep","talking"],"stuff":42}');
$response->headers->set('Content-ID', '<foo>');
......
......@@ -56,18 +56,18 @@ class JsonBlueprintDenormalizerTest extends UnitTestCase {
'action' => 'sing',
'requestId' => 'oop',
'body' => '[]',
'waitFor' => 'foo',
'waitFor' => ['foo'],
];
$subrequests[] = [
'uri' => 'lorem',
'action' => 'create',
'requestId' => 'oof',
'body' => '"bar"',
'waitFor' => 'foo',
'waitFor' => ['foo'],
];
$actual = $this->sut->denormalize($subrequests, SubrequestsTree::class, 'json', []);
$tree = new SubrequestsTree();
$tree->stack([new Subrequest(['waitFor' => '<ROOT>', '_resolved' => FALSE, 'body' => 'bar'] + $subrequests[0])]);
$tree->stack([new Subrequest(['waitFor' => ['<ROOT>'], '_resolved' => FALSE, 'body' => 'bar'] + $subrequests[0])]);
$tree->stack([
new Subrequest(['headers' => [], '_resolved' => FALSE, 'body' => []] + $subrequests[1]),
new Subrequest(['headers' => [], '_resolved' => FALSE, 'body' => 'bar'] + $subrequests[2])
......
......@@ -34,7 +34,7 @@ class JsonSubrequestDenormalizerTest extends UnitTestCase {
'requestId' => 'oof',
'body' => ['bar' => 'foo'],
'headers' => ['Authorization' => 'Basic ' . base64_encode('lorem:ipsum')],
'waitFor' => 'lorem',
'waitFor' => ['lorem'],
'_resolved' => FALSE,
'uri' => 'oop',
'action' => 'create',
......@@ -63,7 +63,7 @@ class JsonSubrequestDenormalizerTest extends UnitTestCase {
'requestId' => 'oof',
'body' => ['bar' => 'foo'],
'headers' => [],
'waitFor' => 'lorem',
'waitFor' => ['lorem'],
'_resolved' => FALSE,
'uri' => 'oop',
'action' => 'create',
......
......@@ -65,7 +65,7 @@ class SubrequestsManagerTest extends UnitTestCase {
'action' => 'view',
'requestId' => 'foo',
'headers' => [],
'waitFor' => '<ROOT>',
'waitFor' => ['<ROOT>'],
'_resolved' => FALSE,
'body' => 'bar',
]);
......@@ -76,7 +76,7 @@ class SubrequestsManagerTest extends UnitTestCase {
'headers' => [],
'_resolved' => FALSE,
'body' => [],
'waitFor' => 'foo',
'waitFor' => ['foo'],
]);
$subrequests[] = new Subrequest([
'uri' => 'dolor',
......@@ -85,7 +85,7 @@ class SubrequestsManagerTest extends UnitTestCase {
'headers' => [],
'_resolved' => FALSE,
'body' => 'bar',
'waitFor' => 'foo',
'waitFor' => ['foo'],
]);
$tree->stack([$subrequests[0]]);
$tree->stack([$subrequests[1], $subrequests[2]]);
......
......@@ -30,7 +30,7 @@ class SubrequestsTreeTest extends UnitTestCase {
'requestId' => 1,
'body' => '',
'headers' => [],
'waitFor' => 1,
'waitFor' => [1],
'_resolved' => FALSE,
'uri' => '',
'action' => '',
......
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