|
@@ -4,9 +4,11 @@ namespace Knuckles\Camel\Output;
|
|
|
|
|
|
use Illuminate\Http\UploadedFile;
|
|
|
use Illuminate\Routing\Route;
|
|
|
+use Illuminate\Support\Arr;
|
|
|
use Illuminate\Support\Str;
|
|
|
use Knuckles\Camel\BaseDTO;
|
|
|
use Knuckles\Camel\Extraction\Metadata;
|
|
|
+use Knuckles\Camel\Extraction\Parameter;
|
|
|
use Knuckles\Camel\Extraction\ResponseCollection;
|
|
|
use Knuckles\Camel\Extraction\ResponseField;
|
|
|
use Knuckles\Scribe\Extracting\Extractor;
|
|
@@ -105,8 +107,8 @@ class OutputEndpointData extends BaseDTO
|
|
|
$this->cleanBodyParameters = Extractor::cleanParams($this->bodyParameters);
|
|
|
$this->cleanQueryParameters = Extractor::cleanParams($this->queryParameters);
|
|
|
$this->cleanUrlParameters = Extractor::cleanParams($this->urlParameters);
|
|
|
- $this->nestedBodyParameters = Extractor::nestArrayAndObjectFields($this->bodyParameters, $this->cleanBodyParameters);
|
|
|
- $this->nestedResponseFields = Extractor::nestArrayAndObjectFields($this->responseFields);
|
|
|
+ $this->nestedBodyParameters = self::nestArrayAndObjectFields($this->bodyParameters, $this->cleanBodyParameters);
|
|
|
+ $this->nestedResponseFields = self::nestArrayAndObjectFields($this->responseFields);
|
|
|
|
|
|
$this->boundUri = u::getUrlWithBoundParameters($this->uri, $this->cleanUrlParameters);
|
|
|
|
|
@@ -142,6 +144,133 @@ class OutputEndpointData extends BaseDTO
|
|
|
return new self($endpoint);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Transform body parameters such that object fields have a `fields` property containing a list of all subfields
|
|
|
+ * Subfields will be removed from the main parameter map
|
|
|
+ * For instance, if $parameters is [
|
|
|
+ * 'dad' => new Parameter(...),
|
|
|
+ * 'dad.age' => new Parameter(...),
|
|
|
+ * 'dad.cars[]' => new Parameter(...),
|
|
|
+ * 'dad.cars[].model' => new Parameter(...),
|
|
|
+ * 'dad.cars[].price' => new Parameter(...),
|
|
|
+ * ],
|
|
|
+ * normalise this into [
|
|
|
+ * 'dad' => [
|
|
|
+ * ...,
|
|
|
+ * '__fields' => [
|
|
|
+ * 'dad.age' => [...],
|
|
|
+ * 'dad.cars' => [
|
|
|
+ * ...,
|
|
|
+ * '__fields' => [
|
|
|
+ * 'model' => [...],
|
|
|
+ * 'price' => [...],
|
|
|
+ * ],
|
|
|
+ * ],
|
|
|
+ * ],
|
|
|
+ * ]]
|
|
|
+ *
|
|
|
+ * @param array $parameters
|
|
|
+ *
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function nestArrayAndObjectFields(array $parameters, array $cleanParameters = []): array
|
|
|
+ {
|
|
|
+ // First, we'll make sure all object fields have parent fields properly set
|
|
|
+ $normalisedParameters = [];
|
|
|
+ foreach ($parameters as $name => $parameter) {
|
|
|
+ if (Str::contains($name, '.')) {
|
|
|
+ // If the user didn't add a parent field, we'll helpfully add it for them
|
|
|
+ $ancestors = [];
|
|
|
+
|
|
|
+ $parts = explode('.', $name);
|
|
|
+ $fieldName = array_pop($parts);
|
|
|
+ $parentName = rtrim(join('.', $parts), '[]');
|
|
|
+
|
|
|
+ // When the body is an array, param names will be "[].paramname",
|
|
|
+ // so $parentName is empty. Let's fix that.
|
|
|
+ if (empty($parentName)) {
|
|
|
+ $parentName = '[]';
|
|
|
+ }
|
|
|
+
|
|
|
+ while ($parentName) {
|
|
|
+ if (!empty($normalisedParameters[$parentName])) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ $details = [
|
|
|
+ "name" => $parentName,
|
|
|
+ "type" => $parentName === '[]' ? "object[]" : "object",
|
|
|
+ "description" => "",
|
|
|
+ "required" => false,
|
|
|
+ ];
|
|
|
+
|
|
|
+ if ($parameter instanceof ResponseField) {
|
|
|
+ $ancestors[] = [$parentName, new ResponseField($details)];
|
|
|
+ } else {
|
|
|
+ $lastParentExample = $details["example"] =
|
|
|
+ [$fieldName => $lastParentExample ?? $parameter->example];
|
|
|
+ $ancestors[] = [$parentName, new Parameter($details)];
|
|
|
+ }
|
|
|
+
|
|
|
+ $fieldName = array_pop($parts);
|
|
|
+ $parentName = rtrim(join('.', $parts), '[]');
|
|
|
+ }
|
|
|
+
|
|
|
+ // We add ancestors in reverse so we can iterate over parents first in the next section
|
|
|
+ foreach (array_reverse($ancestors) as [$ancestorName, $ancestor]) {
|
|
|
+ $normalisedParameters[$ancestorName] = $ancestor;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $normalisedParameters[$name] = $parameter;
|
|
|
+ unset($lastParentExample);
|
|
|
+ }
|
|
|
+
|
|
|
+ $finalParameters = [];
|
|
|
+ foreach ($normalisedParameters as $name => $parameter) {
|
|
|
+ $parameter = $parameter->toArray();
|
|
|
+ if (Str::contains($name, '.')) { // An object field
|
|
|
+ // Get the various pieces of the name
|
|
|
+ $parts = explode('.', $name);
|
|
|
+ $fieldName = array_pop($parts);
|
|
|
+ $baseName = join('.__fields.', $parts);
|
|
|
+
|
|
|
+ // For subfields, the type is indicated in the source object
|
|
|
+ // eg test.items[].more and test.items.more would both have parent field with name `items` and containing __fields => more
|
|
|
+ // The difference would be in the parent field's `type` property (object[] vs object)
|
|
|
+ // So we can get rid of all [] to get the parent name
|
|
|
+ $dotPathToParent = str_replace('[]', '', $baseName);
|
|
|
+ // When the body is an array, param names will be "[].paramname",
|
|
|
+ // so $parts is ['[]']
|
|
|
+ if ($parts[0] == '[]') {
|
|
|
+ $dotPathToParent = '[]' . $dotPathToParent;
|
|
|
+ }
|
|
|
+
|
|
|
+ $dotPath = $dotPathToParent . '.__fields.' . $fieldName;
|
|
|
+ Arr::set($finalParameters, $dotPath, $parameter);
|
|
|
+ } else { // A regular field, not a subfield of anything
|
|
|
+ // Note: we're assuming any subfields of this field are listed *after* it,
|
|
|
+ // and will set __fields correctly when we iterate over them
|
|
|
+ // Hence why we create a new "normalisedParameters" array above and push the parent to that first
|
|
|
+ $parameter['__fields'] = [];
|
|
|
+ $finalParameters[$name] = $parameter;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // Finally, if the body is an array, remove any other items.
|
|
|
+ if (isset($finalParameters['[]'])) {
|
|
|
+ $finalParameters = ["[]" => $finalParameters['[]']];
|
|
|
+ // At this point, the examples are likely [[], []],
|
|
|
+ // but have been correctly set in clean parameters, so let's update them
|
|
|
+ if ($finalParameters["[]"]["example"][0] == [] && !empty($cleanParameters)) {
|
|
|
+ $finalParameters["[]"]["example"] = $cleanParameters;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return $finalParameters;
|
|
|
+ }
|
|
|
+
|
|
|
public function endpointId(): string
|
|
|
{
|
|
|
return $this->httpMethods[0] . str_replace(['/', '?', '{', '}', ':', '\\', '+', '|', '.'], '-', $this->uri);
|