import { RequestMapping } from './../router' import * as _ from 'lodash' import { Controller } from './base' import TaskFactory from '../bll/factory/import/task' import TestcaseFactory from '../bll/factory/import/testcase' import { ilog } from 'ilog' import DbClient from '../bll/client/db' import Cache from '../bll/cache' import * as config from 'config' import {OptionsCollection} from '../types' interface Response { status: string, token?: string, errors?: Object[], totalRows?: number } export class Import extends Controller{ /** * * @api {post} /upload 项目导入任务 * @apiName PostImport * @apiGroup Import * @apiVersion 0.0.2 * * @apiParam {String} [fileType='xlsx'] 文件类型 * @apiParam {String} downloadUrl 文件下载地址 * @apiParam {String} _scenariofieldconfigId 任务类型 ID * @apiParam {String} _tasklistId 任务分组 ID * @apiParam {Boolean} [importTask=false] 是否导入任务 * @apiParam {String} [token] 数据验证标记(第一次数据检查过后返回),导入任务必传 * * @apiSuccess (200) {Object} tasks 任务 * * @apiParamExample {json} 检查数据: { "fileType": "xlsx", "downloadUrl": "http://tcs.project.ci/storage/021758a6e499b08bc&download=response.xlsx" , "_scenariofieldconfigId": "5b60163ce425c00001b0bb80", "_tasklistId": "5b60163c50a4c600124d5651" } * @apiParamExample {json} 强行导入: { "fileType": "xlsx", "downloadUrl": "http://tcs.project.ci/storage/021758a6e49Q_C0ZZZDagYHUA&download=response.xlsx" , "_scenariofieldconfigId": "5b60163ce425c00001b0bb80", "_tasklistId": "5b60163c50a4c600124d5651", "importTask": true, "token": "TIS/1533614337831" } * * * @apiSuccessExample {json} 导入失败: * { * "errors":[{ * "reason":"所属列表不存在", * "row":1, * "celName":"任务列表*", * "content":"待处理" * }], * "status":"failure", * "token": "", * "totalRows": 1 * } * * @apiSuccessExample {json} 导入成功: * { * "status":"success", * "token": "xxx", * "totalRows": 1 * } * */ @RequestMapping.post('/upload', { type: 'object', properties: { importTask: { type: 'boolean' }, fileType: { type: 'string' }, downloadUrl: { type: 'string' }, _scenariofieldconfigId: { type: 'string', format: 'objectid' }, _tasklistId: { type: 'string', format: 'objectid' }, token: { type: 'string' } }, required: ['fileType', 'downloadUrl', '_scenariofieldconfigId', '_tasklistId'] }) async importTasks () { const { downloadUrl, fileType = 'xlsx', _scenariofieldconfigId, _tasklistId, user, importTask = false, token } = this.ctx.state let nativeData const importer = new Cache() const factory = new TaskFactory(this.app, this.ctx) const client = new DbClient(this.app, this.ctx) const [scenariofieldconfig, tasklist] = await Promise.all([ client.getScenariofieldconfigById(_scenariofieldconfigId), client.getTasklistById(_tasklistId) ]) if (importTask) { if (!token) { this.ctx.throw(400, 'token is required') } const cache = await importer.getObjectArrayByToken(token) if (!cache) { this.ctx.throw(400, 'ParamError: token') } let tasks = cache[0] const errorMsgs = cache[1] if (errorMsgs.length) { errorMsgs.forEach((err) => { tasks[err.row - 1] = null }) } tasks = _.compact(tasks) this.app.$emit.emit('import-tasks', { factory, tasks }) // 清空缓存 await importer.delByToken(token) this.ctx.body = { status: 'success', totalRows: tasks.length } } else { const project = await client.getProjectById(scenariofieldconfig._boundToObjectId) // same project required if (scenariofieldconfig._boundToObjectId !== `${tasklist._projectId}`) { this.ctx.throw(400, 'ParamError: _scenariofieldconfigId or _tasklistId') } try { nativeData = await factory.downloadFile(downloadUrl, fileType) } catch (error) { ilog.warning(error) this.ctx.throw(400, 'ParamError: downloadUrl') } await factory.buildHeaders(scenariofieldconfig.scenariofields, project.proTemplateType || project.normalType) const parsedData: Object[] = factory.parse(nativeData, fileType) if (parsedData.length > config.APP.OBJECT_LIMIT.TASK) { this.ctx.throw(400, `ExceedLimit: ${config.APP.OBJECT_LIMIT.TASK}`) } const { tasks, errorMsgs } = await factory.transformToTasks(parsedData, scenariofieldconfig, tasklist, user._id, project) const _token = await importer.getToken(tasks, errorMsgs) const response: Response = { status: 'success', token: _token, totalRows: parsedData.length } if (errorMsgs.length) { response.errors = errorMsgs response.status = 'failure' this.ctx.body = response return } this.ctx.body = response } } /** * * @api {post} /upload/testcase 导入测试用例 * @apiName importTestCase * @apiGroup Import * @apiVersion 0.0.2 * @apiDescription * * 导入测试计划时需要`_scenariofieldconfigId`,`_testplanId`,`_projectId` * * 导入用例库时需要`_testhubId`,`_organizationId` * * @apiParam {String} [fileType='xlsx'] 文件类型 * @apiParam {Boolean} [isImport=false] 是否生成,需验证过后携带token * @apiParam {String} [token] 数据验证标记(第一次数据检查过后返回),导入必传 * @apiParam {String} downloadUrl 文件下载链接 * @apiParam {String} [_scenariofieldconfigId] 任务类型 ID * @apiParam {String} [_testplanId] 测试计划 ID * @apiParam {String} [_projectId] 项目 ID * @apiParam {String} [_testhubId] 用例库 ID * @apiParam {String} [_organizationId] 企业 ID * * * @apiParamExample {json} 检查数据: { "fileType": "xlsx", "downloadUrl": "http://tcs.project.ci/storage/021758a6e499agYHUA&download=response.xlsx", "_scenariofieldconfigId": "5b60163ce425c00001b0bb80", "_projectId": "5b60163c50a4c600124d5651", "_testplanId": "5b60163ce425c00001b0bb80", } * @apiParamExample {json} 强行导入: { "fileType": "xlsx", "downloadUrl": "http://tcs.project.ci/storage/021758a6e499b08bMHUA&download=response.xlsx", "_scenariofieldconfigId": "5b60163ce425c00001b0bb80", "_projectId": "5b60163c50a4c600124d5651", "_testplanId": "5b60163ce425c00001b0bb80", "isImport": true, "token": "TIS/1533614337831" } * * @apiSuccessExample {json} 导入失败: * { * "errors":[{ * "reason":"所属列表不存在", * "row":1, * "celName":"任务列表*", * "content":"待处理" * }], * "status":"failure", * "token": "", * "totalRows": 1 * } * * @apiSuccessExample {json} 导入成功: * { * "status":"success", * "token": "xxx", * "totalRows": 1 * } * * */ @RequestMapping.post('/upload/testcase', { type: 'object', properties: { isImport: { type: 'boolean' }, fileType: { type: 'string' }, downloadUrl: { type: 'string' }, _organizationId: { type: 'string', format: 'objectid' }, _testhubId: { type: 'string', format: 'objectid' }, token: { type: 'string' } }, oneOf: [ { required: ['downloadUrl', '_scenariofieldconfigId', '_testplanId', '_projectId'] }, { required: ['downloadUrl', '_organizationId', '_testhubId'] } ] }) async importTestCase () { const { downloadUrl, fileType = 'xlsx', _scenariofieldconfigId, _testplanId, _projectId, user, _organizationId, _testhubId, isImport, token } = this.ctx.state let nativeData const importer = new Cache() const factory = new TestcaseFactory(this.app, this.ctx, !!_testhubId) const client = new DbClient(this.app, this.ctx) if (isImport) { if (!token) { this.ctx.throw(400, 'token is required') } const cache = await importer.getObjectArrayByToken(token) if (!cache) { this.ctx.throw(400, 'ParamError: token') } let testcases = cache[0] const errorMsgs = cache[1] if (errorMsgs.length) { errorMsgs.forEach((err) => { testcases[err.row - 1] = null }) } testcases = _.compact(testcases) this.app.$emit.emit(_testhubId ? 'import-basictestcases' : 'import-testcases', { factory, testcases }) // 清空缓存 await importer.delByToken(token) this.ctx.body = { status: 'success', totalRows: testcases.length } } else { let project if (!_testhubId) { const scenariofieldconfig = await client.getScenariofieldconfigById(_scenariofieldconfigId) project = await client.getProjectById(scenariofieldconfig._boundToObjectId) // same project required if (scenariofieldconfig._boundToObjectId !== _projectId) { this.ctx.throw(400, 'ParamError: _scenariofieldconfigId or _projectId') } } try { nativeData = await factory.downloadFile(downloadUrl, fileType) } catch (error) { ilog.warning(error) this.ctx.throw(400, 'ParamError: downloadUrl') } await factory.buildHeaders() // 默认第一行为 示例,省略 const parsedData: Object[] = factory.parse(nativeData, fileType).slice(1) const transformOptions: OptionsCollection.TestCase.TransformOtions = { project, _scenariofieldconfigId, _testplanId, _organizationId, _testhubId, } const { testcases, errorMsgs } = await factory.transformToTestCase(parsedData, user, transformOptions) if (testcases.length > config.APP.OBJECT_LIMIT.TESTCASE) { this.ctx.throw(400, `ExceedLimit: ${config.APP.OBJECT_LIMIT.TESTCASE}`) } const _token = await importer.getToken(testcases, errorMsgs) const response: Response = { status: 'success', token: _token, totalRows: testcases.length } if (errorMsgs.length) { response.errors = errorMsgs response.status = 'failure' this.ctx.body = response return } this.ctx.body = response } } }