1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Mpociot\ApiDoc\Tools\ResponseStrategies; |
4
|
|
|
|
5
|
|
|
use Dingo\Api\Dispatcher; |
6
|
|
|
use Illuminate\Http\Request; |
7
|
|
|
use Illuminate\Http\Response; |
8
|
|
|
use Illuminate\Routing\Route; |
9
|
|
|
use Mpociot\ApiDoc\Tools\Utils; |
10
|
|
|
use Mpociot\ApiDoc\Tools\Traits\ParamHelpers; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* Make a call to the route and retrieve its response. |
14
|
|
|
*/ |
15
|
|
|
class ResponseCallStrategy |
16
|
|
|
{ |
17
|
|
|
use ParamHelpers; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @param Route $route |
21
|
|
|
* @param array $tags |
22
|
|
|
* @param array $routeProps |
23
|
|
|
* |
24
|
|
|
* @return array|null |
25
|
|
|
*/ |
26
|
|
|
public function __invoke(Route $route, array $tags, array $routeProps) |
27
|
|
|
{ |
28
|
|
|
$rulesToApply = $routeProps['rules']['response_calls'] ?? []; |
29
|
|
|
if (! $this->shouldMakeApiCall($route, $rulesToApply)) { |
30
|
|
|
return; |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
$this->configureEnvironment($rulesToApply); |
34
|
|
|
$request = $this->prepareRequest($route, $rulesToApply, $routeProps['body'], $routeProps['query']); |
35
|
|
|
|
36
|
|
|
try { |
37
|
|
|
$response = [$this->makeApiCall($request)]; |
38
|
|
|
} catch (\Exception $e) { |
39
|
|
|
$response = null; |
40
|
|
|
} finally { |
41
|
|
|
$this->finish(); |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
return $response; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @param array $rulesToApply |
49
|
|
|
* |
50
|
|
|
* @return void |
51
|
|
|
*/ |
52
|
|
|
private function configureEnvironment(array $rulesToApply) |
53
|
|
|
{ |
54
|
|
|
$this->startDbTransaction(); |
55
|
|
|
$this->setEnvironmentVariables($rulesToApply['env'] ?? []); |
|
|
|
|
56
|
|
|
$this->setLaravelConfigs($rulesToApply['config'] ?? []); |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @param Route $route |
61
|
|
|
* @param array $rulesToApply |
62
|
|
|
* @param array $bodyParams |
63
|
|
|
* @param array $queryParams |
64
|
|
|
* |
65
|
|
|
* @return Request |
66
|
|
|
*/ |
67
|
|
|
private function prepareRequest(Route $route, array $rulesToApply, array $bodyParams, array $queryParams) |
68
|
|
|
{ |
69
|
|
|
$uri = Utils::getFullUrl($route, $rulesToApply['bindings'] ?? []); |
70
|
|
|
$routeMethods = $this->getMethods($route); |
71
|
|
|
$method = array_shift($routeMethods); |
72
|
|
|
$cookies = isset($rulesToApply['cookies']) ? $rulesToApply['cookies'] : []; |
73
|
|
|
$request = Request::create($uri, $method, [], $cookies, [], $this->transformHeadersToServerVars($rulesToApply['headers'] ?? [])); |
74
|
|
|
$request = $this->addHeaders($request, $route, $rulesToApply['headers'] ?? []); |
75
|
|
|
|
76
|
|
|
// Mix in parsed parameters with manually specified parameters. |
77
|
|
|
$queryParams = collect($this->cleanParams($queryParams))->merge($rulesToApply['query'] ?? [])->toArray(); |
78
|
|
|
$bodyParams = collect($this->cleanParams($bodyParams))->merge($rulesToApply['body'] ?? [])->toArray(); |
79
|
|
|
|
80
|
|
|
$request = $this->addQueryParameters($request, $queryParams); |
81
|
|
|
$request = $this->addBodyParameters($request, $bodyParams); |
82
|
|
|
|
83
|
|
|
return $request; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* @param array $config |
|
|
|
|
88
|
|
|
* |
89
|
|
|
* @return void |
90
|
|
|
* |
91
|
|
|
* @deprecated in favour of Laravel config variables |
92
|
|
|
*/ |
93
|
|
|
private function setEnvironmentVariables(array $env) |
94
|
|
|
{ |
95
|
|
|
foreach ($env as $name => $value) { |
96
|
|
|
putenv("$name=$value"); |
97
|
|
|
|
98
|
|
|
$_ENV[$name] = $value; |
99
|
|
|
$_SERVER[$name] = $value; |
100
|
|
|
} |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* @param array $config |
105
|
|
|
* |
106
|
|
|
* @return void |
107
|
|
|
*/ |
108
|
|
|
private function setLaravelConfigs(array $config) |
109
|
|
|
{ |
110
|
|
|
if (empty($config)) { |
111
|
|
|
return; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
foreach ($config as $name => $value) { |
115
|
|
|
config([$name => $value]); |
116
|
|
|
} |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* @return void |
121
|
|
|
*/ |
122
|
|
|
private function startDbTransaction() |
123
|
|
|
{ |
124
|
|
|
try { |
125
|
|
|
app('db')->beginTransaction(); |
126
|
|
|
} catch (\Exception $e) { |
|
|
|
|
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @return void |
132
|
|
|
*/ |
133
|
|
|
private function endDbTransaction() |
134
|
|
|
{ |
135
|
|
|
try { |
136
|
|
|
app('db')->rollBack(); |
137
|
|
|
} catch (\Exception $e) { |
|
|
|
|
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* @return void |
143
|
|
|
*/ |
144
|
|
|
private function finish() |
145
|
|
|
{ |
146
|
|
|
$this->endDbTransaction(); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @param Request $request |
151
|
|
|
* |
152
|
|
|
* @return \Illuminate\Http\JsonResponse|mixed |
153
|
|
|
*/ |
154
|
|
|
public function callDingoRoute(Request $request) |
155
|
|
|
{ |
156
|
|
|
/** @var Dispatcher $dispatcher */ |
157
|
|
|
$dispatcher = app(\Dingo\Api\Dispatcher::class); |
158
|
|
|
|
159
|
|
|
foreach ($request->headers as $header => $value) { |
160
|
|
|
$dispatcher->header($header, $value); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
// set domain and body parameters |
164
|
|
|
$dispatcher->on($request->header('SERVER_NAME')) |
|
|
|
|
165
|
|
|
->with($request->request->all()); |
166
|
|
|
|
167
|
|
|
// set URL and query parameters |
168
|
|
|
$uri = $request->getRequestUri(); |
169
|
|
|
$query = $request->getQueryString(); |
170
|
|
|
if (! empty($query)) { |
171
|
|
|
$uri .= "?$query"; |
172
|
|
|
} |
173
|
|
|
$response = call_user_func_array([$dispatcher, strtolower($request->method())], [$uri]); |
174
|
|
|
|
175
|
|
|
// the response from the Dingo dispatcher is the 'raw' response from the controller, |
176
|
|
|
// so we have to ensure it's JSON first |
177
|
|
|
if (! $response instanceof Response) { |
178
|
|
|
$response = response()->json($response); |
|
|
|
|
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
return $response; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* @param Route $route |
186
|
|
|
* |
187
|
|
|
* @return array |
188
|
|
|
*/ |
189
|
|
|
public function getMethods(Route $route) |
190
|
|
|
{ |
191
|
|
|
return array_diff($route->methods(), ['HEAD']); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* @param Request $request |
196
|
|
|
* @param Route $route |
197
|
|
|
* @param array|null $headers |
198
|
|
|
* |
199
|
|
|
* @return Request |
200
|
|
|
*/ |
201
|
|
|
private function addHeaders(Request $request, Route $route, $headers) |
202
|
|
|
{ |
203
|
|
|
// set the proper domain |
204
|
|
|
if ($route->getDomain()) { |
|
|
|
|
205
|
|
|
$request->headers->add([ |
206
|
|
|
'HOST' => $route->getDomain(), |
207
|
|
|
]); |
208
|
|
|
$request->server->add([ |
209
|
|
|
'HTTP_HOST' => $route->getDomain(), |
210
|
|
|
'SERVER_NAME' => $route->getDomain(), |
211
|
|
|
]); |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
$headers = collect($headers); |
215
|
|
|
|
216
|
|
|
if (($headers->get('Accept') ?: $headers->get('accept')) === 'application/json') { |
217
|
|
|
$request->setRequestFormat('json'); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
return $request; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* @param Request $request |
225
|
|
|
* @param array $query |
226
|
|
|
* |
227
|
|
|
* @return Request |
228
|
|
|
*/ |
229
|
|
|
private function addQueryParameters(Request $request, array $query) |
230
|
|
|
{ |
231
|
|
|
$request->query->add($query); |
232
|
|
|
$request->server->add(['QUERY_STRING' => http_build_query($query)]); |
233
|
|
|
|
234
|
|
|
return $request; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* @param Request $request |
239
|
|
|
* @param array $body |
240
|
|
|
* |
241
|
|
|
* @return Request |
242
|
|
|
*/ |
243
|
|
|
private function addBodyParameters(Request $request, array $body) |
244
|
|
|
{ |
245
|
|
|
$request->request->add($body); |
246
|
|
|
|
247
|
|
|
return $request; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* @param Request $request |
252
|
|
|
* |
253
|
|
|
* @throws \Exception |
254
|
|
|
* |
255
|
|
|
* @return \Illuminate\Http\JsonResponse|mixed|\Symfony\Component\HttpFoundation\Response |
256
|
|
|
*/ |
257
|
|
|
private function makeApiCall(Request $request) |
258
|
|
|
{ |
259
|
|
|
if (config('apidoc.router') == 'dingo') { |
260
|
|
|
$response = $this->callDingoRoute($request); |
261
|
|
|
} else { |
262
|
|
|
$response = $this->callLaravelRoute($request); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
return $response; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* @param Request $request |
270
|
|
|
* |
271
|
|
|
* @throws \Exception |
272
|
|
|
* |
273
|
|
|
* @return \Symfony\Component\HttpFoundation\Response |
274
|
|
|
*/ |
275
|
|
|
private function callLaravelRoute(Request $request): \Symfony\Component\HttpFoundation\Response |
276
|
|
|
{ |
277
|
|
|
$kernel = app(\Illuminate\Contracts\Http\Kernel::class); |
278
|
|
|
$response = $kernel->handle($request); |
279
|
|
|
$kernel->terminate($request, $response); |
280
|
|
|
|
281
|
|
|
return $response; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* @param Route $route |
286
|
|
|
* @param array $rulesToApply |
287
|
|
|
* |
288
|
|
|
* @return bool |
289
|
|
|
*/ |
290
|
|
|
private function shouldMakeApiCall(Route $route, array $rulesToApply): bool |
291
|
|
|
{ |
292
|
|
|
$allowedMethods = $rulesToApply['methods'] ?? []; |
293
|
|
|
if (empty($allowedMethods)) { |
294
|
|
|
return false; |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
if (is_string($allowedMethods) && $allowedMethods == '*') { |
298
|
|
|
return true; |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
if (array_search('*', $allowedMethods) !== false) { |
302
|
|
|
return true; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
$routeMethods = $this->getMethods($route); |
306
|
|
|
if (in_array(array_shift($routeMethods), $allowedMethods)) { |
307
|
|
|
return true; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
return false; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* Transform headers array to array of $_SERVER vars with HTTP_* format. |
315
|
|
|
* |
316
|
|
|
* @param array $headers |
317
|
|
|
* |
318
|
|
|
* @return array |
319
|
|
|
*/ |
320
|
|
|
protected function transformHeadersToServerVars(array $headers) |
321
|
|
|
{ |
322
|
|
|
$server = []; |
323
|
|
|
$prefix = 'HTTP_'; |
324
|
|
|
foreach ($headers as $name => $value) { |
325
|
|
|
$name = strtr(strtoupper($name), '-', '_'); |
326
|
|
|
if (! starts_with($name, $prefix) && $name !== 'CONTENT_TYPE') { |
327
|
|
|
$name = $prefix.$name; |
328
|
|
|
} |
329
|
|
|
$server[$name] = $value; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
return $server; |
333
|
|
|
} |
334
|
|
|
} |
335
|
|
|
|
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.