ExtractedEndpointData.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <?php
  2. namespace Knuckles\Camel\Extraction;
  3. use Illuminate\Routing\Route;
  4. use Illuminate\Support\Str;
  5. use Knuckles\Camel\BaseDTO;
  6. use Knuckles\Scribe\Tools\Utils as u;
  7. use ReflectionClass;
  8. class ExtractedEndpointData extends BaseDTO
  9. {
  10. /**
  11. * @var array<string>
  12. */
  13. public $methods;
  14. /** @var string */
  15. public $uri;
  16. /** @var \Knuckles\Camel\Extraction\Metadata */
  17. public $metadata;
  18. /**
  19. * @var array<string,string>
  20. */
  21. public $headers = [];
  22. /**
  23. * @var array
  24. * @var array<string,\Knuckles\Camel\Extraction\Parameter>
  25. */
  26. public $urlParameters = [];
  27. /**
  28. * @var array<string,mixed>
  29. */
  30. public $cleanUrlParameters = [];
  31. /**
  32. * @var array
  33. * @var array<string,\Knuckles\Camel\Extraction\Parameter>
  34. */
  35. public $queryParameters = [];
  36. /**
  37. * @var array<string,mixed>
  38. */
  39. public $cleanQueryParameters = [];
  40. /**
  41. * @var array
  42. * @var array<string,\Knuckles\Camel\Extraction\Parameter>
  43. */
  44. public $bodyParameters = [];
  45. /**
  46. * @var array<string,mixed>
  47. */
  48. public $cleanBodyParameters = [];
  49. /**
  50. * @var array<string,\Illuminate\Http\UploadedFile|array>
  51. */
  52. public $fileParameters = [];
  53. /**
  54. * @var ResponseCollection|array
  55. */
  56. public $responses;
  57. /**
  58. * @var array
  59. * @var array<string,\Knuckles\Camel\Extraction\ResponseField>
  60. */
  61. public $responseFields = [];
  62. /**
  63. * Authentication info for this endpoint. In the form [{where}, {name}, {sample}]
  64. * Example: ["queryParameters", "api_key", "njiuyiw97865rfyvgfvb1"]
  65. * @var array
  66. */
  67. public $auth = [];
  68. /** @var \ReflectionClass|null */
  69. public $controller;
  70. /** @var \ReflectionFunctionAbstract|null */
  71. public $method;
  72. /** @var \Illuminate\Routing\Route|null */
  73. public $route;
  74. public function __construct(array $parameters = [])
  75. {
  76. $parameters['uri'] = $this->normalizeResourceParamName($parameters['uri'], $parameters['route']);
  77. $parameters['metadata'] = $parameters['metadata'] ?? new Metadata([]);
  78. $parameters['responses'] = $parameters['responses'] ?? new ResponseCollection([]);
  79. parent::__construct($parameters);
  80. }
  81. public static function fromRoute(Route $route, array $extras = []): self
  82. {
  83. $methods = self::getMethods($route);
  84. $uri = $route->uri();
  85. [$controllerName, $methodName] = u::getRouteClassAndMethodNames($route);
  86. $controller = new ReflectionClass($controllerName);
  87. $method = u::getReflectedRouteMethod([$controllerName, $methodName]);
  88. $data = compact('methods', 'uri', 'controller', 'method', 'route');
  89. $data = array_merge($data, $extras);
  90. return new ExtractedEndpointData($data);
  91. }
  92. /**
  93. * @param Route $route
  94. *
  95. * @return array<string>
  96. */
  97. public static function getMethods(Route $route): array
  98. {
  99. $methods = $route->methods();
  100. // Laravel adds an automatic "HEAD" endpoint for each GET request, so we'll strip that out,
  101. // but not if there's only one method (means it was intentional)
  102. if (count($methods) === 1) {
  103. return $methods;
  104. }
  105. return array_diff($methods, ['HEAD']);
  106. }
  107. public function name()
  108. {
  109. return sprintf("[%s] {$this->route->uri}.", implode(',', $this->route->methods));
  110. }
  111. public function endpointId()
  112. {
  113. return $this->methods[0] . str_replace(['/', '?', '{', '}', ':'], '-', $this->uri);
  114. }
  115. public function normalizeResourceParamName(string $uri, Route $route): string
  116. {
  117. $params = [];
  118. preg_match_all('#\{(\w+?)}#', $uri, $params);
  119. $foundResourceParam = false;
  120. foreach ($params[1] as $param) {
  121. $pluralParam = Str::plural($param);
  122. $resourceRouteNames = ["$pluralParam.show", "$pluralParam.update", "$pluralParam.destroy"];
  123. if (Str::contains($route->action['as'] ?? '', $resourceRouteNames)) {
  124. $search = sprintf("%s/{%s}", $pluralParam, $param);
  125. if (!$foundResourceParam) {
  126. // Only the first resource param should be {id}
  127. $replace = "$pluralParam/{id}";
  128. $foundResourceParam = true;
  129. } else {
  130. // Subsequent ones should be {<param>_id}
  131. $replace = sprintf("%s/{%s}", $pluralParam, $param.'_id');
  132. }
  133. $uri = str_replace($search, $replace, $uri);
  134. }
  135. }
  136. return $uri;
  137. }
  138. /**
  139. * Prepare the endpoint data for serialising.
  140. */
  141. public function forSerialisation()
  142. {
  143. $this->metadata = $this->metadata->except('groupName', 'groupDescription');
  144. return $this->except(
  145. // Get rid of all duplicate data
  146. 'cleanQueryParameters', 'cleanUrlParameters', 'fileParameters', 'cleanBodyParameters',
  147. // and objects used only in extraction
  148. 'route', 'controller', 'method', 'auth',
  149. );
  150. }
  151. }