Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
project
subrequests
Commits
70ef7e44
Unverified
Commit
70ef7e44
authored
Sep 23, 2017
by
Mateu Aguiló Bosch
Browse files
fix(Replacer): Fix replacements for multiple matches by JSON path (
#2911254
by e0ipso)
parent
af0aa487
Changes
2
Hide whitespace changes
Inline
Side-by-side
src/JsonPathReplacer.php
View file @
70ef7e44
...
...
@@ -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
);
...
...
@@ -119,37 +127,53 @@ class JsonPathReplacer {
return
$replacements
;
}
/**
* 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 r
eplacements
array structure
.
* @param array $
grouped_by_token
*
R
eplacements
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
);
}
/**
...
...
tests/src/Unit/JsonPathReplacerTest.php
View file @
70ef7e44
...
...
@@ -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
);
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment