From 6e0d31b4433de66894e6190f77d0587feaa14af7 Mon Sep 17 00:00:00 2001 From: Etienne Noel Date: Mon, 31 Oct 2022 14:45:39 -0400 Subject: [PATCH 1/4] - Added caching mechanism and removed dependency to url-parse since nodejs comes with full features --- package-lock.json | 120 ++++++------------ package.json | 1 - packages/common/src/common.module.ts | 1 + .../common/src/utils/request.util.spec.ts | 66 ++++++++++ packages/common/src/utils/request.util.ts | 38 ++++++ packages/common/src/utils/utils.ts | 1 + packages/core/package.json | 2 +- packages/express/package.json | 3 +- packages/http/package.json | 3 +- packages/http/src/clients/http.client.ts | 6 +- .../src/errors/http-client-request.error.ts | 4 +- packages/http/src/wrappers/http.wrapper.ts | 6 +- packages/networking/package.json | 3 +- packages/networking/src/cache/cache.ts | 2 + .../src/cache/cached.router-route.ts | 39 ++++++ packages/networking/src/cache/router.cache.ts | 72 +++++++++++ packages/networking/src/networking.module.ts | 13 ++ .../query-parameter-decorator.resolver.ts | 6 +- .../query-parameters-decorator.resolver.ts | 15 ++- packages/networking/src/router.spec.ts | 116 ++++++++++------- packages/networking/src/router.ts | 69 +++++++--- 21 files changed, 418 insertions(+), 168 deletions(-) create mode 100644 packages/common/src/utils/request.util.spec.ts create mode 100644 packages/common/src/utils/request.util.ts create mode 100644 packages/common/src/utils/utils.ts create mode 100644 packages/networking/src/cache/cache.ts create mode 100644 packages/networking/src/cache/cached.router-route.ts create mode 100644 packages/networking/src/cache/router.cache.ts diff --git a/package-lock.json b/package-lock.json index 1da82307c..2d253e165 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,7 +37,6 @@ "@types/jest": "^27.4.1", "@types/lodash": "^4.14.168", "@types/node": "^14.14.31", - "@types/url-parse": "^1.4.3", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.11.0", @@ -6225,11 +6224,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/url-parse": { - "version": "1.4.3", - "dev": true, - "license": "MIT" - }, "node_modules/@types/uuid": { "version": "8.3.3", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.3.tgz", @@ -14538,10 +14532,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "license": "MIT" - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -15055,10 +15045,6 @@ "dev": true, "license": "ISC" }, - "node_modules/requires-port": { - "version": "1.0.0", - "license": "MIT" - }, "node_modules/resolve": { "version": "1.20.0", "dev": true, @@ -17055,14 +17041,6 @@ "integrity": "sha1-HbSK1CLTQCRpqH99l73r/k+x48g=", "peer": true }, - "node_modules/url-parse": { - "version": "1.5.1", - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/url-search-params": { "version": "0.10.2", "dev": true, @@ -17599,7 +17577,7 @@ }, "packages/auth0": { "name": "@pristine-ts/auth0", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17612,7 +17590,7 @@ }, "packages/aws": { "name": "@pristine-ts/aws", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@aws-sdk/client-dynamodb": "^3.48.0", @@ -17633,7 +17611,7 @@ }, "packages/aws-api-gateway": { "name": "@pristine-ts/aws-api-gateway", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/aws": "file:../common", @@ -17652,7 +17630,7 @@ }, "packages/aws-cognito": { "name": "@pristine-ts/aws-cognito", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17667,7 +17645,7 @@ }, "packages/aws-scheduling": { "name": "@pristine-ts/aws-scheduling", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/aws": "file:../aws", @@ -17678,7 +17656,7 @@ }, "packages/aws-xray": { "name": "@pristine-ts/aws-xray", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17689,7 +17667,7 @@ }, "packages/cloudflare": { "name": "@pristine-ts/cloudflare", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17704,7 +17682,7 @@ }, "packages/common": { "name": "@pristine-ts/common", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "reflect-metadata": "^0.1.13", @@ -17713,7 +17691,7 @@ }, "packages/configuration": { "name": "@pristine-ts/configuration", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common" @@ -17721,7 +17699,7 @@ }, "packages/core": { "name": "@pristine-ts/core", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17729,7 +17707,7 @@ "@pristine-ts/logging": "file:../logging", "@pristine-ts/security": "file:../security", "@pristine-ts/telemetry": "file:../telemetry", - "uuid": "^8.3.2" + "uuid": "^9.0.0" }, "devDependencies": { "@types/uuid": "^8.3.3" @@ -17756,8 +17734,9 @@ "link": true }, "packages/core/node_modules/uuid": { - "version": "8.3.2", - "license": "MIT", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "bin": { "uuid": "dist/bin/uuid" } @@ -17776,7 +17755,7 @@ }, "packages/express": { "name": "@pristine-ts/express", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17784,24 +17763,22 @@ "@pristine-ts/networking": "file:../networking", "@types/express": "^4.17.9", "express": "^4.17.1", - "reflect-metadata": "^0.1.13", - "url-parse": "^1.4.7" + "reflect-metadata": "^0.1.13" } }, "packages/http": { "name": "@pristine-ts/http", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", "@pristine-ts/logging": "file:../logging", - "lodash": "^4.17.21", - "url-parse": "^1.4.7" + "lodash": "^4.17.21" } }, "packages/jwt": { "name": "@pristine-ts/jwt", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17815,7 +17792,7 @@ }, "packages/logging": { "name": "@pristine-ts/logging", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17825,20 +17802,19 @@ }, "packages/networking": { "name": "@pristine-ts/networking", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", "@pristine-ts/core": "file:../core", "@pristine-ts/security": "file:../security", "@pristine-ts/telemetry": "file:../telemetry", - "lodash": "^4.17.21", - "url-parse": "^1.4.7" + "lodash": "^4.17.21" } }, "packages/redis": { "name": "@pristine-ts/redis", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@camaro/redis": "^2.2.5", @@ -17848,7 +17824,7 @@ }, "packages/scheduling": { "name": "@pristine-ts/scheduling", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17858,7 +17834,7 @@ }, "packages/security": { "name": "@pristine-ts/security", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17870,7 +17846,7 @@ }, "packages/sentry": { "name": "@pristine-ts/sentry", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17880,7 +17856,7 @@ }, "packages/stripe": { "name": "@pristine-ts/stripe", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17892,7 +17868,7 @@ }, "packages/telemetry": { "name": "@pristine-ts/telemetry", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/common": "file:../common", @@ -17920,7 +17896,7 @@ }, "packages/validation": { "name": "@pristine-ts/validation", - "version": "0.0.210", + "version": "0.0.214", "license": "ISC", "dependencies": { "@pristine-ts/class-validator": "^1.0.5", @@ -22783,7 +22759,7 @@ "@pristine-ts/security": "file:../security", "@pristine-ts/telemetry": "file:../telemetry", "@types/uuid": "^8.3.3", - "uuid": "^8.3.2" + "uuid": "^9.0.0" }, "dependencies": { "@pristine-ts/common": { @@ -22845,7 +22821,9 @@ } }, "uuid": { - "version": "8.3.2" + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" } } }, @@ -22891,8 +22869,7 @@ "@pristine-ts/networking": "file:../networking", "@types/express": "^4.17.9", "express": "^4.17.1", - "reflect-metadata": "^0.1.13", - "url-parse": "^1.4.7" + "reflect-metadata": "^0.1.13" } }, "@pristine-ts/logging": { @@ -22910,8 +22887,7 @@ "@pristine-ts/core": "file:../core", "@pristine-ts/security": "file:../security", "@pristine-ts/telemetry": "file:../telemetry", - "lodash": "^4.17.21", - "url-parse": "^1.4.7" + "lodash": "^4.17.21" } }, "@pristine-ts/security": { @@ -22938,8 +22914,7 @@ "@pristine-ts/networking": "file:../networking", "@types/express": "^4.17.9", "express": "^4.17.1", - "reflect-metadata": "^0.1.13", - "url-parse": "^1.4.7" + "reflect-metadata": "^0.1.13" } }, "@pristine-ts/http": { @@ -22947,8 +22922,7 @@ "requires": { "@pristine-ts/common": "file:../common", "@pristine-ts/logging": "file:../logging", - "lodash": "^4.17.21", - "url-parse": "^1.4.7" + "lodash": "^4.17.21" } }, "@pristine-ts/jwt": { @@ -22976,8 +22950,7 @@ "@pristine-ts/core": "file:../core", "@pristine-ts/security": "file:../security", "@pristine-ts/telemetry": "file:../telemetry", - "lodash": "^4.17.21", - "url-parse": "^1.4.7" + "lodash": "^4.17.21" } }, "@pristine-ts/redis": { @@ -23382,10 +23355,6 @@ "version": "2.0.0", "dev": true }, - "@types/url-parse": { - "version": "1.4.3", - "dev": true - }, "@types/uuid": { "version": "8.3.3", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.3.tgz", @@ -29085,9 +29054,6 @@ "strict-uri-encode": "^2.0.0" } }, - "querystringify": { - "version": "2.2.0" - }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -29447,9 +29413,6 @@ "version": "2.0.0", "dev": true }, - "requires-port": { - "version": "1.0.0" - }, "resolve": { "version": "1.20.0", "dev": true, @@ -30810,13 +30773,6 @@ "integrity": "sha1-HbSK1CLTQCRpqH99l73r/k+x48g=", "peer": true }, - "url-parse": { - "version": "1.5.1", - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "url-search-params": { "version": "0.10.2", "dev": true diff --git a/package.json b/package.json index c0a63f1a7..485115566 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "@types/jest": "^27.4.1", "@types/lodash": "^4.14.168", "@types/node": "^14.14.31", - "@types/url-parse": "^1.4.3", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "eslint": "^8.11.0", diff --git a/packages/common/src/common.module.ts b/packages/common/src/common.module.ts index 102623990..45798b2f9 100644 --- a/packages/common/src/common.module.ts +++ b/packages/common/src/common.module.ts @@ -7,6 +7,7 @@ export * from "./errors/errors"; export * from "./models/models"; export * from "./interfaces/interfaces"; export * from "./types/types"; +export * from "./utils/utils"; export * from "./common.module.keyname"; export const CommonModule: ModuleInterface = { diff --git a/packages/common/src/utils/request.util.spec.ts b/packages/common/src/utils/request.util.spec.ts new file mode 100644 index 000000000..5af2704c2 --- /dev/null +++ b/packages/common/src/utils/request.util.spec.ts @@ -0,0 +1,66 @@ +import {Request} from "../models/request"; +import {RequestUtil} from "./request.util"; +import {HttpMethod} from "../enums/http-method.enum"; + +describe("Request Util", () => { + it("should hash the same request twice", () => { + const rawBody = {}; + + const request: Request = new Request(HttpMethod.Get, "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"); + request.rawBody = rawBody; + request.setHeaders({ + "header1": "value1", + "header2": "value2", + "header3": "value3", + }); + + const request2: Request = new Request(HttpMethod.Get, "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?sort=ASC&query=searchTerm#anchorLink");; + request2.rawBody = rawBody; + request2.setHeaders({ + "header3": "value3", + "header2": "value2", + "header1": "value1", + }); + + const request1Hash = RequestUtil.hash(request); + const request2Hash = RequestUtil.hash(request2); + + expect(request1Hash).toBe(request2Hash); + }) + + it("should not hash different requests to the same value", () => { + const rawBody = {}; + + const request: Request = new Request(HttpMethod.Get, "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"); + request.rawBody = rawBody; + request.setHeaders({ + "header1": "value1", + "header2": "value2", + "header3": "value3", + }); + + const request2: Request = new Request(HttpMethod.Get, "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?sort=ASC&query=searchTermXXXXX#anchorLink");; + request2.rawBody = rawBody; + request2.setHeaders({ + "header3": "value3", + "header2": "value2", + "header1": "value1", + }); + + const request3: Request = new Request(HttpMethod.Get, "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?sort=ASC&query=searchTerm"); + request2.rawBody = rawBody; + request2.setHeaders({ + "header3": "value3", + "header2": "value2", + "header1": "value1", + }); + + const request1Hash = RequestUtil.hash(request); + const request2Hash = RequestUtil.hash(request2); + const request3Hash = RequestUtil.hash(request3); + + expect(request1Hash).not.toBe(request2Hash); + expect(request1Hash).not.toBe(request3Hash); + expect(request2Hash).not.toBe(request3Hash); + }) +}) \ No newline at end of file diff --git a/packages/common/src/utils/request.util.ts b/packages/common/src/utils/request.util.ts new file mode 100644 index 000000000..e894db3e5 --- /dev/null +++ b/packages/common/src/utils/request.util.ts @@ -0,0 +1,38 @@ +import { createHash } from 'crypto'; +import {Request} from "../models/request"; +import { URL } from 'url'; + +function sort(obj: any) { + const ret: any = {}; + + Object.keys(obj).sort().forEach(function (key) { + ret[key] = obj[key]; + }); + + return ret; +} + +export class RequestUtil { + static hash(request: Request): string | null { + const hash = createHash("md5"); + + const parsedUrl = new URL(request.url); + + parsedUrl.searchParams.sort(); + + hash.update(parsedUrl.pathname); + hash.update(request.httpMethod); + hash.update(parsedUrl.searchParams.toString()); + hash.update(parsedUrl.hash); + hash.update(JSON.stringify(sort(request.headers))); + + try { + hash.write(JSON.stringify(request.body)); + } catch (e) { + return null; + } + + + return hash.digest("hex"); + } +} \ No newline at end of file diff --git a/packages/common/src/utils/utils.ts b/packages/common/src/utils/utils.ts new file mode 100644 index 000000000..076f11d5e --- /dev/null +++ b/packages/common/src/utils/utils.ts @@ -0,0 +1 @@ +export * from "./request.util"; \ No newline at end of file diff --git a/packages/core/package.json b/packages/core/package.json index 6c343e895..c0e2e466f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,7 +25,7 @@ "@pristine-ts/logging": "file:../logging", "@pristine-ts/security": "file:../security", "@pristine-ts/telemetry": "file:../telemetry", - "uuid": "^8.3.2" + "uuid": "^9.0.0" }, "devDependencies": { "@types/uuid": "^8.3.3" diff --git a/packages/express/package.json b/packages/express/package.json index be4a4b336..e562148db 100644 --- a/packages/express/package.json +++ b/packages/express/package.json @@ -25,8 +25,7 @@ "@pristine-ts/networking": "file:../networking", "@types/express": "^4.17.9", "express": "^4.17.1", - "reflect-metadata": "^0.1.13", - "url-parse": "^1.4.7" + "reflect-metadata": "^0.1.13" }, "jest": { "transform": { diff --git a/packages/http/package.json b/packages/http/package.json index 684f2319f..fe77ab8c9 100644 --- a/packages/http/package.json +++ b/packages/http/package.json @@ -17,8 +17,7 @@ "dependencies": { "@pristine-ts/common": "file:../common", "@pristine-ts/logging": "file:../logging", - "lodash": "^4.17.21", - "url-parse": "^1.4.7" + "lodash": "^4.17.21" }, "author": "", "license": "ISC", diff --git a/packages/http/src/clients/http.client.ts b/packages/http/src/clients/http.client.ts index 7682081ba..6db507c23 100644 --- a/packages/http/src/clients/http.client.ts +++ b/packages/http/src/clients/http.client.ts @@ -2,7 +2,7 @@ import {inject, injectable, injectAll} from "tsyringe"; import {HttpClientInterface} from "../interfaces/http-client.interface"; import {HttpRequestInterface} from "../interfaces/http-request.interface"; import {HttpResponseInterface} from "../interfaces/http-response.interface"; -import Url from 'url-parse'; +import { URL } from 'url'; import {ResponseTypeEnum} from "../enums/response-type.enum"; import {HttpRequestOptions} from "../options/http-request.options."; import {assign} from "lodash"; @@ -185,8 +185,8 @@ export class HttpClient implements HttpClientInterface { const updatedRequest = request; // Updated the URL by using the 'location' header returned by the response. - const url = new Url(request.url, true); - url.set("pathname", response.headers.location); + const url = new URL(request.url); + url.pathname = response.headers.location; updatedRequest.url = url.toString() diff --git a/packages/http/src/errors/http-client-request.error.ts b/packages/http/src/errors/http-client-request.error.ts index 03d3a00d5..68db38477 100644 --- a/packages/http/src/errors/http-client-request.error.ts +++ b/packages/http/src/errors/http-client-request.error.ts @@ -1,12 +1,12 @@ import {LoggableError} from "@pristine-ts/common"; import {HttpRequestInterface} from "../interfaces/http-request.interface"; -import Url from "url-parse"; +import { URL } from 'url'; /** * This Error represents an error when making an http request using the http client */ export class HttpClientRequestError extends LoggableError { - public constructor(readonly message: string, readonly request: HttpRequestInterface, readonly url: Url) { + public constructor(readonly message: string, readonly request: HttpRequestInterface, readonly url: URL) { super(message, { request, url, diff --git a/packages/http/src/wrappers/http.wrapper.ts b/packages/http/src/wrappers/http.wrapper.ts index c5d1991f6..e78e9fc91 100644 --- a/packages/http/src/wrappers/http.wrapper.ts +++ b/packages/http/src/wrappers/http.wrapper.ts @@ -4,7 +4,7 @@ import {HttpResponseInterface} from "../interfaces/http-response.interface"; import * as http from "http"; import {IncomingMessage, request as httpRequest, RequestOptions} from "http"; import {request as httpsRequest} from "https"; -import Url from 'url-parse'; +import { URL } from 'url'; import {tag} from "@pristine-ts/common"; import {HttpClientRequestError} from "../errors/http-client-request.error"; import {HttpWrapperInterface} from "../interfaces/http-wrapper.interface"; @@ -24,10 +24,10 @@ export class HttpWrapper implements HttpWrapperInterface { executeRequest(request: HttpRequestInterface): Promise { return new Promise((resolve, reject) => { // Define the options required by the http and https modules. - const url = new Url(request.url, true); + const url = new URL(request.url); const options: RequestOptions = { host: url.hostname, - path: url.pathname + ((url.query === {}) ? "" : "?" + Object.keys(url.query).map(key => key + "=" + querystring.escape(url.query[key] ?? "")).join("&")), + path: url.pathname + url.search, method: request.httpMethod, headers: request.headers, port: url.port, diff --git a/packages/networking/package.json b/packages/networking/package.json index f0bbaaab2..9866dd0ea 100644 --- a/packages/networking/package.json +++ b/packages/networking/package.json @@ -24,8 +24,7 @@ "@pristine-ts/core": "file:../core", "@pristine-ts/security": "file:../security", "@pristine-ts/telemetry": "file:../telemetry", - "lodash": "^4.17.21", - "url-parse": "^1.4.7" + "lodash": "^4.17.21" }, "jest": { "transform": { diff --git a/packages/networking/src/cache/cache.ts b/packages/networking/src/cache/cache.ts new file mode 100644 index 000000000..4728a3d93 --- /dev/null +++ b/packages/networking/src/cache/cache.ts @@ -0,0 +1,2 @@ +export * from "./cached.router-route"; +export * from "./router.cache"; \ No newline at end of file diff --git a/packages/networking/src/cache/cached.router-route.ts b/packages/networking/src/cache/cached.router-route.ts new file mode 100644 index 000000000..361a6ce25 --- /dev/null +++ b/packages/networking/src/cache/cached.router-route.ts @@ -0,0 +1,39 @@ +import {MethodRouterNode} from "../nodes/method-router.node"; +import {Request, RequestUtil} from "@pristine-ts/common"; + +export class CachedRouterRoute { + private cachedControllerMethodArguments: { [requestHash: string]: any[] } = {}; + public routeParameters?: { [key: string]: string }; + + constructor(public readonly methodNode: MethodRouterNode, + ) { + } + + private hashRequest(request: Request): string | null { + return RequestUtil.hash(request); + } + + getCachedControllerMethodArguments(request: Request): any[] | undefined { + // Hashed the request + const hash = this.hashRequest(request); + + if(hash === null) { + return; + } + + // Return the arguments if the hashed request exists + return this.cachedControllerMethodArguments[hash]; + } + + cacheControllerMethodArguments(request: Request, methodArguments: any[]): void { + // Hashed the request + const hash = this.hashRequest(request); + + if(hash === null) { + return; + } + + // Save the method arguments + this.cachedControllerMethodArguments[hash] = methodArguments; + } +} \ No newline at end of file diff --git a/packages/networking/src/cache/router.cache.ts b/packages/networking/src/cache/router.cache.ts new file mode 100644 index 000000000..dbfaceb9b --- /dev/null +++ b/packages/networking/src/cache/router.cache.ts @@ -0,0 +1,72 @@ +import {Request, tag} from "@pristine-ts/common"; +import {singleton, injectable, inject} from "tsyringe"; +import {CachedRouterRoute} from "./cached.router-route"; +import {MethodRouterNode} from "../nodes/method-router.node"; +import {NetworkingModuleKeyname} from "../networking.module.keyname"; + +@injectable() +export class RouterCache { + public currentSize = 0; + + public maximumSize = 100; // In Bytes + + public routes: {[methodAndPath: string] : CachedRouterRoute} = {}; + + public constructor(@inject(`%${NetworkingModuleKeyname}.routerCache.isActive%`) private readonly isActive: boolean) { + } + + private cleanIfNeeded() { + if(this.currentSize < this.maximumSize) { + return; + } + + // For now, we will eliminate starting from the first key to the last, when the maximum size is achieved. + // We will eliminate 25% of the cache whenever we go over the threshold. + } + + public get(keyname: string): CachedRouterRoute | undefined { + if(this.isActive === false) { + return undefined; + } + + return this.routes[keyname]; + } + + public set(keyname: string, methodNode: MethodRouterNode): CachedRouterRoute { + const cachedRouterRoute = new CachedRouterRoute(methodNode); + + if(this.isActive === false) { + return cachedRouterRoute; + } + + // todo Calculate the size and add it to the current total + + // Whenever we add a new elemen to the cache, we have to check if the cache needs to be cleaned + this.cleanIfNeeded(); + + this.routes[keyname] = cachedRouterRoute; + + return cachedRouterRoute; + } + + public getCachedControllerMethodArguments(keyname: string, request: Request): any[] | undefined { + if(this.isActive === false) { + return undefined; + } + + return this.routes[keyname]?.getCachedControllerMethodArguments(request); + } + + public setControllerMethodArguments(keyname: string, request: Request, methodArguments: any[]) { + if(this.isActive === false) { + return; + } + + this.routes[keyname]?.cacheControllerMethodArguments(request, methodArguments); + + // todo Increase the size and add it to the current total; + + return; + } + +} \ No newline at end of file diff --git a/packages/networking/src/networking.module.ts b/packages/networking/src/networking.module.ts index 1aa1cf0fc..125f8ebdd 100644 --- a/packages/networking/src/networking.module.ts +++ b/packages/networking/src/networking.module.ts @@ -4,6 +4,7 @@ import {SecurityModule} from "@pristine-ts/security"; import {TelemetryModule} from "@pristine-ts/telemetry"; import {BooleanResolver, EnvironmentVariableResolver} from "@pristine-ts/configuration"; +export * from "./cache/cache"; export * from "./decorators/decorators"; export * from "./errors/errors"; export * from "./handlers/handlers"; @@ -58,6 +59,18 @@ export const NetworkingModule: ModuleInterface = { defaultResolvers: [ new EnvironmentVariableResolver("PRISTINE_NETWORKING_DEFAULT_CONTENT_TYPE_RESPONSE_HEADER"), ], + }, + + /** + * Activates or deactivates whether the Router Cache is on or off. + */ + { + parameterName: NetworkingModuleKeyname + ".routerCache.isActive", + isRequired: false, + defaultValue: false, + defaultResolvers: [ + new BooleanResolver(new EnvironmentVariableResolver("PRISTINE_NETWORKING_ROUTER_CACHE_IS_ACTIVE")), + ], } ], } diff --git a/packages/networking/src/resolvers/query-parameter-decorator.resolver.ts b/packages/networking/src/resolvers/query-parameter-decorator.resolver.ts index 2db5fb794..538521fc7 100644 --- a/packages/networking/src/resolvers/query-parameter-decorator.resolver.ts +++ b/packages/networking/src/resolvers/query-parameter-decorator.resolver.ts @@ -3,7 +3,7 @@ import {ControllerMethodParameterDecoratorResolverInterface} from "../interfaces import {Request} from "@pristine-ts/common"; import {IdentityInterface, moduleScoped, ServiceDefinitionTagEnum, tag} from "@pristine-ts/common"; import {NetworkingModuleKeyname} from "../networking.module.keyname"; -import Url from 'url-parse'; +import { URL } from 'url'; import {ParameterDecoratorInterface} from "../interfaces/parameter-decorator.interface"; import {QueryParameterDecoratorInterface} from "../interfaces/query-parameter-decorator.interface"; @@ -30,9 +30,9 @@ export class QueryParameterDecoratorResolver implements ControllerMethodParamete request: Request, routeParameters: { [p: string]: string }, identity?: IdentityInterface): Promise { - const url = new Url(request.url, true); + const url = new URL(request.url); - return Promise.resolve(url.query[methodArgument.queryParameterName] ?? null); + return Promise.resolve(url.searchParams.get(methodArgument.queryParameterName) ?? null); } /** diff --git a/packages/networking/src/resolvers/query-parameters-decorator.resolver.ts b/packages/networking/src/resolvers/query-parameters-decorator.resolver.ts index 520e2f925..6904926d0 100644 --- a/packages/networking/src/resolvers/query-parameters-decorator.resolver.ts +++ b/packages/networking/src/resolvers/query-parameters-decorator.resolver.ts @@ -3,7 +3,7 @@ import {ControllerMethodParameterDecoratorResolverInterface} from "../interfaces import {Request} from "@pristine-ts/common"; import {IdentityInterface, moduleScoped, ServiceDefinitionTagEnum, tag} from "@pristine-ts/common"; import {NetworkingModuleKeyname} from "../networking.module.keyname"; -import Url from 'url-parse'; +import { URL } from 'url'; import {ParameterDecoratorInterface} from "../interfaces/parameter-decorator.interface"; import {QueryParametersDecoratorInterface} from "../interfaces/query-parameters-decorator.interface"; @@ -29,9 +29,18 @@ export class QueryParametersDecoratorResolver implements ControllerMethodParamet request: Request, routeParameters: { [p: string]: string }, identity?: IdentityInterface): Promise { - const url = new Url(request.url, true); + const url = new URL(request.url); - return Promise.resolve(url.query ?? null); + let queryParameters: {[id: string]: string} | undefined = undefined; + + for (const [key, value] of url.searchParams.entries()) { + if(queryParameters === undefined) { + queryParameters = {}; + } + + queryParameters[key] = value; + } + return Promise.resolve(queryParameters ?? null); } /** diff --git a/packages/networking/src/router.spec.ts b/packages/networking/src/router.spec.ts index f96b9166d..a236fa1b1 100644 --- a/packages/networking/src/router.spec.ts +++ b/packages/networking/src/router.spec.ts @@ -17,6 +17,7 @@ import {HttpMethod, IdentityInterface, Request} from "@pristine-ts/common"; import {Span, TracingManagerInterface} from "@pristine-ts/telemetry"; import {DependencyContainer, container} from "tsyringe"; import {LogHandlerInterface} from "@pristine-ts/logging"; +import {RouterCache} from "./cache/router.cache"; describe("Router.spec", () => { let root: PathRouterNode; @@ -135,7 +136,7 @@ describe("Router.spec", () => { authenticate(request: Request, routeContext: any, container): Promise { return Promise.resolve(undefined); } - }); + }, new RouterCache(true)); router["root"] = root; @@ -160,7 +161,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { @@ -168,7 +169,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { @@ -176,7 +177,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - https://ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { @@ -184,7 +185,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { @@ -192,7 +193,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { @@ -200,7 +201,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { @@ -208,7 +209,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { @@ -216,7 +217,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { @@ -224,7 +225,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { @@ -232,7 +233,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { @@ -240,7 +241,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { @@ -248,7 +249,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { @@ -256,7 +257,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { @@ -264,7 +265,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { @@ -272,7 +273,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { @@ -280,7 +281,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { @@ -288,7 +289,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { @@ -296,7 +297,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { @@ -304,7 +305,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { @@ -312,7 +313,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { @@ -320,7 +321,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { @@ -328,7 +329,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { @@ -336,7 +337,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { @@ -344,7 +345,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { @@ -352,7 +353,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { @@ -360,7 +361,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { @@ -368,7 +369,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { @@ -376,7 +377,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { @@ -384,7 +385,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { @@ -392,7 +393,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { @@ -400,7 +401,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { @@ -408,7 +409,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { @@ -416,7 +417,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { @@ -424,7 +425,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { @@ -432,7 +433,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { @@ -440,7 +441,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { @@ -448,7 +449,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { @@ -456,7 +457,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { @@ -464,7 +465,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { @@ -472,7 +473,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { @@ -480,7 +481,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { @@ -488,7 +489,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { @@ -496,7 +497,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { @@ -504,7 +505,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { @@ -512,7 +513,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { @@ -520,7 +521,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", null, null, {}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); }) it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { @@ -528,7 +529,7 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); }) it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { @@ -536,7 +537,24 @@ describe("Router.spec", () => { const response = await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + }) + + it("Call twice (with caching) - PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { + request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; + + await router.execute(request, mockContainer); + + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + + // Tweak the request to ensure that the caching properly works + request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; + await router.execute(request, mockContainer); + + request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; + await router.execute(request, mockContainer); + + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); }) }); diff --git a/packages/networking/src/router.ts b/packages/networking/src/router.ts index 3a2f3e23c..6f8719a3e 100644 --- a/packages/networking/src/router.ts +++ b/packages/networking/src/router.ts @@ -8,7 +8,7 @@ import {Route} from "./models/route"; import {MethodRouterNode} from "./nodes/method-router.node"; import {ForbiddenHttpError} from "./errors/forbidden.http-error"; import {ControllerMethodParameterDecoratorResolver} from "./resolvers/controller-method-parameter-decorator.resolver"; -import Url from 'url-parse'; +import { URL } from 'url'; import {tag, HttpMethod, IdentityInterface, ServiceDefinitionTagEnum, Request, Response} from "@pristine-ts/common"; import {AuthenticationManagerInterface, AuthorizerManagerInterface} from "@pristine-ts/security"; import {LogHandlerInterface} from "@pristine-ts/logging"; @@ -19,6 +19,8 @@ import {RouteMethodDecorator} from "./interfaces/route-method-decorator.interfac import {mergeWith} from "lodash"; import {RequestInterceptorInterface} from "./interfaces/request-interceptor.interface"; import {HttpError} from "./errors/http.error"; +import {CachedRouterRoute} from "./cache/cached.router-route"; +import {RouterCache} from "./cache/router.cache"; /** * The router service is the service that creates the routing tree from the controllers. @@ -36,6 +38,7 @@ export class Router implements RouterInterface { * The router service is the service that creates the routing tree from the controllers. * It also executes a request properly by routing it to the intended controller and returns the response. * @param loghandler The log handler + * @param cache The cache that optimizes the response time by caching frequent requests. * @param controllerMethodParameterDecoratorResolver The controller method parameter decorator resolver used to resolve the values. * @param authorizerManager The authorizer manager to validate authorization. * @param authenticationManager The authentication manager to validate authentication. @@ -43,7 +46,8 @@ export class Router implements RouterInterface { public constructor( @inject("LogHandlerInterface") private readonly loghandler: LogHandlerInterface, private readonly controllerMethodParameterDecoratorResolver: ControllerMethodParameterDecoratorResolver, @inject("AuthorizerManagerInterface") private readonly authorizerManager: AuthorizerManagerInterface, - @inject("AuthenticationManagerInterface") private readonly authenticationManager: AuthenticationManagerInterface) { + @inject("AuthenticationManagerInterface") private readonly authenticationManager: AuthenticationManagerInterface, + private readonly cache: RouterCache) { } /** @@ -178,15 +182,27 @@ export class Router implements RouterInterface { return new Promise(async (resolve, reject) => { // Start by decomposing the URL. Set second parameter to true since we want to parse the query strings - const url = new Url(request.url, false); + const url = new URL(request.url); // Split the path name const splitPath = UrlUtil.splitPath(url.pathname); - const methodNodeSpan = tracingManager.startSpan(SpanKeynameEnum.RouterFindMethodRouterNode, SpanKeynameEnum.RouterRequestExecution); - // Retrieve the node to have information about the controller - const methodNode: MethodRouterNode = this.root.find(splitPath, request.httpMethod) as MethodRouterNode; - methodNodeSpan.end(); + // We cache the method node + const cacheKeyname = request.httpMethod + "_" + url.pathname; + + let cachedRouterRoute: CachedRouterRoute | undefined = this.cache.get(cacheKeyname); + + let methodNode: MethodRouterNode | undefined = cachedRouterRoute?.methodNode; + + if(methodNode === undefined) { + const methodNodeSpan = tracingManager.startSpan(SpanKeynameEnum.RouterFindMethodRouterNode, SpanKeynameEnum.RouterRequestExecution); + // Retrieve the node to have information about the controller + methodNode = this.root.find(splitPath, request.httpMethod) as MethodRouterNode; + methodNodeSpan.end(); + + // Cache the method node. + cachedRouterRoute = this.cache.set(cacheKeyname, methodNode); + } this.loghandler.debug("Router - Execute request", { rootNode: this.root, @@ -208,7 +224,11 @@ export class Router implements RouterInterface { } // Get the route parameters - const routeParameters = (methodNode.parent as PathRouterNode).getRouteParameters(splitPath.reverse()); + const routeParameters = cachedRouterRoute?.routeParameters ?? (methodNode.parent as PathRouterNode).getRouteParameters(splitPath.reverse()); + + if(cachedRouterRoute !== undefined && cachedRouterRoute.routeParameters === undefined) { + cachedRouterRoute.routeParameters = routeParameters; + } // Instantiate the controller const routerControllerResolverSpan = tracingManager.startSpan(SpanKeynameEnum.RouterControllerResolver, SpanKeynameEnum.RouterRequestExecution); @@ -275,15 +295,34 @@ export class Router implements RouterInterface { }, NetworkingModuleKeyname) // Resolve the value to inject in the method arguments that have a decorator resolver - const resolvedMethodArguments: any[] = []; + let resolvedMethodArguments: any[] | undefined = this.cache.getCachedControllerMethodArguments(cacheKeyname, interceptedRequest); - for (const methodArgument of methodNode.route.methodArguments) { - resolvedMethodArguments.push(await this.controllerMethodParameterDecoratorResolver.resolve(methodArgument, interceptedRequest, routeParameters, identity)); - } + // If the cache did not contain the cached controller method arguments + if(resolvedMethodArguments === undefined) { + this.loghandler.debug("Resolved method arguments were not cached, currently resolving", { + request, + interceptedRequest, + }, NetworkingModuleKeyname); + resolvedMethodArguments = []; - this.loghandler.debug("Router - Controller argument resolved", { - resolvedMethodArguments, - }, NetworkingModuleKeyname) + for (const methodArgument of methodNode.route.methodArguments) { + resolvedMethodArguments.push(await this.controllerMethodParameterDecoratorResolver.resolve(methodArgument, interceptedRequest, routeParameters, identity)); + } + + this.cache.setControllerMethodArguments(cacheKeyname, interceptedRequest, resolvedMethodArguments); + + this.loghandler.debug("Resolved method arguments cached for next call.", { + request, + interceptedRequest, + resolvedMethodArguments, + }, NetworkingModuleKeyname); + } else { + this.loghandler.debug("Method arguments were successfully cached and will be used.", { + request, + interceptedRequest, + resolvedMethodArguments, + }, NetworkingModuleKeyname); + } const controllerResponse = controller[methodNode.route.methodPropertyKey](...resolvedMethodArguments); From 8612ef47a9435af151492fc69e25602d8148c46d Mon Sep 17 00:00:00 2001 From: Etienne Noel Date: Mon, 31 Oct 2022 14:58:58 -0400 Subject: [PATCH 2/4] - Minor update --- packages/networking/src/router.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/networking/src/router.ts b/packages/networking/src/router.ts index 6f8719a3e..8304f62c4 100644 --- a/packages/networking/src/router.ts +++ b/packages/networking/src/router.ts @@ -182,7 +182,10 @@ export class Router implements RouterInterface { return new Promise(async (resolve, reject) => { // Start by decomposing the URL. Set second parameter to true since we want to parse the query strings - const url = new URL(request.url); + // Node's URL default package absolutely needs a base (or a host) if none is provided in the URL. Pristine doesn't + // care at all about the host. If one is provided in the url, the base provided will do nothing. + // If none is provided, then the default base will be used and all is well. + const url = new URL(request.url, "http://localhost"); // Split the path name const splitPath = UrlUtil.splitPath(url.pathname); From c4bd4ec7bd5a2b1108369c77c901b98a5e8c1a3a Mon Sep 17 00:00:00 2001 From: Etienne Noel Date: Mon, 31 Oct 2022 18:07:59 -0400 Subject: [PATCH 3/4] updated e2e --- packages/networking/src/router.spec.ts | 687 ++++++++++--------------- 1 file changed, 286 insertions(+), 401 deletions(-) diff --git a/packages/networking/src/router.spec.ts b/packages/networking/src/router.spec.ts index a236fa1b1..f93dc87c6 100644 --- a/packages/networking/src/router.spec.ts +++ b/packages/networking/src/router.spec.ts @@ -18,14 +18,13 @@ import {Span, TracingManagerInterface} from "@pristine-ts/telemetry"; import {DependencyContainer, container} from "tsyringe"; import {LogHandlerInterface} from "@pristine-ts/logging"; import {RouterCache} from "./cache/router.cache"; +import {RouterInterface} from "./interfaces/router.interface"; describe("Router.spec", () => { let root: PathRouterNode; let mockController: any; - let router: Router; - let mockContainer: DependencyContainer; let mockTracingManager: TracingManagerInterface = { @@ -42,8 +41,6 @@ describe("Router.spec", () => { }; - let request: Request; - let spyMethodController: any; beforeAll(() => { @@ -113,9 +110,21 @@ describe("Router.spec", () => { dog20PutMethodNode["route"] = route; spyMethodController = jest.spyOn(mockController, "route"); + }); + + // Force the node as the root node + const getRouter = (activateCache: boolean) => { + // Create the MockContainer + mockContainer = container.createChildContainer(); + mockContainer.resolve = (token: any) => { + if (token === "TracingManagerInterface") { + return mockTracingManager; + } - // Force the node as the root node - router = new Router( { + return mockController; + } + + const router = new Router({ critical(message: string, extra?: any): void { }, debug(message: string, extra?: any): void { }, error(message: string, extra?: any): void { @@ -123,7 +132,7 @@ describe("Router.spec", () => { }, warning(message: string, extra?: any): void { }, terminate() { } - },new ControllerMethodParameterDecoratorResolver([ + }, new ControllerMethodParameterDecoratorResolver([ new BodyParameterDecoratorResolver(), new QueryParameterDecoratorResolver(), new QueryParametersDecoratorResolver(), @@ -136,416 +145,288 @@ describe("Router.spec", () => { authenticate(request: Request, routeContext: any, container): Promise { return Promise.resolve(undefined); } - }, new RouterCache(true)); + }, new RouterCache(activateCache)); router["root"] = root; - // Create the MockContainer - mockContainer = container.createChildContainer(); - mockContainer.resolve = (token: any) => { - if(token === "TracingManagerInterface") { - return mockTracingManager; - } + return router; + } - return mockController; - } - }) - beforeEach(() => { - request = new Request(HttpMethod.Put, ""); + it("should call the correct methods for the routers", async () => { + const request = new Request(HttpMethod.Put, ""); request.body = {name: "name"}; - }) - - it("PUT - https://ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { - request.url = "https://ima-tech.ca/api/1.0/dogs/caniche-royal"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { - request.url = "https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { - request.url = "https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - https://ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { - request.url = "https://ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { - request.url = "https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { - request.url = "https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { - request.url = "http://ima-tech.ca/api/1.0/dogs/caniche-royal"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { - request.url = "http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { - request.url = "http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { - request.url = "http://ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { - request.url = "http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { - request.url = "http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { - request.url = "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { - request.url = "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { - request.url = "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { - request.url = "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { - request.url = "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { - request.url = "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { - request.url = "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { - request.url = "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { - request.url = "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { - request.url = "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { - request.url = "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { - request.url = "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { - request.url = "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { - request.url = "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { - request.url = "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { - request.url = "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { - request.url = "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { - request.url = "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { - request.url = "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { - request.url = "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { - request.url = "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { - request.url = "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { - request.url = "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - it("PUT - http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { - request.url = "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { - request.url = "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { - request.url = "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { - request.url = "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { - request.url = "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { - request.url = "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { - request.url = "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", async () => { - request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", async () => { - request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) - - it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", async () => { - request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) - - it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", async () => { - request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink"; - - const response = await router.execute(request, mockContainer); - - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", null, null, null, request.body); - }) - - it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", async () => { - request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink"; + const requestUrls: { url: string, expectedArguments: any[] }[] = [ + { + url: "https://ima-tech.ca/api/1.0/dogs/caniche-royal", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "https://ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "https://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "http://ima-tech.ca/api/1.0/dogs/caniche-royal", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "http://ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "http://ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "https://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "http://www.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "https://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "http://subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "https://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + { + url: "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal#anchorLink", + expectedArguments: ["caniche-royal", null, null, null, request.body], + }, + { + url: "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm", + expectedArguments: ["caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body], + }, + { + url: "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", + expectedArguments: ["caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body], + }, + ]; - const response = await router.execute(request, mockContainer); + for (let router of [getRouter(false), getRouter(true)]) { + for (const requestUrl of requestUrls) { + request.url = requestUrl.url; - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", null, {"query": "searchTerm"}, request.body); - }) + await router.execute(request, mockContainer); - it("PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { - request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; - - const response = await router.execute(request, mockContainer); + expect(spyMethodController).toHaveBeenLastCalledWith(...requestUrl.expectedArguments); + } + } + }); - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); - }) it("Call twice (with caching) - PUT - http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink", async () => { + const router = getRouter(true) + const request = new Request(HttpMethod.Put, ""); request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body); // Tweak the request to ensure that the caching properly works request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; @@ -554,7 +435,11 @@ describe("Router.spec", () => { request.url = "http://www.subdomain.ima-tech.ca/api/1.0/dogs/caniche-royal?query=searchTerm&sort=ASC#anchorLink"; await router.execute(request, mockContainer); - expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", {"query": "searchTerm", "sort": "ASC"}, request.body); + expect(spyMethodController).toHaveBeenLastCalledWith("caniche-royal", "searchTerm", "ASC", { + "query": "searchTerm", + "sort": "ASC" + }, request.body); }) + }); From 1b69621c47b90e435d401988c7a3e6461a229077 Mon Sep 17 00:00:00 2001 From: Etienne Noel Date: Mon, 31 Oct 2022 21:08:53 -0400 Subject: [PATCH 4/4] updated for now. --- packages/networking/src/cache/router.cache.ts | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/packages/networking/src/cache/router.cache.ts b/packages/networking/src/cache/router.cache.ts index dbfaceb9b..690a03c43 100644 --- a/packages/networking/src/cache/router.cache.ts +++ b/packages/networking/src/cache/router.cache.ts @@ -6,22 +6,13 @@ import {NetworkingModuleKeyname} from "../networking.module.keyname"; @injectable() export class RouterCache { - public currentSize = 0; - - public maximumSize = 100; // In Bytes - public routes: {[methodAndPath: string] : CachedRouterRoute} = {}; public constructor(@inject(`%${NetworkingModuleKeyname}.routerCache.isActive%`) private readonly isActive: boolean) { } private cleanIfNeeded() { - if(this.currentSize < this.maximumSize) { - return; - } - - // For now, we will eliminate starting from the first key to the last, when the maximum size is achieved. - // We will eliminate 25% of the cache whenever we go over the threshold. + // todo: implement a proper LRU cache and also, remove the two layers of cache and flatten it to one. } public get(keyname: string): CachedRouterRoute | undefined { @@ -41,7 +32,7 @@ export class RouterCache { // todo Calculate the size and add it to the current total - // Whenever we add a new elemen to the cache, we have to check if the cache needs to be cleaned + // Whenever we add a new element to the cache, we have to check if the cache needs to be cleaned this.cleanIfNeeded(); this.routes[keyname] = cachedRouterRoute; @@ -63,9 +54,7 @@ export class RouterCache { } this.routes[keyname]?.cacheControllerMethodArguments(request, methodArguments); - - // todo Increase the size and add it to the current total; - + return; }