summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMateu Aguiló Bosch2017-09-23 23:48:23 +0200
committerMateu Aguiló Bosch2017-09-23 23:51:52 +0200
commit70ef7e44b8c0079258692e00fe98b3ebb33bfda9 (patch)
treed9e22302865b0cf4b52f90226867a4c3253f7904
parentaf0aa487e6de8139db2ea8d158b5f331118cb654 (diff)
fix(Replacer): Fix replacements for multiple matches by JSON path (#2911254 by e0ipso)
-rw-r--r--src/JsonPathReplacer.php128
-rw-r--r--tests/src/Unit/JsonPathReplacerTest.php64
2 files changed, 128 insertions, 64 deletions
diff --git a/src/JsonPathReplacer.php b/src/JsonPathReplacer.php
index 6cd1dcf..dd5c6cd 100644
--- a/src/JsonPathReplacer.php
+++ b/src/JsonPathReplacer.php
@@ -85,17 +85,23 @@ class JsonPathReplacer {
*/
protected function doReplaceTokensInLocation(array $token_replacements, $tokenized_subrequest, $token_location) {
$replacements = [];
- // For each subrequest, we need to replace all the tokens.
- $tr = [];
- // Go from the flat array with the $id_tuple to an array of arrays.
- foreach ($token_replacements[$token_location] as $id_tuple => $key_val) {
- list($req_id, $idx) = explode('::', $id_tuple);
- $tr[$req_id] = empty($tr[$req_id]) ? [] : $tr[$req_id];
- $tr[$req_id][$idx] = $key_val;
+ $tokens_per_content_id = $token_replacements[$token_location];
+ $index = 0;
+ // First figure out the different token resolutions and their token.
+ $grouped_by_token = [];
+ foreach ($tokens_per_content_id as $resolutions_per_token) {
+ foreach ($resolutions_per_token as $token => $resolutions) {
+ $grouped_by_token[] = array_map(function ($resolution) use ($token) {
+ return [
+ 'token' => $token,
+ 'value' => $resolution,
+ ];
+ }, $resolutions);
+ }
}
- $points = $this->getPoints($tr);
- $keys = array_keys($tr);
- foreach ($points as $index => $point) {
+ // Then calculate the points.
+ $points = $this->getPoints($grouped_by_token);
+ foreach ($points as $point) {
// Clone the subrequest.
$cloned = clone $tokenized_subrequest;
$cloned->requestId = sprintf(
@@ -104,14 +110,16 @@ class JsonPathReplacer {
$token_location,
$index
);
- // Now replace all the tokens in the request member (body or URI).
+ $index++;
+ // Now replace all the tokens in the request member.
$token_subject = $this->serializeMember($token_location, $cloned->{$token_location});
- foreach ($point as $axis => $position) {
- $replacement_info = $tr[$keys[$axis]][$position];
- $value = reset($replacement_info);
- $token = key($replacement_info);
- $regexp = sprintf('/%s/', preg_quote($token), '/');
- $token_subject = preg_replace($regexp, $value, $token_subject);
+ foreach($point as $replacement) {
+ // Do all the different replacements on the same subject.
+ $token_subject = $this->replaceTokenSubject(
+ $replacement['token'],
+ $replacement['value'],
+ $token_subject
+ );
}
$cloned->{$token_location} = $this->deserializeMember($token_location, $token_subject);
array_push($replacements, $cloned);
@@ -120,36 +128,52 @@ class JsonPathReplacer {
}
/**
+ * Does the replacement on the token subject.
+ *
+ * @param string $token
+ * The thing to replace.
+ * @param string $value
+ * The thing to replace it with.
+ * @param string $token_subject
+ * The thing to replace it on.
+ *
+ * @returns string
+ * The replaced string.
+ */
+ protected function replaceTokenSubject($token, $value, $token_subject) {
+ // Escape regular expression.
+ $regexp = sprintf('/%s/', preg_quote($token), '/');
+ return preg_replace($regexp, $value, $token_subject);
+ }
+
+ /**
* Generates a list of sets of coordinates for the token replacements.
*
* Each point (coordinates set) end up creating a new clone of the tokenized
* subrequest.
*
- * @param array $tr
- * Token replacements array structure.
+ * @param array $grouped_by_token
+ * Replacements grouped by token.
*
* @return array
* The coordinates sets.
*/
- protected function getPoints($tr) {
- $indices_matrix = array_reduce($tr, function ($carry, array $found_replacements) {
- $carry[] = array_keys($found_replacements);
- return $carry;
- }, []);
+ protected function getPoints($grouped_by_token) {
+ $current_group = array_shift($grouped_by_token);
+ // If this is not the last group, then call recursively.
+ if (empty($grouped_by_token)) {
+ return array_map(function ($item) {
+ return [$item];
+ }, $current_group);
+ }
$points = [];
- foreach ($indices_matrix as $current) {
- $new_points = [];
- foreach ($current as $index) {
- if (empty($points)) {
- $new_points[] = [$index];
- }
- else {
- foreach ($points as $coordinate_set) {
- $new_points[] = array_merge($coordinate_set, [$index]);
- }
- }
+ foreach ($current_group as $resolution_info) {
+ // Get all the combinations for the next groups.
+ $next_points = $this->getPoints($grouped_by_token);
+ foreach ($next_points as $next_point) {
+ // Prepend the current resolution for each point.
+ $points[] = array_merge([$resolution_info], $next_point);
}
- $points = $new_points;
}
return $points;
}
@@ -227,6 +251,12 @@ class JsonPathReplacer {
// First find all the replacements to do. Use a regular expression to detect
// cases like "…{{req1.body@$.data.attributes.seasons..id}}…"
$found = $this->findTokens($regexp_subject);
+ // Make sure that duplicated tokens in the same location are treated as the
+ // same thing.
+ $found = array_values(array_reduce($found, function ($carry, $match) {
+ $carry[$match[0]] = $match;
+ return $carry;
+ }, []));
// Then calculate the replacements we will need to return.
$reducer = function ($token_replacements, $match) use ($pool) {
// Remove the .body part at the end since we only support the body
@@ -255,7 +285,7 @@ class JsonPathReplacer {
// than one response object (a subject) for a given subrequest, then we
// generate one parallel subrequest per subject.
foreach ($subjects as $subject) {
- $this->addReplacementsForSubject($match, $subject, $token_replacements);
+ $this->addReplacementsForSubject($match, $subject, $provided_id, $token_replacements);
}
return $token_replacements;
@@ -319,26 +349,20 @@ class JsonPathReplacer {
* @param array $token_replacements
* The accumulated replacements. Adds items onto the array.
*/
- protected function addReplacementsForSubject(array $match, Response $subject, array &$token_replacements) {
+ protected function addReplacementsForSubject(array $match, Response $subject, $provided_id, array &$token_replacements) {
$json_object = new JsonObject($subject->getContent());
$to_replace = $json_object->get($match[2]) ?: [];
+ $token = $match[0];
// The replacements need to be strings. If not, then the replacement
// is not valid.
$this->validateJsonPathReplacements($to_replace);
- // Place all the replacement items in the $token_replacements.
- foreach ($to_replace as $index => $replacement_token_value) {
- // Set the match for the Response ID + match item.
- $id_tuple = implode('::', [
- // The subject content ID. It contains the # fragment so we get
- // one per each possible subject.
- $this->getContentId($subject),
- $index,
- ]);
- $replacements_for_item = empty($token_replacements[$id_tuple]) ? [] : $token_replacements[$id_tuple];
- // The whole match string to be replaced.
- $replacements_for_item[$match[0]] = $replacement_token_value;
- $token_replacements[$id_tuple] = $replacements_for_item;
- }
+ $token_replacements[$provided_id] = empty($token_replacements[$provided_id])
+ ? []
+ : $token_replacements[$provided_id];
+ $token_replacements[$provided_id][$token] = empty($token_replacements[$provided_id][$token])
+ ? []
+ : $token_replacements[$provided_id][$token];
+ $token_replacements[$provided_id][$token] = array_merge($token_replacements[$provided_id][$token], $to_replace);
}
/**
diff --git a/tests/src/Unit/JsonPathReplacerTest.php b/tests/src/Unit/JsonPathReplacerTest.php
index 75580fd..7d31681 100644
--- a/tests/src/Unit/JsonPathReplacerTest.php
+++ b/tests/src/Unit/JsonPathReplacerTest.php
@@ -29,7 +29,7 @@ class JsonPathReplacerTest extends UnitTestCase {
public function testReplaceBatch() {
$batch = $responses = [];
$batch[] = new Subrequest([
- 'uri' => '/ipsum/{{foo.body@$.things[*]}}/{{bar.body@$.things[*]}}',
+ 'uri' => '/ipsum/{{foo.body@$.things[*]}}/{{bar.body@$.things[*]}}/{{foo.body@$.stuff}}',
'action' => 'sing',
'requestId' => 'oop',
'headers' => [],
@@ -55,22 +55,62 @@ class JsonPathReplacerTest extends UnitTestCase {
$actual = $this->sut->replaceBatch($batch, $responses);
$this->assertCount(10, $actual);
$paths = array_map(function (Subrequest $subrequest) {
- return $subrequest->uri;
+ return [$subrequest->uri, $subrequest->body];
}, $actual);
$expected_paths = [
- '/ipsum/what/the',
- '/ipsum/keep/the',
- '/ipsum/talking/the',
- '/ipsum/what/plane',
- '/ipsum/keep/plane',
- '/ipsum/talking/plane',
- '/ipsum/what/is',
- '/ipsum/keep/is',
- '/ipsum/talking/is',
- '/dolor/42',
+ ['/ipsum/what/the/42', ['answer' => '42']],
+ ['/ipsum/what/plane/42', ['answer' => '42']],
+ ['/ipsum/what/is/42', ['answer' => '42']],
+ ['/ipsum/keep/the/42', ['answer' => '42']],
+ ['/ipsum/keep/plane/42', ['answer' => '42']],
+ ['/ipsum/keep/is/42', ['answer' => '42']],
+ ['/ipsum/talking/the/42', ['answer' => '42']],
+ ['/ipsum/talking/plane/42', ['answer' => '42']],
+ ['/ipsum/talking/is/42', ['answer' => '42']],
+ ['/dolor/42', 'bar'],
];
$this->assertEquals($expected_paths, $paths);
$this->assertEquals(['answer' => 42], $actual[0]->body);
}
+ /**
+ * @covers ::replaceBatch
+ */
+ public function testReplaceBatchSplit() {
+ $batch = $responses = [];
+ $batch[] = new Subrequest([
+ 'uri' => 'test://{{foo.body@$.things[*].id}}/{{foo.body@$.things[*].id}}',
+ 'action' => 'sing',
+ 'requestId' => 'oop',
+ 'headers' => [],
+ '_resolved' => FALSE,
+ 'body' => ['answer' => '{{foo.body@$.stuff}}'],
+ 'waitFor' => ['foo'],
+ ]);
+ $response = Response::create('{"things":[{"id":"what"},{"id":"keep"},{"id":"talking"}],"stuff":42}');
+ $response->headers->set('Content-ID', '<foo#0>');
+ $responses[] = $response;
+ $response = Response::create('{"things":[{"id":"the"},{"id":"plane"}],"stuff":"delayed"}');
+ $response->headers->set('Content-ID', '<foo#1>');
+ $responses[] = $response;
+ $actual = $this->sut->replaceBatch($batch, $responses);
+ $this->assertCount(10, $actual);
+ $paths = array_map(function (Subrequest $subrequest) {
+ return [$subrequest->uri, $subrequest->body];
+ }, $actual);
+ $expected_paths = [
+ ['test://what/what', ['answer' => '42']],
+ ['test://what/what', ['answer' => 'delayed']],
+ ['test://keep/keep', ['answer' => '42']],
+ ['test://keep/keep', ['answer' => 'delayed']],
+ ['test://talking/talking', ['answer' => '42']],
+ ['test://talking/talking', ['answer' => 'delayed']],
+ ['test://the/the', ['answer' => '42']],
+ ['test://the/the', ['answer' => 'delayed']],
+ ['test://plane/plane', ['answer' => '42']],
+ ['test://plane/plane', ['answer' => 'delayed']],
+ ];
+ $this->assertEquals($expected_paths, $paths);
+ }
+
}