I create HelloController, it can get request parameters by array query strings
/hello_array_parse/hello_comma_parsestyle: 'form', explode: falseexport class HelloController {
constructor() {}
@get("/hello_array_parse", {
responses: {
"200": HELLO_RESPONSE,
},
})
async helloArrayParse(
@param.array("numberArray", "query", {
description: "number array",
items: {
type: "number",
pattern: "[0-9]{4}",
},
}) numberArray: number[],
@param.array("stringArray", "query", {
description: "string array",
items: {
type: "string",
pattern: "[a-zA-Z]{4}",
},
}) stringArray: string[],
): Promise<object> {
return {
numberArray: numberArray,
stringArray: stringArray,
};
}
@get("/hello_comma_array", {
responses: {
"200": HELLO_RESPONSE,
},
})
async helloCommaArray(
// ref. https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#style-values
@param({
name: "numberArray",
in: "query",
description: "number array",
schema: {
type: "array",
items: {
type: "number",
pattern: "[0-9]{4}",
},
},
style: "form",
explode: false,
}) numberArray: number[],
@param({
name: "stringArray",
in: "query",
description: "string array",
schema: {
type: "array",
items: {
type: "string",
pattern: "[a-zA-Z]{4}",
},
},
style: "form",
explode: false,
}) stringArray: string[],
): Promise<object> {
return {
numberArray: numberArray,
stringArray: stringArray,
};
}
}
In detail: https://github.com/shamisonn/reproduce-lb4-bug-oas-array
request for /hello_array_parse
http://[::1]:3000/hello_array_parse?numberArray=12345&numberArray=123&stringArray=abcde&stringArray=abc
response
{
"numberArray": [
"12345",
"123"
],
"stringArray": [
"abcde",
"abc"
]
}
request for /hello_comma_parse
http://[::1]:3000/hello_comma_array?numberArray=12345,678&stringArray=abcde,fgh
response
{
"numberArray": "12345,678",
"stringArray": "abcde,fgh"
}
both of case1 and case2 are same expected like below, if pattern option is nothing.
I expected numberArray is parsed type of array[number].
{
"numberArray": [
12345,
123
],
"stringArray": [
"abcde",
"abc"
]
}
if pattern is there, expected error.
In guideline, Modify the selected example project. but I wrote by orginal.
if you ok, check my original example.
$ node -e 'console.log(process.platform, process.arch, process.versions.node)'
darwin x64 14.3.0
$ npm ls --prod --depth 0 | grep loopback
├── @loopback/[email protected]
├── @loopback/[email protected]
├── @loopback/[email protected]
├── @loopback/[email protected]
├── @loopback/[email protected]
├── @loopback/[email protected]
├── @loopback/[email protected]
I don't have one myself, but I was hoping we could implement it here: https://github.com/strongloop/loopback-next/blob/master/packages/rest/src/coercion/coerce-parameter.ts#L58-L81
like this:
...
case 'array':
return coerceArray(data, spec);
I can't find. if duplicate , close this.
Thank you for reading!
_See Reporting Issues for more tips on writing good issues_
Hi @shamisonn, thanks for the detailed issue! There seems to be 3 issues here:
@param.array() ignores the TypeScript type and OAS3 type: 'number'. This is unexpected behaviour.
I've managed to replicate the issue from my end with the sandbox.
@param.array() will automatically transpose the object into items. So there's no need to use items unless if you're creating a nested array.
For example, the generated OAS3 spec from the sandbox is:
...
"parameters": [
{
"name": "numberArray",
"in": "query",
"schema": {
"type": "array",
"items": {
"description": "number array",
"items": {
"type": "number",
"pattern": "[0-9]{4}"
}
}
}
},
{
"name": "stringArray",
"in": "query",
"schema": {
"type": "array",
"items": {
"description": "string array",
"items": {
"type": "string",
"pattern": "[a-zA-Z]{4}"
}
}
}
}
],
...
Notice the nested items.items. Frankly, I'm surprised that this considered valid OAS3 spec, and that the API Explorer accepts it. It may be an area we might also want to investigate.
To resolve, we can remove the items object:
@param.array('numberArray', 'query', {
description: 'number array',
- items: {
type: 'number',
pattern: '[0-9]{4}',
- },
})
numberArray: number[],
@param.array('stringArray', 'query', {
description: 'string array',
- items: {
type: 'string',
pattern: '[a-zA-Z]{4}',
- },
})
stringArray: string[],
This would result in the following OAS3 spec:
...
"parameters": [
{
"name": "numberArray",
"in": "query",
"schema": {
"type": "array",
"items": {
"description": "number array",
"type": "number",
"pattern": "[0-9]{4}"
}
}
},
{
"name": "stringArray",
"in": "query",
"schema": {
"type": "array",
"items": {
"description": "string array",
"type": "string",
"pattern": "[a-zA-Z]{4}"
}
}
}
],
...
Notice that there's no more nested items.items key.
This, however, does not resolve the first issue.
This also reveals a third issue:
itemsdescription (along with some other properties) should not be placed inside of items. I'm also surprised that the API explorer accepts this as valid OAS3 spec.
This ties in with #4645,
@achrinza
thank you for early reply!
@param.array() will automatically transpose the object into items.
...
description (along with some other properties) should not be placed inside of items
oh, I'll fix the sandbox. thank you for details.
(edited at 2020/06/20) fixed sandbox => https://github.com/shamisonn/reproduce-lb4-bug-oas-array/commit/d7bd2bdeb0746be4ebeea8bafc86667a002edf76
Thank you @achrinza and @shamisonn Good catch. This is loosely related to the story I am fixing in https://github.com/strongloop/loopback-next/issues/4992
Let me summarize the bug here. I tried with Explorer inputs, not working either.
Parsing array value from argument decorated by @param.array() has bug, it doesn't respect the item type specified for each item.
Given controller method
async helloArrayParse(
@param.array("numberArray", "query", {
type: "number",
pattern: "[0-9]{4}",
}) numberArray: number[],
@param.array("stringArray", "query", {
type: "string",
pattern: "[a-zA-Z]{4}",
}) stringArray: string[],
): Promise<object> {
return {
numberArray: numberArray,
stringArray: stringArray,
};
}
Provide some number inputs for the first array param

The returned data is not coerced to number, but a string instead.

{
"numberArray": [
// DIFFERENCE: The values should be numbers instead of strings.
12345,
123
],
"stringArray": [
"12345",
"123"
]
}
There is another issue I noticed that if you only pass one value e.g. ?numberArray=123 then it throws the error with the code INVALID_PARAMETER_VALUE and in details with the message should be array, however ?numberArray=123&numberArray=12345 works just fine.
I also noticed that ?numberArray[]=123 works but thats different from how it used to work and also is not how the api explorer sends the values.
Seems like there is a general issue with the @param.array with parameter location query
There is another issue I noticed that if you only pass one value e.g.
?numberArray=123then it throws the error with the codeINVALID_PARAMETER_VALUEand in details with the messageshould be array, however?numberArray=123&numberArray=12345works just fine.I also noticed that
?numberArray[]=123works but thats different from how it used to work and also is not how the api explorer sends the values.Seems like there is a general issue with the
@param.arraywith parameter locationquery
Also notice the same issue as you mention.
There is another issue I noticed that if you only pass one value e.g.
?numberArray=123then it throws the error with the codeINVALID_PARAMETER_VALUEand in details with the messageshould be array, however?numberArray=123&numberArray=12345works just fine.I also noticed that
?numberArray[]=123works but thats different from how it used to work and also is not how the api explorer sends the values.Seems like there is a general issue with the
@param.arraywith parameter locationquery
This one is resolved. I tried to reproduce the issue above and am not getting it. Will have a look again
Most helpful comment
There is another issue I noticed that if you only pass one value e.g.
?numberArray=123then it throws the error with the codeINVALID_PARAMETER_VALUEand in details with the messageshould be array, however?numberArray=123&numberArray=12345works just fine.I also noticed that
?numberArray[]=123works but thats different from how it used to work and also is not how the api explorer sends the values.Seems like there is a general issue with the
@param.arraywith parameter locationquery