koa-mongo-router
Version:
KOA REST API Router for MongoDB
361 lines (360 loc) • 13.5 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseQueryString = void 0;
const querystring_1 = require("querystring");
function getValue(value) {
if (Array.isArray(value)) {
value = value.map((item) => getValue(item));
}
else {
if (value === '') {
/* do nothing */
}
else if (value === 'true' || value === 'True' || value === 'TRUE') {
value = true;
}
else if (value === 'false' || value === 'False' || value === 'FALSE') {
value = false;
}
else {
const numberValue = Number(value);
if (!isNaN(numberValue)) {
value = numberValue;
}
}
}
return value;
}
var ValueFormat;
(function (ValueFormat) {
ValueFormat[ValueFormat["Default"] = 0] = "Default";
ValueFormat[ValueFormat["String"] = 1] = "String";
ValueFormat[ValueFormat["Date"] = 2] = "Date";
})(ValueFormat || (ValueFormat = {}));
var Operation;
(function (Operation) {
Operation[Operation["None"] = 0] = "None";
Operation[Operation["Equals"] = 1] = "Equals";
Operation[Operation["NotEquals"] = 2] = "NotEquals";
Operation[Operation["GreaterThan"] = 3] = "GreaterThan";
Operation[Operation["GreaterThanEqual"] = 4] = "GreaterThanEqual";
Operation[Operation["LessThan"] = 5] = "LessThan";
Operation[Operation["LessThanEqual"] = 6] = "LessThanEqual";
Operation[Operation["Exists"] = 7] = "Exists";
Operation[Operation["NotExists"] = 8] = "NotExists";
Operation[Operation["Contains"] = 9] = "Contains";
Operation[Operation["StartsWith"] = 10] = "StartsWith";
Operation[Operation["EndsWith"] = 11] = "EndsWith";
})(Operation || (Operation = {}));
function getKeyValue(key, value, filter) {
let operation;
if (value === '') {
if (key.includes('>')) {
operation = Operation.GreaterThan;
const index = key.indexOf('>');
value = key.substr(index + 1);
key = key.substr(0, index);
}
else if (key.includes('<')) {
operation = Operation.LessThan;
const index = key.indexOf('<');
value = key.substr(index + 1);
key = key.substr(0, index);
}
else if (key[0] === '!') {
if (key.length === 1) {
key = '_id';
operation = Operation.None;
}
else {
operation = Operation.NotExists;
key = key.substr(1, key.length - 1);
}
}
else {
operation = Operation.Exists;
}
}
else {
switch (key[key.length - 1]) {
case '!':
key = key.substr(0, key.length - 1);
operation = Operation.NotEquals;
break;
case '>':
key = key.substr(0, key.length - 1);
operation = Operation.GreaterThanEqual;
break;
case '<':
key = key.substr(0, key.length - 1);
operation = Operation.LessThanEqual;
break;
case '~':
key = key.substr(0, key.length - 1);
operation = Operation.Contains;
break;
case '^':
key = key.substr(0, key.length - 1);
operation = Operation.StartsWith;
break;
case '$':
key = key.substr(0, key.length - 1);
operation = Operation.EndsWith;
break;
default:
operation = Operation.Equals;
}
}
let valueFormat = ValueFormat.Default;
/* istanbul ignore else */
if (key.length > 1) {
switch (key[key.length - 1]) {
case ':':
valueFormat = ValueFormat.String;
key = key.substr(0, key.length - 1);
break;
case '@':
valueFormat = ValueFormat.Date;
key = key.substr(0, key.length - 1);
break;
}
}
switch (valueFormat) {
case ValueFormat.Default:
value = getValue(value);
break;
case ValueFormat.Date:
if (Array.isArray(value)) {
value = value.map((item) => {
let dateAsNumber = Number(item);
/* istanbul ignore else */
if (isNaN(dateAsNumber)) {
const dateValue = Date.parse(item);
if (isNaN(dateValue)) {
throw new Error('invalid date format in query string');
}
return dateValue;
}
else {
if (dateAsNumber < 0) {
dateAsNumber = Date.now() - dateAsNumber;
}
return dateAsNumber;
}
});
}
else {
let dateAsNumber = Number(value);
if (isNaN(dateAsNumber)) {
const dateValue = Date.parse(value);
if (isNaN(dateValue)) {
throw new Error('invalid date format in query string');
}
value = dateValue;
}
else {
/* istanbul ignore if */
if (dateAsNumber < 0) {
dateAsNumber = Date.now() + dateAsNumber;
}
value = dateAsNumber;
}
}
break;
}
let keyFilter = filter[key];
if (keyFilter == undefined) {
keyFilter = {};
filter[key] = keyFilter;
}
if (Array.isArray(value)) {
switch (operation) {
case Operation.Contains:
throw new Error("query cannot contain more than one 'contains' criteria for the same key");
case Operation.EndsWith:
throw new Error("query cannot contain more than one 'ends with' criteria for the same key");
case Operation.Equals:
keyFilter.$in = value;
break;
case Operation.GreaterThanEqual:
throw new Error("query cannot contain more than one 'greater than or equal' criteria for the same key");
case Operation.LessThanEqual:
throw new Error("query cannot contain more than one 'less than or equal' criteria for the same key");
case Operation.NotEquals:
keyFilter.$nin = value;
break;
case Operation.StartsWith:
throw new Error("query cannot contain more than one 'starts with' criteria for the same key");
}
}
else {
switch (operation) {
case Operation.Contains:
keyFilter.$regex = value;
keyFilter.$options = 'i';
break;
case Operation.EndsWith:
keyFilter.$regex = `${value}$`;
keyFilter.$options = 'i';
break;
case Operation.Equals:
keyFilter.$eq = value;
break;
case Operation.Exists:
keyFilter.$exists = true;
break;
case Operation.GreaterThan:
if (keyFilter.$gt != undefined) {
throw new Error("query cannot contain more than one 'greater than' criteria for the same key");
}
keyFilter.$gt = value;
break;
case Operation.GreaterThanEqual:
keyFilter.$gte = value;
break;
case Operation.LessThan:
if (keyFilter.$lt != undefined) {
throw new Error("query cannot contain more than one 'less than' criteria for the same key");
}
keyFilter.$lt = value;
break;
case Operation.LessThanEqual:
keyFilter.$lte = value;
break;
case Operation.None:
keyFilter.$exists = false;
break;
case Operation.NotEquals:
keyFilter.$ne = value;
break;
case Operation.NotExists:
keyFilter.$exists = false;
break;
case Operation.StartsWith:
keyFilter.$regex = `^${value}`;
keyFilter.$options = 'i';
break;
}
}
}
function parseQueryString(queryString) {
queryString = decodeURIComponent(queryString);
const andQueryStrings = queryString.split('|');
if (andQueryStrings.length === 1) {
return parseQuery(querystring_1.parse(andQueryStrings[0]));
}
const mongoQuery = {
filter: { $or: [] },
};
for (const andQueryString of andQueryStrings) {
const query = parseQuery(querystring_1.parse(andQueryString));
for (const key of Object.keys(query)) {
if (key !== 'filter') {
;
mongoQuery[key] = query[key];
}
else {
mongoQuery.filter.$or.push(query.filter);
}
}
}
return mongoQuery;
}
exports.parseQueryString = parseQueryString;
function parseQuery(ctxQuery) {
const filter = {};
const query = { filter };
for (const key of Object.keys(ctxQuery)) {
let value = ctxQuery[key];
if (key[0] === '$') {
switch (key) {
case '$limit':
query.limit = Number(value);
if (isNaN(query.limit) || query.limit < 1) {
throw new Error('query string parameter $limit must be a number greater than 0');
}
break;
case '$skip':
query.skip = Number(value);
if (isNaN(query.skip) || query.skip < 0) {
throw new Error('query string parameter $skip must be a number greater than or equal to 0');
}
break;
case '$count':
value = getValue(value);
if (value === '') {
query.count = true;
}
else if (typeof value === 'boolean') {
query.count = value;
}
else {
throw new Error('query string parameter $count must be a boolean');
}
break;
case '$fields':
const fieldValues = value.split(',');
const fields = {};
for (const fieldValue of fieldValues) {
if (fieldValue === '') {
throw new Error('query string parameter $fields cannot contain an empty value');
}
if (fieldValue[0] === '-') {
if (fieldValue.length === 1) {
throw new Error('query string parameter $sort cannot contain an empty value');
}
fields[fieldValue.substr(1)] = 0;
}
else {
fields[fieldValue] = 1;
}
}
query.fields = fields;
break;
case '$sort':
const sortValues = value.split(',');
const sort = [];
for (const sortValue of sortValues) {
if (sortValue === '') {
throw new Error('query string parameter $sort cannot contain an empty value');
}
if (sortValue[0] === '-') {
if (sortValue.length === 1) {
throw new Error('query string parameter $sort cannot contain an empty value');
}
sort.push([sortValue.substring(1), -1]);
}
else {
sort.push([sortValue, 1]);
}
}
/* istanbul ignore else */
if (sort.length === 1) {
query.sort = {};
query.sort[sort[0][0]] = sort[0][1];
}
else {
query.sort = sort;
}
break;
case '$embed':
query.embed = value.split(',');
break;
default:
if (value === '') {
;
query[key.substr(1, key.length - 1)] = true;
}
else {
;
query[key.substr(1, key.length - 1)] = getValue(value);
}
break;
}
}
else {
getKeyValue(key, value, filter);
}
}
// logger.debug('query', query)
return query;
}