Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented separated matched data for different locaitons #453

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 19 additions & 4 deletions README.md
Expand Up @@ -180,15 +180,30 @@ These methods are all available via `require('express-validator/filter')`.

### `matchedData(req[, options])`
- `req`: the express request object.
- `options` *(optional)*: an object of options. Defaults to `{ onlyValidData: true }`
- `options` *(optional)*: an object which accepts the following options:
- `onlyValidData`: if set to `false`, the returned value includes data from fields
that didn't pass their validations. Defaults to `true`.
- `locations`: an array of locations to extract the data from. The acceptable values include
`body`, `cookies`, `headers`, `param` and `query`. Defaults to `undefined`, which means all locations.
> *Returns:* an object of data validated by the `check` APIs.

Extracts data validated by the `check` APIs from the request and builds
an object with them. Nested paths and wildcards are properly handled as well.

By default, only valid data is included; this means if a field didn't pass
its validation, it won't be included in the returned object.
You can include invalid data by passing the option `onlyValidData` as `false`.
```js
// Suppose the request looks like this:
// req.query = { from: '2017-01-12' }
// req.body = { to: '2017-31-12' }

app.post('/room-availability', check(['from', 'to']).isISO8601(), (req, res, next) => {
const queryData = matchedData(req, { locations: ['query'] });
const bodyData = matchedData(req, { locations: ['body'] });
const allData = matchedData(req);
console.log(queryData); // { from: '2017-01-12' }
console.log(bodyData); // { to: '2017-31-12' }
console.log(allData); // { from: '2017-01-12', to: '2017-31-12' }
});
```

### `sanitize(fields)`
- `field`: a string or an array of strings of field names to validate against.
Expand Down
5 changes: 3 additions & 2 deletions filter/index.d.ts
@@ -1,5 +1,5 @@
import * as express from 'express';
import { Sanitizer } from '../shared-typings';
import { Sanitizer, Location } from '../shared-typings';

export function matchedData (req: express.Request, options?: MatchedDataOptions): { [key: string]: any };
export const sanitize: SanitizationChainBuilder;
Expand All @@ -9,7 +9,8 @@ export const sanitizeParam: SanitizationChainBuilder;
export const sanitizeQuery: SanitizationChainBuilder;

interface MatchedDataOptions {
onlyValidData: boolean
onlyValidData?: boolean
locations?: Location[]
}

export interface SanitizationChainBuilder {
Expand Down
28 changes: 20 additions & 8 deletions filter/matched-data.js
@@ -1,17 +1,29 @@
const _ = require('lodash');
const selectFields = require('../check/select-fields');

module.exports = (req, { onlyValidData = true } = {}) => {
const validityFilter = !onlyValidData ? () => true : field => {
return !req._validationErrors.find(error =>
error.param === field.path &&
error.location === field.location
);
};
module.exports = (req, options = {}) => {
const validityFilter = createValidityFilter(req, options);
const locationsFilter = createLocationFilter(options);

return _(req._validationContexts)
.flatMap(context => selectFields(req, context))
.filter(validityFilter)
.filter(locationsFilter)
.reduce((state, field) => _.set(state, field.path, field.value), {})
.valueOf();
};
};

function createValidityFilter(req, { onlyValidData }) {
onlyValidData = onlyValidData === undefined ? true : onlyValidData;
return !onlyValidData ? () => true : field => {
return !req._validationErrors.find(error =>
error.param === field.path &&
error.location === field.location
);
};
}

function createLocationFilter({ locations }) {
const allLocations = !Array.isArray(locations) || locations.length === 0;
return allLocations ? () => true : field => locations.includes(field.location);
}
57 changes: 55 additions & 2 deletions filter/matched-data.spec.js
Expand Up @@ -31,7 +31,7 @@ describe('filter: matchedData', () => {
});
});

describe('when { onlyValidData: false } flag is passed', () => {
describe('when option onlyValidData is set to false', () => {
it('returns object with all data validated in the request', () => {
const req = {
query: { foo: '123', bar: 'abc', baz: 'def' }
Expand Down Expand Up @@ -65,4 +65,57 @@ describe('filter: matchedData', () => {
});
});
});
});

describe('when option locations is passed', () => {
it('gathers only data from the locations specified in the array', () => {
const req = {
query: { foo: '123', bar: 'abc' },
body: { baz: '234' }
};

return check(['foo', 'bar', 'baz']).isInt()(req, {}, () => {}).then(() => {
const data = matchedData(req, { locations: ['body'] });

expect(data).to.eql({
baz: '234'
});
});
});

it('gathers data from all locations if empty array', () => {
const req = {
query: { foo: '123', bar: 'abc' },
body: { baz: '234' }
};

return check(['foo', 'bar', 'baz']).isInt()(req, {}, () => {}).then(() => {
const data = matchedData(req, {
locations: []
});

expect(data).to.eql({
foo: '123',
baz: '234'
});
});
});

it('gathers data from all locations if not an array', () => {
const req = {
query: { foo: '123', bar: 'abc' },
body: { baz: '234' }
};

return check(['foo', 'bar', 'baz']).isInt()(req, {}, () => {}).then(() => {
const data = matchedData(req, {
locations: false
});

expect(data).to.eql({
foo: '123',
baz: '234'
});
});
});
});
});
1 change: 1 addition & 0 deletions filter/type-definition.spec.ts
Expand Up @@ -13,6 +13,7 @@ const res: Response = <Response>{};

matchedData(req).foo;
matchedData(req, { onlyValidData: true }).bar;
matchedData(req, { locations: ['body', 'params'] }).bar;

// Sanitization
sanitize('foo')(req, res, () => {});
Expand Down