Skip to content

Commit

Permalink
Fix Mutation of API Spec (#537)
Browse files Browse the repository at this point in the history
* test: Add Failing Test Case for Issue 535.

Add failing test case for
#535.

The problem manifests itself when calling `middleware()` with an object
multiple times, as the object itself is mutated which might cause errors
on subsequent calls.

In this specific example, the parameters for GET /ping/{value} are
modified in such a way that a second call will cause a validation error.
This happens in `preprocessPathLevelParameters`.

* fix: Clone API Spec to avoid Mutation of Parameter

Clone the API specification passed to the `middleware()` function. This
avoids mutating the `apiSpec` parameter passed to that function, which
can lead to unexpected behaviour.

Fixes #535.
  • Loading branch information
ahilke committed Feb 14, 2021
1 parent 29f5de8 commit 2866ce6
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/index.ts
@@ -1,3 +1,4 @@
import * as cloneDeep from 'lodash.clonedeep';
import * as res from './resolvers';
import { OpenApiValidator, OpenApiValidatorOpts } from './openapi.validator';
import { OpenApiSpecLoader } from './framework/openapi.spec.loader';
Expand Down Expand Up @@ -34,7 +35,7 @@ function openapiValidator(options: OpenApiValidatorOpts) {

return oav.installMiddleware(
new OpenApiSpecLoader({
apiDoc: options.apiSpec,
apiDoc: cloneDeep(options.apiSpec),
validateApiSpec: options.validateApiSpec,
$refParser: options.$refParser,
}).load(),
Expand Down
67 changes: 67 additions & 0 deletions test/535.spec.ts
@@ -0,0 +1,67 @@
import * as express from 'express';
import { Server } from 'http';
import * as request from 'supertest';
import * as OpenApiValidator from '../src';
import { OpenAPIV3 } from '../src/framework/types';
import { startServer } from './common/app.common';
import { deepStrictEqual } from 'assert';

describe('#535 - calling `middleware()` multiple times', () => {
it('does not mutate the API specification', async () => {
const apiSpec = createApiSpec();

const app = await createApp(apiSpec);
await request(app).get('/ping/GNU Sir Terry').expect(200, 'GNU Sir Terry');
app.server.close();

deepStrictEqual(apiSpec, createApiSpec());
});
});

async function createApp(
apiSpec: OpenAPIV3.Document,
): Promise<express.Express & { server?: Server }> {
const app = express();

app.use(
OpenApiValidator.middleware({
apiSpec,
validateRequests: true,
}),
);
app.use(
express.Router().get('/ping/:value', (req, res) => {
res.status(200).send(req.params.value);
}),
);

await startServer(app, 3001);
return app;
}

function createApiSpec(): OpenAPIV3.Document {
return {
openapi: '3.0.3',
info: {
title: 'Ping API',
version: '1.0.0',
},
paths: {
'/ping/{value}': {
parameters: [
{
in: 'path',
name: 'value',
required: true,
schema: { type: 'string' },
},
],
get: {
responses: {
'200': { description: 'pong!' },
},
},
},
},
};
}

0 comments on commit 2866ce6

Please sign in to comment.