Repository: cdimascio/express-openapi-validator (ts)

Issue:
coerceTypes "array" is not working for query params
**Describe the bug**
From my understanding of the documentation, the coerceTypes option is set to 'true' by default. However, I am unable to modify this setting to other options, such as 'array'.

**To Reproduce**
- Create a basic openapi document with an object param that nests an array param 
```json
{
  "paths": {
    "/api/route": {
      "get": {
        "parameters": [
          {
            "in": "query",
            "name": "property",
            "schema": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "values": { "type": "array", "items": { "type": "string" } }
              }
            }
          }
        ]
      }
    }
  }
}
```
- Configure coerceTypes to `array` 
- perform a a request like `http://localhost/api/route?property[values]=value1`


**Actual behavior** 
```json
{
 "errors": [
  {
   "path": "/query/property/values",
   "message": "must be array",
   "errorCode": "type.openapi.validation"
  }
 ]
}
```

**Expected behavior**
Pass validation and coerce the values properly to an array, as per documentation https://ajv.js.org/coercion.html

**Examples and context**
Not sure why but on a simpler case this is working and the value apparently is properly coerced to an array
`http://localhost/api/route?property=value1`
```json
{
  "paths": {
    "/api/route": {
      "get": {
        "parameters": [
         {
            "in": "query",
            "name": "property",
            "schema": { "type": "array", "items": { "type": "string" } }
          }
        ]
      }
    }
  }
}
```



Pull request description:
Uses CoerceTypes option passed.
fixes #808

Patch:
diff --git a/src/middlewares/openapi.request.validator.ts b/src/middlewares/openapi.request.validator.ts
index 3bd2065..96f8d9b 100644
--- a/src/middlewares/openapi.request.validator.ts
+++ b/src/middlewares/openapi.request.validator.ts
@@ -46,7 +46,14 @@ export class RequestValidator {
     delete this.apiDoc.components?.examples;
     this.requestOpts.allowUnknownQueryParameters =
       options.allowUnknownQueryParameters;
-    this.ajv = createRequestAjv(apiDoc, { ...options, coerceTypes: true });
+
+    this.ajv = createRequestAjv(
+      apiDoc,
+      // This should always be true as it handles query params (everything, but the body)
+      // This should always be coerced. Note that coerceTypes = 'array` also operates as true
+      // but also coerces 'array' types
+      !options.coerceTypes ? { ...options, coerceTypes: true } : options,
+    );
     this.ajvBody = createRequestAjv(apiDoc, options);
   }
 
@@ -220,6 +227,7 @@ export class RequestValidator {
         }
       }
     });
+    return null;
   }
 
   private discriminatorValidator(req, discriminator) {


Test patch:
diff --git a/test/coercion.spec.ts b/test/coercion.spec.ts
index 053ac6f..7dda3a0 100644
--- a/test/coercion.spec.ts
+++ b/test/coercion.spec.ts
@@ -7,23 +7,34 @@ import * as packageJson from '../package.json';
 
 describe(packageJson.name, () => {
   let app = null;
+  let arrayCoercedApp = null;
 
   before(async () => {
     // Set up the express app
     const apiSpec = path.join('test', 'resources', 'coercion.yaml');
+    const routes = express
+      .Router()
+      .post(`/pets`, (req, res) => res.json(req.body))
+      .post(`/pets_string_boolean`, (req, res) => res.json(req.body))
+      .get(`/pets_as_array_parameter`, (req, res) => res.json(req.query));
+
     app = await createApp({ apiSpec }, 3005, (app) =>
-      app.use(
-        `${app.basePath}/coercion`,
-        express
-          .Router()
-          .post(`/pets`, (req, res) => res.json(req.body))
-          .post(`/pets_string_boolean`, (req, res) => res.json(req.body)),
-      ),
+      app.use(`${app.basePath}/coercion`, routes),
+    );
+    arrayCoercedApp = await createApp(
+      { apiSpec, validateRequests: { coerceTypes: 'array' } },
+      3006,
+      (appWithCoerceTypes) =>
+        appWithCoerceTypes.use(
+          `${appWithCoerceTypes.basePath}/coercion`,
+          routes,
+        ),
     );
   });
 
   after(() => {
     app.server.close();
+    arrayCoercedApp.server.close();
   });
 
   it('should return 400 since is_cat is passed as string not boolean', async () =>
@@ -35,7 +46,9 @@ describe(packageJson.name, () => {
       })
       .expect(400)
       .then((r) => {
-        expect(r.body.message).to.contain('request/body/is_cat must be boolean');
+        expect(r.body.message).to.contain(
+          'request/body/is_cat must be boolean',
+        );
       }));
 
   it('should return 400 when age is passed as string, but number is expected', async () =>
@@ -102,4 +115,15 @@ describe(packageJson.name, () => {
       .then((r) => {
         expect(r.body.message).to.contain('request/body/is_cat must be string');
       }));
+
+  it('should return 200 when names is a string and coerce names to be an array', async () =>
+    request(arrayCoercedApp)
+      .get(`${arrayCoercedApp.basePath}/coercion/pets_as_array_parameter`)
+      .query({
+        filter: { names: 'test' },
+      })
+      .expect(200)
+      .then((r) => {
+        expect(r.text).to.equal('{"filter":{"names":["test"]}}');
+      }));
 });
diff --git a/test/resources/coercion.yaml b/test/resources/coercion.yaml
index 66b1ae0..5e9344f 100644
--- a/test/resources/coercion.yaml
+++ b/test/resources/coercion.yaml
@@ -46,6 +46,30 @@ paths:
               schema:
                 $ref: '#/components/schemas/PetStringBoolean'
 
+  /coercion/pets_as_array_parameter:
+    get:
+      description: Returns pets by name
+      operationId: addPet
+      parameters:
+      - in: query
+        name: filter
+        schema:
+          type: object
+          additionalProperties: false
+          properties:
+            names:
+              type: array
+              items:
+                type: string
+      responses:
+        '200':
+          description: pet response
+          content:
+            application/json:
+              schema:
+                type: array
+                items: { $ref: '#/components/schemas/Pet' }
+
 components:
   schemas:
     Pet: