diff --git a/app/controller/api/group.js b/app/controller/api/group.js new file mode 100644 index 000000000..12c29b100 --- /dev/null +++ b/app/controller/api/group.js @@ -0,0 +1,50 @@ +'use strict'; + +const { Controller } = require('egg'); + +class GroupController extends Controller { + + async create() { + const ctx = this.ctx; + const { belongedUniqId, groupName, groupType } = ctx.request.body; + ctx.assertParam({ belongedUniqId, groupName, groupType }); + const res = await ctx.service.group.createGroup({ + belongedUniqId, + groupName, + groupType, + }); + ctx.success(res); + } + + async update() { + const ctx = this.ctx; + const uniqId = ctx.params.uniqId; + const { groupName } = ctx.request.body; + const res = await ctx.service.group.updateGroupName({ + uniqId, + groupName, + }); + ctx.success(res); + } + + async delete() { + const ctx = this.ctx; + const { uniqId } = ctx.params; + const res = await ctx.service.group.deleteGroupByUniqId({ uniqId }); + if (res) { + ctx.success(res); + return; + } + ctx.fail(ctx.gettext('common.delete.fail')); + } + + async showAll() { + const ctx = this.ctx; + const { belongedUniqId, groupType } = ctx.query; + ctx.assertParam({ belongedUniqId, groupType }); + const res = await ctx.service.group.queryGroupByBelongedUniqId({ belongedUniqId, groupType }); + ctx.success(res); + } +} + +module.exports = GroupController; diff --git a/app/controller/api/interface.js b/app/controller/api/interface.js index 78025d0da..6548d6c6a 100644 --- a/app/controller/api/interface.js +++ b/app/controller/api/interface.js @@ -11,7 +11,7 @@ class InterfaceController extends Controller { const ctx = this.ctx; const { projectUniqId } = ctx.query; ctx.assertParam({ projectUniqId }); - const res = await ctx.service.interface.queryInterfaceByProjectUniqId({ projectUniqId }); + const res = await ctx.service.interface.queryInterfaceDataByProjectUniqId({ projectUniqId }); ctx.success(res); } @@ -24,10 +24,14 @@ class InterfaceController extends Controller { async create() { const ctx = this.ctx; - const { projectUniqId, pathname, method, description } = ctx.request.body; - ctx.assertParam({ projectUniqId, pathname, method, description }); + const { projectUniqId, pathname, method, description, groupUniqId } = ctx.request.body; + ctx.assertParam({ projectUniqId, pathname, method, description, groupUniqId }); const res = await ctx.service.interface.createInterface({ - projectUniqId, pathname, method: method.toUpperCase(), description, + projectUniqId, + pathname, + method: method.toUpperCase(), + description, + groupUniqId, }); ctx.success(res); } @@ -40,6 +44,7 @@ class InterfaceController extends Controller { 'pathname', 'method', 'description', + 'groupUniqId', 'currentScene', 'proxyConfig', ].forEach(i => { diff --git a/app/controller/api/project.js b/app/controller/api/project.js index 3405a3506..c098b8278 100644 --- a/app/controller/api/project.js +++ b/app/controller/api/project.js @@ -26,22 +26,22 @@ class ProjectController extends Controller { const _res = await ctx.service.project.queryAllProject(); for (const _item of _res) { const item = _item.get({ plain: true }); - const iterfaceList = await ctx.service.interface.queryInterfaceByProjectUniqId({ + const interfaceList = await ctx.service.interface.queryInterfaceByProjectUniqId({ projectUniqId: item.uniqId, }); - const allSceneList = await Promise.all(iterfaceList.map(({ uniqId: interfaceUniqId }) => { + const allSceneList = await Promise.all(interfaceList.map(({ uniqId: interfaceUniqId }) => { return ctx.service.scene.querySceneByInterfaceUniqId({ interfaceUniqId }); })); let bufSize = 0; for (const sceneList of allSceneList) { for (const scene of sceneList) { - const buf = new Buffer(JSON.stringify(scene.data)); + const buf = new Buffer.from(JSON.stringify(scene.data)); bufSize += buf.length; } } item.capacity = { - count: iterfaceList.length, + count: interfaceList.length, size: filesize(bufSize), }; res.push(item); @@ -98,7 +98,7 @@ class ProjectController extends Controller { const { uniqId } = ctx.params; const res = await ctx.service.transfer.downloadProject({ uniqId }); - ctx.body = JSON.stringify(res.data, null, 2); + ctx.body = JSON.stringify(res.dataGroupList, null, 2); ctx.attachment(res.fileName); } diff --git a/app/model/Group.js b/app/model/Group.js new file mode 100644 index 000000000..347cad57e --- /dev/null +++ b/app/model/Group.js @@ -0,0 +1,42 @@ +'use strict'; + +module.exports = app => { + const { + STRING, + UUID, + UUIDV4, + } = app.Sequelize; + + const Group = app.model.define('group', { + uniqId: { + type: UUID, + defaultValue: UUIDV4, + primaryKey: true, + allowNull: false, + }, + groupName: { + type: STRING, + allowNull: false, + }, + groupType: { + type: STRING, + allowNull: false, + }, + belongedUniqId: { + type: STRING, + allowNull: false, + }, + }, { + ...app.config.modelCommonOption, + indexes: [ + { + fields: [ + 'uniqId', + ], + unique: true, + }, + ], + }); + + return Group; +}; diff --git a/app/model/Interface.js b/app/model/Interface.js index 4c7039e2d..7de5dc9d3 100644 --- a/app/model/Interface.js +++ b/app/model/Interface.js @@ -47,6 +47,10 @@ module.exports = app => { primaryKey: true, allowNull: false, }, + groupUniqId: { + type: STRING, + allowNull: true, + }, }, { ...app.config.modelCommonOption, indexes: [ diff --git a/app/router.js b/app/router.js index bef545f84..2dca69bcf 100644 --- a/app/router.js +++ b/app/router.js @@ -46,6 +46,11 @@ module.exports = app => { router.put('/api/scene/:uniqId', controller.api.scene.update); router.delete('/api/scene/:uniqId', controller.api.scene.delete); + router.get('/api/group', controller.api.group.showAll); + router.post('/api/group', controller.api.group.create); + router.put('/api/group/:uniqId', controller.api.group.update); + router.delete('/api/group/:uniqId', controller.api.group.delete); + router.get('/api/preview/scene', controller.api.preview.scene); router.get('/api/schema', controller.api.schema.showAll); diff --git a/app/service/group.js b/app/service/group.js new file mode 100644 index 000000000..1f7ab544d --- /dev/null +++ b/app/service/group.js @@ -0,0 +1,61 @@ +'use strict'; + +const { Service } = require('egg'); + +class GroupService extends Service { + async queryGroupByBelongedUniqId({ + belongedUniqId, + groupType, + }) { + return await this.ctx.model.Group.findAll({ + where: { + belongedUniqId, + groupType, + }, + order: [ + [ + 'createdAt', + 'ASC', + ], + ], + }); + } + + async createGroup({ + belongedUniqId, + groupName, + groupType, + }) { + return await this.ctx.model.Group.create({ + belongedUniqId, + groupName, + groupType, + }); + } + + async updateGroupName({ + uniqId, + groupName, + }) { + return await this.ctx.model.Group.update( + { groupName }, + { + where: { + uniqId, + }, + } + ); + } + + async deleteGroupByUniqId({ + uniqId, + }) { + return await this.ctx.model.Group.destroy({ + where: { + uniqId, + }, + }); + } +} + +module.exports = GroupService; diff --git a/app/service/interface.js b/app/service/interface.js index 91634b833..0a6912c7c 100644 --- a/app/service/interface.js +++ b/app/service/interface.js @@ -4,6 +4,7 @@ const { Service, } = require('egg'); const pathToRegexp = require('path-to-regexp'); +const _ = require('lodash'); class InterfaceService extends Service { @@ -109,6 +110,81 @@ class InterfaceService extends Service { }); } + async queryInterfaceDataByProjectUniqId({ + projectUniqId, + }, options = {}) { + + const { ctx } = this; + + const groups = await ctx.model.Group.findAll({ + ...options, + where: { + belongedUniqId: projectUniqId, + groupType: 'Interface', + }, + order: [ + [ + 'createdAt', + 'ASC', + ], + ], + }); + + // Inventory data initialization default grouping + if (!groups.length) { + const group = await ctx.model.Group.create({ + groupName: ctx.gettext('defaultGroupName'), + groupType: 'Interface', + belongedUniqId: projectUniqId, + }); + + groups.push(group); + + await ctx.model.Interface.update({ groupUniqId: group.uniqId }, { + where: { + projectUniqId, + } + }); + } + + const interfaces = await ctx.model.Interface.findAll({ + ...options, + where: { + projectUniqId, + }, + order: [ + [ + 'createdAt', + 'ASC', + ], + ], + }); + + const interfaceGroupList = _.chain(interfaces) + .groupBy("groupUniqId") + .toPairs() + .map(currentItem => { + return _.zipObject(["groupUniqId", "interfaceList"], currentItem); + }) + .value(); + + const interfaceGroupListNew = []; + + groups.forEach(element => { + const interfaceGroup = interfaceGroupList.find(item => item.groupUniqId === element.uniqId); + interfaceGroupListNew.push({ + groupName: element.groupName, + groupUniqId: element.uniqId, + interfaceList: interfaceGroup ? interfaceGroup.interfaceList : [], + }) + }); + + return { + interfaceGroupList: interfaceGroupListNew, + interfaceList: interfaces, + }; + } + async queryInterfaceByUniqId({ uniqId, }) { @@ -124,13 +200,17 @@ class InterfaceService extends Service { pathname, method, description, + groupUniqId, proxyConfig, }) { - return await this.ctx.model.Interface.create({ + const { ctx } = this; + + return await ctx.model.Interface.create({ projectUniqId, pathname, method, description, + groupUniqId, proxyConfig, }); } @@ -206,6 +286,20 @@ class InterfaceService extends Service { async deleteInterfaceByUniqId({ uniqId, }) { + const { ctx } = this; + + await ctx.model.Scene.destroy({ + where: { + interfaceUniqId: uniqId, + } + }); + + await ctx.model.Schema.destroy({ + where: { + interfaceUniqId: uniqId, + } + }); + return await this.ctx.model.Interface.destroy({ where: { uniqId, @@ -217,15 +311,17 @@ class InterfaceService extends Service { uniqId, scenes }) { + const { ctx } = this; + for (const scene of scenes) { - await this.ctx.model.Scene.create({ + await ctx.model.Scene.create({ interfaceUniqId: uniqId, sceneName: scene.sceneName, contextConfig: scene.contextConfig, data: scene.data, }); } - return null + return null; } async duplicateSchemas({ @@ -239,7 +335,7 @@ class InterfaceService extends Service { data: schema.data, }); } - return null + return null; } } diff --git a/app/service/project.js b/app/service/project.js index 6702deba8..eff753752 100644 --- a/app/service/project.js +++ b/app/service/project.js @@ -33,11 +33,21 @@ class ProjectService extends Service { } async createProject({ projectName, description, globalProxy }) { - return await this.ctx.model.Project.create({ + const { ctx } = this; + + const project = await ctx.model.Project.create({ projectName, description, globalProxy, }); + + await ctx.model.Group.create({ + groupName: ctx.gettext('defaultGroupName'), + groupType: 'Interface', + belongedUniqId: project.uniqId, + }); + + return project; } async updateProject({ uniqId, payload }) { @@ -52,7 +62,26 @@ class ProjectService extends Service { } async deleteProjectByUniqId({ uniqId }) { - return await this.ctx.model.Project.destroy({ + const { ctx } = this; + + await ctx.model.Group.destroy({ + where: { + belongedUniqId: uniqId, + groupType: 'Interface', + } + }); + + const interfaces = await ctx.service.interface.queryInterfaceByProjectUniqId({ + projectUniqId: uniqId, + }); + + for (const interfaceData of interfaces) { + await ctx.service.interface.deleteInterfaceByUniqId({ + uniqId: interfaceData.uniqId, + }); + } + + return await ctx.model.Project.destroy({ where: { uniqId, }, diff --git a/app/service/scene.js b/app/service/scene.js index 429f30ed0..b876e225e 100644 --- a/app/service/scene.js +++ b/app/service/scene.js @@ -1,8 +1,6 @@ 'use strict'; -const { - Service, -} = require('egg'); +const { Service } = require('egg'); class SceneService extends Service { async querySceneByInterfaceUniqId({ diff --git a/app/service/transfer.js b/app/service/transfer.js index fa054b542..3173c371f 100644 --- a/app/service/transfer.js +++ b/app/service/transfer.js @@ -9,39 +9,50 @@ class TransferService extends Service { async downloadProject({ uniqId, }) { - const interfaces = await this.ctx.service.interface.queryInterfaceByProjectUniqId({ + const { ctx } = this; + + const interfaceGroups = await ctx.service.interface.queryInterfaceDataByProjectUniqId({ projectUniqId: uniqId, }); - const data = []; - - for (const interfaceData of interfaces) { - const scenes = await this.ctx.service.scene.querySceneByInterfaceUniqId({ - interfaceUniqId: interfaceData.uniqId, - }); - - const schemas = await this.ctx.service.schema.querySchemaByInterfaceUniqId({ - interfaceUniqId: interfaceData.uniqId, - }); - - data.push({ - pathname: interfaceData.pathname, - method: interfaceData.method, - description: interfaceData.description, - uniqId: interfaceData.uniqId, - contextConfig: interfaceData.contextConfig, - currentScene: interfaceData.currentScene, - proxyConfig: interfaceData.proxyConfig, - scenes, - schemas, - }); + const dataGroupList = []; + + for (const interfaceGroup of interfaceGroups.interfaceGroupList) { + const data = []; + for (const interfaceData of interfaceGroup.interfaceList) { + const scenes = await ctx.service.scene.querySceneByInterfaceUniqId({ + interfaceUniqId: interfaceData.uniqId, + }); + + const schemas = await ctx.service.schema.querySchemaByInterfaceUniqId({ + interfaceUniqId: interfaceData.uniqId, + }); + + data.push({ + pathname: interfaceData.pathname, + method: interfaceData.method, + description: interfaceData.description, + uniqId: interfaceData.uniqId, + groupUniqId: interfaceData.groupUniqId, + contextConfig: interfaceData.contextConfig, + currentScene: interfaceData.currentScene, + proxyConfig: interfaceData.proxyConfig, + scenes, + schemas, + }); + } + dataGroupList.push({ + groupName: interfaceGroup.groupName, + groupUniqId: interfaceGroup.groupUniqId, + interfaceList: data, + }) } - const info = await this.ctx.service.project.queryProjectByUniqId({ uniqId }); + const info = await ctx.service.project.queryProjectByUniqId({ uniqId }); const fileName = `project_${info.projectName}.json`; return { - data, + dataGroupList, fileName, }; } @@ -50,33 +61,55 @@ class TransferService extends Service { projectData, projectUniqId, }) { - await this.ctx.model.Interface.destroy({ + const { ctx } = this; + + await ctx.model.Interface.destroy({ where: { projectUniqId, }, }); - for (const interfaceData of projectData) { - const interfaceStatus = await this.ctx.model.Interface.create({ - projectUniqId, - pathname: interfaceData.pathname, - method: interfaceData.method, - description: interfaceData.description, - contextConfig: interfaceData.contextConfig, - currentScene: interfaceData.currentScene, - proxyConfig: interfaceData.proxyConfig, - }); + await ctx.model.Group.destroy({ + where: { + belongedUniqId: projectUniqId, + }, + }); - await this.ctx.service.interface.duplicateScenes({ - uniqId: interfaceStatus.uniqId, - scenes: interfaceData.scenes, - }); - - await this.ctx.service.interface.duplicateSchemas({ - uniqId: interfaceStatus.uniqId, - schemas: interfaceData.schemas, + // compatible with old project data + const interfaceGroupList = (projectData[0] && projectData[0].interfaceList) ? projectData : [{ + groupName: ctx.gettext('defaultGroupName'), + interfaceList: projectData, + }]; + + for (const interfaceGroup of interfaceGroupList) { + const group = await ctx.service.group.createGroup({ + belongedUniqId: projectUniqId, + groupName: interfaceGroup.groupName, + groupType: 'Interface', }); + for (const interfaceData of interfaceGroup.interfaceList) { + const interfaceStatus = await ctx.model.Interface.create({ + projectUniqId, + pathname: interfaceData.pathname, + method: interfaceData.method, + description: interfaceData.description, + groupUniqId: group.uniqId, + contextConfig: interfaceData.contextConfig, + currentScene: interfaceData.currentScene, + proxyConfig: interfaceData.proxyConfig, + }); + + await ctx.service.interface.duplicateScenes({ + uniqId: interfaceStatus.uniqId, + scenes: interfaceData.scenes, + }); + + await ctx.service.interface.duplicateSchemas({ + uniqId: interfaceStatus.uniqId, + schemas: interfaceData.schemas, + }); + } } return { @@ -87,17 +120,19 @@ class TransferService extends Service { async downloadInterface({ interfaceUniqId, }) { - const scenes = await this.ctx.model.Scene.findAll({ + const { ctx } = this; + + const scenes = await ctx.model.Scene.findAll({ where: { interfaceUniqId, }, }); - const schemas = await this.ctx.service.schema.querySchemaByInterfaceUniqId({ + const schemas = await ctx.service.schema.querySchemaByInterfaceUniqId({ interfaceUniqId, }); - const interfaceData = await this.ctx.service.interface.queryInterfaceByUniqId({ + const interfaceData = await ctx.service.interface.queryInterfaceByUniqId({ uniqId: interfaceUniqId, }); const fileName = `interface_${interfaceData.pathname}_${interfaceData.method}.json`; @@ -109,6 +144,7 @@ class TransferService extends Service { method: interfaceData.method, projectUniqId: interfaceData.uniqId, description: interfaceData.description, + groupUniqId: interfaceData.groupUniqId, contextConfig: interfaceData.contextConfig, currentScene: interfaceData.currentScene, proxyConfig: interfaceData.proxyConfig, @@ -122,36 +158,40 @@ class TransferService extends Service { interfaceData, interfaceUniqId, }) { - const interfaceOldData = await this.ctx.service.interface.queryInterfaceByUniqId({ + const { ctx } = this; + + const interfaceOldData = await ctx.service.interface.queryInterfaceByUniqId({ uniqId: interfaceUniqId, }); - await this.ctx.service.interface.deleteInterfaceByUniqId({ + await ctx.service.interface.deleteInterfaceByUniqId({ uniqId: interfaceUniqId, }); - const interfaceStatus = await this.ctx.model.Interface.create({ + const interfaceStatus = await ctx.model.Interface.create({ pathname: interfaceOldData.pathname, method: interfaceOldData.method, projectUniqId: interfaceOldData.projectUniqId, description: interfaceOldData.description, + groupUniqId: interfaceOldData.groupUniqId, contextConfig: interfaceData.contextConfig, currentScene: interfaceData.currentScene, proxyConfig: interfaceData.proxyConfig, }); - await this.ctx.service.interface.duplicateScenes({ + await ctx.service.interface.duplicateScenes({ uniqId: interfaceStatus.uniqId, scenes: interfaceData.scenes, }); - await this.ctx.service.interface.duplicateSchemas({ + await ctx.service.interface.duplicateSchemas({ uniqId: interfaceStatus.uniqId, schemas: interfaceData.schemas, }); return { success: true, + newInterfaceUniqId: interfaceStatus.uniqId, }; } } diff --git a/config/locale/en-US.js b/config/locale/en-US.js index 22a425f4c..b0faea6fc 100644 --- a/config/locale/en-US.js +++ b/config/locale/en-US.js @@ -5,6 +5,7 @@ module.exports = { dashboard: 'Dashboard', project: 'Project', document: 'Document', + defaultGroupName: 'Default group', // resource manage 'common.delete.fail': 'deletion execute failed', diff --git a/config/locale/zh-CN.js b/config/locale/zh-CN.js index 61a743e3c..d51382bff 100644 --- a/config/locale/zh-CN.js +++ b/config/locale/zh-CN.js @@ -5,6 +5,7 @@ module.exports = { dashboard: '我的项目', project: '项目配置', document: '项目文档', + defaultGroupName: '默认分组', // resource manage 'common.delete.fail': '数据不存在,删除失败', diff --git a/database/migrations/20211224083339-group.js b/database/migrations/20211224083339-group.js new file mode 100644 index 000000000..f1584c257 --- /dev/null +++ b/database/migrations/20211224083339-group.js @@ -0,0 +1,61 @@ +'use strict'; + +module.exports = { + up: async (db, Sequelize) => { + const { STRING, UUID, UUIDV4, DATE } = Sequelize; + + await db.createTable('groups', { + uniqId: { + type: UUID, + defaultValue: UUIDV4, + primaryKey: true, + allowNull: false, + }, + groupName: { + type: STRING, + allowNull: false, + }, + groupType: { + type: STRING, + allowNull: false, + }, + belongedUniqId: { + type: STRING, + allowNull: false, + }, + createdAt: { + type: DATE, + allowNull: false, + }, + updatedAt: { + type: DATE, + allowNull: false, + }, + }, { + indexes: [ + { + fields: [ + 'uniqId', + ], + unique: true, + }, + ], + }); + + await db.addColumn('interfaces', 'groupUniqId', { + type: Sequelize.STRING, + allowNull: true, + }); + + await db.addColumn('scenes', 'groupUniqId', { + type: Sequelize.STRING, + allowNull: true, + }); + }, + + down: async db => { + await db.dropTable('groups'); + await db.removeColumn('interfaces', 'groupUniqId'); + await db.removeColumn('scenes', 'groupUniqId'); + }, +}; diff --git a/database/migrations/20220222121701-remove-scene-group-uniq-id.js b/database/migrations/20220222121701-remove-scene-group-uniq-id.js new file mode 100644 index 000000000..e575003f4 --- /dev/null +++ b/database/migrations/20220222121701-remove-scene-group-uniq-id.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = { + up: async db => { + await db.removeColumn('scenes', 'groupUniqId'); + }, + + down: async (db, Sequelize) => { + await db.addColumn('scenes', 'groupUniqId', { + type: Sequelize.STRING, + allowNull: true, + }); + }, +}; diff --git a/docs/.vuepress/public/assets/20220123234308.png b/docs/.vuepress/public/assets/20220123234308.png new file mode 100644 index 000000000..09d8a3998 Binary files /dev/null and b/docs/.vuepress/public/assets/20220123234308.png differ diff --git a/docs/.vuepress/public/assets/20220228155204.png b/docs/.vuepress/public/assets/20220228155204.png new file mode 100644 index 000000000..ba6370b1e Binary files /dev/null and b/docs/.vuepress/public/assets/20220228155204.png differ diff --git a/docs/.vuepress/public/assets/add-interface-group.gif b/docs/.vuepress/public/assets/add-interface-group.gif new file mode 100644 index 000000000..f6126739d Binary files /dev/null and b/docs/.vuepress/public/assets/add-interface-group.gif differ diff --git a/docs/.vuepress/public/assets/add-interface.gif b/docs/.vuepress/public/assets/add-interface.gif new file mode 100644 index 000000000..518ca445f Binary files /dev/null and b/docs/.vuepress/public/assets/add-interface.gif differ diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md index 09709bcd8..d0b8bd5c0 100644 --- a/docs/guide/quick-start.md +++ b/docs/guide/quick-start.md @@ -5,24 +5,31 @@ Create a new item named sample.
- +
-## Add An Interface +## Create An Interface Group -Add the interface named `test1`, request the interface `http://localhost:8080/api/test1` and get the corresponding mock data. +A default interface group will be created when a new project is created, and interface groups can be added and modified later as required.
- +
+## Add An Interface + +Add the interface named `test1`, request the interface `http://localhost:8080/api/test1` and get the corresponding mock data, Interface grouping can be selected when adding, which is convenient for management. + +
+ +
## Build Interface -The scene management, add scenario content corresponding to Response, and the development environment adds multiple scenarios which is conducive to rapid switching. You cna set the interface response information, and return status code `200` if not set. +The scene management, add scenario content corresponding to Response, and the development environment adds multiple scenarios which is conducive to rapid switching. You can set the interface response information, and return status code `200` if not set.
- +
The proxy pattern, it can be configured if required. diff --git a/docs/zh/guide/quick-start.md b/docs/zh/guide/quick-start.md index e85ebb657..20742a15b 100644 --- a/docs/zh/guide/quick-start.md +++ b/docs/zh/guide/quick-start.md @@ -5,24 +5,31 @@ 创建名字为 sample 项目。
- +
-## 添加接口 +## 添加接口分组 -添加 `test1` 接口, 则对 `http://localhost:8080/api/test1` 请求 mock 数据。 +在新建项目时会创建一个默认接口分组, 后续可根据需求新增和修改接口分组。
- +
+## 添加接口 + +添加 `test1` 接口, 则对 `http://localhost:8080/api/test1` 请求 mock 数据,可在添加时选择接口分组,便于管理。 + +
+ +
## 接口构建 场景管理 - 新增场景即接口返回数据 Response, 可添加多个场景方便开发时进行快速切换。同时,可以对接口响应状态及 header 进行设置, 如不进行特殊配置则返回 `200` 状态码。
- +
代理模式, 如有代理场景需求, 配置代理模式。 diff --git a/package.json b/package.json index e8f97f5e5..292a108bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "macaca-datahub", - "version": "3.6.2", + "version": "4.0.0", "description": "Continuous data provider for development, testing, staging and production.", "bin": { "datahub": "./bin/datahub.js", @@ -58,6 +58,7 @@ "filesize": "^4.2.0", "get-stream": "^4.1.0", "koa-compose": "^4.0.0", + "lodash": "^4.17.21", "macaca-circular-json": "^0.5.10", "macaca-logo": "1", "mockjs": "^1.1.0", diff --git a/test/.setup.js b/test/.setup.js index 9a787207d..ac24f14c6 100644 --- a/test/.setup.js +++ b/test/.setup.js @@ -7,6 +7,7 @@ const restore = async () => { await app.model.Interface.truncate(); await app.model.Scene.truncate(); await app.model.Schema.truncate(); + await app.model.Group.truncate(); } before(restore) diff --git a/test/controller/api/group.test.js b/test/controller/api/group.test.js new file mode 100644 index 000000000..181a5a7b9 --- /dev/null +++ b/test/controller/api/group.test.js @@ -0,0 +1,121 @@ +'use strict'; + +const { + app, + assert, +} = require('egg-mock/bootstrap'); + +describe('test/app/controller/api/group.test.js', () => { + let ctx; + + beforeEach(async () => { + ctx = app.mockContext(); + }); + + it('PUT /api/interface/:uniqId update group', async () => { + const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ + { projectName: 'snapre', description: 'test' }, + ]); + const [{ uniqId: groupUniqId }] = await ctx.model.Group.bulkCreate([ + { + groupName: 'group1', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, + ]); + const { body: createBody } = await app.httpRequest() + .put(`/api/group/${groupUniqId}`) + .send({ + groupName: 'newGroupName' + }); + assert.deepStrictEqual(createBody, { + success: true, data: [ 1 ], + }); + const groupData = await ctx.model.Group.findOne({ + where: { + uniqId: groupUniqId, + }, + }); + assert(groupData.groupName === 'newGroupName'); + }); + + it('GET /api/group get interface groupList', async () => { + const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ + { projectName: 'snapre', description: 'test' }, + ]); + await ctx.model.Group.bulkCreate([ + { + groupName: 'group1', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, + ]); + const body = await app.httpRequest() + .get(`/api/group?belongedUniqId=${projectUniqId}&groupType=Interface`); + assert(body.status === 200); + assert(body.req.method === 'GET'); + const res = JSON.parse(body.text); + + assert(res.success === true); + assert(res.data.length === 1); + assert(res.data[0].groupName === 'group1'); + }); + + it('POST /api/group add interface group', async () => { + const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ + { projectName: 'baz', description: 'bazd' }, + ]); + const body = await app.httpRequest() + .post('/api/group') + .send({ + belongedUniqId: projectUniqId, + groupName: 'group1', + groupType: 'Interface', + }); + assert(body.status === 200); + assert(body.req.method === 'POST'); + + const res = JSON.parse(body.text); + + assert(res.success === true); + assert(res.data.groupName === 'group1'); + }); + + it('DELETE /api/group/:uniqId delete group', async () => { + const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ + { projectName: 'snapre', description: 'test' }, + ]); + const [{ uniqId: groupUniqId }] = await ctx.model.Group.bulkCreate([ + { + groupName: ctx.gettext('defaultGroupName'), + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, + ]); + const { body: createBody } = await app.httpRequest() + .delete(`/api/group/${groupUniqId}`); + assert.deepStrictEqual(createBody, { + success: true, + data: 1, + }); + }); + + it('DELETE /api/group/:uniqId delete group fail', async () => { + const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ + { projectName: 'snapre', description: 'test' }, + ]); + const [{ uniqId: groupUniqId }] = await ctx.model.Group.bulkCreate([ + { + groupName: ctx.gettext('defaultGroupName'), + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, + ]); + const { body: createBody } = await app.httpRequest() + .delete(`/api/group/${groupUniqId}111`); + assert.deepStrictEqual(createBody, { + success: false, + message: 'deletion execute failed', + }); + }); +}); diff --git a/test/controller/api/interface.test.js b/test/controller/api/interface.test.js index 9840468e1..1ed8cde88 100644 --- a/test/controller/api/interface.test.js +++ b/test/controller/api/interface.test.js @@ -7,18 +7,41 @@ const { describe('test/app/controller/api/interface.test.js', () => { let ctx; + let projectUniqId; + let interfaceGroupUniqId; + let interfaceUniqId; beforeEach(async () => { ctx = app.mockContext(); - }); - it('PUT /api/interface/:uniqId delete proxy', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, + const [{ uniqId: _projectUniqId }] = await ctx.model.Project.bulkCreate([ + { + projectName: 'baz', + description: 'bazd', + }, ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, + projectUniqId = _projectUniqId; + const [{ uniqId: _interfaceGroupUniqId }] = await ctx.model.Group.bulkCreate([ + { + groupName: 'interfaceGroup1', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, ]); + interfaceGroupUniqId = _interfaceGroupUniqId; + const [{ uniqId: _interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ + { + projectUniqId, + pathname: 'api/path', + method: 'ALL', + description: 'description', + groupUniqId: interfaceGroupUniqId, + }, + ]); + interfaceUniqId = _interfaceUniqId; + }); + + it('PUT /api/interface/:uniqId delete proxy', async () => { const { body: createBody } = await app.httpRequest() .put(`/api/interface/${interfaceUniqId}`) .send({ @@ -61,12 +84,6 @@ describe('test/app/controller/api/interface.test.js', () => { }); it('GET /api/interface show all interfaces', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); const body = await app.httpRequest() .get(`/api/interface?projectUniqId=${projectUniqId}`); assert(body.status === 200); @@ -75,12 +92,6 @@ describe('test/app/controller/api/interface.test.js', () => { }); it('GET /api/interface/:uniqId show one interfaces', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); const body = await app.httpRequest() .get(`/api/interface/${interfaceUniqId}`); assert(body.status === 200); @@ -89,16 +100,14 @@ describe('test/app/controller/api/interface.test.js', () => { }); it('POST /api/interface/:uniqId add interfaces', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); const body = await app.httpRequest() .post('/api/interface') .send({ projectUniqId, description: 'waldo', method: 'ALL', - pathname: 'api/path', + pathname: 'api/path/v2', + groupUniqId: interfaceGroupUniqId, }); assert(body.status === 200); assert(body.req.method === 'POST'); @@ -106,16 +115,11 @@ describe('test/app/controller/api/interface.test.js', () => { const res = JSON.parse(body.text); assert(res.success === true); - assert(res.data.pathname === 'api/path'); + assert(res.data.pathname === 'api/path/v2'); + assert(res.data.groupUniqId === interfaceGroupUniqId); }); it('DELETE /api/interface/:uniqId delete interfaces', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); const { body: createBody } = await app.httpRequest() .delete(`/api/interface/${interfaceUniqId}`); assert.deepStrictEqual(createBody, { @@ -125,12 +129,6 @@ describe('test/app/controller/api/interface.test.js', () => { }); it('DELETE /api/interface/:uniqId delete interfaces fail', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); const { body: createBody } = await app.httpRequest() .delete(`/api/interface/${interfaceUniqId}111`); assert.deepStrictEqual(createBody, { @@ -140,12 +138,6 @@ describe('test/app/controller/api/interface.test.js', () => { }); it('GET /api/interface/download download interfaces', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); const { body: createBody } = await app.httpRequest() .get(`/api/interface/download?interfaceUniqId=${interfaceUniqId}`); delete createBody.projectUniqId; @@ -153,6 +145,7 @@ describe('test/app/controller/api/interface.test.js', () => { pathname: 'api/path', method: 'ALL', description: 'description', + groupUniqId: interfaceGroupUniqId, currentScene: '', proxyConfig: {}, scenes: [], @@ -161,12 +154,6 @@ describe('test/app/controller/api/interface.test.js', () => { }); it('POST /api/interface/upload upload interfaces', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); const { body: createBody } = await app.httpRequest() .post('/api/interface/upload') .send({ diff --git a/test/controller/api/preview.test.js b/test/controller/api/preview.test.js index 37061b3dd..44783ffbf 100644 --- a/test/controller/api/preview.test.js +++ b/test/controller/api/preview.test.js @@ -5,20 +5,43 @@ const { assert, } = require('egg-mock/bootstrap'); -describe('test/controller/api/preivew.test.js', () => { +describe('test/controller/api/preview.test.js', () => { let ctx; + let projectUniqId; + let interfaceGroupUniqId; + let interfaceUniqId; beforeEach(async () => { ctx = app.mockContext(); - }); - it('GET /api/preview/scene preview scene data', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, + const [{ uniqId: _projectUniqId }] = await ctx.model.Project.bulkCreate([ + { + projectName: 'baz', + description: 'bazd', + }, ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, + projectUniqId = _projectUniqId; + const [{ uniqId: _interfaceGroupUniqId }] = await ctx.model.Group.bulkCreate([ + { + groupName: 'interfaceGroup1', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, ]); + interfaceGroupUniqId = _interfaceGroupUniqId; + const [{ uniqId: _interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ + { + projectUniqId, + pathname: 'api/path', + method: 'ALL', + description: 'description', + groupUniqId: interfaceGroupUniqId, + }, + ]); + interfaceUniqId = _interfaceUniqId; + }); + + it('GET /api/preview/scene preview scene data', async () => { await app.httpRequest() .post('/api/scene/') .send({ @@ -36,12 +59,6 @@ describe('test/controller/api/preivew.test.js', () => { it('GET /api/preview/scene preview scene data fail', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ diff --git a/test/controller/api/project.test.js b/test/controller/api/project.test.js index 5e65af7bb..73939fccc 100644 --- a/test/controller/api/project.test.js +++ b/test/controller/api/project.test.js @@ -42,15 +42,20 @@ describe('test/app/controller/api/project.test.js', () => { const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ { projectName: 'baz', description: 'bazd', globaProxy: 'http://127.0.0.1' }, ]); + const [{ uniqId: interfaceGroupUniqId }] = await ctx.model.Group.bulkCreate([ + { belongedUniqId: projectUniqId, groupName: 'interfaceGroup1', groupType: 'Interface' } + ]); const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, + { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description', groupUniqId: interfaceGroupUniqId }, ]); await app.httpRequest() .post('/api/scene/') .send({ interfaceUniqId, sceneName: 'waldo', + contextConfig: {}, data: { success: true }, + format: 'json', }); const { body: createBody } = await app.httpRequest() .get('/api/project'); @@ -85,8 +90,11 @@ describe('test/app/controller/api/project.test.js', () => { const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ { projectName: 'baz', description: 'bazd' }, ]); + const [{ uniqId: interfaceGroupUniqId }] = await ctx.model.Group.bulkCreate([ + { belongedUniqId: projectUniqId, groupName: 'interfaceGroup1', groupType: 'Interface' } + ]); await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, + { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description', groupUniqId: interfaceGroupUniqId }, ]); const { body: createBody } = await app.httpRequest() .delete(`/api/project/${projectUniqId}`); @@ -100,11 +108,14 @@ describe('test/app/controller/api/project.test.js', () => { const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ { projectName: 'baz', description: 'bazd' }, ]); + const [{ uniqId: interfaceGroupUniqId }] = await ctx.model.Group.bulkCreate([ + { belongedUniqId: projectUniqId, groupName: 'interfaceGroup1', groupType: 'Interface' } + ]); await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, + { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description', groupUniqId: interfaceGroupUniqId }, ]); const { body: createBody } = await app.httpRequest() .get(`/api/project/download/${projectUniqId}`); - assert(createBody[0].pathname, 'api/path'); + assert(createBody[0].interfaceList[0].pathname, 'api/path'); }); }); diff --git a/test/controller/api/scene.test.js b/test/controller/api/scene.test.js index 02331d116..40e8ac604 100644 --- a/test/controller/api/scene.test.js +++ b/test/controller/api/scene.test.js @@ -7,18 +7,41 @@ const { describe('test/app/controller/scene.test.js', () => { let ctx; + let projectUniqId; + let interfaceGroupUniqId; + let interfaceUniqId; beforeEach(async () => { ctx = app.mockContext(); - }); - it('GET /api/scene show scene', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, + const [{ uniqId: _projectUniqId }] = await ctx.model.Project.bulkCreate([ + { + projectName: 'baz', + description: 'bazd', + }, ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, + projectUniqId = _projectUniqId; + const [{ uniqId: _interfaceGroupUniqId }] = await ctx.model.Group.bulkCreate([ + { + groupName: 'interfaceGroup1', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, ]); + interfaceGroupUniqId = _interfaceGroupUniqId; + const [{ uniqId: _interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ + { + projectUniqId, + pathname: 'api/path', + method: 'ALL', + description: 'description', + groupUniqId: interfaceGroupUniqId, + }, + ]); + interfaceUniqId = _interfaceUniqId; + }); + + it('GET /api/scene show scene', async () => { const { body: { data: { uniqId: sceneUniqId } } } = await app.httpRequest() .post('/api/scene/') .send({ @@ -42,13 +65,31 @@ describe('test/app/controller/scene.test.js', () => { assert(createBody2.data.sceneName, 'waldo'); }); + it('POST /api/scene create scene', async () => { + const { body: { data: { uniqId: sceneUniqId } } } = await app.httpRequest() + .post('/api/scene/') + .send({ + interfaceUniqId, + sceneName: 'waldo', + contextConfig: {}, + data: { success: true }, + }); + const interfaceData = await ctx.model.Interface.findOne({ + where: { + uniqId: interfaceUniqId, + }, + }); + const sceneData = await ctx.model.Scene.findOne({ + where: { + uniqId: sceneUniqId, + }, + }); + assert(interfaceData.currentScene === 'waldo'); + assert(sceneData.sceneName === 'waldo'); + assert(sceneData.interfaceUniqId=== interfaceUniqId); + }); + it('PUT /api/scene/:uniqId update sceneName', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); const { body: { data: { uniqId: sceneUniqId } } } = await app.httpRequest() .post('/api/scene/') .send({ @@ -81,12 +122,6 @@ describe('test/app/controller/scene.test.js', () => { }); it('DELETE /api/scene/:uniqId delete scene', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ @@ -112,12 +147,6 @@ describe('test/app/controller/scene.test.js', () => { }); it('DELETE /api/scene/:uniqId delete scene fail', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); const { body: createBody } = await app.httpRequest() .delete('/api/scene/unkown'); assert.deepStrictEqual(createBody, { diff --git a/test/controller/api/sdk.test.js b/test/controller/api/sdk.test.js index bad084c8d..752fd940a 100644 --- a/test/controller/api/sdk.test.js +++ b/test/controller/api/sdk.test.js @@ -7,18 +7,41 @@ const { describe('test/app/controller/sdk.test.js', () => { let ctx; + let projectUniqId; + let interfaceGroupUniqId; + let interfaceUniqId; beforeEach(async () => { ctx = app.mockContext(); - }); - it('GET /api/sdk/scene_data get scene data', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, + const [{ uniqId: _projectUniqId }] = await ctx.model.Project.bulkCreate([ + { + projectName: 'baz', + description: 'bazd', + }, ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, + projectUniqId = _projectUniqId; + const [{ uniqId: _interfaceGroupUniqId }] = await ctx.model.Group.bulkCreate([ + { + groupName: 'interfaceGroup1', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, ]); + interfaceGroupUniqId = _interfaceGroupUniqId; + const [{ uniqId: _interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ + { + projectUniqId, + pathname: 'api/path', + method: 'ALL', + description: 'description', + groupUniqId: interfaceGroupUniqId, + }, + ]); + interfaceUniqId = _interfaceUniqId; + }); + + it('GET /api/sdk/scene_data get scene data', async () => { await app.httpRequest() .post('/api/scene/') .send({ @@ -38,12 +61,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('GET /api/sdk/scene_data get scene data, interface fail', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ @@ -65,12 +82,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('GET /api/sdk/scene_data get scene data, project fail', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ @@ -91,13 +102,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('POST /api/sdk/switch_scene switch scene data', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); - await app.httpRequest() .post('/api/scene/') .send({ @@ -112,6 +116,7 @@ describe('test/app/controller/sdk.test.js', () => { .send({ interfaceUniqId, sceneName: 'fail', + contextConfig: {}, data: { success: false }, }); @@ -148,12 +153,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('POST /api/sdk/switch_scene switch scene data, interface fail', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ @@ -184,12 +183,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('POST /api/sdk/switch_scene switch scene data, project fail', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ @@ -218,13 +211,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('POST /api/sdk/switch_multi_scenes switch multi scene data', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); - await app.httpRequest() .post('/api/scene/') .send({ @@ -239,6 +225,7 @@ describe('test/app/controller/sdk.test.js', () => { .send({ interfaceUniqId, sceneName: 'fail', + contextConfig: {}, data: { success: false }, }); @@ -257,13 +244,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('POST /api/sdk/switch_all_scenes switch all scene data', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); - await app.httpRequest() .post('/api/scene/') .send({ @@ -310,12 +290,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('POST /api/sdk/switch_all_scenes switch all scene data, project fail', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ @@ -342,12 +316,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('POST /api/sdk/switch_all_scenes switch all scene data, scene fail', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ @@ -377,13 +345,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('POST /api/sdk/switch_all_proxy switch all proxy', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); - await app.httpRequest() .post('/api/scene/') .send({ @@ -406,13 +367,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('POST /api/sdk/add_global_proxy add global proxy', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); - await app.httpRequest() .post('/api/scene/') .send({ @@ -443,13 +397,6 @@ describe('test/app/controller/sdk.test.js', () => { }); it('POST /api/sdk/export_data export data', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); - await app.httpRequest() .post('/api/scene/') .send({ diff --git a/test/controller/data.test.js b/test/controller/data.test.js index 0ed7d7596..1260bdad4 100644 --- a/test/controller/data.test.js +++ b/test/controller/data.test.js @@ -7,18 +7,41 @@ const { describe('test/app/controller/data.test.js', () => { let ctx; + let projectUniqId; + let interfaceGroupUniqId; + let interfaceUniqId; beforeEach(async () => { ctx = app.mockContext(); - }); - it('GET /data/baz/api/path', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, + const [{ uniqId: _projectUniqId }] = await ctx.model.Project.bulkCreate([ + { + projectName: 'baz', + description: 'bazd', + }, ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, + projectUniqId = _projectUniqId; + const [{ uniqId: _interfaceGroupUniqId }] = await ctx.model.Group.bulkCreate([ + { + groupName: 'interfaceGroup1', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, ]); + interfaceGroupUniqId = _interfaceGroupUniqId; + const [{ uniqId: _interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ + { + projectUniqId, + pathname: 'api/path', + method: 'ALL', + description: 'description', + groupUniqId: interfaceGroupUniqId, + }, + ]); + interfaceUniqId = _interfaceUniqId; + }); + + it('GET /data/baz/api/path', async () => { await app.httpRequest() .post('/api/scene/') .send({ @@ -35,12 +58,6 @@ describe('test/app/controller/data.test.js', () => { }); it('GET /data/baz/api/path support query.__datahub_scene', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ @@ -65,12 +82,6 @@ describe('test/app/controller/data.test.js', () => { }); it('GET /data/baz/api/path project with empty', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); const body = await app.httpRequest() .get('/data/baz/api/path111'); assert(body.status === 400); @@ -79,12 +90,6 @@ describe('test/app/controller/data.test.js', () => { }); it('GET /data/baz/api/path project modify contextConfig', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ @@ -107,12 +112,6 @@ describe('test/app/controller/data.test.js', () => { }); it('GET /data/baz/api/path project modify proxy', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ @@ -152,12 +151,6 @@ describe('test/app/controller/data.test.js', () => { }); it('GET /data/baz/api/path get data with search', async () => { - const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ - { projectName: 'baz', description: 'bazd' }, - ]); - const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ - { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description' }, - ]); await app.httpRequest() .post('/api/scene/') .send({ diff --git a/test/fixtures/upload_data/interface.json b/test/fixtures/upload_data/interface.json index 1264a62e8..64aa1418a 100644 --- a/test/fixtures/upload_data/interface.json +++ b/test/fixtures/upload_data/interface.json @@ -1 +1,43 @@ -{"pathname":"delete","method":"ALL","projectUniqId":"4535875d-a5f3-4f55-a69d-b556f6cf231b","description":"delete data","contextConfig":{},"currentScene":"fail","proxyConfig":{"enabled":false,"proxyList":[{"proxyUrl":"http://127.0.0.1:7001"}]},"scenes":[{"sceneName":"success","data":{"success":true},"interfaceUniqId":"4535875d-a5f3-4f55-a69d-b556f6cf231b","uniqId":"42bb68e7-cb5c-42f0-8ab9-29177d7e4a31","createdAt":"2018-10-23T13:38:08.678Z","updatedAt":"2018-10-23T13:38:08.678Z"},{"sceneName":"fail","data":{"success":false},"interfaceUniqId":"4535875d-a5f3-4f55-a69d-b556f6cf231b","uniqId":"eb3bf9a1-c7cd-4523-a153-f9e6b1a15866","createdAt":"2018-10-23T13:38:15.833Z","updatedAt":"2018-10-23T13:38:15.833Z"}],"schemas":[{"type":"response","data":{"schemaData":{"type":"object","properties":{"success":{"type":"boolean","description":""}},"required":[]}},"interfaceUniqId":"4535875d-a5f3-4f55-a69d-b556f6cf231b","uniqId":"e4155c39-5df8-432e-a68c-c66f65103cd8","createdAt":"2018-10-23T13:38:19.333Z","updatedAt":"2018-10-23T13:38:19.333Z"}]} \ No newline at end of file +{ + "pathname": "delete", + "method": "ALL", + "projectUniqId": "4535875d-a5f3-4f55-a69d-b556f6cf231b", + "description": "delete data", + "contextConfig": {}, + "currentScene": "fail", + "proxyConfig": { "enabled": false, "proxyList": [{ "proxyUrl": "http://127.0.0.1:7001" }] }, + "scenes": [ + { + "sceneName": "success", + "data": { "success": true }, + "interfaceUniqId": "4535875d-a5f3-4f55-a69d-b556f6cf231b", + "uniqId": "42bb68e7-cb5c-42f0-8ab9-29177d7e4a31", + "createdAt": "2018-10-23T13:38:08.678Z", + "updatedAt": "2018-10-23T13:38:08.678Z" + }, + { + "sceneName": "fail", + "data": { "success": false }, + "interfaceUniqId": "4535875d-a5f3-4f55-a69d-b556f6cf231b", + "uniqId": "eb3bf9a1-c7cd-4523-a153-f9e6b1a15866", + "createdAt": "2018-10-23T13:38:15.833Z", + "updatedAt": "2018-10-23T13:38:15.833Z" + } + ], + "schemas": [ + { + "type": "response", + "data": { + "schemaData": { + "type": "object", + "properties": { "success": { "type": "boolean", "description": "" } }, + "required": [] + } + }, + "interfaceUniqId": "4535875d-a5f3-4f55-a69d-b556f6cf231b", + "uniqId": "e4155c39-5df8-432e-a68c-c66f65103cd8", + "createdAt": "2018-10-23T13:38:19.333Z", + "updatedAt": "2018-10-23T13:38:19.333Z" + } + ] +} diff --git a/test/fixtures/upload_data/project.json b/test/fixtures/upload_data/project.json index c183d13c8..345521c28 100644 --- a/test/fixtures/upload_data/project.json +++ b/test/fixtures/upload_data/project.json @@ -1 +1,88 @@ -[{"pathname":"add","method":"ALL","description":"add data","uniqId":"c34a7d2b-8c69-46ef-b0b8-ed38601a88d7","contextConfig":{"responseDelay":"1","responseHeaders":{}},"currentScene":"fail","proxyConfig":{},"scenes":[{"sceneName":"success","data":{"success":true},"interfaceUniqId":"c34a7d2b-8c69-46ef-b0b8-ed38601a88d7","uniqId":"666dcaa8-a0df-4392-8842-533f160ec5a5","createdAt":"2018-10-23T13:37:38.641Z","updatedAt":"2018-10-23T13:37:38.641Z"},{"sceneName":"fail","data":{"success":false},"interfaceUniqId":"c34a7d2b-8c69-46ef-b0b8-ed38601a88d7","uniqId":"02fa9075-d46b-4d77-a971-7add6ba85824","createdAt":"2018-10-23T13:37:45.537Z","updatedAt":"2018-10-23T13:37:45.537Z"}],"schemas":[{"type":"response","data":{"schemaData":{"type":"object","properties":{"success":{"type":"boolean","description":""}},"required":[]}},"interfaceUniqId":"c34a7d2b-8c69-46ef-b0b8-ed38601a88d7","uniqId":"38aef92d-80bd-42c8-b877-5fbc13a73a15","createdAt":"2018-10-23T13:37:48.824Z","updatedAt":"2018-10-23T13:37:48.824Z"}]},{"pathname":"delete","method":"ALL","description":"delete data","uniqId":"4535875d-a5f3-4f55-a69d-b556f6cf231b","contextConfig":{},"currentScene":"fail","proxyConfig":{"enabled":true,"proxyList":[{"proxyUrl":"http://127.0.0.1:7001"}]},"scenes":[{"sceneName":"success","data":{"success":true},"interfaceUniqId":"4535875d-a5f3-4f55-a69d-b556f6cf231b","uniqId":"42bb68e7-cb5c-42f0-8ab9-29177d7e4a31","createdAt":"2018-10-23T13:38:08.678Z","updatedAt":"2018-10-23T13:38:08.678Z"},{"sceneName":"fail","data":{"success":false},"interfaceUniqId":"4535875d-a5f3-4f55-a69d-b556f6cf231b","uniqId":"eb3bf9a1-c7cd-4523-a153-f9e6b1a15866","createdAt":"2018-10-23T13:38:15.833Z","updatedAt":"2018-10-23T13:38:15.833Z"}],"schemas":[{"type":"response","data":{"schemaData":{"type":"object","properties":{"success":{"type":"boolean","description":""}},"required":[]}},"interfaceUniqId":"4535875d-a5f3-4f55-a69d-b556f6cf231b","uniqId":"e4155c39-5df8-432e-a68c-c66f65103cd8","createdAt":"2018-10-23T13:38:19.333Z","updatedAt":"2018-10-23T13:38:19.333Z"}]}] \ No newline at end of file +[ + { + "pathname": "add", + "method": "ALL", + "description": "add data", + "uniqId": "c34a7d2b-8c69-46ef-b0b8-ed38601a88d7", + "contextConfig": { "responseDelay": "1", "responseHeaders": {} }, + "currentScene": "fail", + "proxyConfig": {}, + "scenes": [ + { + "sceneName": "success", + "data": { "success": true }, + "interfaceUniqId": "c34a7d2b-8c69-46ef-b0b8-ed38601a88d7", + "uniqId": "666dcaa8-a0df-4392-8842-533f160ec5a5", + "createdAt": "2018-10-23T13:37:38.641Z", + "updatedAt": "2018-10-23T13:37:38.641Z" + }, + { + "sceneName": "fail", + "data": { "success": false }, + "interfaceUniqId": "c34a7d2b-8c69-46ef-b0b8-ed38601a88d7", + "uniqId": "02fa9075-d46b-4d77-a971-7add6ba85824", + "createdAt": "2018-10-23T13:37:45.537Z", + "updatedAt": "2018-10-23T13:37:45.537Z" + } + ], + "schemas": [ + { + "type": "response", + "data": { + "schemaData": { + "type": "object", + "properties": { "success": { "type": "boolean", "description": "" } }, + "required": [] + } + }, + "interfaceUniqId": "c34a7d2b-8c69-46ef-b0b8-ed38601a88d7", + "uniqId": "38aef92d-80bd-42c8-b877-5fbc13a73a15", + "createdAt": "2018-10-23T13:37:48.824Z", + "updatedAt": "2018-10-23T13:37:48.824Z" + } + ] + }, + { + "pathname": "delete", + "method": "ALL", + "description": "delete data", + "uniqId": "4535875d-a5f3-4f55-a69d-b556f6cf231b", + "contextConfig": {}, + "currentScene": "fail", + "proxyConfig": { "enabled": true, "proxyList": [{ "proxyUrl": "http://127.0.0.1:7001" }] }, + "scenes": [ + { + "sceneName": "success", + "data": { "success": true }, + "interfaceUniqId": "4535875d-a5f3-4f55-a69d-b556f6cf231b", + "uniqId": "42bb68e7-cb5c-42f0-8ab9-29177d7e4a31", + "createdAt": "2018-10-23T13:38:08.678Z", + "updatedAt": "2018-10-23T13:38:08.678Z" + }, + { + "sceneName": "fail", + "data": { "success": false }, + "interfaceUniqId": "4535875d-a5f3-4f55-a69d-b556f6cf231b", + "uniqId": "eb3bf9a1-c7cd-4523-a153-f9e6b1a15866", + "createdAt": "2018-10-23T13:38:15.833Z", + "updatedAt": "2018-10-23T13:38:15.833Z" + } + ], + "schemas": [ + { + "type": "response", + "data": { + "schemaData": { + "type": "object", + "properties": { "success": { "type": "boolean", "description": "" } }, + "required": [] + } + }, + "interfaceUniqId": "4535875d-a5f3-4f55-a69d-b556f6cf231b", + "uniqId": "e4155c39-5df8-432e-a68c-c66f65103cd8", + "createdAt": "2018-10-23T13:38:19.333Z", + "updatedAt": "2018-10-23T13:38:19.333Z" + } + ] + } +] diff --git a/test/fixtures/upload_data/project_new.json b/test/fixtures/upload_data/project_new.json new file mode 100644 index 000000000..fddd2ab20 --- /dev/null +++ b/test/fixtures/upload_data/project_new.json @@ -0,0 +1,127 @@ +[ + { + "groupName": "默认分组", + "groupUniqId": "386248cb-b72c-46e0-a659-888a3b0bf804", + "interfaceList": [ + { + "pathname": "add", + "method": "ALL", + "description": "add data", + "uniqId": "7c0fb575-da52-4ff9-839c-6ca4123299ee", + "groupUniqId": "386248cb-b72c-46e0-a659-888a3b0bf804", + "currentScene": "fail", + "proxyConfig": {}, + "scenes": [ + { + "sceneName": "success", + "data": { + "success": true + }, + "interfaceUniqId": "7c0fb575-da52-4ff9-839c-6ca4123299ee", + "contextConfig": {}, + "uniqId": "eaeaf02d-57c6-4a21-ac4e-55fce49f72c8", + "format": "json", + "createdAt": "2022-02-27T03:39:47.492Z", + "updatedAt": "2022-02-27T03:39:47.492Z" + }, + { + "sceneName": "fail", + "data": { + "success": false + }, + "interfaceUniqId": "7c0fb575-da52-4ff9-839c-6ca4123299ee", + "contextConfig": {}, + "uniqId": "d2f35cf9-c15a-4a3c-b1c6-85f3a5d17e52", + "format": "json", + "createdAt": "2022-02-27T03:39:47.504Z", + "updatedAt": "2022-02-27T03:39:47.504Z" + } + ], + "schemas": [ + { + "type": "response", + "data": { + "schemaData": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "" + } + }, + "required": [] + } + }, + "interfaceUniqId": "7c0fb575-da52-4ff9-839c-6ca4123299ee", + "uniqId": "7900b0e2-68fd-43ed-bf3e-25f493f96e33", + "createdAt": "2022-02-27T03:39:47.520Z", + "updatedAt": "2022-02-27T03:39:47.520Z" + } + ] + }, + { + "pathname": "delete", + "method": "ALL", + "description": "delete data", + "uniqId": "34d5b818-a288-4d1c-9098-f82b4b88f04d", + "groupUniqId": "386248cb-b72c-46e0-a659-888a3b0bf804", + "currentScene": "fail", + "proxyConfig": { + "enabled": true, + "proxyList": [ + { + "proxyUrl": "http://127.0.0.1:7001" + } + ] + }, + "scenes": [ + { + "sceneName": "success", + "data": { + "success": true + }, + "interfaceUniqId": "34d5b818-a288-4d1c-9098-f82b4b88f04d", + "contextConfig": {}, + "uniqId": "0d47dc65-8da1-4759-847c-f21fb9a3e022", + "format": "json", + "createdAt": "2022-02-27T03:39:47.561Z", + "updatedAt": "2022-02-27T03:39:47.561Z" + }, + { + "sceneName": "fail", + "data": { + "success": false + }, + "interfaceUniqId": "34d5b818-a288-4d1c-9098-f82b4b88f04d", + "contextConfig": {}, + "uniqId": "66ac07e4-2aa7-4520-b0d7-b5a28155b2e7", + "format": "json", + "createdAt": "2022-02-27T03:39:47.569Z", + "updatedAt": "2022-02-27T03:39:47.569Z" + } + ], + "schemas": [ + { + "type": "response", + "data": { + "schemaData": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "" + } + }, + "required": [] + } + }, + "interfaceUniqId": "34d5b818-a288-4d1c-9098-f82b4b88f04d", + "uniqId": "b423defb-77bd-4960-8a97-ef07b8b04099", + "createdAt": "2022-02-27T03:39:47.579Z", + "updatedAt": "2022-02-27T03:39:47.579Z" + } + ] + } + ] + } +] \ No newline at end of file diff --git a/test/model/model.define.test.js b/test/model/model.define.test.js index 4497a07c2..c719e37ae 100644 --- a/test/model/model.define.test.js +++ b/test/model/model.define.test.js @@ -2,7 +2,7 @@ const { assert, app } = require('egg-mock/bootstrap'); -describe('test/app/model.define.js', () => { +describe('test/app/model.define.test.js', () => { let ctx; beforeEach(() => { ctx = app.mockContext(); @@ -24,6 +24,22 @@ describe('test/app/model.define.js', () => { assert(Object.keys(map).length === Object.keys(attributes).length); }); + it('Group model', () => { + const attributes = ctx.model.Group.tableAttributes; + const map = { + uniqId: 'UUID', + groupName: 'STRING', + groupType: 'STRING', + belongedUniqId: 'STRING', + createdAt: 'DATE', + updatedAt: 'DATE', + }; + for (const k in map) { + assert(map[k] === attributes[k].type.constructor.key); + } + assert(Object.keys(map).length === Object.keys(attributes).length); + }); + it('Interface model', () => { const attributes = ctx.model.Interface.tableAttributes; const map = { @@ -37,6 +53,7 @@ describe('test/app/model.define.js', () => { uniqId: 'UUID', createdAt: 'DATE', updatedAt: 'DATE', + groupUniqId: 'STRING', }; for (const k in map) { assert(map[k] === attributes[k].type.constructor.key); diff --git a/test/service/database.test.js b/test/service/database.test.js index 887915ea1..a2d5b3102 100644 --- a/test/service/database.test.js +++ b/test/service/database.test.js @@ -5,7 +5,7 @@ const rimraf = require('rimraf'); const path = require('path'); const { fs } = require('mz'); -describe('test/app/service/database.js', () => { +describe('test/app/service/database.test.js', () => { let ctx; beforeEach(async () => { diff --git a/test/service/group.test.js b/test/service/group.test.js new file mode 100644 index 000000000..d98acad58 --- /dev/null +++ b/test/service/group.test.js @@ -0,0 +1,95 @@ +'use strict'; + +const { assert, app } = require('egg-mock/bootstrap'); + +describe('test/app/service/group.test.js', () => { + let ctx; + let projectUniqId; + + beforeEach(async () => { + ctx = app.mockContext(); + const [{ uniqId }] = await ctx.model.Project.bulkCreate([ + { projectName: 'snapre', description: 'test' }, + ]); + projectUniqId = uniqId; + await ctx.model.Group.bulkCreate([ + { + groupName: 'group1', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, + { + groupName: 'group2', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, + ]); + }); + + it('queryGroupByBelongedUniqId', async () => { + const res = await ctx.service.group.queryGroupByBelongedUniqId({ + belongedUniqId: projectUniqId, + groupType: 'Interface', + }); + assert(res.length === 2); + assert(res[0] instanceof ctx.model.Group); + assert(res[1] instanceof ctx.model.Group); + assert(res[0].groupName === 'group1'); + assert(res[0].groupType === 'Interface'); + assert(res[0].belongedUniqId === projectUniqId); + assert(res[1].groupName === 'group2'); + assert(res[1].groupType === 'Interface'); + assert(res[1].belongedUniqId === projectUniqId); + }); + + it('createGroup', async () => { + let res = await ctx.service.group.createGroup({ + groupName: 'group1', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }); + const uniqId = res.uniqId; + res = await ctx.model.Group.findOne({ + where: { + uniqId, + }, + }); + assert(res.groupName === 'group1'); + assert(res.groupType === 'Interface'); + assert(res.belongedUniqId === projectUniqId); + assert(res instanceof ctx.model.Group); + }); + + it('updateGroupName', async () => { + let res = await ctx.service.group.queryGroupByBelongedUniqId({ + belongedUniqId: projectUniqId, + groupType: 'Interface', + }); + const uniqId = res[0].uniqId; + await ctx.service.group.updateGroupName({ + uniqId, + groupName: 'group3', + }); + res = await ctx.model.Group.findOne({ + where: { + uniqId, + }, + }); + assert(res.groupName === 'group3'); + assert(res instanceof ctx.model.Group); + }); + + it('deleteGroupByUniqId', async () => { + let res = await ctx.service.group.queryGroupByBelongedUniqId({ + belongedUniqId: projectUniqId, + groupType: 'Interface', + }); + const uniqId = res[0].uniqId; + await ctx.service.group.deleteGroupByUniqId({ uniqId }); + res = await ctx.model.Group.findAll(); + assert(res.length === 1); + assert(res[0].groupName === 'group2'); + assert(res[0].groupType === 'Interface'); + assert(res[0] instanceof ctx.model.Group); + }); +}); diff --git a/test/service/interface.test.js b/test/service/interface.test.js index 8bea79d88..90bd237c6 100644 --- a/test/service/interface.test.js +++ b/test/service/interface.test.js @@ -3,24 +3,43 @@ const path = require('path'); const { assert, app } = require('egg-mock/bootstrap'); -describe('test/app/service/interface.js', () => { +describe('test/app/service/interface.test.js', () => { let ctx; let projectUniqId; + let interfaceGroupUniqId1; beforeEach(async () => { ctx = app.mockContext(); - const { uniqId } = await ctx.model.Project.create( - { projectName: 'baz', description: 'bazd' } - ); - projectUniqId = uniqId; + + const [{ uniqId: _projectUniqId }] = await ctx.model.Project.bulkCreate([ + { + projectName: 'baz', + description: 'bazd', + }, + ]); + projectUniqId = _projectUniqId; + + const [{ uniqId: _interfaceGroupUniqId1 }] = await ctx.model.Group.bulkCreate([ + { + groupName: 'group1', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, + { + groupName: 'group2', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, + ]); + interfaceGroupUniqId1 = _interfaceGroupUniqId1; }); it('queryInterfaceByHTTPContext', async () => { await ctx.model.Interface.bulkCreate([ - { pathname: 'api/one', method: 'POST', projectUniqId, description: 'api one POST' }, - { pathname: 'api/one/1', method: 'GET', projectUniqId, description: 'api one 1 GET' }, - { pathname: 'api/one/:id', method: 'GET', projectUniqId, description: 'api one :id GET' }, - { pathname: 'api/two', method: 'ALL', projectUniqId, description: 'api two ALL' }, + { pathname: 'api/one', method: 'POST', projectUniqId, description: 'api one POST', groupUniqId: interfaceGroupUniqId1 }, + { pathname: 'api/one/1', method: 'GET', projectUniqId, description: 'api one 1 GET', groupUniqId: interfaceGroupUniqId1 }, + { pathname: 'api/one/:id', method: 'GET', projectUniqId, description: 'api one :id GET', groupUniqId: interfaceGroupUniqId1 }, + { pathname: 'api/two', method: 'ALL', projectUniqId, description: 'api two ALL', groupUniqId: interfaceGroupUniqId1 }, ]); let res = await ctx.service.interface.queryInterfaceByHTTPContext({ projectUniqId, @@ -65,10 +84,10 @@ describe('test/app/service/interface.js', () => { it('queryInterfaceByHTTPContext with shadowInterface', async () => { await ctx.model.Interface.bulkCreate([ - { pathname: 'api/one', method: 'POST', projectUniqId, description: 'api one POST' }, - { pathname: 'api/one/1', method: 'GET', projectUniqId, description: 'api one 1 GET' }, - { pathname: 'api/one/:id', method: 'GET', projectUniqId, description: 'api one :id GET' }, - { pathname: 'api/two', method: 'ALL', projectUniqId, description: 'api two ALL' }, + { pathname: 'api/one', method: 'POST', projectUniqId, description: 'api one POST', groupUniqId: interfaceGroupUniqId1 }, + { pathname: 'api/one/1', method: 'GET', projectUniqId, description: 'api one 1 GET', groupUniqId: interfaceGroupUniqId1 }, + { pathname: 'api/one/:id', method: 'GET', projectUniqId, description: 'api one :id GET', groupUniqId: interfaceGroupUniqId1 }, + { pathname: 'api/two', method: 'ALL', projectUniqId, description: 'api two ALL', groupUniqId: interfaceGroupUniqId1 }, ]); const res = await ctx.service.interface.queryInterfaceByHTTPContext({ projectUniqId, @@ -90,8 +109,8 @@ describe('test/app/service/interface.js', () => { it('queryInterfaceByHTTPContextAndPathRegexp', async () => { await ctx.model.Interface.bulkCreate([ - { pathname: 'api/one/:id', method: 'GET', projectUniqId, description: 'api one :id GET' }, - { pathname: 'api/one/:id/:sid', method: 'GET', projectUniqId, description: 'api one :id :sid GET' }, + { pathname: 'api/one/:id', method: 'GET', projectUniqId, description: 'api one :id GET', groupUniqId: interfaceGroupUniqId1 }, + { pathname: 'api/one/:id/:sid', method: 'GET', projectUniqId, description: 'api one :id :sid GET', groupUniqId: interfaceGroupUniqId1 }, ]); const res = await ctx.service.interface.queryInterfaceByHTTPContext({ projectUniqId, @@ -252,20 +271,32 @@ describe('test/app/service/interface.js', () => { }); it('deleteInterfaceByUniqId', async () => { - const { uniqId } = await ctx.model.Interface.create({ - projectUniqId, - pathname: 'api/one', - method: 'ALL', - description: 'api one', + const [{ uniqId: interfaceGroupUniqId }] = await ctx.model.Group.bulkCreate([ + { belongedUniqId: projectUniqId, groupName: 'interfaceGroup1', groupType: 'Interface' } + ]); + const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ + { projectUniqId, pathname: 'api/one', method: 'ALL', description: 'api one', groupUniqId: interfaceGroupUniqId }, + ]); + await ctx.service.scene.createScene({ + interfaceUniqId, + sceneName: 'default', + data: { id: 'default' }, }); + const deleteCount = await ctx.service.interface.deleteInterfaceByUniqId({ - uniqId, + uniqId: interfaceUniqId, }); const res = await ctx.model.Interface.findAll({ where: { projectUniqId, }, }); + const scenes = await ctx.model.Scene.findAll({ + where: { + interfaceUniqId, + }, + }); + assert(scenes.length === 0); assert(deleteCount === 1); assert(res.length === 0); }); @@ -276,14 +307,18 @@ describe('test/app/service/interface.js', () => { pathname: 'api/one', method: 'ALL', description: 'api one', + groupUniqId: interfaceGroupUniqId1, }); const res = await ctx.service.transfer.downloadProject({ uniqId: projectUniqId, }); - assert(res.data.length === 1); - assert(res.data[0].pathname === 'api/one'); - assert(res.data[0].method === 'ALL'); - assert(res.data[0].description === 'api one'); + + assert(res.dataGroupList[0].groupName === 'group1'); + assert(res.dataGroupList[0].groupUniqId === interfaceGroupUniqId1); + assert(res.dataGroupList.length === 2); + assert(res.dataGroupList[0].interfaceList[0].pathname === 'api/one'); + assert(res.dataGroupList[0].interfaceList[0].method === 'ALL'); + assert(res.dataGroupList[0].interfaceList[0].description === 'api one'); }); it('uploadProject', async () => { @@ -303,6 +338,23 @@ describe('test/app/service/interface.js', () => { assert(interfaces[1].description === 'delete data'); }); + it('uploadProjectNewData', async () => { + const res = await app.httpRequest() + .post('/api/project/upload') + .attach('file', path.join(__dirname, '..', 'fixtures/upload_data/', 'project_new.json')) + .expect(200); + + assert(res.body.success === true); + + const interfaces = await ctx.model.Interface.findAll(); + + assert(interfaces.length === 2); + assert(interfaces[0].pathname === 'add'); + assert(interfaces[0].description === 'add data'); + assert(interfaces[1].pathname === 'delete'); + assert(interfaces[1].description === 'delete data'); + }); + it('uploadProject swagger.json', async () => { const res = await app.httpRequest() .post('/api/project/upload') diff --git a/test/service/project.test.js b/test/service/project.test.js index 1b7469ac3..dd55e1f6b 100644 --- a/test/service/project.test.js +++ b/test/service/project.test.js @@ -2,7 +2,7 @@ const { assert, app } = require('egg-mock/bootstrap'); -describe('test/app/service/project.js', () => { +describe('test/app/service/project.test.js', () => { let ctx; beforeEach(async () => { @@ -51,6 +51,15 @@ describe('test/app/service/project.js', () => { uniqId, }, }); + const defaultInterfaceGroup = await ctx.model.Group.findOne({ + where: { + belongedUniqId: res.uniqId, + groupType: 'Interface', + } + }); + assert(defaultInterfaceGroup.groupName === ctx.gettext('defaultGroupName')); + assert(defaultInterfaceGroup.groupType === 'Interface'); + assert(defaultInterfaceGroup instanceof ctx.model.Group); assert(res.projectName === 'cprojectName'); assert(res.description === 'cdescription'); assert(res instanceof ctx.model.Project); @@ -76,13 +85,49 @@ describe('test/app/service/project.js', () => { }); it('deleteProjectByUniqId', async () => { + const [{ uniqId: projectUniqId }] = await ctx.model.Project.bulkCreate([ + { projectName: 'qwer', description: 'qwert', globaProxy: 'http://127.0.0.1' }, + ]); + const [{ uniqId: interfaceGroupUniqId }] = await ctx.model.Group.bulkCreate([ + { belongedUniqId: projectUniqId, groupName: 'interfaceGroup1', groupType: 'Interface' } + ]); + const [{ uniqId: interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ + { projectUniqId, pathname: 'api/path', method: 'ALL', description: 'description', groupUniqId: interfaceGroupUniqId }, + ]); + await ctx.service.scene.createScene({ + interfaceUniqId, + sceneName: 'default', + data: { id: 'default' }, + }); let res = await ctx.service.project.queryAllProject(); - const uniqId = res[0].uniqId; + const uniqId = res[2].uniqId; await ctx.service.project.deleteProjectByUniqId({ uniqId }); res = await ctx.model.Project.findAll(); - assert(res.length === 1); - assert(res[0].projectName === 'qux'); - assert(res[0].description === 'quxd'); + const interfaceGroups = await ctx.model.Group.findAll({ + where: { + belongedUniqId: uniqId, + groupType: 'Interface', + }, + }); + const interfaces = await ctx.model.Interface.findAll({ + where: { + projectUniqId: uniqId, + }, + }); + const scenes = await ctx.model.Scene.findAll({ + where: { + interfaceUniqId, + }, + }); + assert(interfaceGroups.length === 0); + assert(interfaces.length === 0); + assert(scenes.length === 0); + assert(res.length === 2); + assert(res[0].projectName === 'baz'); + assert(res[0].description === 'bazd'); assert(res[0] instanceof ctx.model.Project); + assert(res[1].projectName === 'qux'); + assert(res[1].description === 'quxd'); + assert(res[1] instanceof ctx.model.Project); }); }); diff --git a/test/service/scene.test.js b/test/service/scene.test.js index 759f8f823..0760d8fcc 100644 --- a/test/service/scene.test.js +++ b/test/service/scene.test.js @@ -3,25 +3,45 @@ const path = require('path'); const { assert, app } = require('egg-mock/bootstrap'); -describe('test/app/service/scene.js', () => { +describe('test/app/service/scene.test.js', () => { let ctx; let projectUniqId; + let interfaceGroupUniqId; let interfaceUniqId; beforeEach(async () => { ctx = app.mockContext(); - const { uniqId: pid } = await ctx.model.Project.create( - { projectName: 'baz', description: 'bazd' } - ); - projectUniqId = pid; - const { uniqId: iid } = await ctx.model.Interface.create( - { projectUniqId, pathname: 'api/one', method: 'ALL', description: '' } - ); - interfaceUniqId = iid; + + const [{ uniqId: _projectUniqId }] = await ctx.model.Project.bulkCreate([ + { + projectName: 'baz', + description: 'bazd', + }, + ]); + projectUniqId = _projectUniqId; + const [{ uniqId: _interfaceGroupUniqId }] = await ctx.model.Group.bulkCreate([ + { + groupName: 'interfaceGroup1', + groupType: 'Interface', + belongedUniqId: projectUniqId, + }, + ]); + interfaceGroupUniqId = _interfaceGroupUniqId; + const [{ uniqId: _interfaceUniqId }] = await ctx.model.Interface.bulkCreate([ + { + projectUniqId, + pathname: 'api/one', + method: 'ALL', + description: 'description', + groupUniqId: interfaceGroupUniqId, + }, + ]); + interfaceUniqId = _interfaceUniqId; }); describe('query scene', () => { let sceneUniqIdOne; + beforeEach(async () => { const res = await ctx.model.Scene.bulkCreate([ { interfaceUniqId, sceneName: 'success', contextConfig: {}, data: { id: 'success' } }, @@ -29,6 +49,7 @@ describe('test/app/service/scene.js', () => { ]); sceneUniqIdOne = res[0].uniqId; }); + it('querySceneByInterfaceUniqId', async () => { const res = await ctx.service.scene.querySceneByInterfaceUniqId({ interfaceUniqId, @@ -76,7 +97,7 @@ describe('test/app/service/scene.js', () => { }); }); - describe('opetation', () => { + describe('operation', () => { let sceneUniqIdOne; beforeEach(async () => { const res = await ctx.model.Scene.bulkCreate([ @@ -154,7 +175,7 @@ describe('test/app/service/scene.js', () => { const scenes = await ctx.model.Scene.findAll({ where: { - interfaceUniqId, + interfaceUniqId: res.body.newInterfaceUniqId, }, }); diff --git a/test/service/schema.test.js b/test/service/schema.test.js index 883734a38..62f2735e8 100644 --- a/test/service/schema.test.js +++ b/test/service/schema.test.js @@ -2,7 +2,7 @@ const { assert, app } = require('egg-mock/bootstrap'); -describe('test/app/service/schema.js', () => { +describe('test/app/service/schema.test.js', () => { let ctx; let projectUniqId; let interfaceUniqId; diff --git a/view/icon/empty.svg b/view/icon/empty.svg new file mode 100644 index 000000000..69c2569df --- /dev/null +++ b/view/icon/empty.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/view/src/app.less b/view/src/app.less index cd3b36a27..a23d87cbc 100644 --- a/view/src/app.less +++ b/view/src/app.less @@ -48,6 +48,14 @@ body { } } +:global(.ant-modal-content) { + border-radius: 4px; +} + +:global(.ant-modal-header) { + border-radius: 4px 4px 0 0; +} + @media only screen and (max-width: 500px) { .footer-side-items, .main-content { diff --git a/view/src/common/style/util.less b/view/src/common/style/util.less index 39880b723..0d91cfa54 100644 --- a/view/src/common/style/util.less +++ b/view/src/common/style/util.less @@ -1,11 +1,11 @@ .clearfix { - .clearfix() + .clearfix(); } .ellipsis { - .ellipsis() + .ellipsis(); } .text-capitalize { - .capitalize() -} \ No newline at end of file + .capitalize(); +} diff --git a/view/src/components/Experiment.jsx b/view/src/components/Experiment.jsx index 0240b396a..b4e1cdf7d 100644 --- a/view/src/components/Experiment.jsx +++ b/view/src/components/Experiment.jsx @@ -66,7 +66,7 @@ class Experiment extends Component { }); }; - render() { + render () { return (
+ ?
{formatMessage('sceneList.responseDelayShowInfo')}: {contextConfig.responseDelay}s @@ -238,7 +240,9 @@ class InterfaceSceneList extends Component { ? : }
+ { this.renderSceneList() } +
+ + } -
    - { this.renderInterfaceList() } -
+ { this.renderInterfaceList() } + +
); diff --git a/view/src/components/InterfaceList.less b/view/src/components/InterfaceList.less index 45142e6ec..6c1cb6609 100644 --- a/view/src/components/InterfaceList.less +++ b/view/src/components/InterfaceList.less @@ -14,11 +14,43 @@ width: 100%; } + .add-button { + .add-btn { + font-size: 24px; + color: #1890ff; + cursor: pointer; + margin-top: 4px; + } + } + + .interface-group-collapse { + height: calc(100vh - 170px); + overflow-y: auto; + + .interface-group-collapse-panel { + &:hover { + .interface-group-control { + opacity: 1; + } + } + + .interface-group-control { + transition: opacity .5s linear; + opacity: 0; + margin-right: 4px; + font-size: 16px; + cursor: pointer; + + .interface-group-control-edit { + margin-right: 4px; + } + } + } + } + ul { padding: 0; - height: calc(100vh - 100px); overflow-y: auto; - padding-bottom: 110px; .is-compact-view { min-height: 45px; @@ -58,15 +90,10 @@ li { cursor: pointer; position: relative; - padding: 12px 20px 12px 16px; - border-top: 1px solid rgba(0, 0, 0, .05); + padding: 12px 12px 12px 16px; min-height: 100px; overflow: hidden; - &:last-child { - border-bottom: 1px solid rgba(0, 0, 0, .05); - } - &:hover { .interface-control { opacity: 1; @@ -83,7 +110,7 @@ right: 14px; transition: opacity .5s linear; opacity: 0; - margin-right: 5px; + margin-right: 4px; } } @@ -92,7 +119,7 @@ opacity: 1; } background: linear-gradient( - to right, #1890ff 0, #1890ff 6px, #fff 4px, #fff 100% + to right, #1890ff 0, #1890ff 6px, #f7f7f7 4px, #f7f7f7 100% ) no-repeat; } @@ -123,4 +150,39 @@ font-size: 16px; cursor: pointer; } + + :global(.ant-collapse-ghost > .ant-collapse-item > .ant-collapse-content > .ant-collapse-content-box) { + padding: 0; + } + + :global(.ant-input) { + border-radius: 4px; + } + + :global(.ant-input-search > .ant-input-group > .ant-input-group-addon:last-child .ant-input-search-button) { + border-radius: 0 4px 4px 0; + border-left-style: none; + width: 30px; + } +} + +.popover-content { + :global(.ant-popover-inner-content) { + padding: 0; + } +} + + +.add-item { + list-style-type: none; + padding: 8px 0; + + li { + padding: 4px 24px; + cursor: pointer; + + &:hover { + background-color: #f5f5f5; + } + } } diff --git a/view/src/components/RealTimeDetail.jsx b/view/src/components/RealTimeDetail.jsx index ccebb2c3c..11a57cb1e 100644 --- a/view/src/components/RealTimeDetail.jsx +++ b/view/src/components/RealTimeDetail.jsx @@ -30,9 +30,9 @@ function SaveSceneFormComponent (props) { visible, onCancel, onOk, - form, loading, } = props; + const [form] = Form.useForm(); const formatMessage = id => props.intl.formatMessage({ id }); let defaultInterface = ''; const projectName = window.context && window.context.projectName; @@ -53,18 +53,18 @@ function SaveSceneFormComponent (props) { cancelText={formatMessage('common.cancel')} onCancel={onCancel} onOk={() => { - form.validateFields((err, values) => { - if (err) { - message.warn(formatMessage('common.input.invalid')); - return; - } + form.validateFields().then(values => { onOk(values); + }).catch(errorInfo => { + message.warn(formatMessage('common.input.invalid')); + return; }); }} confirmLoading={loading} >
diff --git a/view/src/components/forms/GroupForm.jsx b/view/src/components/forms/GroupForm.jsx new file mode 100644 index 000000000..5b6056870 --- /dev/null +++ b/view/src/components/forms/GroupForm.jsx @@ -0,0 +1,62 @@ +import React from 'react'; + +import { + Modal, + Form, + Input, + message, +} from 'antd'; + +import { + injectIntl, +} from 'react-intl'; + +export default injectIntl(GroupFormComponent); + +function GroupFormComponent (props) { + const { + visible, + onCancel, + onOk, + confirmLoading, + } = props; + const [form] = Form.useForm(); + const formatMessage = id => props.intl.formatMessage({ id }); + return { + form.validateFields().then(values => { + onOk(values); + }).catch(errorInfo => { + message.warn(formatMessage('common.input.invalid')); + return; + }); + }} + confirmLoading={confirmLoading} + > + + + + + +
; +} diff --git a/view/src/components/forms/InterfaceForm.jsx b/view/src/components/forms/InterfaceForm.jsx index 40ae0e09a..91f3e2807 100644 --- a/view/src/components/forms/InterfaceForm.jsx +++ b/view/src/components/forms/InterfaceForm.jsx @@ -23,9 +23,17 @@ function InterfaceFormComponent (props) { onOk, confirmLoading, stageData, + groupList, } = props; const [form] = Form.useForm(); + form.setFieldsValue({ + pathname: stageData && stageData.pathname, + description: stageData && stageData.description, + method: stageData && stageData.method || 'ALL', + groupUniqId: (stageData && stageData.groupUniqId) || (groupList[0] && groupList[0].uniqId), + }); const formatMessage = id => props.intl.formatMessage({ id }); + return DELETE + + + ; } diff --git a/view/src/components/forms/SceneForm.jsx b/view/src/components/forms/SceneForm.jsx index c272d82e7..6c69e0fe7 100644 --- a/view/src/components/forms/SceneForm.jsx +++ b/view/src/components/forms/SceneForm.jsx @@ -1,5 +1,12 @@ import React, { useRef } from 'react'; -import { Form, Input, Modal, message, Collapse, Radio } from 'antd'; +import { + Form, + Input, + Modal, + message, + Collapse, + Radio, +} from 'antd'; import { injectIntl } from 'react-intl'; import { UnControlled as CodeMirror, jsonCodeMirrorOptions, jsCodeMirrorOptions } from '../../common/codemirror'; @@ -15,7 +22,15 @@ const getCode = (stageData) => { }; function SceneFormComponent (props) { - const { visible, onCancel, onOk, onChangeMode, confirmLoading, stageData, experimentConfig } = props; + const { + visible, + onCancel, + onOk, + onChangeMode, + confirmLoading, + stageData, + experimentConfig, + } = props; const [form] = Form.useForm(); let showResInfo = false; if (stageData.contextConfig) { diff --git a/view/src/locale/en_US.js b/view/src/locale/en_US.js index 98690fe8c..848db753c 100644 --- a/view/src/locale/en_US.js +++ b/view/src/locale/en_US.js @@ -38,7 +38,7 @@ const enUS = { 'project.interfaceList': 'API list', 'project.realtimeList': 'Realtime snapshot', - 'project.createApi': 'Please add API', + 'project.createApi': 'No data, please add API', 'project.create': 'Create a hub', 'project.update': 'Update hub', 'project.name': 'Hub name', @@ -49,6 +49,14 @@ const enUS = { 'project.globalProxy': 'Global proxy', 'project.globalProxy.invalid': 'Please input URL', + 'group.create': 'Add group', + 'group.updateName': 'Modify group', + 'group.delete': 'Delete group', + 'group.newGroupInputPlaceholder': 'Please input group name', + 'group.selectGroup': 'Please select group', + 'group.deleteGroupSuccess': 'Group deleted successfully.', + 'group.deleteInterfaceGroupWarning': 'There are interfaces in the group that have not been removed. Please delete or transfer the interfaces first.', + 'interfaceList.addInterface': 'Add API', 'interfaceList.updateInterface': 'Update API', 'interfaceList.searchInterface': 'Search API', diff --git a/view/src/locale/zh_CN.js b/view/src/locale/zh_CN.js index 1d3d2db18..2983b8a14 100644 --- a/view/src/locale/zh_CN.js +++ b/view/src/locale/zh_CN.js @@ -38,7 +38,7 @@ const zhCN = { 'project.interfaceList': '接口列表', 'project.realtimeList': '实时快照', - 'project.createApi': '请添加接口', + 'project.createApi': '暂无数据,请先添加接口', 'project.create': '创建新项目', 'project.update': '更新项目', 'project.name': '项目名称', @@ -49,6 +49,14 @@ const zhCN = { 'project.globalProxy': '全局项目代理', 'project.globalProxy.invalid': '请输入 URL', + 'group.create': '添加分组', + 'group.updateName': '更新分组', + 'group.delete': '删除分组', + 'group.newGroupInputPlaceholder': '请输入分组名', + 'group.selectGroup': '请选择分组', + 'group.deleteGroupSuccess': '分组删除成功', + 'group.deleteInterfaceGroupWarning': '分组中有接口未移出,请先删除或转移接口', + 'interfaceList.addInterface': '添加接口', 'interfaceList.updateInterface': '修改接口', 'interfaceList.searchInterface': '搜索接口', diff --git a/view/src/pages/Document.jsx b/view/src/pages/Document.jsx index 2ee78c65e..ae7e3d5d8 100644 --- a/view/src/pages/Document.jsx +++ b/view/src/pages/Document.jsx @@ -46,6 +46,7 @@ import { sceneService, schemaService, interfaceService, + groupService, } from '../service'; import './Document.less'; @@ -53,39 +54,31 @@ import './Document.less'; const Sider = Layout.Sider; const Content = Layout.Content; +const { uniqId: projectUniqId } = window.context || {}; + class Document extends React.Component { state = { interfaceList: [], + interfaceGroupList: [], selectedInterface: {}, schemaData: [], sceneList: [], currentScene: '', + groupList: [], } - // Initialize data based on hash value - getIndexByHash (res) { - const params = queryParse(location.hash); - - if (!res.success || !res.data) { - return 0; - } - - for (let i = 0; i < res.data.length; i++) { - const item = res.data[i]; + getDefaultSelectedInterface (interfaceGroupList) { + if (!interfaceGroupList.length) return {}; - if (item.method === params.method && - item.pathname === decodeURI(params.pathname)) { - return i; - } - } + const interfaceGroup = interfaceGroupList.find(item => !!item.interfaceList.length); - return 0; + return interfaceGroup ? interfaceGroup.interfaceList[0] : {}; } async componentDidMount () { + await this.fetchGroupList(); const interfaceRes = await this.initInterfaceList(); - const index = this.getIndexByHash(interfaceRes); - const selectedInterface = (interfaceRes.data && interfaceRes.data[index]) || {}; + const selectedInterface = this.getDefaultSelectedInterface(interfaceRes.data.interfaceGroupList) || {}; let schemaRes = {}; let sceneRes = {}; if (selectedInterface.uniqId) { @@ -93,7 +86,8 @@ class Document extends React.Component { sceneRes = await sceneService.getSceneList({ interfaceUniqId: selectedInterface.uniqId }); } this.setState({ - interfaceList: interfaceRes.data || [], + interfaceList: interfaceRes.data.interfaceList || [], + interfaceGroupList: interfaceRes.data.interfaceGroupList || [], selectedInterface, schemaData: schemaRes.data || [], sceneList: sceneRes.data || [], @@ -115,6 +109,16 @@ class Document extends React.Component { } } + fetchGroupList = async () => { + const res = await groupService.getGroupList({ + belongedUniqId: projectUniqId, + groupType: 'Interface', + }); + this.setState({ + groupList: res.data || [], + }); + } + setSelectedInterface = async (uniqId) => { const selectedInterface = this.state.interfaceList.find(i => i.uniqId === uniqId) || {}; @@ -170,7 +174,8 @@ class Document extends React.Component { selectedInterface={this.state.selectedInterface} setSelectedInterface={this.setSelectedInterface} experimentConfig={this.props.experimentConfig} - interfaceList={this.state.interfaceList} + interfaceGroupList={this.state.interfaceGroupList} + groupList={this.state.groupList} /> diff --git a/view/src/pages/Project.jsx b/view/src/pages/Project.jsx index 23a6fd2b1..a28d8e003 100644 --- a/view/src/pages/Project.jsx +++ b/view/src/pages/Project.jsx @@ -12,9 +12,9 @@ import { import { Affix, - Alert, Layout, Tabs, + Empty, } from 'antd'; import InterfaceList from '../components/InterfaceList'; @@ -25,12 +25,9 @@ import RealTimeDetail from '../components/RealTimeDetail'; import { interfaceService, + groupService, } from '../service'; -import { - queryParse, -} from '../common/helper'; - import './Project.less'; const TabPane = Tabs.TabPane; @@ -40,44 +37,41 @@ const Content = Layout.Content; const realTimeTabSymbol = 'REALTIME_TAB_KEY'; const interfaceTabSymbol = 'INTERFACE_TAB_KEY'; +const { uniqId: projectUniqId } = window.context || {}; + class Project extends React.Component { state = { interfaceList: [], + interfaceGroupList: [], // 接口分组数据 selectedInterface: {}, - + groupList: [], subRouter: interfaceTabSymbol, - REALTIME_MAXLINE: 100, + REALTIME_MAX_LINE: 100, realTimeDataList: [], realTimeIndex: 0, showRightSide: false, } - getIndexByHash (res) { - const params = queryParse(location.hash); + getDefaultSelectedInterface (interfaceGroupList) { + if (!interfaceGroupList.length) return {}; - if (!res.success) return 0; - - for (let i = 0; i < res.data.length; i++) { - const item = res.data[i]; - - if (item.method === params.method && - item.pathname === decodeURI(params.pathname)) { - return i; - } - } + const interfaceGroup = interfaceGroupList.find(item => !!item.interfaceList.length); - return 0; + return interfaceGroup ? interfaceGroup.interfaceList[0] : {}; } async componentDidMount () { this.initRealTimeDataList(); + const groupListData = await this.fetchGroupList(); const res = await this.fetchInterfaceList(); - const index = this.getIndexByHash(res); + const defaultSelectedInterface = this.getDefaultSelectedInterface(res.data.interfaceGroupList); this.setState({ - interfaceList: res.data || [], - selectedInterface: (res.data && res.data[index]) || {}, + interfaceList: res.data.interfaceList || [], + interfaceGroupList: res.data.interfaceGroupList || [], + selectedInterface: defaultSelectedInterface || {}, + groupList: groupListData.data || [], }); } @@ -85,25 +79,40 @@ class Project extends React.Component { return await interfaceService.getInterfaceList(); } + fetchGroupList = async () => { + return await groupService.getGroupList({ + belongedUniqId: projectUniqId, + groupType: 'Interface', + }); + } + updateInterfaceList = async () => { + const groupListData = await this.fetchGroupList(); const res = await this.fetchInterfaceList(); this.setState({ - interfaceList: res.data || [], + interfaceList: res.data.interfaceList || [], + interfaceGroupList: res.data.interfaceGroupList || [], selectedInterface: this.getSelectedInterface(res.data), + groupList: groupListData.data || [], }); } getSelectedInterface = data => { - if (!Array.isArray(data)) return {}; + if (!Array.isArray(data.interfaceList)) return {}; let result = null; if (this.state.selectedInterface && this.state.selectedInterface.uniqId) { - result = data.find(value => { + result = data.interfaceList.find(value => { return value.uniqId === this.state.selectedInterface.uniqId; }); } - return result || data[0]; + + if (!result) { + result = this.getDefaultSelectedInterface(data.interfaceGroupList); + } + + return result; } setSelectedInterface = async (uniqId) => { @@ -125,7 +134,7 @@ class Project extends React.Component { logger(data); const newData = [ ...this.state.realTimeDataList, - ].slice(0, this.state.REALTIME_MAXLINE - 1); + ].slice(0, this.state.REALTIME_MAX_LINE - 1); newData.unshift(data); this.setState({ realTimeDataList: newData, @@ -188,8 +197,9 @@ class Project extends React.Component { selectedInterface={this.state.selectedInterface} setSelectedInterface={this.setSelectedInterface} experimentConfig={this.props.experimentConfig} - interfaceList={this.state.interfaceList} + interfaceGroupList={this.state.interfaceGroupList} updateInterfaceList={this.updateInterfaceList} + groupList={this.state.groupList} /> : (
-
) diff --git a/view/src/pages/Project.less b/view/src/pages/Project.less index 98d713086..4826e77f4 100644 --- a/view/src/pages/Project.less +++ b/view/src/pages/Project.less @@ -1,7 +1,24 @@ .add-api-hint { - margin-top: 30px; + margin-top: 128px; } .project-sider .ant-tabs-bar { padding-left: 6px; } + +:global(.ant-tabs-nav) { + padding-left: 16px; +} + +:global(.ant-collapse > .ant-collapse-item > .ant-collapse-header) { + font-weight: 400; + color: #666; +} + +:global(.ant-collapse-header-text) { + width: 94%; +} + +:global(.ant-btn) { + border-radius: 4px; +} diff --git a/view/src/service/group.js b/view/src/service/group.js new file mode 100644 index 000000000..0e966cbf6 --- /dev/null +++ b/view/src/service/group.js @@ -0,0 +1,23 @@ +import request from '../common/request'; + +export async function getGroupList ({ belongedUniqId, groupType }) { + return request(`/api/group?belongedUniqId=${belongedUniqId}&groupType=${groupType}`, 'GET'); +}; + +export async function createGroup ({ belongedUniqId, groupName, groupType }) { + return request('/api/group', 'POST', { + belongedUniqId, + groupName, + groupType, + }); +}; + +export async function updateGroupName ({ uniqId, groupName }) { + return request(`/api/group/${uniqId}`, 'PUT', { + groupName, + }); +}; + +export async function deleteGroup ({ uniqId }) { + return request(`/api/group/${uniqId}`, 'DELETE'); +}; diff --git a/view/src/service/index.js b/view/src/service/index.js index bb40ac653..37f51f2a3 100644 --- a/view/src/service/index.js +++ b/view/src/service/index.js @@ -2,11 +2,13 @@ import * as interfaceService from './interface'; import * as projectService from './project'; import * as sceneService from './scene'; import * as schemaService from './schema'; +import * as groupService from './group'; export { projectService, interfaceService, sceneService, schemaService, + groupService, }; diff --git a/view/src/service/interface.js b/view/src/service/interface.js index e1e9393da..f8097194d 100644 --- a/view/src/service/interface.js +++ b/view/src/service/interface.js @@ -10,12 +10,13 @@ export async function getOneInterface ({ uniqId }) { return request(`/api/interface/${uniqId}`, 'GET'); }; -export async function createInterface ({ pathname, description, method = 'GET' }) { +export async function createInterface ({ pathname, description, method = 'GET', groupUniqId }) { return request('/api/interface', 'POST', { projectUniqId, pathname, description, method, + groupUniqId, }); }; @@ -28,7 +29,7 @@ export async function updateAllProxy ({ projectUniqId, enabled, method = 'POST' export async function updateInterface ({ uniqId, ...payload }) { const fileds = [ - 'pathname', 'description', 'method', + 'pathname', 'description', 'method', 'groupUniqId', 'currentScene', 'proxyConfig', ]; const postData = {};