123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- <?php
- namespace Mpociot\ApiDoc\Tools\ResponseStrategies;
- use Dingo\Api\Dispatcher;
- use Illuminate\Http\Request;
- use Illuminate\Http\Response;
- use Illuminate\Routing\Route;
- use Mpociot\ApiDoc\Tools\Traits\ParamHelpers;
- /**
- * Make a call to the route and retrieve its response.
- */
- class ResponseCallStrategy
- {
- use ParamHelpers;
- /**
- * @param Route $route
- * @param array $tags
- * @param array $routeProps
- *
- * @return array|null
- */
- public function __invoke(Route $route, array $tags, array $routeProps)
- {
- $rulesToApply = $routeProps['rules']['response_calls'] ?? [];
- if (! $this->shouldMakeApiCall($route, $rulesToApply)) {
- return;
- }
- $this->configureEnvironment($rulesToApply);
- $request = $this->prepareRequest($route, $rulesToApply, $routeProps['body'], $routeProps['query']);
- try {
- $response = [$this->makeApiCall($request)];
- } catch (\Exception $e) {
- $response = null;
- } finally {
- $this->finish();
- }
- return $response;
- }
- /**
- * @param array $rulesToApply
- *
- * @return void
- */
- private function configureEnvironment(array $rulesToApply)
- {
- $this->startDbTransaction();
- $this->setEnvironmentVariables($rulesToApply['env'] ?? []);
- $this->setLaravelConfigs($rulesToApply['config'] ?? []);
- }
- /**
- * @param Route $route
- * @param array $rulesToApply
- * @param array $bodyParams
- * @param array $queryParams
- *
- * @return Request
- */
- private function prepareRequest(Route $route, array $rulesToApply, array $bodyParams, array $queryParams)
- {
- $uri = $this->replaceUrlParameterBindings($route, $rulesToApply['bindings'] ?? []);
- $routeMethods = $this->getMethods($route);
- $method = array_shift($routeMethods);
- $cookies = isset($rulesToApply['cookies']) ? $rulesToApply['cookies'] : [];
- $request = Request::create($uri, $method, [], $cookies, [], $this->transformHeadersToServerVars($rulesToApply['headers'] ?? []));
- $request = $this->addHeaders($request, $route, $rulesToApply['headers'] ?? []);
- // Mix in parsed parameters with manually specified parameters.
- $queryParams = collect($this->cleanParams($queryParams))->merge($rulesToApply['query'] ?? [])->toArray();
- $bodyParams = collect($this->cleanParams($bodyParams))->merge($rulesToApply['body'] ?? [])->toArray();
- $request = $this->addQueryParameters($request, $queryParams);
- $request = $this->addBodyParameters($request, $bodyParams);
- return $request;
- }
- /**
- * Transform parameters in URLs into real values (/users/{user} -> /users/2).
- * Uses bindings specified by caller, otherwise just uses '1'.
- *
- * @param Route $route
- * @param array $bindings
- *
- * @return mixed
- */
- protected function replaceUrlParameterBindings(Route $route, $bindings)
- {
- $uri = $route->uri();
- foreach ($bindings as $parameter => $binding) {
- $uri = str_replace($parameter, $binding, $uri);
- }
- // Replace any unbound parameters with '1'
- $uri = preg_replace('/{(.*?)}/', 1, $uri);
- return $uri;
- }
- /**
- * @param array $config
- *
- * @return void
- * @deprecated in favour of Laravel config variables
- */
- private function setEnvironmentVariables(array $env)
- {
- foreach ($env as $name => $value) {
- putenv("$name=$value");
- $_ENV[$name] = $value;
- $_SERVER[$name] = $value;
- }
- }
- /**
- * @param array $config
- *
- * @return void
- */
- private function setLaravelConfigs(array $config)
- {
- if (empty($config)) {
- return;
- }
- foreach ($config as $name => $value) {
- config([$name => $value]);
- }
- }
- /**
- * @return void
- */
- private function startDbTransaction()
- {
- try {
- app('db')->beginTransaction();
- } catch (\Exception $e) {
- }
- }
- /**
- * @return void
- */
- private function endDbTransaction()
- {
- try {
- app('db')->rollBack();
- } catch (\Exception $e) {
- }
- }
- /**
- * @return void
- */
- private function finish()
- {
- $this->endDbTransaction();
- }
- /**
- * @param Request $request
- *
- * @return \Illuminate\Http\JsonResponse|mixed
- */
- public function callDingoRoute(Request $request)
- {
- /** @var Dispatcher $dispatcher */
- $dispatcher = app(\Dingo\Api\Dispatcher::class);
- foreach ($request->headers as $header => $value) {
- $dispatcher->header($header, $value);
- }
- // set domain and body parameters
- $dispatcher->on($request->header('SERVER_NAME'))
- ->with($request->request->all());
- // set URL and query parameters
- $uri = $request->getRequestUri();
- $query = $request->getQueryString();
- if (! empty($query)) {
- $uri .= "?$query";
- }
- $response = call_user_func_array([$dispatcher, strtolower($request->method())], [$uri]);
- // the response from the Dingo dispatcher is the 'raw' response from the controller,
- // so we have to ensure it's JSON first
- if (! $response instanceof Response) {
- $response = response()->json($response);
- }
- return $response;
- }
- /**
- * @param Route $route
- *
- * @return array
- */
- public function getMethods(Route $route)
- {
- return array_diff($route->methods(), ['HEAD']);
- }
- /**
- * @param Request $request
- * @param Route $route
- * @param array|null $headers
- *
- * @return Request
- */
- private function addHeaders(Request $request, Route $route, $headers)
- {
- // set the proper domain
- if ($route->getDomain()) {
- $request->headers->add([
- 'HOST' => $route->getDomain(),
- ]);
- $request->server->add([
- 'HTTP_HOST' => $route->getDomain(),
- 'SERVER_NAME' => $route->getDomain(),
- ]);
- }
- $headers = collect($headers);
- if (($headers->get('Accept') ?: $headers->get('accept')) === 'application/json') {
- $request->setRequestFormat('json');
- }
- return $request;
- }
- /**
- * @param Request $request
- * @param array $query
- *
- * @return Request
- */
- private function addQueryParameters(Request $request, array $query)
- {
- $request->query->add($query);
- $request->server->add(['QUERY_STRING' => http_build_query($query)]);
- return $request;
- }
- /**
- * @param Request $request
- * @param array $body
- *
- * @return Request
- */
- private function addBodyParameters(Request $request, array $body)
- {
- $request->request->add($body);
- return $request;
- }
- /**
- * @param Request $request
- *
- * @throws \Exception
- *
- * @return \Illuminate\Http\JsonResponse|mixed|\Symfony\Component\HttpFoundation\Response
- */
- private function makeApiCall(Request $request)
- {
- if (config('apidoc.router') == 'dingo') {
- $response = $this->callDingoRoute($request);
- } else {
- $response = $this->callLaravelRoute($request);
- }
- return $response;
- }
- /**
- * @param Request $request
- *
- * @throws \Exception
- *
- * @return \Symfony\Component\HttpFoundation\Response
- */
- private function callLaravelRoute(Request $request): \Symfony\Component\HttpFoundation\Response
- {
- $kernel = app(\Illuminate\Contracts\Http\Kernel::class);
- $response = $kernel->handle($request);
- $kernel->terminate($request, $response);
- return $response;
- }
- /**
- * @param Route $route
- * @param array $rulesToApply
- *
- * @return bool
- */
- private function shouldMakeApiCall(Route $route, array $rulesToApply): bool
- {
- $allowedMethods = $rulesToApply['methods'] ?? [];
- if (empty($allowedMethods)) {
- return false;
- }
- if (is_string($allowedMethods) && $allowedMethods == '*') {
- return true;
- }
- if (array_search('*', $allowedMethods) !== false) {
- return true;
- }
- $routeMethods = $this->getMethods($route);
- if (in_array(array_shift($routeMethods), $allowedMethods)) {
- return true;
- }
- return false;
- }
- /**
- * Transform headers array to array of $_SERVER vars with HTTP_* format.
- *
- * @param array $headers
- *
- * @return array
- */
- protected function transformHeadersToServerVars(array $headers)
- {
- $server = [];
- $prefix = 'HTTP_';
- foreach ($headers as $name => $value) {
- $name = strtr(strtoupper($name), '-', '_');
- if (! starts_with($name, $prefix) && $name !== 'CONTENT_TYPE') {
- $name = $prefix.$name;
- }
- $server[$name] = $value;
- }
- return $server;
- }
- }
|