OutputEndpointData.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. <?php
  2. namespace Knuckles\Camel\Output;
  3. use Illuminate\Http\UploadedFile;
  4. use Illuminate\Routing\Route;
  5. use Illuminate\Support\Str;
  6. use Knuckles\Camel\BaseDTO;
  7. use Knuckles\Camel\Extraction\Metadata;
  8. use Knuckles\Camel\Extraction\ResponseCollection;
  9. use Knuckles\Camel\Extraction\ResponseField;
  10. use Knuckles\Scribe\Extracting\Extractor;
  11. use Knuckles\Scribe\Tools\Utils as u;
  12. /**
  13. * Endpoint DTO, optimized for generating HTML output.
  14. * Unneeded properties removed, extra properties and helper methods added.
  15. */
  16. class OutputEndpointData extends BaseDTO
  17. {
  18. /**
  19. * @var array<string>
  20. */
  21. public array $httpMethods;
  22. public string $uri;
  23. public Metadata $metadata;
  24. /**
  25. * @var array<string,string>
  26. */
  27. public array $headers = [];
  28. /**
  29. * @var array<string,\Knuckles\Camel\Output\Parameter>
  30. */
  31. public array $urlParameters = [];
  32. /**
  33. * @var array<string,mixed>
  34. */
  35. public array $cleanUrlParameters = [];
  36. /**
  37. * @var array<string,\Knuckles\Camel\Output\Parameter>
  38. */
  39. public array $queryParameters = [];
  40. /**
  41. * @var array<string,mixed>
  42. */
  43. public array $cleanQueryParameters = [];
  44. /**
  45. * @var array<string, \Knuckles\Camel\Output\Parameter>
  46. */
  47. public array $bodyParameters = [];
  48. /**
  49. * @var array<string,mixed>
  50. */
  51. public array $cleanBodyParameters = [];
  52. /**
  53. * @var array<string,\Illuminate\Http\UploadedFile>
  54. */
  55. public array $fileParameters = [];
  56. public ResponseCollection $responses;
  57. /**
  58. * @var array<string,\Knuckles\Camel\Extraction\ResponseField>
  59. */
  60. public array $responseFields = [];
  61. /**
  62. * The same as bodyParameters, but organized in a hierarchy.
  63. * So, top-level items first, with a __fields property containing their children, and so on.
  64. * Useful so we can easily render and nest on the frontend.
  65. * @var array<string, array>
  66. */
  67. public array $nestedBodyParameters = [];
  68. /**
  69. * @var array<string, array>
  70. */
  71. public array $nestedResponseFields = [];
  72. public ?string $boundUri;
  73. public function __construct(array $parameters = [])
  74. {
  75. // spatie/dto currently doesn't auto-cast nested DTOs like that
  76. $parameters['responses'] = new ResponseCollection($parameters['responses'] ?? []);
  77. $parameters['bodyParameters'] = array_map(fn($param) => new Parameter($param), $parameters['bodyParameters'] ?? []);
  78. $parameters['queryParameters'] = array_map(fn($param) => new Parameter($param), $parameters['queryParameters'] ?? []);
  79. $parameters['urlParameters'] = array_map(fn($param) => new Parameter($param), $parameters['urlParameters'] ?? []);
  80. $parameters['responseFields'] = array_map(fn($param) => new ResponseField($param), $parameters['responseFields'] ?? []);
  81. parent::__construct($parameters);
  82. $this->cleanBodyParameters = Extractor::cleanParams($this->bodyParameters);
  83. $this->cleanQueryParameters = Extractor::cleanParams($this->queryParameters);
  84. $this->cleanUrlParameters = Extractor::cleanParams($this->urlParameters);
  85. $this->nestedBodyParameters = Extractor::nestArrayAndObjectFields($this->bodyParameters, $this->cleanBodyParameters);
  86. $this->nestedResponseFields = Extractor::nestArrayAndObjectFields($this->responseFields);
  87. $this->boundUri = u::getUrlWithBoundParameters($this->uri, $this->cleanUrlParameters);
  88. [$files, $regularParameters] = static::getFileParameters($this->cleanBodyParameters);
  89. if (count($files)) {
  90. $this->headers['Content-Type'] = 'multipart/form-data';
  91. }
  92. $this->fileParameters = $files;
  93. $this->cleanBodyParameters = $regularParameters;
  94. }
  95. /**
  96. * @param Route $route
  97. *
  98. * @return array<string>
  99. */
  100. public static function getMethods(Route $route): array
  101. {
  102. $methods = $route->methods();
  103. // Laravel adds an automatic "HEAD" endpoint for each GET request, so we'll strip that out,
  104. // but not if there's only one method (means it was intentional)
  105. if (count($methods) === 1) {
  106. return $methods;
  107. }
  108. return array_diff($methods, ['HEAD']);
  109. }
  110. public static function fromExtractedEndpointArray(array $endpoint): OutputEndpointData
  111. {
  112. return new self($endpoint);
  113. }
  114. public function endpointId(): string
  115. {
  116. return $this->httpMethods[0] . str_replace(['/', '?', '{', '}', ':', '\\', '+', '|'], '-', $this->uri);
  117. }
  118. public function name(): string
  119. {
  120. return $this->metadata->title ?: ($this->httpMethods[0] . " " . $this->uri);
  121. }
  122. public function fullSlug(): string
  123. {
  124. $groupSlug = Str::slug($this->metadata->groupName);
  125. $endpointId = $this->endpointId();
  126. return "$groupSlug-$endpointId";
  127. }
  128. public function hasResponses(): bool
  129. {
  130. return count($this->responses) > 0;
  131. }
  132. public function hasFiles(): bool
  133. {
  134. return count($this->fileParameters) > 0;
  135. }
  136. public function isArrayBody(): bool
  137. {
  138. return count($this->nestedBodyParameters) === 1
  139. && array_keys($this->nestedBodyParameters)[0] === "[]";
  140. }
  141. public function isGet(): bool
  142. {
  143. return in_array('GET', $this->httpMethods);
  144. }
  145. public function hasHeadersOrQueryOrBodyParams(): bool
  146. {
  147. return !empty($this->headers)
  148. || !empty($this->cleanQueryParameters)
  149. || !empty($this->cleanBodyParameters);
  150. }
  151. public static function getFileParameters(array $parameters): array
  152. {
  153. $files = [];
  154. $regularParameters = [];
  155. foreach ($parameters as $name => $example) {
  156. if ($example instanceof UploadedFile) {
  157. $files[$name] = $example;
  158. } else if (is_array($example) && !empty($example)) {
  159. [$subFiles, $subRegulars] = static::getFileParameters($example);
  160. foreach ($subFiles as $subName => $subExample) {
  161. $files[$name][$subName] = $subExample;
  162. }
  163. foreach ($subRegulars as $subName => $subExample) {
  164. $regularParameters[$name][$subName] = $subExample;
  165. }
  166. } else {
  167. $regularParameters[$name] = $example;
  168. }
  169. }
  170. return [$files, $regularParameters];
  171. }
  172. }