Skip to content

Commit

Permalink
filter GET /v1/subjects/ by tags
Browse files Browse the repository at this point in the history
  • Loading branch information
pallavi2209 committed Oct 21, 2016
1 parent a402180 commit d39942b
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 17 deletions.
1 change: 1 addition & 0 deletions api/v1/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ module.exports = {
SEQ_DEFAULT_SCOPE: 'defaultScope',
SEQ_DESC: 'DESC',
SEQ_LIKE: '$iLike',
SEQ_CONTAINS: '$contains',
SEQ_OR: '$or',
SEQ_WILDCARD: '%',
SLASH: '/',
Expand Down
1 change: 1 addition & 0 deletions api/v1/helpers/nouns/subjects.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,5 @@ module.exports = {
fieldsWithJsonArrayType,
fieldsWithArrayType,
loggingEnabled,
tagFilterName: 'tags',
}; // exports
52 changes: 35 additions & 17 deletions api/v1/helpers/verbs/findUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
const u = require('./utils');
const constants = require('../../constants');
const defaults = require('../../../../config').api.defaults;
const filterSubjByTags = require('../../../../config').filterSubjByTags;

/**
* Escapes all percent literals so they're not treated as wildcards.
Expand Down Expand Up @@ -52,10 +53,17 @@ function toSequelizeWildcards(val) {
* case-insensitive string matching
*
* @param {String} val - The value to transform into a Sequelize where clause
* @param {Object} props - The helpers/nouns module for the given DB model.
* @returns {Object} a Sequelize where clause using "$ilike" for
* case-insensitive string matching
*/
function toWhereClause(val) {
function toWhereClause(val, props) {
if (filterSubjByTags && Array.isArray(val) && props.tagFilterName) {
const containsClause = {};
containsClause[constants.SEQ_CONTAINS] = val;
return containsClause;
}

// TODO handle non-string data types like dates and numbers
if (typeof val !== 'string') {
return val;
Expand All @@ -72,9 +80,10 @@ function toWhereClause(val) {
* a Sequelize "where" object.
*
* @param {Object} filter
* @param {Object} props - The helpers/nouns module for the given DB model.
* @returns {Object} a Sequelize "where" object
*/
function toSequelizeWhere(filter) {
function toSequelizeWhere(filter, props) {
const where = {};
const keys = Object.keys(filter);
for (let i = 0; i < keys.length; i++) {
Expand All @@ -85,23 +94,32 @@ function toSequelizeWhere(filter) {
}

const values = [];
for (let j = 0; j < filter[key].length; j++) {
const v = filter[key][j];
if (typeof v === 'boolean') {
values.push(v);
} else if (typeof v === 'string') {
const arr = v.split(constants.COMMA);
for (let k = 0; k < arr.length; k++) {
values.push(toWhereClause(arr[k]));
// if tag filter is enables and key is "tags", then create a contains
// clause and add it to where clause,
// like, { where : { '$contains': ['tag1', 'tag2'] } }
if (filterSubjByTags && props.tagFilterName && key === props.tagFilterName) {
const tagArr = filter[key];
values.push(toWhereClause(tagArr, props));
where[key] = values[0];
} else {
for (let j = 0; j < filter[key].length; j++) {
const v = filter[key][j];
if (typeof v === 'boolean') {
values.push(v);
} else if (typeof v === 'string') {
const arr = v.split(constants.COMMA);
for (let k = 0; k < arr.length; k++) {
values.push(toWhereClause(arr[k]));
}
}
}
}

if (values.length === 1) {
where[key] = values[0];
} else if (values.length > 1) {
where[key] = {};
where[key][constants.SEQ_OR] = values;
if (values.length === 1) {
where[key] = values[0];
} else if (values.length > 1) {
where[key] = {};
where[key][constants.SEQ_OR] = values;
}
}
}
}
Expand Down Expand Up @@ -172,7 +190,7 @@ function options(params, props) {
}

if (filter) {
opts.where = toSequelizeWhere(filter);
opts.where = toSequelizeWhere(filter, props);
}

return opts;
Expand Down
11 changes: 11 additions & 0 deletions api/v1/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3094,6 +3094,17 @@ paths:
Filter by parentAbsolutePath; asterisk (*) wildcards ok.
required: false
type: string
-
name: tags
in: query
items:
type: string
maxLength: 60
pattern: ^[0-9A-Za-z_][0-9A-Za-z_\\-]{1,59}$
description: >-
Filter by tags.
required: false
type: array
responses:
200:
description: >-
Expand Down
4 changes: 4 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ const optimizeUpsert = pe.OPTIMIZE_UPSERT === 'true' ||
// env variable to enable caching for /GET /v1/perspectives/{key}
const enableCachePerspective = pe.ENABLE_CACHE_PERSPECTIVE || false;

const filterSubjByTags = pe.FILTER_SUBJ_BY_TAGS === 'true' ||
pe.FILTER_SUBJ_BY_TAGS === true || false;

module.exports = {

api: {
Expand Down Expand Up @@ -247,4 +250,5 @@ module.exports = {
optimizeUpsert,
enableCachePerspective,
enableClockDyno,
filterSubjByTags,
};
38 changes: 38 additions & 0 deletions tests/api/v1/subjects/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const u = require('./utils');
const Subject = tu.db.Subject;
const path = '/v1/subjects';
const expect = require('chai').expect;
const filterSubjByTags = require('../../../../config').filterSubjByTags;

describe(`api: GET ${path}`, () => {
let token;
Expand All @@ -30,10 +31,12 @@ describe(`api: GET ${path}`, () => {
const us = {
name: `${tu.namePrefix}UnitedStates`,
description: 'country',
tags: ['US'],
};
const vt = {
name: `${tu.namePrefix}Vermont`,
description: 'state',
tags: ['US', 'NE'],
};

before((done) => {
Expand Down Expand Up @@ -185,6 +188,41 @@ describe(`api: GET ${path}`, () => {
});
});

if (filterSubjByTags) {
it('GET with tag filter :: one tag', (done) => {
api.get(`${path}/?tags=US`)
.set('Authorization', token)
.expect(constants.httpStatus.OK)
.end((err, res) => {
if (err) {
return done(err);
}

expect(res.body.length).to.equal(2);
expect(res.body[0].tags).to.eql(['US']);
expect(res.body[1].tags).to.eql(['US', 'NE']);

done();
});
});

it('GET with tag filter :: multiple tags', (done) => {
api.get(`${path}/?tags=NE,US`)
.set('Authorization', token)
.expect(constants.httpStatus.OK)
.end((err, res) => {
if (err) {
return done(err);
}

expect(res.body.length).to.equal(1);
expect(res.body[0].tags).to.eql(['US', 'NE']);

done();
});
});
}

it('pagination tests');
it('childCount, descendentCount');
it('by id');
Expand Down

0 comments on commit d39942b

Please sign in to comment.