typescript-mock-server 1.10.0 → 1.11.2
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/README.md +13 -3
- package/dist/command-line.js +9 -0
- package/dist/impl/command-line-impl.js +28 -0
- package/dist/impl/logger-impl.js +31 -0
- package/dist/impl/typescript-mock-server-impl.js +212 -0
- package/dist/index.js +6 -0
- package/dist/logger.js +2 -0
- package/dist/models/config.js +2 -0
- package/dist/models/registered-endpoint.js +2 -0
- package/dist/src/command-line.js +9 -0
- package/dist/src/impl/command-line-impl.js +28 -0
- package/dist/src/impl/logger-impl.js +31 -0
- package/dist/src/impl/typescript-mock-server-impl.js +208 -0
- package/dist/src/index.js +6 -0
- package/dist/src/logger.js +2 -0
- package/dist/src/models/config.js +2 -0
- package/dist/src/models/registered-endpoint.js +2 -0
- package/dist/src/types/http-verbs.js +2 -0
- package/dist/src/typescript-mock-server.js +2 -0
- package/dist/test/impl/command-line-impl.spec.js +40 -0
- package/dist/test/impl/typescript-mock-server-impl.spec.js +182 -0
- package/dist/tms-models/dash-example/get-dash-in-id.js +6 -0
- package/dist/tms-models/dash-example/get-test1.js +6 -0
- package/dist/tms-models/dash-example/get.js +20 -0
- package/dist/tms-models/users/delete-1.js +8 -0
- package/dist/tms-models/users/get-1.js +14 -0
- package/dist/tms-models/users/get-dynamic.js +15 -0
- package/dist/tms-models/users/get.js +20 -0
- package/dist/tms-models/users/head-1.js +4 -0
- package/dist/tms-models/users/options-1.js +4 -0
- package/dist/tms-models/users/patch-1.js +8 -0
- package/dist/tms-models/users/post-1.js +8 -0
- package/dist/tms-models/users/profile/get-1.js +12 -0
- package/dist/tms-models/users/put-1.js +8 -0
- package/dist/types/http-verbs.js +2 -0
- package/dist/typescript-mock-server.js +2 -0
- package/jest.config.js +12 -0
- package/package.json +9 -4
- package/src/impl/typescript-mock-server-impl.ts +72 -31
- package/src/index.ts +1 -1
- package/src/models/config.ts +10 -0
- package/test/impl/command-line-impl.spec.ts +49 -0
- package/test/impl/typescript-mock-server-impl.spec.ts +181 -0
- package/tms-models/users/get-dynamic.ts +22 -0
- package/tsconfig.json +5 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_line_impl_1 = require("../../src/impl/command-line-impl");
|
|
4
|
+
const command_line_1 = require("../../src/command-line");
|
|
5
|
+
describe('CommandLineImpl', () => {
|
|
6
|
+
const originalArgv = process.argv;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
jest.resetModules();
|
|
9
|
+
});
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
process.argv = originalArgv;
|
|
12
|
+
});
|
|
13
|
+
it('should parse command line arguments correctly', () => {
|
|
14
|
+
process.argv = ['node', 'index.js', '--port=4000', '--path=./test-models', '--cors=http://localhost:3000'];
|
|
15
|
+
const commandLine = new command_line_impl_1.CommandLineImpl();
|
|
16
|
+
expect(commandLine.getCommand(command_line_1.Command.PORT)).toBe('4000');
|
|
17
|
+
expect(commandLine.getCommand(command_line_1.Command.PATH)).toBe('./test-models');
|
|
18
|
+
expect(commandLine.getCommand(command_line_1.Command.CORS)).toBe('http://localhost:3000');
|
|
19
|
+
});
|
|
20
|
+
it('should handle arguments with quotes', () => {
|
|
21
|
+
process.argv = ['node', 'index.js', '--path="C:/Program Files/Models"', "--cors='*'", '--port=5000'];
|
|
22
|
+
const commandLine = new command_line_impl_1.CommandLineImpl();
|
|
23
|
+
expect(commandLine.getCommand(command_line_1.Command.PATH)).toBe('C:/Program Files/Models');
|
|
24
|
+
expect(commandLine.getCommand(command_line_1.Command.CORS)).toBe('*');
|
|
25
|
+
expect(commandLine.getCommand(command_line_1.Command.PORT)).toBe('5000');
|
|
26
|
+
});
|
|
27
|
+
it('should return undefined for missing commands', () => {
|
|
28
|
+
process.argv = ['node', 'index.js'];
|
|
29
|
+
const commandLine = new command_line_impl_1.CommandLineImpl();
|
|
30
|
+
expect(commandLine.getCommand(command_line_1.Command.PORT)).toBeUndefined();
|
|
31
|
+
});
|
|
32
|
+
it('should return all commands', () => {
|
|
33
|
+
process.argv = ['node', 'index.js', '--port=4000', '--path=./models'];
|
|
34
|
+
const commandLine = new command_line_impl_1.CommandLineImpl();
|
|
35
|
+
const commands = commandLine.getCommands();
|
|
36
|
+
expect(commands.size).toBe(2);
|
|
37
|
+
expect(commands.get(command_line_1.Command.PORT)).toBe('4000');
|
|
38
|
+
expect(commands.get(command_line_1.Command.PATH)).toBe('./models');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
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);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
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
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
|
45
|
+
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
|
46
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
47
|
+
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
48
|
+
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
49
|
+
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
|
|
50
|
+
function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
|
|
51
|
+
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
|
52
|
+
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
|
53
|
+
function fulfill(value) { resume("next", value); }
|
|
54
|
+
function reject(value) { resume("throw", value); }
|
|
55
|
+
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
56
|
+
};
|
|
57
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
58
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
59
|
+
};
|
|
60
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
61
|
+
const supertest_1 = __importDefault(require("supertest"));
|
|
62
|
+
const express_1 = __importDefault(require("express"));
|
|
63
|
+
const typescript_mock_server_impl_1 = require("../../src/impl/typescript-mock-server-impl");
|
|
64
|
+
const fsPromises = __importStar(require("fs/promises"));
|
|
65
|
+
const path_1 = __importDefault(require("path"));
|
|
66
|
+
jest.mock('fs/promises');
|
|
67
|
+
describe('TypescriptMockServerImpl', () => {
|
|
68
|
+
let server;
|
|
69
|
+
let app;
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
jest.clearAllMocks();
|
|
72
|
+
app = (0, express_1.default)();
|
|
73
|
+
// Spy on listen to prevent it from actually starting
|
|
74
|
+
jest.spyOn(app, 'listen').mockImplementation((port, callback) => {
|
|
75
|
+
if (callback)
|
|
76
|
+
callback();
|
|
77
|
+
return {};
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
it('should start and have the state endpoint', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
81
|
+
fsPromises.opendir.mockResolvedValue(function () {
|
|
82
|
+
return __asyncGenerator(this, arguments, function* () {
|
|
83
|
+
// yield nothing
|
|
84
|
+
});
|
|
85
|
+
}());
|
|
86
|
+
server = new typescript_mock_server_impl_1.TypescriptMockServerImpl(app);
|
|
87
|
+
yield server.start();
|
|
88
|
+
const response = yield (0, supertest_1.default)(app).get('/state');
|
|
89
|
+
expect(response.status).toBe(200);
|
|
90
|
+
expect(response.body).toEqual({ status: 'started' });
|
|
91
|
+
}));
|
|
92
|
+
it('should have the state endpoint even if other routes are loaded', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
93
|
+
fsPromises.opendir.mockResolvedValue(function () {
|
|
94
|
+
return __asyncGenerator(this, arguments, function* () {
|
|
95
|
+
// yield nothing
|
|
96
|
+
});
|
|
97
|
+
}());
|
|
98
|
+
server = new typescript_mock_server_impl_1.TypescriptMockServerImpl(app);
|
|
99
|
+
yield server.start();
|
|
100
|
+
const response = yield (0, supertest_1.default)(app).get('/state');
|
|
101
|
+
expect(response.status).toBe(200);
|
|
102
|
+
}));
|
|
103
|
+
it('should load routes from directory', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
104
|
+
const mockFiles = [
|
|
105
|
+
{ name: 'get-test.ts', isDirectory: () => false },
|
|
106
|
+
{ name: 'post-data.ts', isDirectory: () => false }
|
|
107
|
+
];
|
|
108
|
+
fsPromises.opendir.mockResolvedValue(function () {
|
|
109
|
+
return __asyncGenerator(this, arguments, function* () {
|
|
110
|
+
for (const file of mockFiles) {
|
|
111
|
+
yield yield __await(file);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}());
|
|
115
|
+
// Mock loadModule
|
|
116
|
+
const loadModuleSpy = jest.spyOn(typescript_mock_server_impl_1.TypescriptMockServerImpl, 'loadModule');
|
|
117
|
+
loadModuleSpy.mockImplementation((...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
118
|
+
const path = args[0];
|
|
119
|
+
if (path.includes('get-test')) {
|
|
120
|
+
return { data: { message: 'get success' } };
|
|
121
|
+
}
|
|
122
|
+
if (path.includes('post-data')) {
|
|
123
|
+
return { data: { message: 'post success' } };
|
|
124
|
+
}
|
|
125
|
+
return {};
|
|
126
|
+
}));
|
|
127
|
+
server = new typescript_mock_server_impl_1.TypescriptMockServerImpl(app);
|
|
128
|
+
yield server.start();
|
|
129
|
+
const getResponse = yield (0, supertest_1.default)(app).get('/test');
|
|
130
|
+
expect(getResponse.status).toBe(200);
|
|
131
|
+
expect(getResponse.body).toEqual({ message: 'get success' });
|
|
132
|
+
const postResponse = yield (0, supertest_1.default)(app).post('/data');
|
|
133
|
+
expect(postResponse.status).toBe(200);
|
|
134
|
+
expect(postResponse.body).toEqual({ message: 'post success' });
|
|
135
|
+
}));
|
|
136
|
+
it('should handle status codes and delay from config', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
137
|
+
const mockFiles = [
|
|
138
|
+
{ name: 'get-config.ts', isDirectory: () => false }
|
|
139
|
+
];
|
|
140
|
+
fsPromises.opendir.mockResolvedValue(function () {
|
|
141
|
+
return __asyncGenerator(this, arguments, function* () {
|
|
142
|
+
yield yield __await(mockFiles[0]);
|
|
143
|
+
});
|
|
144
|
+
}());
|
|
145
|
+
const loadModuleSpy = jest.spyOn(typescript_mock_server_impl_1.TypescriptMockServerImpl, 'loadModule');
|
|
146
|
+
loadModuleSpy.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
147
|
+
return {
|
|
148
|
+
data: { message: 'custom' },
|
|
149
|
+
config: { statusCode: 201, delay: 10 }
|
|
150
|
+
};
|
|
151
|
+
}));
|
|
152
|
+
server = new typescript_mock_server_impl_1.TypescriptMockServerImpl(app);
|
|
153
|
+
yield server.start();
|
|
154
|
+
const response = yield (0, supertest_1.default)(app).get('/config');
|
|
155
|
+
expect(response.status).toBe(201);
|
|
156
|
+
expect(response.body).toEqual({ message: 'custom' });
|
|
157
|
+
}));
|
|
158
|
+
it('should resolve absolute paths correctly', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
159
|
+
fsPromises.opendir.mockResolvedValue(function () {
|
|
160
|
+
return __asyncGenerator(this, arguments, function* () {
|
|
161
|
+
// yield nothing
|
|
162
|
+
});
|
|
163
|
+
}());
|
|
164
|
+
const absolutePath = path_1.default.resolve('/absolute/path/to/models');
|
|
165
|
+
process.argv = ['node', 'index.js', `--path=${absolutePath}`];
|
|
166
|
+
server = new typescript_mock_server_impl_1.TypescriptMockServerImpl(app);
|
|
167
|
+
// @ts-ignore
|
|
168
|
+
expect(server.basePath).toBe(absolutePath);
|
|
169
|
+
}));
|
|
170
|
+
it('should resolve relative paths from process.cwd()', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
171
|
+
fsPromises.opendir.mockResolvedValue(function () {
|
|
172
|
+
return __asyncGenerator(this, arguments, function* () {
|
|
173
|
+
// yield nothing
|
|
174
|
+
});
|
|
175
|
+
}());
|
|
176
|
+
const relativePath = 'custom-models';
|
|
177
|
+
process.argv = ['node', 'index.js', `--path=${relativePath}`];
|
|
178
|
+
server = new typescript_mock_server_impl_1.TypescriptMockServerImpl(app);
|
|
179
|
+
// @ts-ignore
|
|
180
|
+
expect(server.basePath).toBe(path_1.default.join(process.cwd(), relativePath));
|
|
181
|
+
}));
|
|
182
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.config = exports.data = exports.newDate = void 0;
|
|
4
|
+
const newDate = () => new Date();
|
|
5
|
+
exports.newDate = newDate;
|
|
6
|
+
exports.data = [{
|
|
7
|
+
id: 1,
|
|
8
|
+
firstName: 'Guy',
|
|
9
|
+
lastName: 'Theuws',
|
|
10
|
+
creationDate: (0, exports.newDate)()
|
|
11
|
+
}, {
|
|
12
|
+
id: 2,
|
|
13
|
+
firstName: 'Generation Y',
|
|
14
|
+
lastName: 'Development',
|
|
15
|
+
creationDate: (0, exports.newDate)()
|
|
16
|
+
}];
|
|
17
|
+
exports.config = {
|
|
18
|
+
delay: 2000,
|
|
19
|
+
statusCode: 418
|
|
20
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.config = exports.data = void 0;
|
|
4
|
+
exports.data = {
|
|
5
|
+
id: 1,
|
|
6
|
+
firstName: 'Guy',
|
|
7
|
+
lastName: 'Theuws',
|
|
8
|
+
};
|
|
9
|
+
exports.config = {
|
|
10
|
+
delay: {
|
|
11
|
+
min: 1000,
|
|
12
|
+
max: 5000,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.config = exports.data = void 0;
|
|
4
|
+
const data = (req, res) => {
|
|
5
|
+
const userId = req.query.id ? parseInt(req.query.id) : 1;
|
|
6
|
+
return {
|
|
7
|
+
id: userId,
|
|
8
|
+
username: `user_${userId}`,
|
|
9
|
+
email: `user_${userId}@example.com`
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
exports.data = data;
|
|
13
|
+
exports.config = {
|
|
14
|
+
statusCode: 200
|
|
15
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.config = exports.data = exports.newDate = void 0;
|
|
4
|
+
const newDate = () => new Date();
|
|
5
|
+
exports.newDate = newDate;
|
|
6
|
+
exports.data = [{
|
|
7
|
+
id: 1,
|
|
8
|
+
firstName: 'Guy',
|
|
9
|
+
lastName: 'Theuws',
|
|
10
|
+
creationDate: (0, exports.newDate)()
|
|
11
|
+
}, {
|
|
12
|
+
id: 2,
|
|
13
|
+
firstName: 'Generation Y',
|
|
14
|
+
lastName: 'Development',
|
|
15
|
+
creationDate: (0, exports.newDate)()
|
|
16
|
+
}];
|
|
17
|
+
exports.config = {
|
|
18
|
+
delay: 2000,
|
|
19
|
+
statusCode: 418
|
|
20
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.data = void 0;
|
|
4
|
+
const get_1 = require("../get");
|
|
5
|
+
exports.data = {
|
|
6
|
+
id: 1, title: 'Hello profile', user: {
|
|
7
|
+
id: 1,
|
|
8
|
+
firstName: 'Guy',
|
|
9
|
+
lastName: 'Theuws',
|
|
10
|
+
creationDate: (0, get_1.newDate)()
|
|
11
|
+
}
|
|
12
|
+
};
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { createDefaultPreset } = require("ts-jest");
|
|
2
|
+
|
|
3
|
+
const tsJestTransformCfg = createDefaultPreset().transform;
|
|
4
|
+
|
|
5
|
+
/** @type {import("jest").Config} **/
|
|
6
|
+
module.exports = {
|
|
7
|
+
testEnvironment: "node",
|
|
8
|
+
testPathIgnorePatterns: ["/node_modules/", "/dist/"],
|
|
9
|
+
transform: {
|
|
10
|
+
...tsJestTransformCfg,
|
|
11
|
+
},
|
|
12
|
+
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typescript-mock-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.2",
|
|
4
4
|
"description": "Simple mock server that can be used in front end development. Instead of creating json files you can just publish TypeScript objects as json",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"typescript-mock-server": "dist/src/index.js"
|
|
8
|
+
},
|
|
5
9
|
"scripts": {
|
|
6
|
-
"test": "
|
|
7
|
-
"
|
|
8
|
-
"
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"build": "tsc --outDir dist && sed -i '' '1s|.*|#!/usr/bin/env node|' dist/src/index.js",
|
|
12
|
+
"example": "npm run build && node dist/src/index.js --path=tms-models --port=5200 --cors=http://localhost:5200",
|
|
13
|
+
"start": "npm run build && node dist/src/index.js",
|
|
9
14
|
"update-deps": "npm update",
|
|
10
15
|
"get-version": "echo $npm_package_version",
|
|
11
16
|
"publish-to-npm": "git tag -a ${npm_package_version} -m \"v${npm_package_version}\" && git push origin ${npm_package_version} && npm publish"
|
|
@@ -9,7 +9,8 @@ import { opendir } from 'fs/promises';
|
|
|
9
9
|
import { LoggerImpl } from './logger-impl';
|
|
10
10
|
import { Logger } from '../logger';
|
|
11
11
|
import { TypescriptMockServer } from '../typescript-mock-server';
|
|
12
|
-
import { Interval } from '../models/config';
|
|
12
|
+
import { Interval, MockModel, ServerConfig } from '../models/config';
|
|
13
|
+
import path from 'path';
|
|
13
14
|
|
|
14
15
|
export class TypescriptMockServerImpl implements TypescriptMockServer{
|
|
15
16
|
|
|
@@ -19,12 +20,21 @@ export class TypescriptMockServerImpl implements TypescriptMockServer{
|
|
|
19
20
|
private readonly basePath;
|
|
20
21
|
private registeredEndpoints: RegisteredEndpoint[] = [];
|
|
21
22
|
|
|
22
|
-
constructor() {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
constructor(config?: ServerConfig | Express) {
|
|
24
|
+
if (config && 'use' in (config as any)) {
|
|
25
|
+
this.app = config as Express;
|
|
26
|
+
this.basePath = this.getPath();
|
|
27
|
+
} else {
|
|
28
|
+
const serverConfig = config as ServerConfig;
|
|
29
|
+
this.app = serverConfig?.app || express();
|
|
30
|
+
this.basePath = this.getPath(serverConfig?.path);
|
|
31
|
+
}
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
private static async loadModule(moduleName: string) {
|
|
35
|
+
if (moduleName.endsWith('.ts')) {
|
|
36
|
+
require('ts-node').register({ transpileOnly: true });
|
|
37
|
+
}
|
|
28
38
|
return await import(moduleName);
|
|
29
39
|
}
|
|
30
40
|
|
|
@@ -36,43 +46,55 @@ export class TypescriptMockServerImpl implements TypescriptMockServer{
|
|
|
36
46
|
};
|
|
37
47
|
|
|
38
48
|
this.app.use(cors(corsSetting))
|
|
49
|
+
// add started endpoint
|
|
50
|
+
this.addEndpoint('state', 'get', { data: { status: 'started' } });
|
|
51
|
+
|
|
39
52
|
await this.readRoutes(this.basePath).catch(error => this.log.error(error));
|
|
40
53
|
this.app.listen(port, () => {
|
|
41
54
|
this.log.info(`App is listening on port ${port}!`);
|
|
42
55
|
});
|
|
43
56
|
|
|
44
|
-
|
|
45
|
-
this.addEndpoint('state', 'get', { data: "{\"status\": \"started\"}" });
|
|
46
|
-
this.log.info(`Started mock server on port ${this.commandLine.getCommand(Command.PORT)}`);
|
|
57
|
+
this.log.info(`Started mock server on port ${port}`);
|
|
47
58
|
}
|
|
48
59
|
|
|
49
|
-
private async readRoutes(
|
|
50
|
-
const dir = await opendir(
|
|
60
|
+
private async readRoutes(dirPath: string) {
|
|
61
|
+
const dir = await opendir(dirPath);
|
|
51
62
|
for await (const dirent of dir) {
|
|
52
63
|
if (dirent.isDirectory()) {
|
|
53
|
-
await this.readRoutes(`${
|
|
64
|
+
await this.readRoutes(`${dirPath}/${dirent.name}`);
|
|
54
65
|
} else {
|
|
55
|
-
this.handleFile(
|
|
66
|
+
await this.handleFile(dirPath, dirent);
|
|
56
67
|
}
|
|
57
68
|
}
|
|
58
|
-
|
|
69
|
+
const port = this.commandLine.getCommand(Command.PORT) || 3000;
|
|
70
|
+
this.registeredEndpoints.forEach(endpoint => this.log.info(`${endpoint.httpVerb.toUpperCase()} http://localhost:${port}${endpoint.endpoint}`));
|
|
59
71
|
this.registeredEndpoints = [];
|
|
60
72
|
}
|
|
61
73
|
|
|
62
|
-
private handleFile(
|
|
74
|
+
private async handleFile(dirPath: string, dirent: Dirent) {
|
|
63
75
|
const httpVerb = (dirent.name.indexOf('-') > -1 ? dirent.name.split('-')[0] : dirent.name.split('.')[0]) as HttpVerb;
|
|
64
|
-
this.handleRequest(
|
|
76
|
+
await this.handleRequest(dirPath, dirent, httpVerb);
|
|
65
77
|
}
|
|
66
78
|
|
|
67
|
-
private addEndpoint(endpoint: string, httpVerb: HttpVerb, model:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
private addEndpoint(endpoint: string, httpVerb: HttpVerb, model: MockModel) {
|
|
80
|
+
const route = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
|
81
|
+
this.app[httpVerb](route, (req: any, res: any) => {
|
|
82
|
+
let responseData = model.data;
|
|
83
|
+
let statusCode = model?.config?.statusCode;
|
|
84
|
+
let delay = model?.config?.delay;
|
|
85
|
+
|
|
86
|
+
if (typeof model.data === 'function') {
|
|
87
|
+
responseData = model.data(req, res);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (statusCode) {
|
|
91
|
+
res.statusCode = statusCode;
|
|
71
92
|
}
|
|
72
|
-
|
|
73
|
-
|
|
93
|
+
|
|
94
|
+
if (delay) {
|
|
95
|
+
setTimeout(() => res.send(responseData), this.getDelayValue(delay));
|
|
74
96
|
} else {
|
|
75
|
-
return res.send(
|
|
97
|
+
return res.send(responseData);
|
|
76
98
|
}
|
|
77
99
|
});
|
|
78
100
|
}
|
|
@@ -86,17 +108,30 @@ export class TypescriptMockServerImpl implements TypescriptMockServer{
|
|
|
86
108
|
return 0;
|
|
87
109
|
}
|
|
88
110
|
|
|
89
|
-
private handleRequest(
|
|
90
|
-
const endpoint = this.convertFileNameToEndpoint(
|
|
91
|
-
|
|
111
|
+
private async handleRequest(dirPath: string, dirent: Dirent, httpVerb: HttpVerb) {
|
|
112
|
+
const endpoint = this.convertFileNameToEndpoint(dirPath, dirent, httpVerb);
|
|
113
|
+
let modulePath = `${dirPath}/${dirent.name}`;
|
|
92
114
|
this.registeredEndpoints.push({ httpVerb, endpoint });
|
|
93
|
-
|
|
115
|
+
|
|
116
|
+
if (__filename.endsWith('.js') && modulePath.endsWith('.ts')) {
|
|
117
|
+
const distPath = path.join(process.cwd(), 'dist');
|
|
118
|
+
const potentialJsPath = modulePath
|
|
119
|
+
.replace(process.cwd(), distPath)
|
|
120
|
+
.replace(/\.ts$/, '.js');
|
|
121
|
+
|
|
122
|
+
const fs = require('fs');
|
|
123
|
+
if (fs.existsSync(potentialJsPath)) {
|
|
124
|
+
modulePath = potentialJsPath;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
await TypescriptMockServerImpl.loadModule(modulePath)
|
|
94
129
|
.then(model => this.addEndpoint(endpoint, httpVerb, model))
|
|
95
130
|
.catch(error => this.log.error(error));
|
|
96
131
|
}
|
|
97
132
|
|
|
98
|
-
private convertFileNameToEndpoint(
|
|
99
|
-
const endpoint = `${
|
|
133
|
+
private convertFileNameToEndpoint(dirPath: string, dirent: Dirent, httpVerb: HttpVerb): string {
|
|
134
|
+
const endpoint = `${dirPath.replace(this.basePath, '')}/${dirent.name}`
|
|
100
135
|
.replace('.ts', '')
|
|
101
136
|
.replace(`${httpVerb}-`, '')
|
|
102
137
|
.replace(httpVerb, '');
|
|
@@ -107,11 +142,17 @@ export class TypescriptMockServerImpl implements TypescriptMockServer{
|
|
|
107
142
|
return endpoint;
|
|
108
143
|
}
|
|
109
144
|
|
|
110
|
-
private getPath(): string {
|
|
145
|
+
private getPath(defaultPath: string = 'tms-models'): string {
|
|
146
|
+
let definedPath = defaultPath;
|
|
111
147
|
if (!this.commandLine.getCommands().has(Command.PATH)) {
|
|
112
|
-
this.log.warn(`Path parameter not set, fallback to default
|
|
113
|
-
|
|
148
|
+
this.log.warn(`Path parameter not set, fallback to default ${defaultPath}`);
|
|
149
|
+
} else {
|
|
150
|
+
definedPath = this.commandLine.getCommand(Command.PATH)!!;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (path.isAbsolute(definedPath)) {
|
|
154
|
+
return definedPath;
|
|
114
155
|
}
|
|
115
|
-
return
|
|
156
|
+
return path.join(process.cwd(), definedPath);
|
|
116
157
|
}
|
|
117
158
|
}
|
package/src/index.ts
CHANGED
package/src/models/config.ts
CHANGED
|
@@ -7,7 +7,17 @@ export interface RequestConfig {
|
|
|
7
7
|
statusCode?: number; // Status code of response
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
export interface MockModel {
|
|
11
|
+
data: any | ((req: any, res: any) => any);
|
|
12
|
+
config?: RequestConfig;
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
export interface Interval {
|
|
11
16
|
min: number; // Minimum boundary, including the value
|
|
12
17
|
max: number; // Maximum boundary, including the value
|
|
13
18
|
}
|
|
19
|
+
|
|
20
|
+
export interface ServerConfig {
|
|
21
|
+
app?: any; // Express app instance
|
|
22
|
+
path?: string; // Path to models folder
|
|
23
|
+
}
|