mockapi-msi 2.0.0 → 2.4.0
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/LICENSE +164 -164
- package/README.md +319 -116
- package/apiHandlers/myCustomHandler.js +26 -9
- package/main.js +87 -53
- package/modules/HttpException.js +8 -30
- package/modules/banner.js +22 -0
- package/modules/cli.js +106 -106
- package/modules/configWatcher.js +60 -0
- package/modules/configurationParser.js +43 -43
- package/modules/constants.js +95 -95
- package/modules/core.js +288 -107
- package/modules/csv.js +121 -77
- package/modules/log.js +42 -38
- package/modules/moduleProxy.js +51 -54
- package/modules/readers.js +42 -42
- package/modules/urlParser.js +53 -22
- package/package.json +38 -20
- package/testdata/data.csv +5 -5
- package/.dockerignore +0 -24
- package/.mockapi-config +0 -47
- package/Dockerfile +0 -8
- package/docker-compose.debug.yml +0 -14
- package/docker-compose.yml +0 -12
package/README.md
CHANGED
|
@@ -1,116 +1,319 @@
|
|
|
1
|
-
# Dynamic mocking descriptive API (MockAPI)
|
|
2
|
-
|
|
3
|
-
MockAPI help you buiding your app when requires connecting to a not yet built external API.
|
|
4
|
-
|
|
5
|
-
MockAPI let you create fake responses with pre defined and dynamic data for defined endpoints.
|
|
6
|
-
|
|
7
|
-
MockAPI also is intended to help you when, during testing phase, you cannot afford complex and expensive products (And you do not need them) that requires bulky configuration steps or depends directly on third party providers that you cannot control.
|
|
8
|
-
|
|
9
|
-
## Version 2.
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
|
|
1
|
+
# Dynamic mocking descriptive API (MockAPI)
|
|
2
|
+
|
|
3
|
+
MockAPI help you buiding your app when requires connecting to a not yet built external API.
|
|
4
|
+
|
|
5
|
+
MockAPI let you create fake responses with pre defined and dynamic data for defined endpoints.
|
|
6
|
+
|
|
7
|
+
MockAPI also is intended to help you when, during testing phase, you cannot afford complex and expensive products (And you do not need them) that requires bulky configuration steps or depends directly on third party providers that you cannot control.
|
|
8
|
+
|
|
9
|
+
## Version 2.4.0 notes
|
|
10
|
+
|
|
11
|
+
- **HTTPS support**: New `tls` configuration option with `cert` and `key` paths. When provided, MockAPI starts an HTTPS server instead of HTTP.
|
|
12
|
+
- **Unit tests**: Test suite added using Node.js built-in test runner. Tests cover CSV parsing, URL parsing, path matching, HttpException, and Core HTTP server behavior. Run with `npm test`.
|
|
13
|
+
|
|
14
|
+
## Version 2.3.0 notes
|
|
15
|
+
|
|
16
|
+
- **CORS fine-tuning**: `enableCors` now accepts an object to configure specific origins, methods, and headers. Preflight (OPTIONS) requests are handled automatically. Boolean `true` is still supported for allow-all behavior.
|
|
17
|
+
- **Graceful shutdown**: The server now handles `SIGTERM` and `SIGINT` signals, cleanly closing the HTTP server and config watcher before exiting.
|
|
18
|
+
- **Static file serving**: New `staticPath` configuration option serves files from a local directory. Requests that don't match any endpoint will attempt to serve a static file before returning 404.
|
|
19
|
+
|
|
20
|
+
## Version 2.2.0 notes
|
|
21
|
+
|
|
22
|
+
- **Path parameters**: Endpoints now support path parameters using `:param` syntax (e.g., `/users/:id`). Extracted parameters are available in custom handlers via `requestInformation.params`.
|
|
23
|
+
- **Query parameters**: Query string parameters are now parsed and available in custom handlers via `requestInformation.query`.
|
|
24
|
+
- **Response delay**: Endpoints can now simulate slow APIs with a `delay` property (in milliseconds).
|
|
25
|
+
- **Hot-reload**: The configuration file is watched for changes. Endpoints and data sources are automatically reloaded without restarting the server.
|
|
26
|
+
|
|
27
|
+
## Version 2.1.0 notes
|
|
28
|
+
|
|
29
|
+
- Codebase modernized to ES6 classes (`HttpException`, `Log`, `CSV`).
|
|
30
|
+
- CSV parser rewritten with RFC 4180 compliant state machine. Correctly handles commas inside quoted fields, escaped quotes, and mixed line endings (`\r\n`, `\n`, `\r`).
|
|
31
|
+
- Fixed bug in `urlParser` (`searchParamss` typo).
|
|
32
|
+
- Fixed bug in `moduleProxy.execute()` referencing a variable outside its scope.
|
|
33
|
+
- Fixed invalid top-level `return` statements in `main.js`.
|
|
34
|
+
- Docker image updated from Node 12 (EOL) to Node 20 LTS; switched to `npm ci --omit=dev`.
|
|
35
|
+
- Removed deprecated `version` key from Docker Compose files.
|
|
36
|
+
- `package.json` updated with `files`, `keywords`, and engine requirement bumped to `>=18`.
|
|
37
|
+
- Custom handler example (`myCustomHandler.js`) modernized with JSDoc and meaningful sample logic.
|
|
38
|
+
- ASCII art banner displayed on application startup.
|
|
39
|
+
|
|
40
|
+
## Version 2.0.1 notes
|
|
41
|
+
|
|
42
|
+
- A bug related to custom handlers was detected and fixed.
|
|
43
|
+
|
|
44
|
+
## Version 2.0.0 notes
|
|
45
|
+
|
|
46
|
+
- Using NodeJS managers such as NVM causes configuration file not being picked from the execution/working folder.
|
|
47
|
+
- New CORE class created and code moved from the main module.
|
|
48
|
+
- Additional checking for the configuration file.
|
|
49
|
+
- Folder file readear incorrect path contactenation fixed.
|
|
50
|
+
|
|
51
|
+
## Configuration file
|
|
52
|
+
|
|
53
|
+
MockAPI can be used as it is. Without any additional coding activity. Just configure your endpoints and run ```main.js``` file.
|
|
54
|
+
|
|
55
|
+
### Configuring MockAPI
|
|
56
|
+
|
|
57
|
+
Edit ```.mockapi-config``` to add your own endpoints, responses, parsers and data. Use standard YAML notation for this file.
|
|
58
|
+
|
|
59
|
+
#### Main entry points
|
|
60
|
+
|
|
61
|
+
**port** - Specify the HTTP port to be used by MockAPI to expose the defined endpoints.
|
|
62
|
+
|
|
63
|
+
**enableCors** - Configure CORS for MockAPI. Accepts ```true``` for allow-all behavior, or an object for fine-grained control.
|
|
64
|
+
|
|
65
|
+
**externalModulesPath** - Optional configuration. Allows to specify a different path where the user custom handlers are located.
|
|
66
|
+
|
|
67
|
+
**staticPath** - Optional configuration. Path to a local directory to serve static files from. Requests that don't match any endpoint will fall back to static file serving.
|
|
68
|
+
|
|
69
|
+
**data** -
|
|
70
|
+
Holds and describe the available data for all endpoints and responses.
|
|
71
|
+
|
|
72
|
+
**endpoints** - Describe all available endpoints, its verbs and responses.
|
|
73
|
+
|
|
74
|
+
**log** - Define MockAPI log level.
|
|
75
|
+
|
|
76
|
+
**customHandlers** - Import a custom HTTP data handler. Use these custom handlers to manipulate the output of your responses for a particular endpoint.
|
|
77
|
+
|
|
78
|
+
Within ```data``` entry, it is possible to define how the data must be handled. There are three built-in readers that comes with MockAPI.
|
|
79
|
+
|
|
80
|
+
**csv** - Reads the defined data as CSV.
|
|
81
|
+
|
|
82
|
+
**text** - Considers the data source as plain text.
|
|
83
|
+
|
|
84
|
+
**folder** - Reads the files from the folder matching the incoming request name.
|
|
85
|
+
|
|
86
|
+
#### Data definition
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
myRows:
|
|
90
|
+
path: "./testdata/data.csv"
|
|
91
|
+
reader: csv
|
|
92
|
+
properties:
|
|
93
|
+
- json
|
|
94
|
+
- seq
|
|
95
|
+
- 0
|
|
96
|
+
```
|
|
97
|
+
The previous example defines a data source called ```myRows```, which will read the data from the defined ```path```, using the ```csv``` handler, parsing each row as ```json```, reading the values in a ```sequential``` order, starting from record ```0```.
|
|
98
|
+
|
|
99
|
+
#### Endpoint definition
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
"/users":
|
|
103
|
+
verb: get
|
|
104
|
+
data: myRows
|
|
105
|
+
responseStatus: 200
|
|
106
|
+
responseContentType: "application/json"
|
|
107
|
+
```
|
|
108
|
+
From the previous code snippet, we are defining an endpoint ```[MockAPI URL]:[PORT]/users```, which will accept ```get``` requests, answering always with ```200``` status code, using data from ```myRows``` data definition in ```JSON``` format.
|
|
109
|
+
|
|
110
|
+
#### Path parameters
|
|
111
|
+
|
|
112
|
+
Endpoints support path parameters using the ```:param``` syntax. Parameters are extracted from the URL and made available to custom handlers.
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
"/users/:id":
|
|
116
|
+
verb: get
|
|
117
|
+
data: myRows
|
|
118
|
+
responseStatus: 200
|
|
119
|
+
responseContentType: "application/json"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
A request to ```/users/42``` will match this endpoint and extract ```{ id: "42" }``` as path parameters. Multiple path parameters are supported (e.g., ```/users/:userId/orders/:orderId```).
|
|
123
|
+
|
|
124
|
+
A custom handler can then use these parameters:
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
const process = (requestInformation, data) => {
|
|
128
|
+
const userId = requestInformation.params.id;
|
|
129
|
+
// requestInformation.params contains all extracted path parameters
|
|
130
|
+
// requestInformation.query contains all query string parameters
|
|
131
|
+
return JSON.stringify({ userId, data: JSON.parse(data) });
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
module.exports = { process };
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Combining path and query parameters, a request to ```/users/42?fields=name,email``` would provide:
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
requestInformation.params // { id: "42" }
|
|
141
|
+
requestInformation.query // { fields: "name,email" }
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### Query parameters
|
|
145
|
+
|
|
146
|
+
Query string parameters are automatically parsed from the request URL. For example, a request to ```/users?role=admin&active=true``` will make ```{ role: "admin", active: "true" }``` available in custom handlers via ```requestInformation.query```.
|
|
147
|
+
|
|
148
|
+
#### Response delay
|
|
149
|
+
|
|
150
|
+
Simulate slow API responses by adding a ```delay``` property (in milliseconds) to any endpoint:
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
"/users":
|
|
154
|
+
verb: get
|
|
155
|
+
data: myRows
|
|
156
|
+
delay: 2000
|
|
157
|
+
responseStatus: 200
|
|
158
|
+
responseContentType: "application/json"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The previous example will wait ```2000ms``` before sending the response, which is useful for testing loading states, spinners and timeout handling.
|
|
162
|
+
|
|
163
|
+
#### Hot-reload
|
|
164
|
+
|
|
165
|
+
MockAPI watches the ```.mockapi-config``` file for changes. When the file is saved, endpoints and data sources are automatically reloaded without restarting the server. This allows you to add, remove, or modify endpoints while the server is running.
|
|
166
|
+
|
|
167
|
+
#### CORS configuration
|
|
168
|
+
|
|
169
|
+
CORS can be configured in three ways:
|
|
170
|
+
|
|
171
|
+
**Disabled** (default):
|
|
172
|
+
```yaml
|
|
173
|
+
enableCors: false
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Allow all** (same as previous versions):
|
|
177
|
+
```yaml
|
|
178
|
+
enableCors: true
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Fine-grained control** — specify allowed origins, methods, and headers:
|
|
182
|
+
```yaml
|
|
183
|
+
enableCors:
|
|
184
|
+
origins:
|
|
185
|
+
- "http://localhost:3000"
|
|
186
|
+
- "https://myapp.example.com"
|
|
187
|
+
methods:
|
|
188
|
+
- GET
|
|
189
|
+
- POST
|
|
190
|
+
- PUT
|
|
191
|
+
headers:
|
|
192
|
+
- Content-Type
|
|
193
|
+
- Authorization
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
When using fine-grained CORS, MockAPI automatically handles ```OPTIONS``` preflight requests. If the request origin is not in the allowed list, CORS headers are not set.
|
|
197
|
+
|
|
198
|
+
#### Static file serving
|
|
199
|
+
|
|
200
|
+
MockAPI can serve static files from a local directory. Add the ```staticPath``` property to your configuration:
|
|
201
|
+
|
|
202
|
+
```yaml
|
|
203
|
+
staticPath: "./public"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
When a request does not match any configured endpoint, MockAPI will attempt to serve a matching file from the specified directory. The file's MIME type is determined automatically from its extension. Supported types include HTML, CSS, JavaScript, JSON, PNG, JPG, GIF, SVG, PDF, and more.
|
|
207
|
+
|
|
208
|
+
For example, with ```staticPath: "./public"```, a request to ```/images/logo.png``` will serve the file at ```./public/images/logo.png```.
|
|
209
|
+
|
|
210
|
+
#### HTTPS
|
|
211
|
+
|
|
212
|
+
MockAPI supports HTTPS. Add a ```tls``` section to your configuration with the paths to your certificate and private key files:
|
|
213
|
+
|
|
214
|
+
```yaml
|
|
215
|
+
tls:
|
|
216
|
+
cert: "./certs/server.crt"
|
|
217
|
+
key: "./certs/server.key"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
When TLS is configured, MockAPI starts an HTTPS server instead of HTTP. If the certificate files cannot be read, MockAPI falls back to HTTP and logs an error.
|
|
221
|
+
|
|
222
|
+
To generate a self-signed certificate for local development:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
openssl req -x509 -newkey rsa:2048 -keyout server.key -out server.crt -days 365 -nodes -subj "/CN=localhost"
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### Custom handlers
|
|
229
|
+
|
|
230
|
+
A custom handler let you manipulate the data as your will. First, define the handler as follows:
|
|
231
|
+
|
|
232
|
+
```yaml
|
|
233
|
+
customHandlers:
|
|
234
|
+
"custom":
|
|
235
|
+
"myCustomHandler"
|
|
236
|
+
```
|
|
237
|
+
The previous code defines a custom handler called ```custom``` and will use the script code called ```myCustomHandler```. The custom code must be placed inside of ```scripts``` folder and coded in JavaScript with NodeJS support.
|
|
238
|
+
|
|
239
|
+
Your custom script must implement the following export format:
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
module.exports = {
|
|
243
|
+
process: [Your function entry point]
|
|
244
|
+
};
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### Setting up the log level
|
|
248
|
+
|
|
249
|
+
MockAPI logs information into the execution console. There are different levels of logs that can be used.
|
|
250
|
+
|
|
251
|
+
```yaml
|
|
252
|
+
log: verbose
|
|
253
|
+
#debug <- Useful for custom handlers
|
|
254
|
+
#error <- Only exposes internal errors
|
|
255
|
+
#verbose <- Logs debug, information and errors
|
|
256
|
+
#none <- Turn off the logs
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## MockAPI CLI
|
|
260
|
+
|
|
261
|
+
MockAPI provides a small but helpful CLI. Type ```mockapi --help``` to get the available commands from the CLI once MockAPI is installed.
|
|
262
|
+
|
|
263
|
+
#### The ```init``` command
|
|
264
|
+
|
|
265
|
+
Once MockAPI is globally installed, you will need a configuration file with minimal information to be able of start mocking the API. The ```init``` command argument will lead you through different basic questions helping you to initialize this configuration file.
|
|
266
|
+
|
|
267
|
+
```powershell
|
|
268
|
+
mockapi --init
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
You can skip every question which will assign some default values to the configuration file. Later you could change these values using any text editor.
|
|
272
|
+
|
|
273
|
+
## Running MockAPI with Docker
|
|
274
|
+
|
|
275
|
+
MockAPI includes Docker support for running the application in a containerized environment.
|
|
276
|
+
|
|
277
|
+
### Building the Docker image
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
docker build -t mockapi .
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Running with Docker
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
docker run -p 3001:8001 -v $(pwd)/.mockapi-config:/usr/src/app/.mockapi-config mockapi
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
This maps port `3001` on your host to port `8001` inside the container and mounts your local configuration file into the container. Adjust the port mapping to match the `port` value in your `.mockapi-config` file.
|
|
290
|
+
|
|
291
|
+
### Running with Docker Compose
|
|
292
|
+
|
|
293
|
+
#### Production mode
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
docker compose up
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
This builds and starts MockAPI using the `docker-compose.yml` file, exposing the API on port `3001`.
|
|
300
|
+
|
|
301
|
+
#### Debug mode
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
docker compose -f docker-compose.debug.yml up
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
This starts MockAPI with the Node.js inspector enabled on port `9229`, allowing you to attach a debugger. The API is available on port `3000`.
|
|
308
|
+
|
|
309
|
+
### Mounting custom data and handlers
|
|
310
|
+
|
|
311
|
+
To use your own data files and custom handlers with Docker, mount them as volumes:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
docker run -p 3001:8001 \
|
|
315
|
+
-v $(pwd)/.mockapi-config:/usr/src/app/.mockapi-config \
|
|
316
|
+
-v $(pwd)/testdata:/usr/src/app/testdata \
|
|
317
|
+
-v $(pwd)/apiHandlers:/usr/src/app/apiHandlers \
|
|
318
|
+
mockapi
|
|
319
|
+
```
|
|
@@ -1,9 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Example custom handler for MockAPI.
|
|
3
|
+
*
|
|
4
|
+
* Custom handlers let you manipulate the response for an endpoint.
|
|
5
|
+
* When an endpoint uses a handler, this module processes the data
|
|
6
|
+
* instead of returning it directly.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} requestInformation - Incoming request details.
|
|
9
|
+
* @param {string} requestInformation.method - HTTP method (get, post, etc.).
|
|
10
|
+
* @param {string} requestInformation.url - Matched endpoint URL.
|
|
11
|
+
* @param {string} requestInformation.body - Raw request body.
|
|
12
|
+
* @param {string} data - Pre-processed data from the configured data reader.
|
|
13
|
+
* @returns {string} The response body to send back to the client.
|
|
14
|
+
*/
|
|
15
|
+
const process = (requestInformation, data) => {
|
|
16
|
+
// Example: wrap the data in an envelope with request metadata
|
|
17
|
+
const response = {
|
|
18
|
+
endpoint: requestInformation.url,
|
|
19
|
+
method: requestInformation.method,
|
|
20
|
+
payload: data ? JSON.parse(data) : null
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return JSON.stringify(response);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
module.exports = { process };
|
package/main.js
CHANGED
|
@@ -1,53 +1,87 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
const LOG = require('./modules/log');
|
|
6
|
-
const YAML = require('yaml');
|
|
7
|
-
const constants = require('./modules/constants');
|
|
8
|
-
const readers = require('./modules/readers');
|
|
9
|
-
const ModuleProxy = require('./modules/moduleProxy');
|
|
10
|
-
const CLI = require('./modules/cli');
|
|
11
|
-
const CORE = require('./modules/core');
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
log.message(
|
|
44
|
-
|
|
45
|
-
log.message(
|
|
46
|
-
|
|
47
|
-
log.message(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
log.message(
|
|
53
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const LOG = require('./modules/log');
|
|
6
|
+
const YAML = require('yaml');
|
|
7
|
+
const constants = require('./modules/constants');
|
|
8
|
+
const readers = require('./modules/readers');
|
|
9
|
+
const ModuleProxy = require('./modules/moduleProxy');
|
|
10
|
+
const CLI = require('./modules/cli');
|
|
11
|
+
const CORE = require('./modules/core');
|
|
12
|
+
const banner = require('./modules/banner');
|
|
13
|
+
const ConfigWatcher = require('./modules/configWatcher');
|
|
14
|
+
|
|
15
|
+
const rootPath = process.cwd();
|
|
16
|
+
const configFilePath = `${rootPath}/${constants.CONFIG_FILE_NAME}`;
|
|
17
|
+
|
|
18
|
+
const cli = new CLI(configFilePath);
|
|
19
|
+
|
|
20
|
+
if (cli.hasCommands()) {
|
|
21
|
+
cli.executeCommandLine();
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!readers.file_exists(configFilePath)) {
|
|
26
|
+
console.log(`Configuration file not found. Please run ${constants.COLOR.fgGreen}--init${constants.COLOR.reset} using the CLI.`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const configFile = readers.text_reader(configFilePath);
|
|
31
|
+
const parsedConfiguration = YAML.parse(configFile());
|
|
32
|
+
|
|
33
|
+
if (parsedConfiguration.port === undefined) throw new Error("port property is required");
|
|
34
|
+
|
|
35
|
+
const logLevel = parsedConfiguration.log || constants.LOG_LEVELS.ALL;
|
|
36
|
+
const log = new LOG(logLevel);
|
|
37
|
+
const moduleProxy = new ModuleProxy(`file://${rootPath}/${(parsedConfiguration.externalModulesPath || constants.EXTERNAL_MODULES_PATH)}`, log);
|
|
38
|
+
|
|
39
|
+
if (parsedConfiguration.customHandlers !== undefined) {
|
|
40
|
+
moduleProxy.load(parsedConfiguration.customHandlers);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
log.message(``);
|
|
44
|
+
banner.display();
|
|
45
|
+
log.message(`Mock API configuration:`);
|
|
46
|
+
log.message(` PORT: ${constants.COLOR.fgGreen}${parsedConfiguration.port}${constants.COLOR.reset}`);
|
|
47
|
+
log.message(` CORS enabled: ${parsedConfiguration.enableCors ? constants.COLOR.fgGreen : constants.COLOR.fgRed}${!!parsedConfiguration.enableCors}${constants.COLOR.reset}`);
|
|
48
|
+
log.message(` HTTPS: ${parsedConfiguration.tls ? constants.COLOR.fgGreen + 'enabled' : constants.COLOR.fgRed + 'disabled'}${constants.COLOR.reset}`);
|
|
49
|
+
if (parsedConfiguration.staticPath) {
|
|
50
|
+
log.message(` Static files: ${constants.COLOR.fgGreen}${parsedConfiguration.staticPath}${constants.COLOR.reset}`);
|
|
51
|
+
}
|
|
52
|
+
log.message(``);
|
|
53
|
+
|
|
54
|
+
const protocol = parsedConfiguration.tls ? 'https' : 'http';
|
|
55
|
+
|
|
56
|
+
log.message(`> Mock API attempting to use port: ${constants.COLOR.fgRed}${parsedConfiguration.port}${constants.COLOR.reset}`)
|
|
57
|
+
|
|
58
|
+
const core = new CORE(log, parsedConfiguration, moduleProxy);
|
|
59
|
+
core.run();
|
|
60
|
+
|
|
61
|
+
log.message(`> Mock API listening on ${constants.COLOR.fgGreen}${protocol}://localhost:${parsedConfiguration.port}${constants.COLOR.reset}`);
|
|
62
|
+
|
|
63
|
+
const watcher = new ConfigWatcher(configFilePath, log, (newConfig) => {
|
|
64
|
+
core.reload(newConfig);
|
|
65
|
+
log.message(`> Configuration reloaded. Endpoints updated.`);
|
|
66
|
+
});
|
|
67
|
+
watcher.watch();
|
|
68
|
+
|
|
69
|
+
log.message(`> Hot-reload enabled. Watching ${constants.COLOR.fgYellow}${constants.CONFIG_FILE_NAME}${constants.COLOR.reset} for changes.`);
|
|
70
|
+
log.message(``);
|
|
71
|
+
|
|
72
|
+
let isShuttingDown = false;
|
|
73
|
+
|
|
74
|
+
const shutdown = (signal) => {
|
|
75
|
+
if (isShuttingDown) return;
|
|
76
|
+
isShuttingDown = true;
|
|
77
|
+
|
|
78
|
+
log.message(`\n> ${signal} received. Shutting down gracefully...`);
|
|
79
|
+
watcher.stop();
|
|
80
|
+
core.stop(() => {
|
|
81
|
+
log.message(`> Mock API stopped.`);
|
|
82
|
+
process.exit(0);
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
87
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
package/modules/HttpException.js
CHANGED
|
@@ -1,31 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if (Error.captureStackTrace) {
|
|
10
|
-
Error.captureStackTrace(instance, HttpException);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return instance;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
HttpException.prototype = Object.create(Error.prototype, {
|
|
17
|
-
constructor: {
|
|
18
|
-
value: Error,
|
|
19
|
-
enumerable: false,
|
|
20
|
-
writable: true,
|
|
21
|
-
configurable: true
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
if (Object.setPrototypeOf){
|
|
26
|
-
Object.setPrototypeOf(HttpException, Error);
|
|
27
|
-
} else {
|
|
28
|
-
HttpException.__proto__ = Error;
|
|
29
|
-
}
|
|
30
|
-
|
|
1
|
+
class HttpException extends Error {
|
|
2
|
+
constructor(httpStatusCode, message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'HttpException';
|
|
5
|
+
this.httpStatusCode = httpStatusCode;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
31
9
|
module.exports = HttpException;
|