Skip to content

Commit

Permalink
replacing dots in Names. Closes zackyang000#106
Browse files Browse the repository at this point in the history
  • Loading branch information
r1mar committed Nov 4, 2022
1 parent dd4f72f commit 8ecd192
Show file tree
Hide file tree
Showing 13 changed files with 346 additions and 110 deletions.
35 changes: 35 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
// Verwendet IntelliSense zum Ermitteln möglicher Attribute.
// Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
// Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Mocha single Test",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": [
"--require",
"@babel/register",
"--reporter",
"dot",
"--timeout",
"300000",
"test/odata.query.filter.functions.js"
]
},
{
"type": "node",
"request": "launch",
"name": "Launch Complex Resource",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/examples/complex-resource/index.js"
}
]
}
10 changes: 0 additions & 10 deletions src/db/idPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,6 @@ import * as uuid from 'uuid';

/*eslint-disable */
export default function (schema) {
// add _id to schema.
if (!schema.paths._id) {
schema.add({
_id: {
type: String,
unique: true,
}
});
}

// display value of _id when request id.
if (!schema.paths.id) {
schema.virtual('id').get(function getId() {
Expand Down
7 changes: 5 additions & 2 deletions src/metadata/ODataMetadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,10 @@ export default class Metadata {
.filter((path) => path !== '_id')
.reduce((previousProperty, curentProperty) => {
const modelProperty = this.resolveModelproperty(model, curentProperty);
const propertyName = curentProperty.replace(/\./g, '-');
const result = {
...previousProperty,
[curentProperty]: this.visitor('Property', node[curentProperty], modelProperty, root),
[propertyName]: this.visitor('Property', node[curentProperty], modelProperty, root),
};

return result;
Expand All @@ -139,9 +140,11 @@ export default class Metadata {
const properties = Object.keys(node)
.filter((item) => item !== '_id')
.reduce((previousProperty, curentProperty) => {
const propertyName = curentProperty.replace(/\./g, '-');
const modelProperty = this.resolveModelproperty(model, curentProperty);
const result = {
...previousProperty,
[curentProperty]: this.visitor('Property', node[curentProperty], model[curentProperty], root),
[propertyName]: this.visitor('Property', node[curentProperty], modelProperty, root),
};

return result;
Expand Down
2 changes: 1 addition & 1 deletion src/metadata/ODataServiceDocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default class Metadata {
return new Promise((resolve) => {
resolve({
status: 200,
entity: document,
serviceDocument: document,
});
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/parser/countParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default (mongooseModel, $count, $filter) => new Promise((resolve, reject)
switch ($count) {
case 'true': {
const query = mongooseModel.find();
filterParser(query, $filter);
filterParser(query, $filter, mongooseModel);
query.count((err, count) => {
resolve(count);
});
Expand Down
175 changes: 111 additions & 64 deletions src/parser/filterParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,48 @@ const stringHelper = {
},
};

class KeyParser {
constructor(model) {
this._model = model;
}

getConvertedKey(input) {
let key = input;

if (key === 'id') {
key = '_id';
return key;
}
if (this._model[key]) {
// known simple property
return key;
}

const match = key.match(/\s*(contains|indexof|year)\(\s*([\w+-]+)/);

if (match) {
// contains function was called with id e.g. contains(title, 'ggm')
const functionKey = match[2];

key = key.replace(functionKey, this.getConvertedKey(functionKey));
} else {
key = Object.keys(this._model.model.schema.paths).find((item) => {
const replacedDots = item.replace(/\./g, '(.|-){1}');
const regex = new RegExp(`^${replacedDots}$`);

return key.match(regex);
});
if (!key) {
const error = new Error(`Unknown property '${this._input}' in entity '${this._model.name}'`);

error.status = '400';
throw error;
}
}
return key;
}
}

const validator = {
formatValue: (value) => {
let val;
Expand All @@ -56,85 +98,90 @@ const validator = {
},
};

export default (query, $filter) => new Promise((resolve, reject) => {
export default (query, $filter, model) => new Promise((resolve, reject) => {
if (!$filter) {
resolve();
return;
}

const condition = split($filter, ['and', 'or'])
.filter((item) => (item !== 'and' && item !== 'or'));
try {
const condition = split($filter, ['and', 'or'])
.filter((item) => (item !== 'and' && item !== 'or'));

condition.forEach((item) => {
// parse "indexof(title,'X1ML') gt 0"
const conditionArr = split(item, OPERATORS_KEYS);
if (conditionArr.length === 0) {
// parse "contains(title,'X1ML')"
conditionArr.push(item);
}
if (conditionArr.length !== 3 && conditionArr.length !== 1) {
return reject(new Error(`Syntax error at '${item}'.`));
}
condition.forEach((item) => {
// parse "indexof(title,'X1ML') gt 0"
const conditionArr = split(item, OPERATORS_KEYS);
if (conditionArr.length === 0) {
// parse "contains(title,'X1ML')"
conditionArr.push(item);
}
if (conditionArr.length !== 3 && conditionArr.length !== 1) {
throw new Error(`Syntax error at '${item}'.`);
}

let key = conditionArr[0];
const [, odataOperator, value] = conditionArr;
const keyParser = new KeyParser(model);
let key = conditionArr[0];
const [, odataOperator, value] = conditionArr;

if (key === 'id') key = '_id';
key = keyParser.getConvertedKey(key);

let val;
if (value !== undefined) {
const result = validator.formatValue(value);
if (result.err) {
return reject(result.err);
let val;
if (value !== undefined) {
const result = validator.formatValue(value);
if (result.err) {
return reject(result.err);
}
val = result.val;
}
val = result.val;
}

// function query
const functionKey = key.substring(0, key.indexOf('('));
if (['indexof', 'year', 'contains'].indexOf(functionKey) > -1) {
functions[functionKey](query, key, odataOperator, val);
} else {
if (conditionArr.length === 1) {
return reject(new Error(`Syntax error at '${item}'.`));
}
if (value === 'null') {
// function query
const functionKey = key.substring(0, key.indexOf('('));
if (['indexof', 'year', 'contains'].indexOf(functionKey) > -1) {
functions[functionKey](query, key, odataOperator, val);
} else {
if (conditionArr.length === 1) {
return reject(new Error(`Syntax error at '${item}'.`));
}
if (value === 'null') {
switch (odataOperator) {
case 'eq':
query.exists(key, false);
return resolve();
case 'ne':
query.exists(key, true);
return resolve();
default:
break;
}
}
// operator query
switch (odataOperator) {
case 'eq':
query.exists(key, false);
return resolve();
query.where(key).equals(val);
break;
case 'ne':
query.exists(key, true);
return resolve();
default:
query.where(key).ne(val);
break;
case 'gt':
query.where(key).gt(val);
break;
case 'ge':
query.where(key).gte(val);
break;
case 'lt':
query.where(key).lt(val);
break;
case 'le':
query.where(key).lte(val);
break;
default:
return reject(new Error("Incorrect operator at '#{item}'."));
}
}
// operator query
switch (odataOperator) {
case 'eq':
query.where(key).equals(val);
break;
case 'ne':
query.where(key).ne(val);
break;
case 'gt':
query.where(key).gt(val);
break;
case 'ge':
query.where(key).gte(val);
break;
case 'lt':
query.where(key).lt(val);
break;
case 'le':
query.where(key).lte(val);
break;
default:
return reject(new Error("Incorrect operator at '#{item}'."));
}
}
return query;
});
resolve();
return query;
});
resolve();
} catch (error) {
reject(error);
}
});
31 changes: 9 additions & 22 deletions src/pipes.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import http from 'http';
import XmlWriter from './metadata/xmlWriter';
import XmlWriter from './writer/xmlWriter';
import JsonWirter from './writer/jsonWriter';

const xmlWriter = new XmlWriter();

function writeJson(res, data, status, resolve) {
res.type('application/json');
res.status(status).jsonp(data);
resolve(data);
}
const jsonWriter = new JsonWirter();

function getMediaType(accept, data) {
// reduce multi mimetypes to most weigth mimetype
Expand All @@ -27,7 +23,7 @@ function getMediaType(accept, data) {
return result;
}, {});

if (!data.entity && mostWeightMimetype.mimetype.match(/((application|\*)\/(xml|\*)|^xml$)/)) {
if (data.metadata && mostWeightMimetype.mimetype.match(/((application|\*)\/(xml|\*)|^xml$)/)) {
return 'application/xml';
} if (mostWeightMimetype.mimetype.match(/((application|\*)\/(json|\*)|^json$)/)) {
return 'application/json';
Expand All @@ -53,10 +49,10 @@ function getWriter(req, result) {
// xml representation of metadata
switch (mediaType) {
case 'application/json':
return writeJson;
return jsonWriter.writeJson.bind(jsonWriter);

case 'application/xml':
if (result.entity) {
if (!result.metadata) {
// xml wirter for entities and actions is not implemented
const error406 = new Error('Not acceptable');

Expand All @@ -67,8 +63,8 @@ function getWriter(req, result) {

default:
// no media type requested set defaults depend of context
if (result.entity) {
return writeJson; // default for entities and actions
if (result.entity || result.serviceDocument) {
return jsonWriter.writeJson.bind(jsonWriter); // default for entities and actions
}

return xmlWriter.writeXml.bind(xmlWriter); // default for metadata
Expand Down Expand Up @@ -105,17 +101,8 @@ const respondPipe = (req, res, result) => new Promise((resolve, reject) => {

const status = result.status || 200;
const writer = getWriter(req, result);
let data;

if (result.entity) {
// json Representation of data
data = result.entity;
} else {
// xml representation of metadata
data = result.metadata;
}

writer(res, data, status, resolve);
writer(res, result, status, resolve);
} catch (error) {
reject(error);
}
Expand Down
2 changes: 1 addition & 1 deletion src/rest/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function _dataQuery(model, {
}, options) {
return new Promise((resolve, reject) => {
const query = model.find();
filterParser(query, filter)
filterParser(query, filter, model)
.then(() => orderbyParser(query, orderby || options.orderby))
.then(() => skipParser(query, skip, options.maxSkip))
.then(() => topParser(query, top, options.maxTop))
Expand Down
Loading

0 comments on commit 8ecd192

Please sign in to comment.