19
19
use ApiPlatform \Hydra \PartialCollectionView ;
20
20
use ApiPlatform \Metadata \CollectionOperationInterface ;
21
21
use ApiPlatform \Metadata \Error ;
22
+ use ApiPlatform \Metadata \IriConverterInterface ;
22
23
use ApiPlatform \Metadata \Operation ;
24
+ use ApiPlatform \Metadata \Operation \Factory \OperationMetadataFactoryInterface ;
23
25
use ApiPlatform \Metadata \QueryParameterInterface ;
26
+ use ApiPlatform \Metadata \ResourceClassResolverInterface ;
24
27
use ApiPlatform \Metadata \UrlGeneratorInterface ;
25
28
use ApiPlatform \Metadata \Util \IriHelper ;
26
29
use ApiPlatform \State \Pagination \PaginatorInterface ;
27
30
use ApiPlatform \State \Pagination \PartialPaginatorInterface ;
28
31
use ApiPlatform \State \ProcessorInterface ;
32
+ use ApiPlatform \State \Util \HttpResponseHeadersTrait ;
33
+ use ApiPlatform \State \Util \HttpResponseStatusTrait ;
29
34
use Symfony \Component \HttpFoundation \Response ;
30
35
use Symfony \Component \HttpFoundation \StreamedResponse ;
31
36
use Symfony \Component \JsonStreamer \StreamWriterInterface ;
32
37
use Symfony \Component \TypeInfo \Type ;
33
38
39
+ /**
40
+ * @implements ProcessorInterface<mixed,mixed>
41
+ */
34
42
final class JsonStreamerProcessor implements ProcessorInterface
35
43
{
44
+ use HttpResponseHeadersTrait;
45
+ use HttpResponseStatusTrait;
46
+
47
+ /**
48
+ * @param ProcessorInterface<mixed,mixed> $processor
49
+ * @param StreamWriterInterface<array<string,mixed>> $jsonStreamer
50
+ */
36
51
public function __construct (
37
52
private readonly ProcessorInterface $ processor ,
38
53
private readonly StreamWriterInterface $ jsonStreamer ,
54
+ ?IriConverterInterface $ iriConverter = null ,
55
+ ?ResourceClassResolverInterface $ resourceClassResolver = null ,
56
+ ?OperationMetadataFactoryInterface $ operationMetadataFactory = null ,
39
57
private readonly string $ pageParameterName = 'page ' ,
40
58
private readonly string $ enabledParameterName = 'pagination ' ,
41
- private readonly int $ urlGenerationStrategy = UrlGeneratorInterface::ABS_PATH
59
+ private readonly int $ urlGenerationStrategy = UrlGeneratorInterface::ABS_PATH ,
42
60
) {
61
+ $ this ->resourceClassResolver = $ resourceClassResolver ;
62
+ $ this ->iriConverter = $ iriConverter ;
63
+ $ this ->operationMetadataFactory = $ operationMetadataFactory ;
43
64
}
44
65
66
+ public function process (mixed $ data , Operation $ operation , array $ uriVariables = [], array $ context = [])
67
+ {
68
+ if (!$ operation ->getJsonStream () || !($ request = $ context ['request ' ] ?? null )) {
69
+ return $ this ->processor ->process ($ data , $ operation , $ uriVariables , $ context );
70
+ }
71
+
72
+ // TODO: remove this before merging
73
+ if ($ request ->query ->has ('skip_json_stream ' )) {
74
+ return $ this ->processor ->process ($ data , $ operation , $ uriVariables , $ context );
75
+ }
76
+
77
+ if ($ operation instanceof Error || $ data instanceof Response) {
78
+ return $ this ->processor ->process ($ data , $ operation , $ uriVariables , $ context );
79
+ }
80
+
81
+ if ($ operation instanceof CollectionOperationInterface) {
82
+ $ requestUri = $ request ->getRequestUri () ?? '' ;
83
+ $ collection = new Collection ();
84
+ $ collection ->member = $ data ;
85
+ $ collection ->view = $ this ->getView ($ data , $ requestUri , $ operation );
86
+
87
+ if ($ operation ->getParameters ()) {
88
+ $ collection ->search = $ this ->getSearch ($ operation , $ requestUri );
89
+ }
90
+
91
+ if ($ data instanceof PaginatorInterface) {
92
+ $ collection ->totalItems = $ data ->getTotalItems ();
93
+ }
94
+
95
+ if (\is_array ($ data ) || ($ data instanceof \Countable && !$ data instanceof PartialPaginatorInterface)) {
96
+ $ collection ->totalItems = \count ($ data );
97
+ }
98
+
99
+ $ data = $ this ->jsonStreamer ->write (
100
+ $ collection ,
101
+ Type::generic (Type::object ($ collection ::class), Type::object ($ operation ->getClass ())),
102
+ ['data ' => $ data , 'operation ' => $ operation ],
103
+ );
104
+ } else {
105
+ $ data = $ this ->jsonStreamer ->write ($ data , Type::object ($ operation ->getClass ()), [
106
+ 'data ' => $ data ,
107
+ 'operation ' => $ operation ,
108
+ ]);
109
+ }
110
+
111
+ /** @var iterable<string> $data */
112
+ $ response = new StreamedResponse (
113
+ $ data ,
114
+ $ this ->getStatus ($ request , $ operation , $ context ),
115
+ $ this ->getHeaders ($ request , $ operation , $ context )
116
+ );
117
+
118
+ return $ this ->processor ->process ($ response , $ operation , $ uriVariables , $ context );
119
+ }
120
+
121
+ // TODO: These come from our Hydra collection normalizer, try to share the logic
45
122
private function getSearch (Operation $ operation , string $ requestUri ): IriTemplate
46
123
{
47
124
/** @var list<IriTemplateMapping> */
@@ -67,6 +144,7 @@ private function getSearch(Operation $operation, string $requestUri): IriTemplat
67
144
}
68
145
69
146
$ parts = parse_url ($ requestUri );
147
+
70
148
return new IriTemplate (
71
149
variableRepresentation: 'BasicRepresentation ' ,
72
150
mapping: $ mapping ,
@@ -91,11 +169,11 @@ private function getView(mixed $object, string $requestUri, Operation $operation
91
169
// TODO: This needs to be changed as well as I wrote in the CollectionFiltersNormalizer
92
170
// We should not rely on the request_uri but instead rely on the UriTemplate
93
171
// This needs that we implement the RFC and that we do more parsing before calling the serialization (MainController)
94
- $ parsed = IriHelper::parseIri ($ requestUri ?? ' / ' , $ this ->pageParameterName );
172
+ $ parsed = IriHelper::parseIri ($ requestUri , $ this ->pageParameterName );
95
173
$ appliedFilters = $ parsed ['parameters ' ];
96
174
unset($ appliedFilters [$ this ->enabledParameterName ]);
97
175
98
- $ urlGenerationStrategy = $ operation? ->getUrlGenerationStrategy() ?? $ this ->urlGenerationStrategy ;
176
+ $ urlGenerationStrategy = $ operation ->getUrlGenerationStrategy () ?? $ this ->urlGenerationStrategy ;
99
177
$ id = IriHelper::createIri ($ parsed ['parts ' ], $ parsed ['parameters ' ], $ this ->pageParameterName , $ paginated ? $ currentPage : null , $ urlGenerationStrategy );
100
178
if (!$ appliedFilters && !$ paginated ) {
101
179
return new PartialCollectionView ($ id );
@@ -117,46 +195,4 @@ private function getView(mixed $object, string $requestUri, Operation $operation
117
195
118
196
return new PartialCollectionView ($ id , $ first , $ last , $ previous , $ next );
119
197
}
120
-
121
- public function process (mixed $ data , Operation $ operation , array $ uriVariables = [], array $ context = [])
122
- {
123
- if ($ context ['request ' ]->query ->has ('skip_json_stream ' )) {
124
- return $ this ->processor ->process ($ data , $ operation , $ uriVariables , $ context );
125
- }
126
-
127
- if ($ operation instanceof Error || $ data instanceof Response) {
128
- return $ this ->processor ->process ($ data , $ operation , $ uriVariables , $ context );
129
- }
130
-
131
- if ($ operation instanceof CollectionOperationInterface) {
132
- $ requestUri = $ context ['request ' ]->getRequestUri () ?? '' ;
133
- $ collection = new Collection ();
134
- $ collection ->member = $ data ;
135
- $ collection ->view = $ this ->getView ($ data , $ requestUri , $ operation );
136
-
137
- if ($ operation ->getParameters ()) {
138
- $ collection ->search = $ this ->getSearch ($ operation , $ requestUri );
139
- }
140
-
141
- if ($ data instanceof PaginatorInterface) {
142
- $ collection ->totalItems = $ data ->getTotalItems ();
143
- }
144
-
145
- if (\is_array ($ data ) || ($ data instanceof \Countable && !$ data instanceof PartialPaginatorInterface)) {
146
- $ collection ->totalItems = \count ($ data );
147
- }
148
-
149
- $ response = new StreamedResponse ($ this ->jsonStreamer ->write ($ collection , Type::generic (Type::object ($ collection ::class), Type::object ($ operation ->getClass ())), [
150
- 'data ' => $ data ,
151
- 'operation ' => $ operation ,
152
- ]));
153
- } else {
154
- $ response = new StreamedResponse ($ this ->jsonStreamer ->write ($ data , Type::object ($ operation ->getClass ()), [
155
- 'data ' => $ data ,
156
- 'operation ' => $ operation ,
157
- ]));
158
- }
159
-
160
- return $ this ->processor ->process ($ response , $ operation , $ uriVariables , $ context );
161
- }
162
198
}
0 commit comments