Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
project
subrequests
Commits
8a63860c
Unverified
Commit
8a63860c
authored
Mar 20, 2017
by
Mateu Aguiló Bosch
Browse files
Allow nested trees
parent
90edcc2d
Changes
4
Hide whitespace changes
Inline
Side-by-side
src/Blueprint/RequestTree.php
View file @
8a63860c
...
...
@@ -2,15 +2,21 @@
namespace
Drupal\subrequests\Blueprint
;
use
Drupal\Component\Serialization\Json
;
use
Rs\Json\Pointer
;
use
Symfony\Component\HttpFoundation\Request
;
use
Symfony\Component\HttpFoundation\Response
;
/**
* Contains the hierarchical information of the requests.
*/
class
RequestTree
{
const
ROOT_TREE_ID
=
'#ROOT#'
;
const
SUBREQUEST_TREE
=
'_subrequests_tree_object'
;
const
SUBREQUEST_ID
=
'_subrequests_content_id'
;
const
SUBREQUEST_PARENT_ID
=
'_subrequests_parent_id'
;
const
SUBREQUEST_DONE
=
'_subrequests_is_done'
;
/**
...
...
@@ -20,6 +26,7 @@ class RequestTree {
/**
* If this tree sprouts from another requests, save the request id here.
*
* @var string
*/
protected
$parentId
;
...
...
@@ -78,6 +85,7 @@ class RequestTree {
$trees
=
array_map
(
function
(
Request
$request
)
{
return
$request
->
attributes
->
get
(
static
::
SUBREQUEST_TREE
);
},
$this
->
getRequests
());
return
array_filter
(
$trees
);
}
...
...
@@ -104,11 +112,13 @@ class RequestTree {
if
(
!
$sub_tree
=
$request
->
attributes
->
get
(
static
::
SUBREQUEST_TREE
))
{
return
FALSE
;
}
return
$sub_tree
->
getDescendant
(
$request_id
);
});
if
(
count
(
$found
))
{
return
reset
(
$found
);
}
return
NULL
;
}
...
...
@@ -125,6 +135,59 @@ class RequestTree {
},
TRUE
);
}
/**
* Resolves the JSON Pointer references.
*
* @todo For now we are forcing the use of JSON Pointer as the only format to
* reference properties in existing responses. Allow pluggability, this step
* should probably be better placed in the subrequest normalizer.
*
* @param \Symfony\Component\HttpFoundation\Response[] $responses
* Previous responses available.
*/
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
=
'/\{\{\/([^\{\}]+)@(\/[^\{\}]+)\}\}/'
;
// 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
;
}
$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
);
// 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
;
}
}
return
$request
;
},
$this
->
getRequests
());
}
/**
* Check if a request and all its possible children are done.
*
...
...
@@ -146,7 +209,19 @@ class RequestTree {
return
FALSE
;
}
}
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
)
{
return
$response
->
headers
->
get
(
'Content-ID'
)
===
sprintf
(
'<%s>'
,
$id
);
})[
0
];
// Find the data in the response output.
$pointer
=
new
Pointer
(
$response
->
getContent
());
return
$pointer
->
get
(
$json_pointer_path
);
}
}
src/Controller/FrontController.php
View file @
8a63860c
...
...
@@ -52,6 +52,11 @@ class FrontController extends ControllerBase {
$trees
=
[
$root_tree
];
// Handle all the sub-requests.
while
(
!
$root_tree
->
isDone
())
{
// Requests in the current level may have references to older responses.
// This step resolves those.
array_walk
(
$trees
,
function
(
RequestTree
$tree
)
use
(
$responses
)
{
$tree
->
dereference
(
$responses
);
});
// Get all the requests in the trees for the previous pass.
$requests
=
array_reduce
(
$trees
,
function
(
array
$carry
,
RequestTree
$tree
)
{
return
array_merge
(
$carry
,
$tree
->
getRequests
());
...
...
src/Normalizer/JsonBlueprintDenormalizer.php
View file @
8a63860c
...
...
@@ -35,7 +35,33 @@ class JsonBlueprintDenormalizer implements DenormalizerInterface, SerializerAwar
$requests
=
array_map
(
function
(
$item
)
use
(
$format
,
$context
)
{
return
$this
->
serializer
->
denormalize
(
$item
,
Request
::
class
,
$format
,
$context
);
},
$data
);
return
new
RequestTree
(
$requests
);
// We want to create one tree per parent, but for that we need to identify
// the parents first.
$requests_per_parent
=
array_reduce
(
$requests
,
function
(
array
$carry
,
Request
$request
)
{
$parent_id
=
$request
->
attributes
->
get
(
RequestTree
::
SUBREQUEST_PARENT_ID
,
RequestTree
::
ROOT_TREE_ID
);
if
(
empty
(
$carry
[
$parent_id
]))
{
$carry
[
$parent_id
]
=
[];
}
$carry
[
$parent_id
][]
=
$request
;
return
$carry
;
},
[]);
// Now get all the requests for the root parent to create the root tree.
$root_tree
=
new
RequestTree
(
$requests_per_parent
[
RequestTree
::
ROOT_TREE_ID
]);
unset
(
$requests_per_parent
[
RequestTree
::
ROOT_TREE_ID
]);
// Iterate through all the parents to find them in the tree. The attach the
// 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_request
->
attributes
->
set
(
RequestTree
::
SUBREQUEST_TREE
,
new
RequestTree
(
$children_requests
)
);
}
return
$root_tree
;
}
/**
...
...
src/Normalizer/JsonSubrequestDenormalizer.php
View file @
8a63860c
...
...
@@ -3,11 +3,12 @@
namespace
Drupal\subrequests\Normalizer
;
use
Drupal\Component\Utility\NestedArray
;
use
Drupal\subrequests\Blueprint\Parser
;
use
Drupal\subrequests\Blueprint\RequestTree
;
use
Symfony\Component\HttpFoundation\HeaderBag
;
use
Symfony\Component\HttpFoundation\Request
;
use
Symfony\Component\Serializer\Normalizer\DenormalizerInterface
;
use
Drupal\Component\Utility\NestedArray
;
class
JsonSubrequestDenormalizer
implements
DenormalizerInterface
{
/**
...
...
@@ -58,6 +59,12 @@ class JsonSubrequestDenormalizer implements DenormalizerInterface {
?
md5
(
serialize
(
$data
))
:
$data
[
'requestId'
];
$request
->
headers
->
set
(
'Content-ID'
,
'<'
.
$content_id
.
'>'
);
$request
->
attributes
->
set
(
RequestTree
::
SUBREQUEST_ID
,
$content_id
);
// If there is a parent, then add the ID to construct the tree.
if
(
!
empty
(
$data
[
'waitFor'
]))
{
$request
->
attributes
->
set
(
RequestTree
::
SUBREQUEST_PARENT_ID
,
$data
[
'waitFor'
]);
}
return
$request
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a 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