Skip to content

Commit

Permalink
Allow ignorePaths to take function or regexp (#459)
Browse files Browse the repository at this point in the history
  • Loading branch information
rgaino committed Nov 15, 2020
1 parent 6a88a9c commit 9b855d1
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 9 deletions.
10 changes: 7 additions & 3 deletions README.md
Expand Up @@ -717,13 +717,17 @@ module.exports = {

### 鈻笍 ignorePaths (optional)

Defines a regular expression that determines whether a path(s) should be ignored. Any path that matches the regular expression will be ignored by the validator.

The following ignores any path that ends in `/pets` e.g. `/v1/pets`
Defines a regular expression or function that determines whether a path(s) should be ignored. If it's a regular expression, any path that matches the regular expression will be ignored by the validator. If it's a function, it will ignore any paths that returns a truthy value.

The following ignores any path that ends in `/pets` e.g. `/v1/pets`.
As a regular expression:
```
ignorePaths: /.*\/pets$/
```
or as a function:
```
ignorePaths: (path) => path.endsWith('/pets')
```

### 鈻笍 fileUploader (optional)

Expand Down
7 changes: 4 additions & 3 deletions src/framework/openapi.context.ts
@@ -1,5 +1,6 @@
import { OpenAPIV3 } from './types';
import { Spec, RouteMetadata } from './openapi.spec.loader';
import { Console } from 'console';


export interface RoutePair {
Expand All @@ -12,9 +13,9 @@ export class OpenApiContext {
public readonly openApiRouteMap = {};
public readonly routes: RouteMetadata[] = [];
private readonly basePaths: string[];
private readonly ignorePaths: RegExp;
private readonly ignorePaths: RegExp | Function;

constructor(spec: Spec, ignorePaths: RegExp) {
constructor(spec: Spec, ignorePaths: RegExp | Function) {
this.apiDoc = spec.apiDoc;
this.basePaths = spec.basePaths;
this.routes = spec.routes;
Expand All @@ -32,7 +33,7 @@ export class OpenApiContext {
}

public shouldIgnoreRoute(path: string) {
return this.ignorePaths?.test(path);
return typeof this.ignorePaths === 'function' ? this.ignorePaths(path) : this.ignorePaths?.test(path);
}

public routePair(route: string): RoutePair {
Expand Down
2 changes: 1 addition & 1 deletion src/framework/types.ts
Expand Up @@ -69,7 +69,7 @@ export interface OpenApiValidatorOpts {
validateResponses?: boolean | ValidateResponseOpts;
validateRequests?: boolean | ValidateRequestOpts;
validateSecurity?: boolean | ValidateSecurityOpts;
ignorePaths?: RegExp;
ignorePaths?: RegExp | Function;
securityHandlers?: SecurityHandlers;
coerceTypes?: boolean | 'array';
unknownFormats?: true | string[] | 'ignore';
Expand Down
103 changes: 101 additions & 2 deletions test/ignore.paths.spec.ts
Expand Up @@ -2,9 +2,8 @@ import * as path from 'path';
import { expect } from 'chai';
import * as request from 'supertest';
import { createApp } from './common/app';
import * as packageJson from '../package.json';

describe(packageJson.name, () => {
describe('ignorePaths as RegExp', () => {
let app = null;
let basePath = null;

Expand Down Expand Up @@ -105,3 +104,103 @@ describe(packageJson.name, () => {
}));
});
});

describe('ignorePaths as Function', () => {
let app = null;
let basePath = null;

before(async () => {
const apiSpec = path.join('test', 'resources', 'ignore.paths.yaml');

app = await createApp(
{ apiSpec, ignorePaths: (path) => path.endsWith('/hippies') },
3005,
app => {
app.all('/v1/hippies', (req, res) => {
res.json([
{ id: 1, name: 'farah' },
{ id: 2, name: 'fred' },
]);
});
app.get('/v1/hippies/1', (req, res) => {
res.json({ id: 1, name: 'farah' });
});
app.get('/v1/pets/1', (req, res) => {
res.json({ id: 1, name: 'sparky' });
});
},
);
basePath = app.basePath;
});

after(() => app.server.close());

it('should ignore path and return 200, rather than validate', async () =>
request(app)
.get(`${basePath}/hippies`)
.query({
test: 'one',
limit: 2,
})
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200));

it('should ignore path and return 200, rather than validate', async () =>
request(app)
.post(`${basePath}/hippies?test`)
.query({
test: 'one',
limit: 2,
})
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200));

it('should not ignore path and return 404', async () =>
request(app)
.get(`${basePath}/hippies/1`)
.query({
test: 'one',
limit: 2,
})
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(404));

describe(`GET ${basePath}/pets/:id`, () => {
it('should validate a path within the base path that is not ignored', async () => {
const id = 'my_id';
return request(app)
.get(`${basePath}/pets/${id}`)
.expect(400)
.then(r => {
const e = r.body.errors;
expect(e[0].path).contains('id');
expect(e[0].message).equals('should be integer');
});
});

it('should validate a route defined in openapi but not express with invalid params', async () =>
request(app)
.get(`${basePath}/route_defined_in_openapi_only`)
.expect(400)
.then(r => {
const e = r.body.errors;
expect(e[0].message).to.equal("should have required property 'id'");
}));

it('should return 404 if route is defined in openapi but not express and params are valid', async () =>
request(app)
.get(`${basePath}/route_defined_in_openapi_only`)
.query({ id: 123 })
.expect(404)
.then(r => {
const e = r.body;
console.log(e)
// There is no route defined by express, hence the validator verifies parameters,
// then it fails over to the express error handler. In this case returns empty
expect(e).to.be.empty;
}));
});
});

0 comments on commit 9b855d1

Please sign in to comment.