Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 63 additions & 2 deletions libs/executor.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,41 @@ const executeQuery = require("./executeQuery.cjs");
const arrayToObject = require("./arrayToObject.cjs");
const dataPath = require("./dataPath.cjs");

/**
* Using waterfall structure for better readability
* @param {*} query
* @returns
*/
function waterfallParser(query) {
if (query.name && Array.isArray(query.computes)) {
const result = {};
const current = result;

query.computes.reduce((acc, item) => {
if (typeof item === "string") {
acc[item] = {};
return acc[item];
} else if (typeof item === "object" && item !== null) {
Object.assign(acc, item);
return acc;
}
}, current);

return {
[query.name]: result,
};
} else {
return query;
}
}

/**
* Options for query execution.
*
* @typedef {object} QueryOptions
* @property {object} methods - Methods configuration.
* @property {object} config - Configuration settings.
* @property {string} dataUrl - Data url path.
* @property {string} [dataUrl] - Data url path.
*/

function postProcessing(options) {
Expand All @@ -29,12 +57,45 @@ function postProcessing(options) {
* @returns {Promise<object>} A promise that resolves to the result object.
*/
const rq = (query, options) => {
return executeQuery(query, null, options)
const parseQuery = waterfallParser(query);
return executeQuery(parseQuery, null, options)
.then(postProcessing(options))
.catch((error) => console.error("rql Error:", error.message));
};

class RqExtender {
constructor() {
this.methods = {};
}
compute(payload) {
return rq(payload, {
methods: this.methods,
config: (param) => this.getMethodsMap()[param],
});
}

getMethodsMap() {
const prototype = Object.getPrototypeOf(this);
const methodNames = Object.getOwnPropertyNames(prototype).filter(
(name) =>
typeof this[name] === "function" &&
name !== "constructor" &&
name !== "compute" &&
name !== "getMethodsMap",
);
const methodsMap = {};
for (const name of methodNames) {
methodsMap[name] = this[name].bind(this);
}
return methodsMap;
}
}

global.rq = rq;
exports.rq = rq;
module.exports = rq;
module.exports.default = rq;

global.RqExtender = RqExtender;
exports.RqExtender = RqExtender;
module.exports.RqExtender = RqExtender;
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "requrse",
"version": "0.3.4",
"version": "0.4.0",
"type": "module",
"description": "Lightweight driven query language",
"main": "libs/executor.cjs",
Expand All @@ -17,7 +17,9 @@
"test:array-string": "node test/arrayString.test.mjs",
"test:duplicate": "node test/duplicateField.test.mjs",
"test:array-index": "node test/arrayIndex.test.mjs",
"test": "npm run test:starwars && npm run test:mongoose && npm run test:mongoose-lookup && npm run test:redis && npm run test:requrse && npm run test:inflight-request-cancelation && npm run test:fantasy && npm run test:basic && npm run test:array && npm run test:array-string && npm run test:duplicate && npm run test:array-index",
"test:waterfall": "node test/waterfall.test.mjs",
"test:class": "node test/class.test.mjs",
"test": "npm run test:starwars && npm run test:mongoose && npm run test:mongoose-lookup && npm run test:redis && npm run test:requrse && npm run test:inflight-request-cancelation && npm run test:fantasy && npm run test:basic && npm run test:array && npm run test:array-string && npm run test:duplicate && npm run test:array-index && npm run test:waterfall && npm run test:class",
"test:coverage": "c8 --exclude samples --exclude test npm run test",
"test:lcov": "c8 --exclude samples --exclude test --reporter lcov npm run test",
"coverage": "coveralls < coverage/lcov.info"
Expand Down
67 changes: 67 additions & 0 deletions test/class.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import assert from "assert";
import { RqExtender } from "../libs/executor.cjs";
import { test } from "./fixture/test.mjs";

await test("Classes config", () => {
class TestConfig extends RqExtender {
constructor() {
super();
this.methods = {
area: "area",
occupation: "occupation",
person: "getPerson",
birth: "birth",
};
}
area() {
return { city: "NY" };
}
occupation() {
return { type: "CT0" };
}
birth() {
return { year: "1981" };
}
getPerson(name) {
return { name, age: 42 };
}
}

const test = new TestConfig();

const payload = {
Test: {
test: {
person: {
$params: { name: "Foo" },
name: 1,
age: 1,
birth: {
year: 1,
area: {
city: 1,
},
},
occupation: {
type: 1,
},
},
},
},
};

test.compute(payload).then((result) => {
assert.deepEqual(result, {
Test: {
test: {
person: {
name: "Foo",
age: 42,
birth: { year: "1981", area: { city: "NY" } },
occupation: { type: "CT0" },
},
},
},
});
});
});
32 changes: 32 additions & 0 deletions test/waterfall.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import assert from "assert";
import rq from "../libs/executor.cjs";
import { test } from "./fixture/test.mjs";

await test("Foo Bar", () => {
rq(
{
name: "Test",
computes: [
"test", // placeholder
"foo",
{
bar: "*",
},
],
},
{
methods: {
bar() {
return "another";
},
foo() {
return {
bar: "foobar",
};
},
},
},
).then((result) => {
assert.deepEqual(result, { Test: { test: { foo: { bar: "another" } } } });
});
});