mock-service-cli 2.1.0 → 2.3.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/CHANGELOG.md +36 -0
- package/README.md +2 -1
- package/bin/mock-service-cli +24 -3
- package/lib/manageMockFiles.js +46 -13
- package/lib/mockServer.js +2 -2
- package/lib/utils.js +81 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [2.3.2](https://github.com/chandq/mock-service-cli/compare/v2.3.1...v2.3.2) (2022-01-02)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* be sure to write file successfully ([55415e3](https://github.com/chandq/mock-service-cli/commit/55415e387af4d8232db2bede0a51dad1dca474b3))
|
|
11
|
+
|
|
12
|
+
### [2.3.1](https://github.com/chandq/mock-service-cli/compare/v2.2.0...v2.3.1) (2022-01-01)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* issue of execute unit test case failed ([69f4609](https://github.com/chandq/mock-service-cli/commit/69f46099ed5b26de5d4cca523c56afcbf4db5f2c))
|
|
18
|
+
|
|
19
|
+
## [2.2.0](https://github.com/chandq/mock-service-cli/compare/v2.1.1...v2.2.0) (2021-12-31)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* 丰富日志工具函数 ([4dcd69c](https://github.com/chandq/mock-service-cli/commit/4dcd69c6400f65aaf77ea4a2923c9900d5954d09))
|
|
25
|
+
* 监听文件变化和写文件互斥执行 ([cfeab8e](https://github.com/chandq/mock-service-cli/commit/cfeab8ede05573974125a9fa91f1db6afa8a898d))
|
|
26
|
+
* record operation log ([42b74d8](https://github.com/chandq/mock-service-cli/commit/42b74d8be0e694600c999490d3fe93bfb3620051))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Bug Fixes
|
|
30
|
+
|
|
31
|
+
* issue of write large file failed ([d1c470d](https://github.com/chandq/mock-service-cli/commit/d1c470daf9829916016b73993b114fc5ffe3c985))
|
|
32
|
+
|
|
33
|
+
### [2.1.1](https://github.com/chandq/mock-service-cli/compare/v2.1.0...v2.1.1) (2021-12-30)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Bug Fixes
|
|
37
|
+
|
|
38
|
+
* issue of friendly show error tip ([e9822c2](https://github.com/chandq/mock-service-cli/commit/e9822c2038ad411d91a170af33e7541e266ca8e6))
|
|
39
|
+
* issue of empty file content ([3949e00](https://github.com/chandq/mock-service-cli/commit/3949e00a46b4555bc08ec8564221cc230c780dcb))
|
|
40
|
+
|
|
5
41
|
## [2.1.0](https://github.com/chandq/mock-service-cli/compare/v2.0.5...v2.1.0) (2021-12-30)
|
|
6
42
|
|
|
7
43
|
|
package/README.md
CHANGED
|
@@ -55,6 +55,7 @@ This will install `mock-service-cli` globally so that it may be run from the com
|
|
|
55
55
|
| `-v` or `--version` | Print the version and exit. | |
|
|
56
56
|
| `-S` or `--socket-server` | Start socket server which used to save api response data for future mock. | false |
|
|
57
57
|
| `-a` or `--api-stat` | Whether print api url and file path info or not, default false. | false |
|
|
58
|
+
| `-l` or `--log` | Whether record operation info by write file, default false. | false |
|
|
58
59
|
|
|
59
60
|
## Example
|
|
60
61
|
|
|
@@ -161,7 +162,7 @@ const socket = io.connect('http://192.168.31.54:8090/mock-data', {
|
|
|
161
162
|
}
|
|
162
163
|
});
|
|
163
164
|
|
|
164
|
-
socket.emit('mock-dir-stat', '/home/chen/projects/
|
|
165
|
+
socket.emit('mock-dir-stat', '/home/chen/projects/model-ui/mock');
|
|
165
166
|
socket.on('mock-dir-stat', function (data) {
|
|
166
167
|
console.debug('mock-dir-stat:', data);
|
|
167
168
|
});
|
package/bin/mock-service-cli
CHANGED
|
@@ -6,6 +6,8 @@ const colors = require('colors/safe'),
|
|
|
6
6
|
nodemon = require('nodemon');
|
|
7
7
|
const argv = require('minimist')(process.argv.slice(2));
|
|
8
8
|
const { logger } = require('../lib/utils');
|
|
9
|
+
const spawn = require('child_process').spawn;
|
|
10
|
+
const node = process.execPath;
|
|
9
11
|
process.title = 'mock-service-cli';
|
|
10
12
|
|
|
11
13
|
if (argv.h || argv.help) {
|
|
@@ -22,6 +24,7 @@ if (argv.h || argv.help) {
|
|
|
22
24
|
' -s --silent Suppress log messages from output',
|
|
23
25
|
' -S --socket-server Whether start socket server or not, default false.',
|
|
24
26
|
' -a --api-stat Whether print api url and file path or not, default false.',
|
|
27
|
+
' -l --log Whether record operation info by write file, default false.',
|
|
25
28
|
'',
|
|
26
29
|
' -h --help Print this list and exit.',
|
|
27
30
|
' -v --version Print the version and exit.'
|
|
@@ -94,16 +97,34 @@ const watchMockFiles = function (watchDir) {
|
|
|
94
97
|
);
|
|
95
98
|
});
|
|
96
99
|
};
|
|
100
|
+
// Node子进程启动MockServer
|
|
101
|
+
const startMockServer = function () {
|
|
102
|
+
spawn(node, [path.resolve(__dirname, '../lib/mockServer.js')], {
|
|
103
|
+
stdio: 'inherit'
|
|
104
|
+
});
|
|
105
|
+
};
|
|
97
106
|
|
|
98
107
|
if (specifiedFile) {
|
|
99
108
|
process.env.SPECIFIED_FILE = path.resolve(process.cwd(), specifiedFile);
|
|
100
|
-
|
|
109
|
+
if (isStartSocketServer) {
|
|
110
|
+
startMockServer();
|
|
111
|
+
} else {
|
|
112
|
+
watchMockFiles(process.env.SPECIFIED_FILE);
|
|
113
|
+
}
|
|
101
114
|
} else if (watchDir) {
|
|
102
115
|
process.env.SPECIFIED_DIR = path.resolve(process.cwd(), watchDir);
|
|
103
116
|
// log.info('SPECIFIED_DIR::', process.env.SPECIFIED_DIR);
|
|
104
|
-
|
|
117
|
+
if (isStartSocketServer) {
|
|
118
|
+
startMockServer();
|
|
119
|
+
} else {
|
|
120
|
+
watchMockFiles(process.env.SPECIFIED_DIR);
|
|
121
|
+
}
|
|
105
122
|
} else {
|
|
106
123
|
watchDir = process.env.SPECIFIED_DIR = path.resolve(process.cwd(), './mock');
|
|
107
124
|
// log.info('SPECIFIED_DIR::', process.env.SPECIFIED_DIR);
|
|
108
|
-
|
|
125
|
+
if (isStartSocketServer) {
|
|
126
|
+
startMockServer();
|
|
127
|
+
} else {
|
|
128
|
+
watchMockFiles(watchDir);
|
|
129
|
+
}
|
|
109
130
|
}
|
package/lib/manageMockFiles.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @description: 生成Mock文件、获取mock数据统计
|
|
3
3
|
* @Date: 2021-12-22 16:57:08
|
|
4
4
|
* @LastEditors: chendq
|
|
5
|
-
* @LastEditTime:
|
|
5
|
+
* @LastEditTime: 2022-01-02 12:16:12
|
|
6
6
|
* @Author: chendq
|
|
7
7
|
*/
|
|
8
8
|
const fsa = require('fs-extra'),
|
|
@@ -10,10 +10,34 @@ const fsa = require('fs-extra'),
|
|
|
10
10
|
path = require('path'),
|
|
11
11
|
_ = require('lodash'),
|
|
12
12
|
colors = require('colors/safe');
|
|
13
|
-
const {
|
|
13
|
+
const {
|
|
14
|
+
getFileLatestContent,
|
|
15
|
+
SupportMethods,
|
|
16
|
+
filePath2ApiUrl,
|
|
17
|
+
logger,
|
|
18
|
+
getDataType,
|
|
19
|
+
getLogger,
|
|
20
|
+
writeStream
|
|
21
|
+
} = require('./utils');
|
|
14
22
|
const log = logger(process.env.SILENT);
|
|
23
|
+
const processArgv = process.env.ARGV ? JSON.parse(process.env.ARGV) : {};
|
|
15
24
|
|
|
16
25
|
const methodRegExp = new RegExp(`(${SupportMethods.join('|')}) +(.*)`, 'i');
|
|
26
|
+
/**
|
|
27
|
+
* @description: 记录日志文件函数
|
|
28
|
+
* @param {boolean} isWriteLogFile 是否写入日志文件
|
|
29
|
+
* @param {string} mockDir 存放日志文件的目录
|
|
30
|
+
* @return {object}
|
|
31
|
+
*/
|
|
32
|
+
const logFile = (isWriteLogFile = false, mockDir) => {
|
|
33
|
+
if (isWriteLogFile) {
|
|
34
|
+
return getLogger(mockDir);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
log: function () {},
|
|
38
|
+
error: function () {}
|
|
39
|
+
};
|
|
40
|
+
};
|
|
17
41
|
/**
|
|
18
42
|
* @description: 生成mock文件(自动生成mock-list.json文件,并维护<url, [method]>的关系映射)
|
|
19
43
|
* @param {string} apiUrl 请求url
|
|
@@ -23,19 +47,27 @@ const methodRegExp = new RegExp(`(${SupportMethods.join('|')}) +(.*)`, 'i');
|
|
|
23
47
|
* @return {*} void
|
|
24
48
|
*
|
|
25
49
|
*/
|
|
26
|
-
const genMockFiles = function ({ url: apiUrl, method, data: resJsonData, dir: mockDir }) {
|
|
50
|
+
const genMockFiles = async function ({ url: apiUrl, method, data: resJsonData, dir: mockDir }) {
|
|
51
|
+
const type = getDataType(resJsonData);
|
|
27
52
|
if (!apiUrl || !method || !resJsonData || !mockDir) {
|
|
28
53
|
log.info(
|
|
29
|
-
colors.red(
|
|
30
|
-
[`参数: apiUrl: ${apiUrl}, method: ${method}, mockDir: ${mockDir}, resJsonData: `, `必须是有效值`].join(',')
|
|
31
|
-
)
|
|
54
|
+
colors.red([`参数: apiUrl: ${apiUrl}, method: ${method}, mockDir: ${mockDir} `, `必须是有效值`].join(','))
|
|
32
55
|
);
|
|
33
56
|
return;
|
|
34
|
-
} else if (
|
|
35
|
-
|
|
57
|
+
} else if (
|
|
58
|
+
!['object', 'array'].includes(type) ||
|
|
59
|
+
(type === 'object' && Object.keys(resJsonData).length === 0) ||
|
|
60
|
+
(type === 'array' && resJsonData.length === 0)
|
|
61
|
+
) {
|
|
62
|
+
log.info(
|
|
63
|
+
`参数: apiUrl: ${apiUrl}, method: ${method}, resJsonData:`,
|
|
64
|
+
resJsonData,
|
|
65
|
+
colors.red(', 参数字段resJsonData必须是非空的数组或对象!')
|
|
66
|
+
);
|
|
36
67
|
return;
|
|
37
68
|
}
|
|
38
69
|
apiUrl = apiUrl.trim().split('?')[0];
|
|
70
|
+
const LOGGER = logFile(processArgv.l || processArgv.log, mockDir);
|
|
39
71
|
const normalizeApiUrl = filePath2ApiUrl(path.normalize(apiUrl));
|
|
40
72
|
const mockListFilePath = path.resolve(process.cwd(), `${mockDir}/mock-list.json`);
|
|
41
73
|
const mockFilePath = path.resolve(process.cwd(), `${mockDir}/${encodeURIComponent(path.normalize(apiUrl))}.json`);
|
|
@@ -71,12 +103,13 @@ const genMockFiles = function ({ url: apiUrl, method, data: resJsonData, dir: mo
|
|
|
71
103
|
return;
|
|
72
104
|
}
|
|
73
105
|
mockFileContent[method] = resJsonData;
|
|
74
|
-
|
|
75
|
-
|
|
106
|
+
await writeStream(mockFilePath, JSON.stringify(mockFileContent, null, 2));
|
|
107
|
+
LOGGER.log(`Update file: ${method} ${apiUrl} ${mockFilePath}`);
|
|
76
108
|
} else {
|
|
77
109
|
// fsa.ensureFileSync(mockFilePath);
|
|
78
|
-
|
|
79
|
-
|
|
110
|
+
|
|
111
|
+
await writeStream(mockFilePath, JSON.stringify({ [method]: resJsonData }, null, 2));
|
|
112
|
+
LOGGER.log(`Write new file: ${method} ${apiUrl} ${mockFilePath}`);
|
|
80
113
|
}
|
|
81
114
|
// console.debug('file::', `${mockDir}/${apiUrl}`, newMockListContent);
|
|
82
115
|
};
|
|
@@ -169,7 +202,7 @@ const getMockStatFromDir = dirPath => {
|
|
|
169
202
|
}
|
|
170
203
|
}
|
|
171
204
|
});
|
|
172
|
-
|
|
205
|
+
writeStream(mockListFilePath, JSON.stringify(reverseMockList, null, 2));
|
|
173
206
|
}
|
|
174
207
|
return mockDataMap;
|
|
175
208
|
};
|
package/lib/mockServer.js
CHANGED
|
@@ -150,9 +150,9 @@ if (process.env.SOCKET_SERVER) {
|
|
|
150
150
|
socket.on('mock-file-stat', function (filePath) {
|
|
151
151
|
socket.emit('mock-file-stat', getMockStatFromFile(filePath));
|
|
152
152
|
});
|
|
153
|
-
socket.on('save-data', function (data) {
|
|
153
|
+
socket.on('save-data', async function (data) {
|
|
154
154
|
// console.log('lis save-data:', data);
|
|
155
|
-
genMockFiles(data);
|
|
155
|
+
await genMockFiles(data);
|
|
156
156
|
});
|
|
157
157
|
|
|
158
158
|
socket.on('disconnect', function () {
|
package/lib/utils.js
CHANGED
|
@@ -1,27 +1,40 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* @description:
|
|
2
|
+
* @description: 工具函数库
|
|
3
3
|
* @Date: 2021-12-25 17:52:48
|
|
4
4
|
* @LastEditors: chendq
|
|
5
|
-
* @LastEditTime:
|
|
5
|
+
* @LastEditTime: 2022-01-02 12:32:33
|
|
6
6
|
* @Author: chendq
|
|
7
7
|
*/
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
8
10
|
/**
|
|
9
11
|
* @description: 输出和错误输出写入不同文件
|
|
10
|
-
* @param {
|
|
11
|
-
* @return {
|
|
12
|
+
* @param {string} logFileDirPath
|
|
13
|
+
* @return {object}
|
|
12
14
|
*/
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const getLogger = function (logFileDirPath) {
|
|
16
|
+
const options = {
|
|
17
|
+
flags: 'a', // append模式
|
|
18
|
+
encoding: 'utf8' // utf8编码
|
|
19
|
+
};
|
|
20
|
+
const output = fs.createWriteStream(
|
|
21
|
+
path.resolve(logFileDirPath ? logFileDirPath : process.cwd(), './stdout.log'),
|
|
22
|
+
options
|
|
23
|
+
);
|
|
24
|
+
const errorOutput = fs.createWriteStream(
|
|
25
|
+
path.resolve(logFileDirPath ? logFileDirPath : process.cwd(), './stderr.log'),
|
|
26
|
+
options
|
|
27
|
+
);
|
|
18
28
|
// 自定义日志打印
|
|
19
29
|
const logger = new console.Console(output, errorOutput);
|
|
20
30
|
|
|
21
|
-
logger.log('向 stdout 中写入数据');
|
|
22
|
-
logger.error('向 stderr 中写入数据');
|
|
31
|
+
// logger.log('向 stdout 中写入数据');
|
|
32
|
+
// logger.error('向 stderr 中写入数据');
|
|
23
33
|
|
|
24
|
-
return
|
|
34
|
+
return {
|
|
35
|
+
log: (...args) => logger.log.call(null, `[${dateFormat('YYYY-mm-dd HH:MM:SS:fff')}] - `, ...args),
|
|
36
|
+
error: (...args) => logger.error.call(null, `[${dateFormat('YYYY-mm-dd HH:MM:SS:fff')}] - `, ...args)
|
|
37
|
+
};
|
|
25
38
|
};
|
|
26
39
|
/**
|
|
27
40
|
* @description: 格式化时间日期
|
|
@@ -29,7 +42,7 @@ const getLogger = function () {
|
|
|
29
42
|
* @param {*} date
|
|
30
43
|
* @returns {*}
|
|
31
44
|
*/
|
|
32
|
-
function dateFormat(fmt, date) {
|
|
45
|
+
function dateFormat(fmt, date = new Date()) {
|
|
33
46
|
let ret;
|
|
34
47
|
const opt = {
|
|
35
48
|
'Y+': date.getFullYear().toString(), // 年
|
|
@@ -37,7 +50,8 @@ function dateFormat(fmt, date) {
|
|
|
37
50
|
'd+': date.getDate().toString(), // 日
|
|
38
51
|
'H+': date.getHours().toString(), // 时
|
|
39
52
|
'M+': date.getMinutes().toString(), // 分
|
|
40
|
-
'S+': date.getSeconds().toString() // 秒
|
|
53
|
+
'S+': date.getSeconds().toString(), // 秒
|
|
54
|
+
'f+': date.getMilliseconds().toString() // 毫秒
|
|
41
55
|
// 有其他格式化字符需求可以继续添加,必须转化成字符串
|
|
42
56
|
};
|
|
43
57
|
// eslint-disable-next-line guard-for-in
|
|
@@ -101,6 +115,56 @@ const filePath2ApiUrl = function (filePath) {
|
|
|
101
115
|
const getDataType = function (data) {
|
|
102
116
|
return Object.prototype.toString.call(data).slice(8, -1).toLowerCase();
|
|
103
117
|
};
|
|
118
|
+
/**
|
|
119
|
+
* @description: 流方式写文件(适合写超大文件)
|
|
120
|
+
* @param {string} filePath
|
|
121
|
+
* @param {object} data
|
|
122
|
+
* @return {promise}
|
|
123
|
+
*/
|
|
124
|
+
const writeStream = function (filePath, data) {
|
|
125
|
+
// 创建一个可写流 也会创建一个test1.txt文件
|
|
126
|
+
const writerStream = fs.createWriteStream(filePath);
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
// 将数据写入流
|
|
129
|
+
writerStream.write(data, 'utf-8');
|
|
130
|
+
// 标记文件的结束
|
|
131
|
+
writerStream.end();
|
|
132
|
+
writerStream.on('finish', () => {
|
|
133
|
+
resolve(data);
|
|
134
|
+
// console.log('写入完成');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
writerStream.on('error', err => {
|
|
138
|
+
reject(err);
|
|
139
|
+
console.error(`${filePath} 写入失败`);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* @description: 流方式读文件(适合写超大文件)
|
|
145
|
+
* @param {string} filePath
|
|
146
|
+
* @return {*}
|
|
147
|
+
*/
|
|
148
|
+
const readeStream = function (filePath) {
|
|
149
|
+
let data = '';
|
|
150
|
+
// 创建可读流
|
|
151
|
+
return new Promise((resovle, reject) => {
|
|
152
|
+
const readerStream = fs.createReadStream(filePath);
|
|
153
|
+
// 设置编码为 utf8。
|
|
154
|
+
readerStream.setEncoding('UTF8');
|
|
155
|
+
// 处理流事件 --> data, end, and error
|
|
156
|
+
readerStream.on('data', function (chunk) {
|
|
157
|
+
data += chunk;
|
|
158
|
+
});
|
|
159
|
+
readerStream.on('end', function () {
|
|
160
|
+
resovle(data);
|
|
161
|
+
});
|
|
162
|
+
readerStream.on('error', function (err) {
|
|
163
|
+
reject(err);
|
|
164
|
+
console.log(err.stack);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
};
|
|
104
168
|
|
|
105
169
|
module.exports = {
|
|
106
170
|
getLogger,
|
|
@@ -110,5 +174,7 @@ module.exports = {
|
|
|
110
174
|
isValidMethod,
|
|
111
175
|
getFileLatestContent,
|
|
112
176
|
filePath2ApiUrl,
|
|
113
|
-
getDataType
|
|
177
|
+
getDataType,
|
|
178
|
+
writeStream,
|
|
179
|
+
readeStream
|
|
114
180
|
};
|