UNPKG

@notadd/cli

Version:

notadd core none dependence

946 lines (926 loc) 36.4 kB
import { Command } from '@notadd/cli-core'; import { join } from 'path'; import { readFileSync, writeFileSync, ensureDirSync, readdirSync, ensureFileSync, readJSONSync, pathExistsSync } from 'fs-extra'; import { upperFirst, lowerFirst, compact } from "lodash"; import { Controller, Args } from '@notadd/core'; @Controller() export class GenerateCommand { @Command(`generate`) build(@Args('merge') merge: boolean = false) { const entities = this._readEntities(); this._createEntities(entities); this._createBasic(entities); this._createDomain(entities); if (merge) { this._createApi(entities); } else { this._createApiV2(entities); } // this._createJestTest(entities); } private _readEntities() { const fileNames = readdirSync(process.cwd()).filter((it) => it.includes('.entity.ts')); const dirName = process.cwd().split('/').pop(); if (!dirName) throw new Error('文件夹名或位置不正确'); if (fileNames.length == 0) { throw new Error('未找到.entity.ts文件'); } const entities = fileNames.map((it) => { const content = readFileSync(join(process.cwd(), it)).toString(); const entityName = content.match(/class ([^\s]*Entity)/); if (!entityName || !entityName[1]) throw new Error(`${it}文件没有Entity结尾的class`); const fileds = content.match(/[^\s]*\??\:\s*(number|string|any|Date|boolean|Array|Object).*/g); if (!fileds || fileds.length == 0) throw new Error(`${it}文件没有Entity结尾的class`); const filedNames = compact(fileds.map(it => { const filedsMatch = it.match(/([^\s]*)\??\:\s*(number|string|any|Date|boolean|Array|Object).*/); if (filedsMatch) { const filedName = filedsMatch[1].replace('?', '').trim(); const type = filedsMatch[2].trim(); return filedName; } return null; })) return { fileName: it, content, entityName: entityName[1].trim(), fileds, dirName, filedNames }; }); return entities; } private _createEntities(entities: any[]) { // 导入文件 导入文件名 导出文件 const { importFile, exportFile } = this._importExportFileAndImportNames(entities, 'entity'); // 创建.module.ts const moduleContent = ` import { Module } from '@notadd/core'; import { TypeormModule } from '@notadd/typeorm';${importFile} @Module({ imports: [TypeormModule.forFeature(${JSON.stringify(entities.map(it => it.entityName)).replace(/"/g, '')})] }) export class ${upperFirst(entities[0].dirName)}EntitiesModule {} `; const entitiesModuleUrl = join(process.cwd(), `${entities[0].dirName}.module.ts`); if (pathExistsSync(entitiesModuleUrl)) { console.log(`entities层已存在module文件,所以未生成`) } else { writeFileSync(entitiesModuleUrl, moduleContent); } //创建此模块的index.ts 和修改模块的导出 this._createIndexTs(entities[0].dirName, process.cwd(), exportFile) } private _createBasic(entities: any[]) { const dirName = entities[0].dirName; // 获取不同层的package.json const { basicDir, entitiesPakcageJson } = this._getPackageJsons(entities); ensureDirSync(basicDir) //创建basic文件 entities.forEach(it => { const thisFileUrl = join(basicDir, it.fileName.replace('.entity.ts', '.basic.ts')) // 如果在basic层已经存在就return if (pathExistsSync(thisFileUrl)) { console.log(`basic层${it.fileName.replace('.entity.ts', '.basic.ts')}已存在,所以未生成`) return } const basicName = it.entityName.replace(/Entity$/, 'Basic'); const usedEntity = lowerFirst(it.entityName.replace(/Entity$/, '')) const basicContent = ` import { Injectable, Inject } from '@notadd/core'; import { Repository } from '@notadd/typeorm'; import { Limit } from '@notadd/graphql'; import { ${it.entityName} } from '${entitiesPakcageJson.name}'; @Injectable() export class ${basicName} { constructor( @Inject(${it.entityName}) public ${usedEntity}: Repository<${it.entityName}> ) {} /** * 查询列表 */ async find${it.entityName.replace(/Entity$/, '')}List( where?: any, limit?: Limit, order?: any ): Promise<[${it.entityName}[], number]> { const page = limit && limit.page ? limit.page : 1; const psize = limit && limit.psize ? limit.psize : 20; let it = this.${usedEntity} .createQueryBuilder('entity') return it .orderBy(order) .skip((page - 1) * psize) .take(psize) .getManyAndCount(); } }` writeFileSync(thisFileUrl, basicContent); }); // 导入文件 导入文件名 导出文件 const { importFile, importNames, exportFile } = this._importExportFileAndImportNames(entities, 'basic'); // 创建.module.ts const moduleContent = ` import { Module } from '@notadd/core'; import { ${upperFirst(dirName)}EntitiesModule } from '${entitiesPakcageJson.name}';${importFile} @Module({ imports: [ ${upperFirst(dirName)}EntitiesModule, ], providers: [ ${importNames} ] }) export class ${upperFirst(dirName)}BasicModule {} `; const basicModuleName = `${dirName}.module.ts` const basicModuleUrl = join(basicDir, basicModuleName) if (pathExistsSync(basicModuleUrl)) { console.log(`basic层已存在${basicModuleName}文件,所以未生成`) } else { writeFileSync(basicModuleUrl, moduleContent); } //创建此模块的index.ts 和修改模块的导出 this._createIndexTs(dirName, basicDir, exportFile) } private _createDomain(entities: any[]) { const dirName = entities[0].dirName; // 获取不同层的package.json const { domainDir, entitiesPakcageJson, basicPakcageJson } = this._getPackageJsons(entities); ensureDirSync(domainDir) //创建domain文件 entities.forEach(it => { const thisFileUrl = join(domainDir, it.fileName.replace('.entity.ts', '.service.ts')) // 如果在domain层已经存在就return if (pathExistsSync(thisFileUrl)) { console.log(`domain层${it.fileName.replace('.entity.ts', '.service.ts')}已存在,所以未生成`) return } const basicName = it.entityName.replace(/Entity$/, 'Basic'); const domainName = it.entityName.replace(/Entity$/, 'Service'); const usedEntity = lowerFirst(it.entityName.replace(/Entity$/, '')) const domainContent = ` import { TypeormError, TypeError } from '@ganker/error'; import { Limit } from '@notadd/graphql'; import { Injectable, Inject, Logger } from '@notadd/core'; import { DeepPartial, Repository } from '@notadd/typeorm'; import { ClassValidatorService } from '@notadd/class-validator'; import { ${it.entityName} } from '${entitiesPakcageJson.name}'; import { ${basicName} } from '${basicPakcageJson.name}'; @Injectable() export class ${domainName} { constructor( @Inject(${it.entityName}) private ${usedEntity}: Repository<${it.entityName}>, private readonly ${lowerFirst(basicName)}: ${basicName}, private readonly validator: ClassValidatorService, private logger: Logger, ) {} /** * 添加 */ async add(input: DeepPartial<${it.entityName}>): Promise<${it.entityName}> { const errMessage = await this.validator.validateOrMessage(input, ${it.entityName}); if (errMessage) throw new TypeError('02', errMessage); try { const entity = this.${usedEntity}.create(input); const res = await this.${usedEntity}.save(entity); res.id = this.${usedEntity}.getId(res); return res; } catch (e) { this.logger.info(e.message); throw new TypeormError('01', e.message); } } /** * 删除 */ async deleteById(id: number): Promise<any> { if (!id || id <= 0) { throw new TypeError('02', 'id不能为空'); } const item = await this.${usedEntity}.findOne({ id }); if (!item) { throw new TypeError('03', '此记录未找到或已删除'); } try { await this.${usedEntity}.delete({ id }); return item; } catch (e) { this.logger.info(e.message); throw new TypeormError('01', e.message); } } /** * 修改 */ async updateById(id: number, entity: any): Promise<any> { if (!id || id <= 0) { throw new TypeError('03', 'id不能为空'); } const item = await this.${usedEntity}.findOne({ id }); if (!item) { throw new TypeError('04', '修改失败,未找到要修改的数据或已删除'); } const errMessage = await this.validator.validateOrMessage(entity, ${it.entityName}, { skipMissingProperties: true, }); if (errMessage) throw new TypeError('02', errMessage); try { await this.${usedEntity}.update({ id }, entity); return item; } catch (e) { this.logger.info(e.message); throw new TypeormError('01', e.message); } } /** * 列表 */ async list(where?: any, limit?: Limit, order?: any): Promise<any> { try { if (order) { order = Object.keys(order).reduce((newData, key) => { let newKey = 'entity.' + key; newData[newKey] = order[key]; return newData; }, {}); } else { order = { 'entity.createDate': 'DESC' }; } const [list, count] = await this.${lowerFirst(basicName)}.find${it.entityName.replace(/Entity$/, '')}List(where, limit, order); return { list, count }; } catch (e) { this.logger.info(e.message); throw new TypeormError('01', e.message); } } /** * 详情 */ async detailById(id: number): Promise<${it.entityName}> { if (!id || id <= 0) { throw new TypeError('01', 'id不能为空'); } const item = await this.${usedEntity}.findOne({ id }); if (!item) { throw new TypeError('02', '查询失败,未找到要查询的数据或已删除'); } return item; } }` writeFileSync(thisFileUrl, domainContent); }); // 导入文件 导入文件名 导出文件 const { importFile, importNames, exportFile } = this._importExportFileAndImportNames(entities, 'service'); // 创建.module.ts const moduleContent = ` import { Module } from '@notadd/core'; import { ClassValidatorModule } from '@notadd/class-validator'; import { ${upperFirst(dirName)}BasicModule } from '${basicPakcageJson.name}';${importFile} @Module({ imports: [ ${upperFirst(dirName)}BasicModule, ClassValidatorModule, ], providers: [ ${importNames} ] }) export class ${upperFirst(dirName)}DomainModule {} `; const basicModuleName = `${dirName}.module.ts` const basicModuleUrl = join(domainDir, basicModuleName) if (pathExistsSync(basicModuleUrl)) { console.log(`domain层已存在${basicModuleName}文件,所以未生成`) } else { writeFileSync(basicModuleUrl, moduleContent); } //创建此模块的index.ts 和修改模块的导出 this._createIndexTs(dirName, domainDir, exportFile) } private _createApi(entities: any[]) { const dirName = entities[0].dirName; // 获取不同层的package.json const { apiDir, entitiesPakcageJson, domainPakcageJson } = this._getPackageJsons(entities); ensureDirSync(apiDir); //创建api文件 entities.forEach(it => { const filedNames: string[] = it.filedNames; const pickList = filedNames.map((filed, filedIndex) => { if (filedIndex == filedNames.length - 1) { return `'${filed}'` } return `'${filed}' | ` }).reduce((acc, cur) => { return (acc += cur); }, ''); const thisFileUrl = join(apiDir, it.fileName.replace('.entity.ts', '.api.ts')) // 如果在domain层已经存在就return if (pathExistsSync(thisFileUrl)) { console.log(`api层${it.fileName.replace('.entity.ts', '.api.ts')}已存在,所以未生成`) return } const domainName = it.entityName.replace(/Entity$/, 'Service'); const apiName = it.entityName.replace(/Entity$/, 'ApiController'); const usedEntity = lowerFirst(it.entityName.replace(/Entity$/, '')) const entityPre = it.entityName.replace(/Entity$/, ''); const apiContent = ` import { Mutation, Query, Limit, ListCount } from '@notadd/graphql'; import { Args, Controller } from '@notadd/core'; import { GraphqlError } from '@ganker/error'; import { ${domainName} } from '${domainPakcageJson.name}'; import { ${it.entityName} } from '${entitiesPakcageJson.name}'; export type ${entityPre}AddInput = Pick< ${it.entityName}, ${pickList} >; interface ${entityPre}ListInputWhere { // TUDO } type ${entityPre}UpdateInputEntity = Partial< Pick< ${it.entityName}, ${pickList} > >; interface ${entityPre}ListInputOrder { createDate?: string; } type ${entityPre}ListResult = Partial< Pick< ${it.entityName}, ${pickList} > >; @Controller() export class ${apiName} { constructor(private readonly ${usedEntity}: ${domainName}) {} /** * 添加 */ @Mutation() async ${usedEntity}Add( @Args('entity') entity: ${entityPre}AddInput ): Promise<any> { try { return await this.${usedEntity}.add(entity); } catch (e) { throw new GraphqlError(\`B200101\${e.code}\`, e.message); } } /** * 删除 */ @Mutation() async ${usedEntity}DeleteById(@Args('id') id: number): Promise<any> { try { return await this.${usedEntity}.deleteById(id); } catch (e) { throw new GraphqlError(\`B200102\${e.code}\`, e.message); } } /** * 列表 */ @Query() async ${usedEntity}List( @Args('where') where?: ${entityPre}ListInputWhere, @Args('limit') limit?: Limit, @Args('order') order?: ${entityPre}ListInputOrder ): Promise<ListCount<${entityPre}ListResult>> { try { return await this.${usedEntity}.list(where, limit, order); } catch (e) { throw new GraphqlError(\`B200103\${e.code}\`, e.message); } } /** * 修改 */ @Mutation() async ${usedEntity}UpdateById( @Args('id') id: number, @Args('entity') entity: ${entityPre}UpdateInputEntity ): Promise<any> { try { return await this.${usedEntity}.updateById(id, entity); } catch (e) { throw new GraphqlError(\`B200104\${e.code}\`, e.message); } } /** * 详情 */ @Mutation() async ${usedEntity}DetailById( @Args('id') id: number ): Promise<${it.entityName}> { try { return await this.${usedEntity}.detailById(id); } catch (e) { throw new GraphqlError(\`B200105\${e.code}\`, e.message); } } } ` writeFileSync(thisFileUrl, apiContent); }); // 导入文件 导入文件名 导出文件 const { importFile, importNames, exportFile } = this._importExportFileAndImportNames(entities, 'api'); // 创建.module.ts const moduleContent = ` import { Module } from '@notadd/core'; import { ${upperFirst(dirName)}DomainModule } from '${domainPakcageJson.name}';${importFile} @Module({ imports: [ ${upperFirst(dirName)}DomainModule, ], controllers: [ ${importNames} ] }) export class ${upperFirst(dirName)}ApiModule {} `; const apiModuleName = `${dirName}.module.ts` const apiModuleUrl = join(apiDir, apiModuleName) if (pathExistsSync(apiModuleUrl)) { console.log(`api层已存在${apiModuleName}文件,所以未生成`) } else { writeFileSync(apiModuleUrl, moduleContent); } //创建此模块的index.ts 和修改模块的导出 this._createIndexTs(dirName, apiDir, exportFile) } private _createApiV2(entities: any[]) { const dirName = entities[0].dirName; // 获取不同层的package.json const { apiDir, entitiesPakcageJson, domainPakcageJson } = this._getPackageJsons(entities); ensureDirSync(apiDir); //创建api文件 entities.forEach(it => { const filedNames: string[] = it.filedNames; const pickList = filedNames.map((filed, filedIndex) => { if (filedIndex == filedNames.length - 1) { return `'${filed}'` } return `'${filed}' | ` }).reduce((acc, cur) => { return (acc += cur); }, ''); const entityFileName = it.fileName.replace('.entity.ts', '') const domainName = it.entityName.replace(/Entity$/, 'Service'); const usedEntity = lowerFirst(it.entityName.replace(/Entity$/, '')) const entityPre = it.entityName.replace(/Entity$/, ''); const addName = it.entityName.replace(/Entity$/, 'AddApiController'); const listName = it.entityName.replace(/Entity$/, 'ListApiController'); const updateName = it.entityName.replace(/Entity$/, 'UpdateApiController'); const deleteName = it.entityName.replace(/Entity$/, 'DeleteApiController'); const detailName = it.entityName.replace(/Entity$/, 'DetailApiController'); const addContent = ` import { Mutation } from '@notadd/graphql'; import { Args, Controller, EntityService } from '@notadd/core'; import { ClassValidatorService } from '@notadd/class-validator'; import { GraphqlError, TypeError } from '@ganker/error'; import { ${domainName} } from '${domainPakcageJson.name}'; import { ${it.entityName} } from '${entitiesPakcageJson.name}'; export type ${entityPre}AddInput = Pick< ${it.entityName}, ${pickList} >; @Controller() export class ${addName} { constructor( private readonly entityService: EntityService, private readonly validator: ClassValidatorService, private readonly ${usedEntity}: ${domainName} ) {} /** * 添加 */ @Mutation() async ${usedEntity}Add( @Args('entity') entity: ${entityPre}AddInput ): Promise<any> { try { const created = await this.entityService.createType(${it.entityName}, entity); const errMessage = await this.validator.validateOrMessage(created, ${it.entityName}); if (errMessage) throw new TypeError('02', errMessage); return await this.${usedEntity}.add(created); } catch (e) { throw new GraphqlError(\`B200101\${e.code}\`, e.message); } } } ` if (!pathExistsSync(apiDir + `/${entityFileName}-add.api.ts`)) { writeFileSync(apiDir + `/${entityFileName}-add.api.ts`, addContent); } const listContent = ` import { Query, Limit, ListCount } from '@notadd/graphql'; import { Args, Controller } from '@notadd/core'; import { GraphqlError } from '@ganker/error'; import { ${domainName} } from '${domainPakcageJson.name}'; import { ${it.entityName} } from '${entitiesPakcageJson.name}'; interface ${entityPre}ListInputWhere { ids?: number[] } interface ${entityPre}ListInputOrder { createDate?: string; } type ${entityPre}ListResult = Partial< Pick< ${it.entityName}, ${pickList} > >; @Controller() export class ${listName} { constructor(private readonly ${usedEntity}: ${domainName}) {} /** * 列表 */ @Query() async ${usedEntity}List( @Args('where') where?: ${entityPre}ListInputWhere, @Args('limit') limit?: Limit, @Args('order') order?: ${entityPre}ListInputOrder ): Promise<ListCount<${entityPre}ListResult>> { try { return await this.${usedEntity}.list(where, limit, order); } catch (e) { throw new GraphqlError(\`B200103\${e.code}\`, e.message); } } } ` if (!pathExistsSync(apiDir + `/${entityFileName}-list.api.ts`)) { writeFileSync(apiDir + `/${entityFileName}-list.api.ts`, listContent); } const updateContent = ` import { Mutation } from '@notadd/graphql'; import { Args, Controller } from '@notadd/core'; import { ClassValidatorService } from '@notadd/class-validator'; import { GraphqlError, TypeError } from '@ganker/error'; import { ${domainName} } from '${domainPakcageJson.name}'; import { ${it.entityName} } from '${entitiesPakcageJson.name}'; type ${entityPre}UpdateInputEntity = Partial< Pick< ${it.entityName}, ${pickList} > >; @Controller() export class ${updateName} { constructor( private readonly validator: ClassValidatorService, private readonly ${usedEntity}: ${domainName} ) {} /** * 修改 */ @Mutation() async ${usedEntity}UpdateById( @Args('id') id: number, @Args('entity') entity: ${entityPre}UpdateInputEntity ): Promise<any> { try { const errMessage = await this.validator.validateOrMessage(entity, ${it.entityName}, { skipMissingProperties: true, }); if (errMessage) throw new TypeError('02', errMessage); return await this.${usedEntity}.updateById(id, entity); } catch (e) { throw new GraphqlError(\`B200104\${e.code}\`, e.message); } } } ` if (!pathExistsSync(apiDir + `/${entityFileName}-update.api.ts`)) { writeFileSync(apiDir + `/${entityFileName}-update.api.ts`, updateContent); } const deleteContent = ` import { Mutation } from '@notadd/graphql'; import { Args, Controller } from '@notadd/core'; import { GraphqlError } from '@ganker/error'; import { ${domainName} } from '${domainPakcageJson.name}'; @Controller() export class ${deleteName} { constructor(private readonly ${usedEntity}: ${domainName}) {} /** * 删除 */ @Mutation() async ${usedEntity}DeleteById(@Args('id') id: number): Promise<any> { try { return await this.${usedEntity}.deleteById(id); } catch (e) { throw new GraphqlError(\`B200102\${e.code}\`, e.message); } } } ` if (!pathExistsSync(apiDir + `/${entityFileName}-delete.api.ts`)) { writeFileSync(apiDir + `/${entityFileName}-delete.api.ts`, deleteContent); } const detailContent = ` import { Query } from '@notadd/graphql'; import { Args, Controller } from '@notadd/core'; import { GraphqlError } from '@ganker/error'; import { ${domainName} } from '${domainPakcageJson.name}'; import { ${it.entityName} } from '${entitiesPakcageJson.name}'; @Controller() export class ${detailName} { constructor(private readonly ${usedEntity}: ${domainName}) {} /** * 详情 */ @Query() async ${usedEntity}DetailById( @Args('id') id: number ): Promise<${it.entityName}> { try { return await this.${usedEntity}.detailById(id); } catch (e) { throw new GraphqlError(\`B200105\${e.code}\`, e.message); } } } ` if (!pathExistsSync(apiDir + `/${entityFileName}-detail.api.ts`)) { writeFileSync(apiDir + `/${entityFileName}-detail.api.ts`, detailContent); } }); // 导入文件 导入文件名 导出文件 const importFile = entities.map(it => { const entityFileName = it.fileName.replace('.entity.ts', '') const addName = it.entityName.replace(/Entity$/, 'AddApiController'); const listName = it.entityName.replace(/Entity$/, 'ListApiController'); const updateName = it.entityName.replace(/Entity$/, 'UpdateApiController'); const deleteName = it.entityName.replace(/Entity$/, 'DeleteApiController'); const detailName = it.entityName.replace(/Entity$/, 'DetailApiController'); return ` import { ${addName} } from './${entityFileName}-add.api'; import { ${listName} } from './${entityFileName}-list.api'; import { ${updateName} } from './${entityFileName}-update.api'; import { ${deleteName} } from './${entityFileName}-delete.api'; import { ${detailName} } from './${entityFileName}-detail.api';` }).reduce((acc, cur) => { return (acc += cur); }, ''); const importNames = entities.map(it => { const addName = it.entityName.replace(/Entity$/, 'AddApiController'); const listName = it.entityName.replace(/Entity$/, 'ListApiController'); const updateName = it.entityName.replace(/Entity$/, 'UpdateApiController'); const deleteName = it.entityName.replace(/Entity$/, 'DeleteApiController'); const detailName = it.entityName.replace(/Entity$/, 'DetailApiController'); return `${addName}, ${listName}, ${updateName}, ${deleteName}, ${detailName}, ` }).reduce((acc, cur) => { return (acc += cur); }, '').trim(); const exportFile = entities.map(it => { const entityFileName = it.fileName.replace('.entity.ts', '') return ` export * from './${entityFileName}-add.api'; export * from './${entityFileName}-list.api'; export * from './${entityFileName}-update.api'; export * from './${entityFileName}-delete.api'; export * from './${entityFileName}-detail.api';` }).reduce((acc, cur) => { return (acc += cur); }, '') // 创建.module.ts const moduleContent = ` import { Module } from '@notadd/core'; import { ClassValidatorModule } from '@notadd/class-validator'; import { ${upperFirst(dirName)}DomainModule } from '${domainPakcageJson.name}';${importFile} @Module({ imports: [ ${upperFirst(dirName)}DomainModule, ClassValidatorModule, ], controllers: [ ${importNames} ] }) export class ${upperFirst(dirName)}ApiModule {} `; const apiModuleName = `${dirName}.module.ts` const apiModuleUrl = join(apiDir, apiModuleName) if (pathExistsSync(apiModuleUrl)) { console.log(`api层已存在${apiModuleName}文件,所以未生成`) } else { writeFileSync(apiModuleUrl, moduleContent); } //创建此模块的index.ts 和修改模块的导出 this._createIndexTs(dirName, apiDir, exportFile) } private _createJestTest(entities: any[]) { const dirName = entities[0].dirName; // 获取不同层的package.json const { apiDir, apiPakcageJson, entitiesPakcageJson } = this._getPackageJsons(entities); ensureDirSync(apiDir); //创建api文件 entities.forEach(it => { const thisFileUrl = join(apiDir, it.fileName.replace('.entity.ts', '.api.test.ts')) // 如果在api层已经存在就return if (pathExistsSync(thisFileUrl)) { console.log(`api层${it.fileName.replace('.entity.ts', '.api.test.ts')}已存在,所以未生成`) return } const apiName = it.entityName.replace(/Entity$/, 'ApiController'); const usedEntity = lowerFirst(it.entityName.replace(/Entity$/, '')) const entityPre = it.entityName.replace(/Entity$/, ''); const apiContent = ` import { Injector, MAIN_PATH, Module, CurrentMetadataKey, CURRENT } from '@notadd/core'; import { HTTP_IP } from '@notadd/http'; import { platformNode } from '@notadd/platform-node'; import { TypeormPostgresEnvModule } from '@notadd/typeorm'; import { join } from 'path'; import { equal } from 'assert'; import { graphqlProviders } from '@notadd/graphql/lib/handlers'; import { ${it.entityName} } from '${entitiesPakcageJson.name}'; import { ${apiName}, ${entityPre}AddInput } from './${it.fileName.replace('.entity.ts', '.api')}'; import { ${upperFirst(dirName)}ApiModule } from './${dirName}.module'; @Module({ imports: [TypeormPostgresEnvModule.forRoot(), ${upperFirst(dirName)}ApiModule], providers: [...graphqlProviders, ${apiName}], }) export class JestApiModule {} describe('${apiPakcageJson.name}/${dirName}/${it.fileName.replace('.entity.ts', '.api')}', () => { let root: Injector; let api: ${apiName}; beforeAll(async (down) => { const ref = platformNode([ { provide: MAIN_PATH, useValue: join(__dirname, '../main.ts'), }, { provide: CURRENT, useValue: { openid: 'fromUser', id: 1, username: 'system', domainId: 1, departmentId: 1, }, }, ]); const moduleRef = await ref.bootstrapModule(JestApiModule); root = moduleRef.injector; const req = root.create([ { provide: HTTP_IP, useValue: '127.0.0.1', }, { provide: CurrentMetadataKey, useValue: currentHandler, }, ]); api = req.get(${apiName}); down(); }, 1000 * 20); it('test list', async () => { const list = await api.${usedEntity}List(); equal(list.count >= 0, true); const paramList = await api.${usedEntity}List({}, { page: 1, psize: 20 }, { createDate: 'DESC' }); equal(paramList.count >= 0, true); }); // TUDO const addOptions: ${entityPre}AddInput = { }; let seccessAdded: ${it.entityName}; it('test add', async () => { seccessAdded = await api.${usedEntity}Add(addOptions); }); it('test update', async () => { try { const item = await api.${usedEntity}UpdateById(-1, {}); } catch (e) { equal(e.extensions.code == 'B20010403', true); } // TUDO // const item = await api.${usedEntity}UpdateById(seccessAdded.id, { title: '自动化测试修改的名称' }); // equal(item.id == seccessAdded.id, true); }); it('test detail', async () => { try { const item = await api.${usedEntity}DetailById(-1); } catch (e) { equal(e.extensions.code == 'B20010501', true); } const item = await api.${usedEntity}DetailById(seccessAdded.id); equal(item.id == seccessAdded.id, true); }); it('test delete', async () => { try { const del = await api.${usedEntity}DeleteById(-1); } catch (e) { equal(e.extensions.code == 'B20010202', true); } const del = await api.${usedEntity}DeleteById(seccessAdded.id); equal(del.id == seccessAdded.id, true); }); it('test deleted update', async () => { try { const item = await api.${usedEntity}UpdateById(seccessAdded.id, {}); } catch (e) { equal(e.extensions.code == 'B20010404', true); } }); it('test deleted detail', async () => { try { const item = await api.${usedEntity}DetailById(seccessAdded.id); } catch (e) { equal(e.extensions.code == 'B20010502', true); } }); it('test deleted delete', async () => { try { const item = await api.${usedEntity}DeleteById(seccessAdded.id); } catch (e) { equal(e.extensions.code == 'B20010203', true); } }); }); ` writeFileSync(thisFileUrl, apiContent); }); } /** * 导入/导出/导入类名 的列表 * @param {any[]} entities * @param {string} ext */ private _importExportFileAndImportNames(entities: any[], ext: 'entity' | 'basic' | 'service' | 'api') { const upperExt = upperFirst(ext); const importFile = entities.map(it => { const name = it.entityName.replace(/Entity$/, `${upperExt == 'Api' ? 'ApiController' : upperExt}`); return ` import { ${name} } from './${it.fileName.replace('.entity.ts', `.${ext}`)}';` }).reduce((acc, cur) => { return (acc += cur); }, ''); const exportFile = entities.map(it => { return ` export * from './${it.fileName.replace('.entity.ts', `.${ext}`)}';` }).reduce((acc, cur) => { return (acc += cur); }, ''); const importNames = entities.map(it => { const name = it.entityName.replace(/Entity$/, `${upperExt == 'Api' ? 'ApiController' : upperExt}`); return ` ${name},` }).reduce((acc, cur) => { return (acc += cur); }, '').trim(); return { importFile, exportFile, importNames } } /** * 创建index.ts 并在本层导出 * @param {string} dirName 文件夹名 * @param {string} dirUrl 文件夹地址 * @param {string} exportFile 导出文件列表(string) */ private _createIndexTs(dirName: string, dirUrl: string, exportFile: string) { //创建此模块的index.ts const moduleIndex = `export * from './${dirName}.module';${exportFile}`; writeFileSync(join(dirUrl, `index.ts`), moduleIndex); // 本层的index.ts ensureFileSync(join(dirUrl, '../index.ts')); let thisIndex = readFileSync(join(dirUrl, '../index.ts')).toString(); if (!thisIndex.includes(`${dirName}`)) { thisIndex += ` export * from './${dirName}';`; writeFileSync(join(dirUrl, '../index.ts'), thisIndex); } } /** * 获取所有层的package.json * @param {any[]} entities */ private _getPackageJsons(entities: any[]) { const dirName = entities[0].dirName; const libUrl = join(process.cwd(), '../../../') const basicDir = join(libUrl, './basic/src', dirName); const domainDir = join(libUrl, './domain/src', dirName); const entitiesDir = join(libUrl, './entities/src', dirName); const apiDir = join(libUrl, './api/src', dirName); // entity const entitiesPakcageJson = this.__getPackageByLayerName('entities'); // basic const basicPakcageJson = this.__getPackageByLayerName('basic'); // domain const domainPakcageJson = this.__getPackageByLayerName('domain') // api const apiPakcageJson = this.__getPackageByLayerName('api'); return { apiPakcageJson, domainPakcageJson, basicPakcageJson, entitiesPakcageJson, apiDir, entitiesDir, domainDir, basicDir }; } private __getPackageByLayerName(layerName: 'entities' | 'basic' | 'domain' | 'api'): any { const packageUrl = join(process.cwd(), '../../../', layerName, 'package.json'); if (!pathExistsSync(packageUrl)) throw new Error(`请创建${layerName}层package.json`) const pakcageJson = readJSONSync(packageUrl) if (!pakcageJson.name || !pakcageJson.version) throw new Error(`${layerName}层package.json没有name或version`); return pakcageJson; } }