Arrays of values are not properly formatted
I'm trying to send an array of strings, the "laravel way", so not in a single query param and then split it, but more like an array of query params, ex
tags[]=a&tags[]=b&tags[]=c etc
Not sure if Scramble supports this out of the box, but from my attempts, I haven't really been able to get it to work, you can find more info below
Environment:
- Laravel 11
- Scramble v0.12.11
Example: Validation rules
public function rules()
{
return [
'first' => 'nullable|integer|min:1|max:100',
'page' => 'nullable|integer|min:1',
'channel' => 'nullable|string|max:16',
/**
* Filter by tag slugs
*
* @example ["french", "norwegian"]
*/
'tags' => 'sometimes|array',
/**
* Filter by status
*
* @example ["live", "past"]
*/
'status' => 'sometimes|array',
];
}
Generated CURL Request
curl --request GET \
--url 'https://api.....local/api/v2/streams?tags=french' \
--header 'Accept: application/json' \
Response, makes sense, since it's not formatted as an array of strings
{
"message": "The tags must be an array.",
"errors": {
"tags": [
"The tags must be an array."
]
}
}
Expected CURL Request
curl --request GET \
--url 'https://api.....local/api/v2/streams?tags[]=french' \
--header 'Accept: application/json' \
Please let me know if there's a better approach to do this or if it's outright unsupported as of now, and if it is, are there's any plans to bring this kind of functionality in the future :)
Thanks!
Hey @krzkz94
This is indeed a problem. Related to https://github.com/dedoc/scramble/issues/650.
I plan to work on it, but I'm still thinking how to approach this. One way is to document array parameter like this:
{
"name": "tags[]",
"in": "query",
"schema": {
"type": "string"
}
}
And the other way is to specify the serialization of tags parameters:
{
"name": "tags",
"in": "query",
"content": {
// ... something is here
}
}
But again, I'm not yet sure about how I want to approach it. Personally it feels like the first option would be simpler. Let me know if you have any preference or if you have any additional context.
@romalytvynenko I think the correct way to document it should be something like this:
{
"name": "tags[]",
"in": "query",
"schema": {
"type": "array",
"items": {"type": "string" }
}
}
There's an example in the swagger spec and documentation on serialization ("style": "form" and "explode":true are the defaults for query parameters)
In the future it should be possible to use arbitrary query strings to support this without explicitly needing [] in the param name, but that won't be available until at least v3.3 of the spec, which is not due until September '25 at the earliest.
Please let me know if you need any help implementing this!
@PaulKiddle
The schema you suggest will be expecting an array of strings for tags[] parameter. Meaning that for tags parameter it will be an array of string arrays (string[][]).
Yes the value for tags[] would be an array, but when it gets serialised to a URL it would be tags[]= repeated multiple times. For example, if the API client call was like this:
client.getStreams({
"tags[]": ["french", "english", "german"]
});
Then the query string would be serialized as ?tags[]=french&tags[]=english&tags[]=german - which is correct for PHP.
If you use "tags[]": { schema: { type: string } } then any client generated from the OpenAPI document wouldn't know that tags[] is allowed to accept multiple values.
Unfortunately it doesn't seem like there's currently a way in OpenAPI to use a parameter called tags (without []) and have it serialized as tags[]. It's possible that some clients would do this if you use style: "deepObject", but it's explicitly not supported for arrays by the standard and there's no guarantee it would be parsable by many consumers.)
@romalytvynenko I'm willing to contribute a fix for this issue, can you point me to the file(s) I should be looking at?
@PaulKiddle I think we can stick to your suggested way with this example. My only concern about it is how Stoplight Elements UI will render this parameter:
{
"name": "tags[]",
"in": "query",
"schema": {
"type": "array",
"items": {"type": "string" }
},
"style": "form",
"explode": true
}
If it will render it as a tags[]: string[] - this may be confusing and this is the reason this issue sits here for so long.
Speaking of files, this issue is complex and all around and requires the refactoring of how Scramble handles query parameters. Here is how it all works:
- For every route RequestBodyExtension kicks in the middleware-like process of extracting parameters.
- Parameters extractors - these boys are responsible for extracting raw parameters from AST or other sources (like attributes) and passing them to the next parameter extractor (they work like middleware).
- After parameters are extracted, they are being added to the operation that is being generated by RequestBodyExtension. This is very nuanced process: dot names of parameters are used to make them "deep" objects; parameters must be added either to the request body or to the parameters list; the adding process must respect schemas (form requests are documented as reusable schema); etc.
So implementing proper complex query parameters documentation require:
- Do not create nested structures but rather a flat parameter name when the parameter is passed as a query (for example, a parameter coming from the following validation rule
['foo.bar' => ['required', 'string']]should be documented asfoo[bar]: string, not as afoo: {bar: string}. - Make sure to handle reusable form requests properly when used as query parameters! I guess it should not create a schema in such cases.
- And everything else that can be affected 😀
I hope it makes sense.
I planned to do something with this this week. So let me know if you are ready to take it on!
Thanks for the details, it definitely seems more complex that I was hoping. I can try to get to grips with it but I think it will take me a while, so if you want to work on it this week be my guest.
@krzkz94 @PaulKiddle The fix is released in v0.12.24: https://github.com/dedoc/scramble/releases/tag/v0.12.24
(But please bump to 0.12.25 as it contains performance fixes)