@centeredgesoft/runtime-mock-routes 2.9.2 → 2.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devcontainer/devcontainer.json +31 -16
- package/build/index.js +22 -8
- package/build/run.js +16 -27
- package/build/server.d.ts +7 -0
- package/build/server.js +104 -0
- package/build/src/app.d.ts +7 -7
- package/build/src/app.js +24 -24
- package/build/src/appV2.d.ts +7 -7
- package/build/src/appV2.js +49 -23
- package/package.json +15 -12
- package/run.ts +18 -34
- package/server.ts +103 -0
- package/src/app.test.ts +1 -1
- package/src/app.ts +6 -6
- package/src/appV2.test.ts +1 -1
- package/src/appV2.ts +39 -10
- package/.devcontainer/Dockerfile +0 -16
|
@@ -1,32 +1,47 @@
|
|
|
1
1
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
|
2
2
|
// https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/typescript-node
|
|
3
3
|
{
|
|
4
|
-
"name": "
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"args": {
|
|
11
|
-
"VARIANT": "16-bullseye"
|
|
4
|
+
"name": "Aspire",
|
|
5
|
+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
|
6
|
+
"image": "mcr.microsoft.com/devcontainers/dotnet:dev-10.0-noble",
|
|
7
|
+
"features": {
|
|
8
|
+
"ghcr.io/devcontainers/features/node:1": {
|
|
9
|
+
"version": 24
|
|
12
10
|
}
|
|
13
11
|
},
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
"hostRequirements": {
|
|
14
|
+
"cpus": 4,
|
|
15
|
+
"memory": "16gb",
|
|
16
|
+
"storage": "32gb"
|
|
17
|
+
},
|
|
17
18
|
|
|
19
|
+
"onCreateCommand": "curl -sSL https://aspire.dev/install.sh | bash",
|
|
20
|
+
"postStartCommand": "dotnet dev-certs https --trust",
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
"customizations": {
|
|
23
|
+
"vscode": {
|
|
24
|
+
// Set *default* container specific settings.json values on container create.
|
|
25
|
+
"settings": {},
|
|
26
|
+
// Add the IDs of extensions you want installed when the container is created.
|
|
27
|
+
"extensions": [
|
|
28
|
+
"ms-dotnettools.csdevkit",
|
|
29
|
+
"ms-vscode.vscode-typescript-next",
|
|
30
|
+
"dbaeumer.vscode-eslint",
|
|
31
|
+
"GitHub.copilot-chat",
|
|
32
|
+
"GitHub.copilot",
|
|
33
|
+
"github.vscode-github-actions",
|
|
34
|
+
"github.vscode-pull-request-github"
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
23
38
|
|
|
24
39
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
25
40
|
// "forwardPorts": [],
|
|
26
41
|
|
|
27
42
|
// Use 'postCreateCommand' to run commands after the container is created.
|
|
28
|
-
|
|
43
|
+
"postCreateCommand": "npm install"
|
|
29
44
|
|
|
30
45
|
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
|
31
|
-
"remoteUser": "node"
|
|
46
|
+
// "remoteUser": "node"
|
|
32
47
|
}
|
package/build/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -11,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
11
15
|
}) : function(o, v) {
|
|
12
16
|
o["default"] = v;
|
|
13
17
|
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
21
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
36
|
exports.appV2 = exports.appV1 = void 0;
|
|
23
37
|
exports.appV1 = __importStar(require("./src/app"));
|
package/build/run.js
CHANGED
|
@@ -1,32 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
AppVersion["v2"] = "v2";
|
|
12
|
-
})(AppVersion || (AppVersion = {}));
|
|
13
|
-
commander_1.program.version("2.1.0")
|
|
14
|
-
.option("-p, --port <port>", "Port to run the server one", process.env.RUNTIME_MOCK_ROUTES_PORT || "8080")
|
|
15
|
-
.option("-s, --seed <filePath>", "File path to seed the application", process.env.RUNTIME_MOCK_ROUTES_FILE_PATH)
|
|
16
|
-
.option("-a, --appVersion <appVersion>", "App Version to use", AppVersion.v2);
|
|
17
|
-
commander_1.program.parse(process.argv);
|
|
18
|
-
let initialRequests = {};
|
|
19
|
-
if (commander_1.program.seed) {
|
|
20
|
-
const filePath = path_1.resolve(commander_1.program.seed);
|
|
21
|
-
const funcOrJson = require(filePath);
|
|
22
|
-
initialRequests = typeof funcOrJson === 'function' ? funcOrJson() : funcOrJson;
|
|
4
|
+
const sdk_node_1 = require("@opentelemetry/sdk-node");
|
|
5
|
+
const instrumentation_express_1 = require("@opentelemetry/instrumentation-express");
|
|
6
|
+
const instrumentation_http_1 = require("@opentelemetry/instrumentation-http");
|
|
7
|
+
let logRecordProcessors = undefined;
|
|
8
|
+
if (!process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {
|
|
9
|
+
// OTLP isn't configured, fallback to console logging
|
|
10
|
+
logRecordProcessors = [new sdk_node_1.logs.SimpleLogRecordProcessor(new sdk_node_1.logs.ConsoleLogRecordExporter())];
|
|
23
11
|
}
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
app.listen(commander_1.program.port, () => {
|
|
31
|
-
console.log(`⚡️[server]: Server is running at http://localhost:${commander_1.program.port}`);
|
|
12
|
+
const sdk = new sdk_node_1.NodeSDK({
|
|
13
|
+
logRecordProcessors: logRecordProcessors,
|
|
14
|
+
instrumentations: [
|
|
15
|
+
new instrumentation_http_1.HttpInstrumentation(),
|
|
16
|
+
new instrumentation_express_1.ExpressInstrumentation()
|
|
17
|
+
],
|
|
32
18
|
});
|
|
19
|
+
sdk.start();
|
|
20
|
+
// Delay import so we register the OTEL SDK before loading modules like http/express
|
|
21
|
+
require("./server").runServer(sdk);
|
package/build/server.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.runServer = exports.AppVersion = void 0;
|
|
16
|
+
const fs_1 = __importDefault(require("fs"));
|
|
17
|
+
const http_1 = __importDefault(require("http"));
|
|
18
|
+
const https_1 = __importDefault(require("https"));
|
|
19
|
+
const commander_1 = require("commander");
|
|
20
|
+
const path_1 = require("path");
|
|
21
|
+
const app_1 = require("./src/app");
|
|
22
|
+
const appV2_1 = require("./src/appV2");
|
|
23
|
+
var AppVersion;
|
|
24
|
+
(function (AppVersion) {
|
|
25
|
+
AppVersion["v1"] = "v1";
|
|
26
|
+
AppVersion["v2"] = "v2";
|
|
27
|
+
})(AppVersion || (exports.AppVersion = AppVersion = {}));
|
|
28
|
+
const runServer = (sdk) => {
|
|
29
|
+
commander_1.program.version("2.1.0")
|
|
30
|
+
.option("-p, --port <port>", "Port to run the server one", process.env.RUNTIME_MOCK_ROUTES_PORT || "8080")
|
|
31
|
+
.option("--https-port <httpsPort>", "Enable TLS for the server", process.env.RUNTIME_MOCK_ROUTES_HTTPS_PORT || "8443")
|
|
32
|
+
.option("--tlsCert <tlsCertPath>", "Path to the TLS certificate file", process.env.HTTPS_CERT_FILE)
|
|
33
|
+
.option("--tlsKey <tlsKeyPath>", "Path to the TLS key file", process.env.HTTPS_CERT_KEY_FILE)
|
|
34
|
+
.option("-s, --seed <filePath>", "File path to seed the application", process.env.RUNTIME_MOCK_ROUTES_FILE_PATH)
|
|
35
|
+
.option("-a, --appVersion <appVersion>", "App Version to use", AppVersion.v2);
|
|
36
|
+
commander_1.program.parse(process.argv);
|
|
37
|
+
let initialRequests = {};
|
|
38
|
+
if (commander_1.program.seed) {
|
|
39
|
+
const filePath = (0, path_1.resolve)(commander_1.program.seed);
|
|
40
|
+
const funcOrJson = require(filePath);
|
|
41
|
+
initialRequests = typeof funcOrJson === 'function' ? funcOrJson() : funcOrJson;
|
|
42
|
+
}
|
|
43
|
+
const appVersions = {
|
|
44
|
+
v1: app_1.appFactory,
|
|
45
|
+
v2: appV2_1.appFactory
|
|
46
|
+
};
|
|
47
|
+
const appFactory = appVersions[commander_1.program.appVersion] || appV2_1.appFactory;
|
|
48
|
+
const app = appFactory(initialRequests);
|
|
49
|
+
// Setup HTTPS options
|
|
50
|
+
const httpsOptions = commander_1.program.tlsCert && commander_1.program.tlsKey && fs_1.default.existsSync(commander_1.program.tlsCert) && fs_1.default.existsSync(commander_1.program.tlsKey)
|
|
51
|
+
? {
|
|
52
|
+
cert: fs_1.default.readFileSync(commander_1.program.tlsCert),
|
|
53
|
+
key: fs_1.default.readFileSync(commander_1.program.tlsKey),
|
|
54
|
+
enabled: true
|
|
55
|
+
}
|
|
56
|
+
: { enabled: false };
|
|
57
|
+
const httpServer = http_1.default.createServer(app);
|
|
58
|
+
const httpsServer = httpsOptions.enabled ? https_1.default.createServer(httpsOptions, app) : null;
|
|
59
|
+
httpServer.listen(commander_1.program.port, () => {
|
|
60
|
+
console.log(`⚡️[server]: Server is running at http://localhost:${commander_1.program.port}`);
|
|
61
|
+
});
|
|
62
|
+
if (httpsServer) {
|
|
63
|
+
httpsServer.listen(commander_1.program.httpsPort, () => {
|
|
64
|
+
console.log(`⚡️[server]: Server is running at https://localhost:${commander_1.program.httpsPort}`);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// Register signal handlers
|
|
68
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
69
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
70
|
+
// Graceful shutdown handler
|
|
71
|
+
let isShuttingDown = false;
|
|
72
|
+
function gracefulShutdown(signal) {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
if (isShuttingDown)
|
|
75
|
+
return;
|
|
76
|
+
isShuttingDown = true;
|
|
77
|
+
// Close servers
|
|
78
|
+
const closePromises = [];
|
|
79
|
+
closePromises.push(closeServer(httpServer));
|
|
80
|
+
closePromises.push(closeServer(httpsServer));
|
|
81
|
+
yield Promise.all(closePromises);
|
|
82
|
+
try {
|
|
83
|
+
yield sdk.shutdown();
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
console.log("Error shutting down OTEL SDK", err);
|
|
87
|
+
}
|
|
88
|
+
console.log('Shutdown complete, exiting process');
|
|
89
|
+
process.exit(0);
|
|
90
|
+
function closeServer(httpServer) {
|
|
91
|
+
if (!httpServer)
|
|
92
|
+
return Promise.resolve();
|
|
93
|
+
const serverType = httpServer instanceof https_1.default.Server ? 'HTTPS' : 'HTTP';
|
|
94
|
+
return new Promise(resolve => {
|
|
95
|
+
httpServer.close(() => {
|
|
96
|
+
resolve();
|
|
97
|
+
});
|
|
98
|
+
httpServer.closeAllConnections();
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
exports.runServer = runServer;
|
package/build/src/app.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
type SupportedMethodsType = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
|
2
2
|
export declare const SupportedMethodsColection: SupportedMethodsType;
|
|
3
|
-
export
|
|
4
|
-
export declare const isSupportedMethod: (obj: any) => obj is
|
|
3
|
+
export type SupportedMethod = SupportedMethodsType[number];
|
|
4
|
+
export declare const isSupportedMethod: (obj: any) => obj is SupportedMethod;
|
|
5
5
|
export interface RuntimeRequestBody {
|
|
6
6
|
path: string;
|
|
7
7
|
body: any;
|
|
@@ -13,8 +13,8 @@ export interface RuntimeRequestCollection {
|
|
|
13
13
|
[path: string]: RuntimeRequestBody;
|
|
14
14
|
}
|
|
15
15
|
export declare const isRuntimeRequestCollection: (obj: any) => obj is RuntimeRequestCollection;
|
|
16
|
-
export
|
|
17
|
-
export declare const isMethodBasedRuntimeRequestCollection: (obj: any) => obj is
|
|
16
|
+
export type MethodBasedRuntimeRequestCollection = Partial<Record<SupportedMethod, RuntimeRequestCollection>>;
|
|
17
|
+
export declare const isMethodBasedRuntimeRequestCollection: (obj: any) => obj is MethodBasedRuntimeRequestCollection;
|
|
18
18
|
interface CleanRuntimeRequestCollectionParams {
|
|
19
19
|
collection: RuntimeRequestCollection;
|
|
20
20
|
method: SupportedMethod;
|
|
@@ -22,6 +22,6 @@ interface CleanRuntimeRequestCollectionParams {
|
|
|
22
22
|
export declare const cleanRuntimeRequestCollection: (params: CleanRuntimeRequestCollectionParams) => RuntimeRequestCollection;
|
|
23
23
|
export declare const cleanMethodBasedRuntimeRequestCollection: (obj: MethodBasedRuntimeRequestCollection) => MethodBasedRuntimeRequestCollection;
|
|
24
24
|
export declare const singleQueryParam: (obj: any) => string;
|
|
25
|
-
export declare const appFactory: (runtimeCollection?: MethodBasedRuntimeRequestCollection) =>
|
|
26
|
-
declare const app:
|
|
25
|
+
export declare const appFactory: (runtimeCollection?: MethodBasedRuntimeRequestCollection) => any;
|
|
26
|
+
declare const app: any;
|
|
27
27
|
export default app;
|
package/build/src/app.js
CHANGED
|
@@ -27,7 +27,7 @@ const sort_keys_1 = __importDefault(require("sort-keys"));
|
|
|
27
27
|
const chance = new chance_1.default();
|
|
28
28
|
handlebars_1.default.registerHelper('faker', (funcName, ...rest) => {
|
|
29
29
|
try {
|
|
30
|
-
const func = lodash_1.get(faker_1.default, funcName, () => '');
|
|
30
|
+
const func = (0, lodash_1.get)(faker_1.default, funcName, () => '');
|
|
31
31
|
return func(...rest);
|
|
32
32
|
}
|
|
33
33
|
catch (err) {
|
|
@@ -36,7 +36,7 @@ handlebars_1.default.registerHelper('faker', (funcName, ...rest) => {
|
|
|
36
36
|
});
|
|
37
37
|
handlebars_1.default.registerHelper('chance', (funcName, ...rest) => {
|
|
38
38
|
try {
|
|
39
|
-
const func = lodash_1.get(chance, funcName, () => '');
|
|
39
|
+
const func = (0, lodash_1.get)(chance, funcName, () => '');
|
|
40
40
|
return func.call(chance, ...rest);
|
|
41
41
|
}
|
|
42
42
|
catch (err) {
|
|
@@ -51,7 +51,7 @@ const isRuntimeRequestBody = (obj) => {
|
|
|
51
51
|
return typeof obj.path === 'string'
|
|
52
52
|
&& obj.hasOwnProperty('body')
|
|
53
53
|
&& (obj.status === undefined || !isNaN(Number(obj.status)))
|
|
54
|
-
&& exports.isSupportedMethod(obj.method);
|
|
54
|
+
&& (0, exports.isSupportedMethod)(obj.method);
|
|
55
55
|
}
|
|
56
56
|
catch (_) {
|
|
57
57
|
return false;
|
|
@@ -60,7 +60,7 @@ const isRuntimeRequestBody = (obj) => {
|
|
|
60
60
|
exports.isRuntimeRequestBody = isRuntimeRequestBody;
|
|
61
61
|
const isRuntimeRequestCollection = (obj) => {
|
|
62
62
|
try {
|
|
63
|
-
return !Object.keys(obj).some(k => !exports.isRuntimeRequestBody(obj[k]));
|
|
63
|
+
return !Object.keys(obj).some(k => !(0, exports.isRuntimeRequestBody)(obj[k]));
|
|
64
64
|
}
|
|
65
65
|
catch (_) {
|
|
66
66
|
return false;
|
|
@@ -69,7 +69,7 @@ const isRuntimeRequestCollection = (obj) => {
|
|
|
69
69
|
exports.isRuntimeRequestCollection = isRuntimeRequestCollection;
|
|
70
70
|
const isMethodBasedRuntimeRequestCollection = (obj) => {
|
|
71
71
|
try {
|
|
72
|
-
return !Object.keys(obj).some(k => !(exports.isSupportedMethod(k) && exports.isRuntimeRequestCollection(obj[k])));
|
|
72
|
+
return !Object.keys(obj).some(k => !((0, exports.isSupportedMethod)(k) && (0, exports.isRuntimeRequestCollection)(obj[k])));
|
|
73
73
|
}
|
|
74
74
|
catch (_) {
|
|
75
75
|
return false;
|
|
@@ -88,7 +88,7 @@ const cleanMethodBasedRuntimeRequestCollection = (obj) => {
|
|
|
88
88
|
return Object.keys(obj).reduce((acc, curr) => {
|
|
89
89
|
const method = curr;
|
|
90
90
|
const collection = obj[curr];
|
|
91
|
-
return Object.assign(Object.assign({}, acc), { [curr]: exports.cleanRuntimeRequestCollection({ method, collection }) });
|
|
91
|
+
return Object.assign(Object.assign({}, acc), { [curr]: (0, exports.cleanRuntimeRequestCollection)({ method, collection }) });
|
|
92
92
|
}, {});
|
|
93
93
|
};
|
|
94
94
|
exports.cleanMethodBasedRuntimeRequestCollection = cleanMethodBasedRuntimeRequestCollection;
|
|
@@ -96,17 +96,17 @@ const singleQueryParam = (obj) => Array.isArray(obj) ? `${obj[0]}` : `${obj}`;
|
|
|
96
96
|
exports.singleQueryParam = singleQueryParam;
|
|
97
97
|
const appFactory = (runtimeCollection) => {
|
|
98
98
|
let runtimeRequestCollection = runtimeCollection || {};
|
|
99
|
-
if (!exports.isMethodBasedRuntimeRequestCollection(runtimeRequestCollection)) {
|
|
99
|
+
if (!(0, exports.isMethodBasedRuntimeRequestCollection)(runtimeRequestCollection)) {
|
|
100
100
|
throw new Error('Intial requests JSON is invalid');
|
|
101
101
|
}
|
|
102
|
-
runtimeRequestCollection = exports.cleanMethodBasedRuntimeRequestCollection(runtimeRequestCollection);
|
|
103
|
-
const app = express_1.default();
|
|
104
|
-
app.use(cors_1.default());
|
|
102
|
+
runtimeRequestCollection = (0, exports.cleanMethodBasedRuntimeRequestCollection)(runtimeRequestCollection);
|
|
103
|
+
const app = (0, express_1.default)();
|
|
104
|
+
app.use((0, cors_1.default)());
|
|
105
105
|
app.use(body_parser_1.default.json());
|
|
106
|
-
app.get('/', (_req, res) => res.send(sort_keys_1.default(runtimeRequestCollection, { deep: true })));
|
|
106
|
+
app.get('/', (_req, res) => res.send((0, sort_keys_1.default)(runtimeRequestCollection, { deep: true })));
|
|
107
107
|
app.post('/', (req, res) => {
|
|
108
108
|
const { body } = req;
|
|
109
|
-
if (exports.isRuntimeRequestBody(body)) {
|
|
109
|
+
if ((0, exports.isRuntimeRequestBody)(body)) {
|
|
110
110
|
const fixedPath = `/${body.path}`.replace(/\/\//g, '/');
|
|
111
111
|
body.path = fixedPath;
|
|
112
112
|
const method = body.method;
|
|
@@ -120,9 +120,9 @@ const appFactory = (runtimeCollection) => {
|
|
|
120
120
|
if (!path || !method) {
|
|
121
121
|
return res.status(400).send();
|
|
122
122
|
}
|
|
123
|
-
const cleanPath = exports.singleQueryParam(path);
|
|
124
|
-
const cleanMethod = exports.singleQueryParam(method);
|
|
125
|
-
if (!exports.isSupportedMethod(cleanMethod)) {
|
|
123
|
+
const cleanPath = (0, exports.singleQueryParam)(path);
|
|
124
|
+
const cleanMethod = (0, exports.singleQueryParam)(method);
|
|
125
|
+
if (!(0, exports.isSupportedMethod)(cleanMethod)) {
|
|
126
126
|
return res.status(400).send();
|
|
127
127
|
}
|
|
128
128
|
if (!Object.keys(runtimeRequestCollection).includes(cleanMethod)) {
|
|
@@ -135,14 +135,14 @@ const appFactory = (runtimeCollection) => {
|
|
|
135
135
|
});
|
|
136
136
|
const catchAllHandler = (req, res) => {
|
|
137
137
|
const method = req.method;
|
|
138
|
-
if (!exports.isSupportedMethod(method)) {
|
|
138
|
+
if (!(0, exports.isSupportedMethod)(method)) {
|
|
139
139
|
return res.status(400).send();
|
|
140
140
|
}
|
|
141
141
|
const collection = runtimeRequestCollection[method];
|
|
142
142
|
if (!collection) {
|
|
143
143
|
return res.status(404).send();
|
|
144
144
|
}
|
|
145
|
-
const matcher = feather_route_matcher_1.default(collection);
|
|
145
|
+
const matcher = (0, feather_route_matcher_1.default)(collection);
|
|
146
146
|
const routeMatch = matcher(req.path);
|
|
147
147
|
if (routeMatch) {
|
|
148
148
|
const reqInfo = routeMatch.value;
|
|
@@ -171,18 +171,18 @@ const appFactory = (runtimeCollection) => {
|
|
|
171
171
|
}
|
|
172
172
|
return res.status(404).send();
|
|
173
173
|
};
|
|
174
|
-
app.get(
|
|
175
|
-
app.post(
|
|
176
|
-
app.put(
|
|
177
|
-
app.patch(
|
|
178
|
-
app.delete(
|
|
174
|
+
app.get(/\/.*/, catchAllHandler);
|
|
175
|
+
app.post(/\/.*/, catchAllHandler);
|
|
176
|
+
app.put(/\/.*/, catchAllHandler);
|
|
177
|
+
app.patch(/\/.*/, catchAllHandler);
|
|
178
|
+
app.delete(/\/.*/, catchAllHandler);
|
|
179
179
|
Object.defineProperty(app, 'runtimeRequestCollection', {
|
|
180
180
|
get: () => {
|
|
181
|
-
return lodash_1.cloneDeep(runtimeRequestCollection);
|
|
181
|
+
return (0, lodash_1.cloneDeep)(runtimeRequestCollection);
|
|
182
182
|
}
|
|
183
183
|
});
|
|
184
184
|
return app;
|
|
185
185
|
};
|
|
186
186
|
exports.appFactory = appFactory;
|
|
187
|
-
const app = exports.appFactory();
|
|
187
|
+
const app = (0, exports.appFactory)();
|
|
188
188
|
exports.default = app;
|
package/build/src/appV2.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
type SupportedMethodsType = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
|
2
2
|
export declare const SupportedMethodsColection: SupportedMethodsType;
|
|
3
|
-
export
|
|
4
|
-
export declare const isSupportedMethod: (obj: any) => obj is
|
|
3
|
+
export type SupportedMethod = SupportedMethodsType[number];
|
|
4
|
+
export declare const isSupportedMethod: (obj: any) => obj is SupportedMethod;
|
|
5
5
|
export interface RuntimeRequestMethodBody {
|
|
6
6
|
body: any;
|
|
7
7
|
status?: any;
|
|
@@ -15,8 +15,8 @@ export interface RequestParameters {
|
|
|
15
15
|
headers: any;
|
|
16
16
|
}
|
|
17
17
|
export declare const isRuntimeRequestMethodBody: (obj: any) => obj is RuntimeRequestMethodBody;
|
|
18
|
-
export
|
|
19
|
-
export declare const isRuntimeRequestMethodBodyCollection: (obj: any) => obj is
|
|
18
|
+
export type RuntimeRequestMethodBodyCollection = Partial<Record<SupportedMethod, RuntimeRequestMethodBody>>;
|
|
19
|
+
export declare const isRuntimeRequestMethodBodyCollection: (obj: any) => obj is RuntimeRequestMethodBodyCollection;
|
|
20
20
|
export interface RuntimeRequestBody {
|
|
21
21
|
path: string;
|
|
22
22
|
methods: RuntimeRequestMethodBodyCollection;
|
|
@@ -26,6 +26,6 @@ export interface RuntimeRequestCollection {
|
|
|
26
26
|
[path: string]: RuntimeRequestBody;
|
|
27
27
|
}
|
|
28
28
|
export declare const isRuntimeRequestCollection: (obj: any) => obj is RuntimeRequestCollection;
|
|
29
|
-
export declare const appFactory: (runtimeCollection?: RuntimeRequestCollection) =>
|
|
30
|
-
declare const app:
|
|
29
|
+
export declare const appFactory: (runtimeCollection?: RuntimeRequestCollection) => any;
|
|
30
|
+
declare const app: any;
|
|
31
31
|
export default app;
|
package/build/src/appV2.js
CHANGED
|
@@ -23,10 +23,11 @@ const feather_route_matcher_1 = __importDefault(require("feather-route-matcher")
|
|
|
23
23
|
const handlebars_1 = __importDefault(require("handlebars"));
|
|
24
24
|
const lodash_1 = require("lodash");
|
|
25
25
|
const sort_keys_1 = __importDefault(require("sort-keys"));
|
|
26
|
+
const api_logs_1 = require("@opentelemetry/api-logs");
|
|
26
27
|
const chance = new chance_1.default();
|
|
27
28
|
handlebars_1.default.registerHelper('faker', (funcName, ...rest) => {
|
|
28
29
|
try {
|
|
29
|
-
const func = lodash_1.get(faker_1.default, funcName, () => '');
|
|
30
|
+
const func = (0, lodash_1.get)(faker_1.default, funcName, () => '');
|
|
30
31
|
return func(...rest);
|
|
31
32
|
}
|
|
32
33
|
catch (err) {
|
|
@@ -35,7 +36,7 @@ handlebars_1.default.registerHelper('faker', (funcName, ...rest) => {
|
|
|
35
36
|
});
|
|
36
37
|
handlebars_1.default.registerHelper('chance', (funcName, ...rest) => {
|
|
37
38
|
try {
|
|
38
|
-
const func = lodash_1.get(chance, funcName, () => '');
|
|
39
|
+
const func = (0, lodash_1.get)(chance, funcName, () => '');
|
|
39
40
|
return func.call(chance, ...rest);
|
|
40
41
|
}
|
|
41
42
|
catch (err) {
|
|
@@ -56,7 +57,7 @@ const isRuntimeRequestMethodBody = (obj) => {
|
|
|
56
57
|
exports.isRuntimeRequestMethodBody = isRuntimeRequestMethodBody;
|
|
57
58
|
const isRuntimeRequestMethodBodyCollection = (obj) => {
|
|
58
59
|
try {
|
|
59
|
-
return !Object.keys(obj).some(k => !(exports.isSupportedMethod(k) && exports.isRuntimeRequestMethodBody(obj[k])));
|
|
60
|
+
return !Object.keys(obj).some(k => !((0, exports.isSupportedMethod)(k) && (0, exports.isRuntimeRequestMethodBody)(obj[k])));
|
|
60
61
|
}
|
|
61
62
|
catch (_) {
|
|
62
63
|
return false;
|
|
@@ -66,7 +67,7 @@ exports.isRuntimeRequestMethodBodyCollection = isRuntimeRequestMethodBodyCollect
|
|
|
66
67
|
const isRuntimeRequestBody = (obj) => {
|
|
67
68
|
try {
|
|
68
69
|
return typeof obj.path === 'string'
|
|
69
|
-
&& exports.isRuntimeRequestMethodBodyCollection(obj.methods);
|
|
70
|
+
&& (0, exports.isRuntimeRequestMethodBodyCollection)(obj.methods);
|
|
70
71
|
}
|
|
71
72
|
catch (_) {
|
|
72
73
|
return false;
|
|
@@ -75,7 +76,7 @@ const isRuntimeRequestBody = (obj) => {
|
|
|
75
76
|
exports.isRuntimeRequestBody = isRuntimeRequestBody;
|
|
76
77
|
const isRuntimeRequestCollection = (obj) => {
|
|
77
78
|
try {
|
|
78
|
-
return !Object.keys(obj).some(k => !exports.isRuntimeRequestBody(obj[k]));
|
|
79
|
+
return !Object.keys(obj).some(k => !(0, exports.isRuntimeRequestBody)(obj[k]));
|
|
79
80
|
}
|
|
80
81
|
catch (_) {
|
|
81
82
|
return false;
|
|
@@ -84,7 +85,7 @@ const isRuntimeRequestCollection = (obj) => {
|
|
|
84
85
|
exports.isRuntimeRequestCollection = isRuntimeRequestCollection;
|
|
85
86
|
const appFactory = (runtimeCollection) => {
|
|
86
87
|
let runtimeRequestCollection = runtimeCollection || {};
|
|
87
|
-
if (!exports.isRuntimeRequestCollection(runtimeRequestCollection)) {
|
|
88
|
+
if (!(0, exports.isRuntimeRequestCollection)(runtimeRequestCollection)) {
|
|
88
89
|
throw new Error('Intial requests JSON is invalid');
|
|
89
90
|
}
|
|
90
91
|
runtimeRequestCollection = Object.keys(runtimeRequestCollection).reduce((acc, curr) => {
|
|
@@ -92,31 +93,56 @@ const appFactory = (runtimeCollection) => {
|
|
|
92
93
|
const body = Object.assign(Object.assign({}, runtimeCollection[curr]), { path: fixedPath });
|
|
93
94
|
return Object.assign(Object.assign({}, acc), { [fixedPath]: body });
|
|
94
95
|
}, {});
|
|
95
|
-
const app = express_1.default();
|
|
96
|
-
app.use(cors_1.default());
|
|
96
|
+
const app = (0, express_1.default)();
|
|
97
|
+
app.use((0, cors_1.default)());
|
|
97
98
|
app.use(express_1.default.json({
|
|
98
99
|
strict: false,
|
|
99
100
|
type: ['application/json', 'application/*+json', 'text/json']
|
|
100
101
|
}));
|
|
101
102
|
// log the request
|
|
102
103
|
if (process.env.LOG_MOCK_REQUESTS === 'true') {
|
|
104
|
+
const logger = api_logs_1.logs.getLogger('mock-requests-logger');
|
|
103
105
|
app.use((req, res, next) => {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
// log the request
|
|
107
|
+
logger.emit({
|
|
108
|
+
severityNumber: api_logs_1.SeverityNumber.INFO,
|
|
109
|
+
severityText: 'INFO',
|
|
110
|
+
body: `Request ${req.path}`,
|
|
111
|
+
attributes: {
|
|
112
|
+
'http.headers': req.headers,
|
|
113
|
+
'http.method': req.method,
|
|
114
|
+
'http.url': req.url,
|
|
115
|
+
'http.request_body': req.body,
|
|
116
|
+
}
|
|
117
|
+
});
|
|
107
118
|
// log the response
|
|
108
119
|
const originalSend = res.send;
|
|
120
|
+
let firstSend = true;
|
|
109
121
|
res.send = function (body) {
|
|
110
|
-
|
|
122
|
+
if (firstSend) {
|
|
123
|
+
// For some reason send gets called twice per request, this ensures we only log once
|
|
124
|
+
firstSend = false;
|
|
125
|
+
logger.emit({
|
|
126
|
+
severityNumber: api_logs_1.SeverityNumber.INFO,
|
|
127
|
+
severityText: 'INFO',
|
|
128
|
+
body: `Response ${req.path}`,
|
|
129
|
+
attributes: {
|
|
130
|
+
'http.status_code': res.statusCode,
|
|
131
|
+
'http.method': req.method,
|
|
132
|
+
'http.url': req.url,
|
|
133
|
+
'http.response_body': body,
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
111
137
|
return originalSend.apply(this, arguments);
|
|
112
138
|
};
|
|
113
139
|
next();
|
|
114
140
|
});
|
|
115
141
|
}
|
|
116
|
-
app.get('/', (_req, res) => res.send(sort_keys_1.default(runtimeRequestCollection, { deep: true })));
|
|
142
|
+
app.get('/', (_req, res) => res.send((0, sort_keys_1.default)(runtimeRequestCollection, { deep: true })));
|
|
117
143
|
app.post('/', (req, res) => {
|
|
118
144
|
const { body } = req;
|
|
119
|
-
if (exports.isRuntimeRequestBody(body)) {
|
|
145
|
+
if ((0, exports.isRuntimeRequestBody)(body)) {
|
|
120
146
|
const fixedPath = `/${body.path}`.replace(/\/\//g, '/');
|
|
121
147
|
body.path = fixedPath;
|
|
122
148
|
runtimeRequestCollection = Object.assign(Object.assign({}, runtimeRequestCollection), { [fixedPath]: body });
|
|
@@ -126,7 +152,7 @@ const appFactory = (runtimeCollection) => {
|
|
|
126
152
|
});
|
|
127
153
|
app.put('/', (req, res) => {
|
|
128
154
|
const { body } = req;
|
|
129
|
-
if (!exports.isRuntimeRequestCollection(body)) {
|
|
155
|
+
if (!(0, exports.isRuntimeRequestCollection)(body)) {
|
|
130
156
|
return res.status(400).send('BAD_REQUEST_BODY');
|
|
131
157
|
}
|
|
132
158
|
const cleanBody = Object.keys(body).reduce((acc, curr) => {
|
|
@@ -150,7 +176,7 @@ const appFactory = (runtimeCollection) => {
|
|
|
150
176
|
return res.status(204).send();
|
|
151
177
|
});
|
|
152
178
|
const catchAllHandler = (req, res) => {
|
|
153
|
-
const matcher = feather_route_matcher_1.default(runtimeRequestCollection);
|
|
179
|
+
const matcher = (0, feather_route_matcher_1.default)(runtimeRequestCollection);
|
|
154
180
|
const routeMatch = matcher(req.path);
|
|
155
181
|
if (routeMatch) {
|
|
156
182
|
const reqInfo = routeMatch.value;
|
|
@@ -203,18 +229,18 @@ const appFactory = (runtimeCollection) => {
|
|
|
203
229
|
}
|
|
204
230
|
return res.status(404).send();
|
|
205
231
|
};
|
|
206
|
-
app.get(
|
|
207
|
-
app.post(
|
|
208
|
-
app.put(
|
|
209
|
-
app.patch(
|
|
210
|
-
app.delete(
|
|
232
|
+
app.get(/\/.*/, catchAllHandler);
|
|
233
|
+
app.post(/\/.*/, catchAllHandler);
|
|
234
|
+
app.put(/\/.*/, catchAllHandler);
|
|
235
|
+
app.patch(/\/.*/, catchAllHandler);
|
|
236
|
+
app.delete(/\/.*/, catchAllHandler);
|
|
211
237
|
Object.defineProperty(app, 'runtimeRequestCollection', {
|
|
212
238
|
get: () => {
|
|
213
|
-
return lodash_1.cloneDeep(runtimeRequestCollection);
|
|
239
|
+
return (0, lodash_1.cloneDeep)(runtimeRequestCollection);
|
|
214
240
|
}
|
|
215
241
|
});
|
|
216
242
|
return app;
|
|
217
243
|
};
|
|
218
244
|
exports.appFactory = appFactory;
|
|
219
|
-
const app = exports.appFactory();
|
|
245
|
+
const app = (0, exports.appFactory)();
|
|
220
246
|
exports.default = app;
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@centeredgesoft/runtime-mock-routes",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "tsc --project ./",
|
|
8
8
|
"start": "nodemon -r dotenv/config index.ts",
|
|
9
|
-
"test": "jest"
|
|
9
|
+
"test": "jest",
|
|
10
|
+
"serve": "ts-node run.ts -s ./example/seed.js"
|
|
10
11
|
},
|
|
11
12
|
"bin": {
|
|
12
13
|
"runtime-mock-routes": "./build/run.js"
|
|
@@ -23,10 +24,13 @@
|
|
|
23
24
|
},
|
|
24
25
|
"homepage": "https://github.com/CenterEdge/runtime-mock-routes#readme",
|
|
25
26
|
"dependencies": {
|
|
27
|
+
"@opentelemetry/instrumentation-express": "^0.57.0",
|
|
28
|
+
"@opentelemetry/instrumentation-http": "^0.208.0",
|
|
29
|
+
"@opentelemetry/sdk-node": "^0.208.0",
|
|
26
30
|
"chance": "^1.1.7",
|
|
27
31
|
"commander": "^6.2.1",
|
|
28
32
|
"cors": "^2.8.5",
|
|
29
|
-
"express": "^
|
|
33
|
+
"express": "^5.2.1",
|
|
30
34
|
"faker": "^5.1.0",
|
|
31
35
|
"feather-route-matcher": "^4.0.0",
|
|
32
36
|
"handlebars": "^4.7.6",
|
|
@@ -36,18 +40,17 @@
|
|
|
36
40
|
"devDependencies": {
|
|
37
41
|
"@types/chance": "^1.1.1",
|
|
38
42
|
"@types/cors": "^2.8.9",
|
|
39
|
-
"@types/express": "^4.17.11",
|
|
40
43
|
"@types/faker": "^5.1.5",
|
|
41
|
-
"@types/jest": "^
|
|
44
|
+
"@types/jest": "^30.0.0",
|
|
42
45
|
"@types/lodash": "^4.14.167",
|
|
43
|
-
"@types/node": "^
|
|
46
|
+
"@types/node": "^22.9.0",
|
|
44
47
|
"@types/supertest": "^2.0.10",
|
|
45
|
-
"dotenv": "^
|
|
46
|
-
"jest": "^
|
|
47
|
-
"nodemon": "^
|
|
48
|
+
"dotenv": "^17.2.3",
|
|
49
|
+
"jest": "^30.2.0",
|
|
50
|
+
"nodemon": "^3.1.11",
|
|
48
51
|
"supertest": "^6.0.1",
|
|
49
|
-
"ts-jest": "^
|
|
50
|
-
"ts-node": "^9.
|
|
51
|
-
"typescript": "^
|
|
52
|
+
"ts-jest": "^29.4.6",
|
|
53
|
+
"ts-node": "^10.9.2",
|
|
54
|
+
"typescript": "^5.9.3"
|
|
52
55
|
}
|
|
53
56
|
}
|
package/run.ts
CHANGED
|
@@ -1,39 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
v2 = 'v2'
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
program.version("2.1.0")
|
|
14
|
-
.option("-p, --port <port>", "Port to run the server one", process.env.RUNTIME_MOCK_ROUTES_PORT || "8080")
|
|
15
|
-
.option("-s, --seed <filePath>", "File path to seed the application", process.env.RUNTIME_MOCK_ROUTES_FILE_PATH)
|
|
16
|
-
.option("-a, --appVersion <appVersion>", "App Version to use", AppVersion.v2)
|
|
17
|
-
|
|
18
|
-
program.parse(process.argv);
|
|
19
|
-
let initialRequests: any = {};
|
|
20
|
-
if (program.seed) {
|
|
21
|
-
const filePath = resolve(program.seed);
|
|
22
|
-
const funcOrJson = require(filePath);
|
|
23
|
-
initialRequests = typeof funcOrJson === 'function' ? funcOrJson() : funcOrJson;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
type AppFactory = (...args: any[]) => Express;
|
|
27
|
-
|
|
28
|
-
const appVersions: Record<AppVersion, AppFactory> = {
|
|
29
|
-
v1: appFactoryV1,
|
|
30
|
-
v2: appFactoryV2
|
|
2
|
+
import { NodeSDK, logs } from '@opentelemetry/sdk-node';
|
|
3
|
+
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
|
|
4
|
+
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
|
|
5
|
+
|
|
6
|
+
let logRecordProcessors: logs.LogRecordProcessor[] | undefined = undefined;
|
|
7
|
+
if (!process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {
|
|
8
|
+
// OTLP isn't configured, fallback to console logging
|
|
9
|
+
logRecordProcessors = [new logs.SimpleLogRecordProcessor(new logs.ConsoleLogRecordExporter())];
|
|
31
10
|
}
|
|
32
|
-
const appFactory = appVersions[program.appVersion] || appFactoryV2;
|
|
33
11
|
|
|
12
|
+
const sdk = new NodeSDK({
|
|
13
|
+
logRecordProcessors: logRecordProcessors,
|
|
14
|
+
instrumentations: [
|
|
15
|
+
new HttpInstrumentation(),
|
|
16
|
+
new ExpressInstrumentation()
|
|
17
|
+
],
|
|
18
|
+
});
|
|
34
19
|
|
|
35
|
-
|
|
20
|
+
sdk.start()
|
|
36
21
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
22
|
+
// Delay import so we register the OTEL SDK before loading modules like http/express
|
|
23
|
+
require("./server").runServer(sdk);
|
package/server.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import https from 'https';
|
|
4
|
+
import { Express } from 'express'
|
|
5
|
+
import { program } from 'commander';
|
|
6
|
+
import { resolve } from 'path';
|
|
7
|
+
import { appFactory as appFactoryV1 } from './src/app';
|
|
8
|
+
import { appFactory as appFactoryV2 } from './src/appV2';
|
|
9
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
10
|
+
|
|
11
|
+
export enum AppVersion {
|
|
12
|
+
v1 = 'v1',
|
|
13
|
+
v2 = 'v2'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const runServer = (sdk: NodeSDK) => {
|
|
17
|
+
program.version("2.1.0")
|
|
18
|
+
.option("-p, --port <port>", "Port to run the server one", process.env.RUNTIME_MOCK_ROUTES_PORT || "8080")
|
|
19
|
+
.option("--https-port <httpsPort>", "Enable TLS for the server", process.env.RUNTIME_MOCK_ROUTES_HTTPS_PORT || "8443")
|
|
20
|
+
.option("--tlsCert <tlsCertPath>", "Path to the TLS certificate file", process.env.HTTPS_CERT_FILE)
|
|
21
|
+
.option("--tlsKey <tlsKeyPath>", "Path to the TLS key file", process.env.HTTPS_CERT_KEY_FILE)
|
|
22
|
+
.option("-s, --seed <filePath>", "File path to seed the application", process.env.RUNTIME_MOCK_ROUTES_FILE_PATH)
|
|
23
|
+
.option("-a, --appVersion <appVersion>", "App Version to use", AppVersion.v2)
|
|
24
|
+
|
|
25
|
+
program.parse(process.argv);
|
|
26
|
+
|
|
27
|
+
let initialRequests: any = {};
|
|
28
|
+
if (program.seed) {
|
|
29
|
+
const filePath = resolve(program.seed);
|
|
30
|
+
const funcOrJson = require(filePath);
|
|
31
|
+
initialRequests = typeof funcOrJson === 'function' ? funcOrJson() : funcOrJson;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type AppFactory = (...args: any[]) => Express;
|
|
35
|
+
|
|
36
|
+
const appVersions: Record<AppVersion, AppFactory> = {
|
|
37
|
+
v1: appFactoryV1,
|
|
38
|
+
v2: appFactoryV2
|
|
39
|
+
}
|
|
40
|
+
const appFactory = appVersions[program.appVersion] || appFactoryV2;
|
|
41
|
+
|
|
42
|
+
const app = appFactory(initialRequests);
|
|
43
|
+
|
|
44
|
+
// Setup HTTPS options
|
|
45
|
+
const httpsOptions = program.tlsCert && program.tlsKey && fs.existsSync(program.tlsCert) && fs.existsSync(program.tlsKey)
|
|
46
|
+
? {
|
|
47
|
+
cert: fs.readFileSync(program.tlsCert),
|
|
48
|
+
key: fs.readFileSync(program.tlsKey),
|
|
49
|
+
enabled: true
|
|
50
|
+
}
|
|
51
|
+
: { enabled: false };
|
|
52
|
+
|
|
53
|
+
const httpServer = http.createServer(app);
|
|
54
|
+
const httpsServer = httpsOptions.enabled ? https.createServer(httpsOptions, app) : null;
|
|
55
|
+
|
|
56
|
+
httpServer.listen(program.port, () => {
|
|
57
|
+
console.log(`⚡️[server]: Server is running at http://localhost:${program.port}`);
|
|
58
|
+
});
|
|
59
|
+
if (httpsServer) {
|
|
60
|
+
httpsServer.listen(program.httpsPort, () => {
|
|
61
|
+
console.log(`⚡️[server]: Server is running at https://localhost:${program.httpsPort}`);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Register signal handlers
|
|
66
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
67
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
68
|
+
|
|
69
|
+
// Graceful shutdown handler
|
|
70
|
+
let isShuttingDown = false;
|
|
71
|
+
async function gracefulShutdown(signal) {
|
|
72
|
+
if (isShuttingDown) return;
|
|
73
|
+
isShuttingDown = true;
|
|
74
|
+
|
|
75
|
+
// Close servers
|
|
76
|
+
const closePromises = [];
|
|
77
|
+
closePromises.push(closeServer(httpServer));
|
|
78
|
+
closePromises.push(closeServer(httpsServer));
|
|
79
|
+
await Promise.all(closePromises);
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
await sdk.shutdown();
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.log("Error shutting down OTEL SDK", err);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log('Shutdown complete, exiting process');
|
|
88
|
+
process.exit(0);
|
|
89
|
+
|
|
90
|
+
function closeServer(httpServer) {
|
|
91
|
+
if (!httpServer) return Promise.resolve();
|
|
92
|
+
const serverType = httpServer instanceof https.Server ? 'HTTPS' : 'HTTP'
|
|
93
|
+
return new Promise<void>(resolve => {
|
|
94
|
+
httpServer.close(() => {
|
|
95
|
+
resolve();
|
|
96
|
+
});
|
|
97
|
+
httpServer.closeAllConnections();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { runServer };
|
package/src/app.test.ts
CHANGED
package/src/app.ts
CHANGED
|
@@ -216,12 +216,12 @@ export const appFactory = (runtimeCollection?: MethodBasedRuntimeRequestCollecti
|
|
|
216
216
|
return res.status(404).send();
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
app.get(
|
|
220
|
-
app.post(
|
|
221
|
-
app.put(
|
|
222
|
-
app.patch(
|
|
223
|
-
app.delete(
|
|
224
|
-
|
|
219
|
+
app.get(/\/.*/, catchAllHandler);
|
|
220
|
+
app.post(/\/.*/, catchAllHandler);
|
|
221
|
+
app.put(/\/.*/, catchAllHandler);
|
|
222
|
+
app.patch(/\/.*/, catchAllHandler);
|
|
223
|
+
app.delete(/\/.*/, catchAllHandler);
|
|
224
|
+
|
|
225
225
|
Object.defineProperty(app, 'runtimeRequestCollection', {
|
|
226
226
|
get: () => {
|
|
227
227
|
return cloneDeep(runtimeRequestCollection);
|
package/src/appV2.test.ts
CHANGED
package/src/appV2.ts
CHANGED
|
@@ -6,6 +6,7 @@ import createMatcher from 'feather-route-matcher';
|
|
|
6
6
|
import Handlebars from 'handlebars';
|
|
7
7
|
import { cloneDeep, get } from 'lodash';
|
|
8
8
|
import sortKeys from 'sort-keys';
|
|
9
|
+
import { logs, SeverityNumber } from '@opentelemetry/api-logs';
|
|
9
10
|
|
|
10
11
|
const chance = new Chance();
|
|
11
12
|
|
|
@@ -117,15 +118,43 @@ export const appFactory = (runtimeCollection?: RuntimeRequestCollection) => {
|
|
|
117
118
|
|
|
118
119
|
// log the request
|
|
119
120
|
if (process.env.LOG_MOCK_REQUESTS === 'true') {
|
|
121
|
+
const logger = logs.getLogger('mock-requests-logger');
|
|
122
|
+
|
|
120
123
|
app.use((req, res, next) => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
// log the request
|
|
125
|
+
logger.emit({
|
|
126
|
+
severityNumber: SeverityNumber.INFO,
|
|
127
|
+
severityText: 'INFO',
|
|
128
|
+
body: `Request ${req.path}`,
|
|
129
|
+
attributes: {
|
|
130
|
+
'http.headers': req.headers,
|
|
131
|
+
'http.method': req.method,
|
|
132
|
+
'http.url': req.url,
|
|
133
|
+
'http.request_body': req.body,
|
|
134
|
+
}
|
|
135
|
+
});
|
|
124
136
|
|
|
125
137
|
// log the response
|
|
126
138
|
const originalSend = res.send;
|
|
139
|
+
let firstSend = true;
|
|
127
140
|
res.send = function (body) {
|
|
128
|
-
|
|
141
|
+
if (firstSend) {
|
|
142
|
+
// For some reason send gets called twice per request, this ensures we only log once
|
|
143
|
+
firstSend = false;
|
|
144
|
+
|
|
145
|
+
logger.emit({
|
|
146
|
+
severityNumber: SeverityNumber.INFO,
|
|
147
|
+
severityText: 'INFO',
|
|
148
|
+
body: `Response ${req.path}`,
|
|
149
|
+
attributes: {
|
|
150
|
+
'http.status_code': res.statusCode,
|
|
151
|
+
'http.method': req.method,
|
|
152
|
+
'http.url': req.url,
|
|
153
|
+
'http.response_body': body,
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
129
158
|
return originalSend.apply(this, arguments);
|
|
130
159
|
};
|
|
131
160
|
|
|
@@ -267,12 +296,12 @@ export const appFactory = (runtimeCollection?: RuntimeRequestCollection) => {
|
|
|
267
296
|
return res.status(404).send();
|
|
268
297
|
}
|
|
269
298
|
|
|
270
|
-
app.get(
|
|
271
|
-
app.post(
|
|
272
|
-
app.put(
|
|
273
|
-
app.patch(
|
|
274
|
-
app.delete(
|
|
275
|
-
|
|
299
|
+
app.get(/\/.*/, catchAllHandler);
|
|
300
|
+
app.post(/\/.*/, catchAllHandler);
|
|
301
|
+
app.put(/\/.*/, catchAllHandler);
|
|
302
|
+
app.patch(/\/.*/, catchAllHandler);
|
|
303
|
+
app.delete(/\/.*/, catchAllHandler);
|
|
304
|
+
|
|
276
305
|
Object.defineProperty(app, 'runtimeRequestCollection', {
|
|
277
306
|
get: () => {
|
|
278
307
|
return cloneDeep(runtimeRequestCollection);
|
package/.devcontainer/Dockerfile
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/typescript-node/.devcontainer/base.Dockerfile
|
|
2
|
-
|
|
3
|
-
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 16, 14, 12, 16-bullseye, 14-bullseye, 12-bullseye, 16-buster, 14-buster, 12-buster
|
|
4
|
-
ARG VARIANT="16-bullseye"
|
|
5
|
-
FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT}
|
|
6
|
-
|
|
7
|
-
# [Optional] Uncomment this section to install additional OS packages.
|
|
8
|
-
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
|
9
|
-
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
|
10
|
-
|
|
11
|
-
# [Optional] Uncomment if you want to install an additional version of node using nvm
|
|
12
|
-
# ARG EXTRA_NODE_VERSION=10
|
|
13
|
-
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
|
|
14
|
-
|
|
15
|
-
# [Optional] Uncomment if you want to install more global node packages
|
|
16
|
-
# RUN su node -c "npm install -g <your-package-list -here>"
|