106 lines
3.3 KiB
JavaScript
106 lines
3.3 KiB
JavaScript
/** This module defines a express.Router() instance
|
|
* - supporting filter=key1,key2
|
|
* - it sets res.locals.filter to a string "key1 key2"
|
|
* - calls next with error if a filter=key is given, but key does not exist (not raised on empty item arrays!)
|
|
*
|
|
* Note: it expects to be called before any data is fetched from DB
|
|
* Note: it sets an Error-Object to next with error.status set to HTTP status code 400
|
|
*
|
|
* @author Johannes Konert
|
|
* @licence CC BY-SA 4.0
|
|
*
|
|
* @module restapi/filter-middleware-mongo
|
|
* @type {Factory function returning an Router}
|
|
* @param Schema {Object} a MongooseSchema.path value or similar object with attributes representing the valid keys
|
|
* @param suppressId {Boolean} if true, the _id is not returned implicitly
|
|
*/
|
|
|
|
// remember: in modules you have 3 variables given by CommonJS
|
|
// 1.) require() function
|
|
// 2.) module.exports
|
|
// 3.) exports (which is module.exports)
|
|
'use strict';
|
|
|
|
const express = require('express');
|
|
const logger = require('debug')('middleware:filterware');
|
|
|
|
/**
|
|
* private helper function to filter Objects by given keys
|
|
*
|
|
* @param {Array} keys - the keys from GET parameter filter
|
|
* @param {Object} schema - containing the keys as attributes that are allowed
|
|
* @return {Object} either the filtered items or an Error object
|
|
*/
|
|
const limitFilterToSchema = (keys, schema) => {
|
|
if (!keys || !schema) { // empty arrays evaluate to false
|
|
return undefined; // means no filter at all
|
|
}
|
|
|
|
let error = null;
|
|
const result = [];
|
|
// now for each given filter=key1,key2in the array check that the schema allows this key and store it in result
|
|
keys.forEach((key) => {
|
|
if (schema.hasOwnProperty(key)) {
|
|
result.push(key);
|
|
} else {
|
|
error = new Error('given key for filter does not exist in ressource: ' + key);
|
|
}
|
|
});
|
|
return error ? error : result;
|
|
};
|
|
|
|
/**
|
|
* closure function as factory returning the router
|
|
*
|
|
* @param Schema {Object} a MongooseSchema.path value or similar object with attributes representing the valid keys
|
|
* @param suppressId {Boolean} if true, the _id is not returned implicitly
|
|
* @returns {Router}
|
|
*/
|
|
|
|
const createFilterRouter = (schema, supressID) => {
|
|
const router = new express.Router();
|
|
// the exported router with handler
|
|
router.use((req, res, next) => {
|
|
const filterString = req.query.filter;
|
|
let filterKeys = [];
|
|
let err = null;
|
|
|
|
if (filterString !== undefined) {
|
|
filterKeys = filterString.split(',');
|
|
filterKeys.forEach((item, index, array) => {
|
|
array[index] = item.trim();
|
|
});
|
|
filterKeys = filterKeys.filter((item) => {
|
|
return item.length > 0;
|
|
});
|
|
if (filterKeys.length === 0) {
|
|
err = new Error('given filter does not contain any keys');
|
|
err.status = 400;
|
|
} else {
|
|
const result = limitFilterToSchema(filterKeys, schema);
|
|
if (result instanceof Error) {
|
|
err = result;
|
|
err.status = 400;
|
|
} else {
|
|
res.locals.filter = result.join(' '); // create a string with space as seperator
|
|
if (supressID) {
|
|
res.locals.filter = '-_id ' + res.locals.filter;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (err) {
|
|
logger(err);
|
|
next(err);
|
|
} else {
|
|
if (res.locals.filter) {
|
|
logger('Successfully set filter to ' + res.locals.filter);
|
|
}
|
|
next();
|
|
}
|
|
});
|
|
return router;
|
|
};
|
|
|
|
module.exports = createFilterRouter;
|