@lushijie/thrift-mock
Advanced tools
Comparing version 3.0.2 to 3.1.1
220
bin/cli.js
#!/usr/bin/env node | ||
/* eslint-disable */ | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const program = require('commander'); | ||
const Utils = require('@lushijie/utils'); | ||
const chalk = require('chalk'); | ||
@@ -15,5 +13,12 @@ const stringifyObject = require('stringify-object'); | ||
/** | ||
* @Genetate by: thrift-mock | ||
* @Github: https://github.com/lushijie/thrift-mock | ||
* @Date: ${Utils.dateTimeFormat(new Date(), 'yyyy-MM-dd hh:mm:ss')} | ||
* genetate by thrift-mock | ||
* https://github.com/lushijie/thrift-mock | ||
* ------------------------------------------------------------------ | ||
* legend: | ||
* '◎': 基本类型,如bool, byte, i16, i32, i64 ,double, string, void | ||
* '★': 必填字段 | ||
* '☆': 选填字段 | ||
* '✼': enum 类型 | ||
* '┇': enum 数值连接符 | ||
* ------------------------------------------------------------------ | ||
*/ | ||
@@ -28,9 +33,9 @@ | ||
.option('-f, --file <value>', '编译指定的单个 .thrift 文件') | ||
.option('--name <value>', '输出特定的 thrift 结构,如只输出某一个 service 的名字') | ||
.option('--method <value>', '输出 service 中的某个方法') | ||
.option('--match <value>', '需要获取的数据结构, struct.User') | ||
.option('--output <value>', '输出目录, 默认文件所在目录') | ||
.option('--outext <value>', '输出文件后缀,.js 或者 .json', '.js') | ||
.option('--inext <value>', '输入文件后缀', '.thrift') | ||
.option('--auto', '自动生成 mock 数据') | ||
.option('--outputExt <value>', '输出文件后缀, .js 或者 .json', '.js') | ||
.option('--inputExt <value>', '输入文件后缀', '.thrift') | ||
.option('--auto', '自动生成 mock 数据'); | ||
// for tmock inquirer | ||
program | ||
@@ -41,24 +46,22 @@ .command('run') | ||
type: 'list', | ||
name: 'dfType', | ||
name: 'dirOrFile', | ||
message: '请选择, 编译文件还是目录:', | ||
default: 'dir', | ||
choices: [{ | ||
name: '目录', | ||
value: 'dir' | ||
}, { | ||
name: '文件', | ||
value: 'file' | ||
}] | ||
choices: [ | ||
{ name: '目录', value: 'dir' }, | ||
{ name: '文件', value: 'file' } | ||
] | ||
}]; | ||
inquirer.prompt(promps).then(answers => { | ||
params.dfType = answers.dfType; | ||
const tempText = answers.dfType === 'dir' ? '目录路径' : '文件地址'; | ||
params.dirOrFile = answers.dirOrFile; | ||
const thriftText = `请输入, ${answers.dirOrFile === 'dir' ? '目录路径' : '文件地址'}: `; | ||
const matchText = '请输入, 要获取的数据结构(如service.serviceName.methodName), 默认全部: '; | ||
promps = [{ | ||
type: 'input', | ||
name: 'dfPath', | ||
message: `请输入, ${tempText}:`, | ||
validate: function (input) { | ||
name: 'thrift', // thrift 路径,dir path 或者 file path | ||
message: thriftText, | ||
validate: (input) => { | ||
if (!input) { | ||
console.log(chalk.red(`✘ 请输入${tempText}`));; | ||
return false; | ||
return console.log(chalk.red(`✘ ${thriftText}`)); | ||
}; | ||
@@ -68,63 +71,46 @@ return true; | ||
}, { | ||
type: 'list', | ||
name: 'auto', | ||
message: '是否自动生成 mock 数据', | ||
default: false, | ||
choices: [{ | ||
name: 'Y', | ||
value: true | ||
}, { | ||
name: 'N', | ||
value: false | ||
}] | ||
type: 'input', | ||
name: 'match', // 需要获取的数据结构,如 service.serviceName.methodName | ||
message: matchText | ||
}]; | ||
inquirer.prompt(promps).then(answers => { | ||
console.log(chalk.green('-- 以下参数都可以使用默认值 ---')); | ||
params[params.dfType] = answers.dfPath; | ||
params.auto = answers.auto; | ||
const outputDefault = '默认 .thrift 文件所在的目录'; | ||
const nameDefault = '默认全部输出'; | ||
const methodDefault = '如果指定method, 则必须指定 name 为特定的 service 名称'; | ||
params.thrift = answers.thrift; | ||
params.match = answers.match; | ||
console.log(chalk.green('<<< 以下参数都可以使用默认值, 一路回车即可 >>>')); | ||
const outputDefault = '默认与 thrift 文件相同位置:'; | ||
promps = [{ | ||
type: 'input', | ||
name: 'output', | ||
message: `请输入,输出的目录路径:`, | ||
default: outputDefault, | ||
message: '请输入,输出的目录路径:', | ||
default: outputDefault | ||
}, { | ||
type: 'input', | ||
name: 'name', | ||
message: `请输入,特定的 thrift 结构名称(如某一个 struct 名称):`, | ||
default: nameDefault, | ||
type: 'list', | ||
name: 'outputExt', | ||
message: '请选择,输出文件的后缀:', | ||
default: '.js', | ||
choices: [ | ||
{ name: '.js', value: '.js' }, | ||
{ name: '.json', value: '.json' } | ||
] | ||
}, { | ||
type: 'input', | ||
name: 'method', | ||
message: `请输入,输出 service 中特定的 method:`, | ||
default: methodDefault | ||
name: 'inputExt', | ||
message: `请输入,输入文件的后缀:`, | ||
default: '.thrift' | ||
}, { | ||
type: 'list', | ||
name: 'outext', | ||
message: '请选择,输出后缀:', | ||
default: '.js', | ||
name: 'auto', | ||
message: '是否自动生成 mock 数据', | ||
default: false, | ||
choices: [ | ||
{ | ||
name: '.js', | ||
value: '.js' | ||
}, | ||
{ | ||
name: '.json', | ||
value: '.json' | ||
} | ||
{ name: 'Y', value: true }, | ||
{ name: 'N', value: false } | ||
] | ||
}, { | ||
type: 'input', | ||
name: 'inext', | ||
message: `请输入,输入的 thrift 文件后缀:`, | ||
default: '.thrift', | ||
}]; | ||
inquirer.prompt(promps).then(answers => { | ||
params.name = (answers.name === nameDefault) ? undefined : answers.name; | ||
params.method = (answers.method === methodDefault) ? undefined : answers.method; | ||
params.output = (answers.output === outputDefault) ? undefined : answers.output; | ||
params.outext = answers.outext; | ||
params.inext = answers.inext; | ||
params.output = (answers.output === outputDefault) ? null : answers.output; | ||
params.outputExt = answers.outputExt; | ||
params.inputExt = answers.inputExt; | ||
params.auto = answers.auto; | ||
run(params); | ||
@@ -136,16 +122,9 @@ }); | ||
program.parse(process.argv); | ||
params = { | ||
dir: program.dir, | ||
file: program.file, | ||
output: program.output, | ||
outext: program.outext, | ||
inext: program.inext, | ||
method: program.method, | ||
name: Utils.isFunction(program.name) ? undefined : program.name, // 参数形式下不传name,默认返回funtion | ||
auto: program.auto | ||
}; | ||
function getFileFromDir(dir, ext){ | ||
/** | ||
* 在 dir 文件夹中查找 ext 后缀的文件,返回文件路径列表 | ||
* @param {string} dir 目录 | ||
* @param {string} ext 后缀 | ||
* @return {array} 文件路径数组 | ||
*/ | ||
function getFileFromDir(dir, ext) { | ||
let res = []; | ||
@@ -157,3 +136,3 @@ if (!fs.existsSync(dir)) { | ||
const files = fs.readdirSync(dir); | ||
for(let i = 0; i< files.length; i++) { | ||
for (let i = 0; i < files.length; i++) { | ||
var filename = path.join(dir, files[i]); | ||
@@ -170,5 +149,9 @@ var stat = fs.lstatSync(filename); | ||
function convertFile(filePath) { | ||
/** | ||
* @description thrift 文件解析 | ||
* @param {string} filePath 文件路径 | ||
*/ | ||
function parseThriftFile(filePath) { | ||
let outputPath = params.output; | ||
outputPath = outputPath || path.dirname(filePath); // 未指定,使用filePath dirname | ||
outputPath = outputPath || path.dirname(filePath); // 未指定,使用filePath dirname | ||
outputPath = path.resolve(outputPath); | ||
@@ -178,4 +161,4 @@ filePath = path.resolve(filePath); | ||
// 如果输入文件不带后缀,补充后缀 | ||
if (!filePath.endsWith(params.inext)) { | ||
filePath = filePath + params.inext; | ||
if (!filePath.endsWith(params.inputExt)) { | ||
filePath = filePath + params.inputExt; | ||
} | ||
@@ -186,3 +169,3 @@ const fileName = path.parse(filePath).name; | ||
if (!(outputPath.endsWith('.js') || outputPath.endsWith('.json'))) { | ||
outputPath = path.join(outputPath, fileName + '.mock' + params.outext); | ||
outputPath = path.join(outputPath, fileName + '.mock' + params.outputExt); | ||
} | ||
@@ -195,7 +178,10 @@ | ||
const thriftTool = new ThriftTool(); | ||
let res = thriftTool.parse({filePath, name: params.name, auto: params.auto}) || {}; | ||
let res = thriftTool.parse({filePath, auto: params.auto}) || {}; | ||
// 获取特定service中的特定method | ||
if (params.method) { | ||
res = res[params.method]; | ||
if (params.match) { | ||
const condition = params.match.split('.'); | ||
while (condition.length) { | ||
const key = condition.shift(); | ||
res = res[key]; | ||
} | ||
} | ||
@@ -207,5 +193,5 @@ | ||
let outputResult = ''; | ||
if (params.outext === '.js') { // js 类型输出 | ||
if (params.outputExt === '.js') { // js 类型输出 | ||
outputResult = jsFileHeader + stringifyObject(res, { | ||
indent: ' ', | ||
indent: ' ' | ||
}) + ';'; | ||
@@ -219,16 +205,13 @@ } else { // .json 类型输出 | ||
/** | ||
* @description 主函数 | ||
* @param { Object } params 运行参数 | ||
* @returns undefined | ||
*/ | ||
function run(params) { | ||
// if (!params.dir && !params.file) { | ||
// console.log(chalk.red('✘ Please input dir or file of the thrift file...')); | ||
// } | ||
if (!params.name && params.method) { | ||
return console.log(chalk.red('✘ name is required if method exist...')); | ||
} | ||
if (params.file) { | ||
convertFile(params.file) | ||
} else if (params.dir) { | ||
getFileFromDir(params.dir, params.inext).forEach(function(filePath) { | ||
convertFile(filePath); | ||
if (params.dirOrFile === 'file') { | ||
parseThriftFile(params.thirft); | ||
} else if (params.dirOrFile === 'dir') { | ||
getFileFromDir(params.thrift, params.inputExt).forEach(function(filePath) { | ||
parseThriftFile(filePath); | ||
}); | ||
@@ -238,4 +221,15 @@ } | ||
if (params.dir || params.file) { | ||
// for tmock cli | ||
program.parse(process.argv); | ||
params = { | ||
dirOrFile: program.dir ? 'dir' : 'file', | ||
thrift: program.dir || program.file, | ||
match: program.match, | ||
output: program.output, | ||
outputExt: program.outputExt, | ||
inputExt: program.inputExt, | ||
auto: program.auto | ||
}; | ||
if (params.thrift) { | ||
run(params); | ||
} | ||
} |
{ | ||
"name": "@lushijie/thrift-mock", | ||
"version": "3.0.2", | ||
"version": "3.1.1", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -17,3 +17,3 @@ # thrift-mock | ||
`tmock -f a.thrift --name User` | ||
`tmock -f a.thrift` | ||
@@ -28,12 +28,15 @@ <p> | ||
* 支持多层数据结构的嵌套 | ||
* 支持 include thrift文件 | ||
* 支持 service 的 extend 语法 | ||
* 支持 include & include alias | ||
* 支持 service extend | ||
### 4. 如何使用 | ||
##### 4.1. 命令行 | ||
npm install @lushijie/thrift-mock -g | ||
tmock 参数: | ||
* -d 要编译的目录 | ||
* -d 要编译的包含thrift文件的目录 | ||
* -f 要编译的thrift文件 | ||
* [--match] 需要获取的数据结构, 如 struct.User | ||
* [--auto] 是否自动生成mock数据 | ||
@@ -43,4 +46,2 @@ * [--output <value>] 重定向文件输出目录,默认文件所在的目录 | ||
* [--inext <value>] thrift 文件后缀,默认 .thrift | ||
* [--name <value>] 输出特定的 thrift 结构,例如只输出某一个 service 或者某一个 struct(注意这里不能是 service 中的方法名),默认输出所有的 thrift 结构 | ||
* [--method <value>] 输出 service 中特定的 method(需要指定 --name 为特定的 service 名称) | ||
@@ -69,3 +70,3 @@ ``` | ||
5. 编译 case 下的 a.thrift 中 User 结构体(在 case 目录下生成 a.json,仅包含 User 结构体) | ||
tmock -f ./case/a.thrift --name User | ||
tmock -f ./case/a.thrift --match struct.User | ||
@@ -76,3 +77,3 @@ 6. 编译 case 下所有的 .thrift 文件,生成 .json 文件(在 case 目录下生成 a.mock.json、b.mock.json) | ||
7. 编译 case 下的 a.thrift 文件, 输出 service 中 SMSService 的 send 方法 | ||
tmock -f a.thrift --name SMSService --method send | ||
tmock -f a.thrift --match service.SMSService.send | ||
@@ -82,3 +83,5 @@ 8. 自动生成 mock 数据 | ||
``` | ||
##### 4.2. 交互式命令 | ||
tmock run | ||
@@ -94,3 +97,3 @@ | ||
const thriftTool = require('@lushijie/thrift-mock'); | ||
const res = thriftTool.parse({filePath: '/usr/a.thrift', name: 'User', auto: true}); | ||
const res = thriftTool.parse({filePath: '/usr/a.thrift', auto: true}); | ||
console.log(res); | ||
@@ -103,3 +106,3 @@ ``` | ||
module.exports = { | ||
basic: '◎', // 基本类型,如i32, string, bool ... | ||
basic: '◎', // 基本类型,如 bool, byte, i16, i32, i64 ,double, string, void | ||
required: '★', // 必填字段 | ||
@@ -114,10 +117,6 @@ optional: '☆', // 选填字段 | ||
获取到数据结构之后,接下来就是生成 mock 数据 ~~~ | ||
获取到数据结构之后,接下来就是生成 mock 数据,但是现实的情况是每个字段都有很强的语义,比如一个菜品 mock 出来一个城市名,这是不行的!也不可能为每个字段指定 mock 规则。 | ||
但是现实的情况是每个字段都有很强的语义,比如一个菜品 mock 出来一个城市名,这是不行的!也不可能为每个字段指定 mock 规则。 | ||
所以提供了 --auto 参数,如果提供了 auto 参数,会根据 thrift 基本类型生成一些无语义的随机数...如果mock 数据语义化要求较高,可以在生成的 .js 文件中自行使用各种各样的 random 类库创造属于自己的 mock 数据 !!! | ||
所以提供了 --auto 参数,如果提供了 auto 参数,会根据 thrift 基本类型生成一些无语义的随机数... | ||
如果mock 数据语义化要求较高,可以在生成的 .js 文件中自行使用各种各样的 random 类库创造属于自己的 mock 数据 !!! | ||
### 相关问题 | ||
@@ -124,0 +123,0 @@ 1. [https://github.com/thriftrw/thriftrw-node/issues/162](https://github.com/thriftrw/thriftrw-node/issues/162) |
Sorry, the diff of this file is not supported yet
485480
35
721
123