From 7044593c1b5f145bb39536b216cdf4bf92371416 Mon Sep 17 00:00:00 2001 From: Shahrul Nizam Selamat Date: Sun, 31 Aug 2025 15:42:09 +0800 Subject: [PATCH 1/2] added waterfall, class Extender --- libs/executor.cjs | 66 ++++++++++++++++++++++++++++++++++++++-- package.json | 6 ++-- test/class.test.mjs | 67 +++++++++++++++++++++++++++++++++++++++++ test/waterfall.test.mjs | 32 ++++++++++++++++++++ 4 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 test/class.test.mjs create mode 100644 test/waterfall.test.mjs diff --git a/libs/executor.cjs b/libs/executor.cjs index 380ebb6..6614da4 100644 --- a/libs/executor.cjs +++ b/libs/executor.cjs @@ -3,13 +3,42 @@ 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; + } + 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) { @@ -29,12 +58,45 @@ function postProcessing(options) { * @returns {Promise} 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; diff --git a/package.json b/package.json index cbbe1a2..f7a2d3f 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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" diff --git a/test/class.test.mjs b/test/class.test.mjs new file mode 100644 index 0000000..2f2223a --- /dev/null +++ b/test/class.test.mjs @@ -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" }, + }, + }, + }, + }); + }); +}); diff --git a/test/waterfall.test.mjs b/test/waterfall.test.mjs new file mode 100644 index 0000000..ec7d5b4 --- /dev/null +++ b/test/waterfall.test.mjs @@ -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" } } } }); + }); +}); From d45c3f2c932d3f8a26a34c239025d58d12ff706a Mon Sep 17 00:00:00 2001 From: Shahrul Nizam Selamat Date: Sun, 31 Aug 2025 15:46:46 +0800 Subject: [PATCH 2/2] coverage fix --- libs/executor.cjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/executor.cjs b/libs/executor.cjs index 6614da4..0b67312 100644 --- a/libs/executor.cjs +++ b/libs/executor.cjs @@ -21,7 +21,6 @@ function waterfallParser(query) { Object.assign(acc, item); return acc; } - return acc; }, current); return { @@ -70,7 +69,7 @@ class RqExtender { } compute(payload) { return rq(payload, { - methods: this.methods || {}, + methods: this.methods, config: (param) => this.getMethodsMap()[param], }); }