feat: sync from RAP2@alibaba-internal to RAP2@CE

bryanYao-master 2.2.0
huoyong.msb 5 years ago
parent 9d6ace20af
commit 0b71d7fa0e

@ -0,0 +1,26 @@
server {
listen 80 default_server;
server_name www.taobao.com;
location / {
proxy_pass http://127.0.0.1:6001;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
error_page 400 403 405 408 410 411 412 413 414 415 /error.html;
error_page 501 502 503 506 /error.html;
error_page 404 /404.json;
error_page 500 /500.json;
}
server {
listen 80;
server_name status.taobao.com;
tmd off;
location = /nginx_status {
stub_status on;
}
}

@ -1,2 +0,0 @@
ALTER TABLE `Properties`
MODIFY COLUMN `type` enum('String','Number','Boolean','Object','Array','Function','RegExp','Null') NOT NULL;

@ -1,11 +0,0 @@
ALTER TABLE repositories_collaborators
DROP COLUMN createdat ;
ALTER TABLE repositories_collaborators
DROP COLUMN updatedat ;
ALTER TABLE repositories_members
DROP COLUMN createdat ;
ALTER TABLE repositories_members
DROP COLUMN updatedat ;

@ -1,5 +0,0 @@
ALTER TABLE `properties`
ADD COLUMN `pos` INT(10) NULL DEFAULT 2;
ALTER TABLE `interfaces`
ADD COLUMN `status` INT(10) NULL DEFAULT 200;

@ -1,2 +0,0 @@
ALTER TABLE Interfaces
MODIFY COLUMN `method` VARCHAR(256) NOT NULL;

@ -1,2 +0,0 @@
ALTER TABLE `Properties`
ADD COLUMN `required` TINYINT(1) NOT NULL DEFAULT 0;

@ -0,0 +1,13 @@
CREATE TABLE `history_log` (
`id` int NOT NULL AUTO_INCREMENT,
`entityType` int NOT NULL,
`entityId` int NOT NULL,
`changeLog` text COLLATE utf8mb4_unicode_ci NOT NULL,
`relatedJSONData` text COLLATE utf8mb4_unicode_ci,
`userId` int NOT NULL,
`createdAt` datetime NOT NULL,
`updatedAt` datetime NOT NULL,
`deletedAt` datetime DEFAULT NULL,
CONSTRAINT `hisotry_log_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `Users` (`id`) ON UPDATE CASCADE,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

@ -33,13 +33,6 @@ services:
# production / development
- NODE_ENV=production
# email 如果想让邮箱找回密码能力生效需要配置邮件发送
- MAIL_HOST=smtp.aliyun.com
- MAIL_PORT=465
- MAIL_USER=rap2org@service.alibaba.com
- MAIL_PASS=xxxxxx
- MAIL_SENDER=rap2org@service.alibaba.com
###### 'sleep 30 && node scripts/init' will drop the tables
###### RUN ONLY ONCE THEN REMOVE 'sleep 30 && node scripts/init'
command: /bin/sh -c 'node dispatch.js'

@ -0,0 +1,202 @@
## 2017.05.1505.26 计划
1. RAP1 数据迁移测试
2. 发布线上服务,自测在项目中的体验
3. 编写公开 API 的文档(注释)
4. 其他参与的同学参照 v2.1 需求和约定 http://gitlab.alibaba-inc.com/thx/rap2-delos/blob/master/docs/Design.md和仓库中的 TODO 任务注释
5. 陪产假期间在家办公,业务支持直接电话我
> TODO 消消乐
## 2017.06.1206.16 部署
**线上 RAP1 数据迁移基本完成,正在测试迁移数据。**
### 服务端 rap2-delos
1. 正式发布部署和迁移
### 前端 rap2-dolores
1. 正式发布部署和测试
## 2017.06.0506.09 部署
**v2.1 开发完成,部署基本完成。**
### 服务端 rap2-delos
1. 正式发布部署
1. 接入 KeyCenter
2. 优化 分离拥有的仓库和加入的仓库
3. 优化 分离拥有的组织和加入的组织
4. 新增 fetch 拦截插件
### 前端 rap2-dolores
1. 正式发布部署
1. 接入 线上统一登录
2. 接入 线上域名
2. 协同 服务端的『分离拥有的仓库和加入的仓库』
3. 协同 服务端的『分离拥有的组织和加入的组织』
4. 新增 支持查看其他用户的仓库
5. 其他零散代码、视觉、交互优化
1. 视觉 增加拥有者 icon
2. 交互 首页 新用户显示引导文案『新建仓库』
## 2017.05.3106.02
### 服务端 rap2-delos **2.1 开发完成**
1. 修复 数字属性、布尔属性、数组属性的解析和初始化
2. 修复 当前端发送 JSONP 请求,并且响应内容是字符串时,字符串响应再次执行 JSON.stringify(),导致响应内容格式错误
3. 增加 JSONSchema 接口 /app/mock/schema/:interfaceId
4. 完善 RAP1 迁移脚本
1. 修正 类型 array<number|string|object|boolean> => Array
2. 修正 模拟值 @mock=function(){} => Function
3. 修正 顺序值 $order => Array|+1: []
5. 完善 仓库接口测试页面,支持动态仓库 id
6. 新增 支持虚拟属性 __root__
7. 正式发布部署(未完)
1. 调整 Dockerfile 配置
2. 接入 VIPServer
3. 数据库上线
4. 接入 KeyCenter未完
### 前端 rap2-dolores
1. 增加 生成规则帮助链接
2. 增加 访问不存在仓库的编辑器时提示 404
3. 协同 后端的『修复 数字属性、布尔属性、数组属性的解析和初始化』
4. 增加 公开接口
5. 代码优化
1. 删除 遗留的无效注释
2. 删除 不再使用的 Fetching 组件
3. 增加 RModal 重定位截流
4. 完善 登陆时只使用 email 和 password丢弃其他属性非 BUS SSO 场景)
5. 删除 遗留的 corporation、product、grouping 代码
6. 完善 补全团队列表的 propTypes
7. 修复 不解析原始类型的初始值
8. 部署 暂时访问 daily 环境,上线后再恢复
6. 视觉优化
1. 增加 自动获得焦点:组织、仓库、模块、接口、属性、导入器、注册、登陆
2. 视觉 润色首页日志格式
3. 视觉 仓库列表和团队列表的最小高度为 10rem增大没有找到匹配数据时的字号
4. 恢复 团队成员头像
5. 增加 协同仓库的帮助信息
6. 增加 组件 Popover 支持自定义 width
7. 视觉 组件 MembersInput 默认底部外边距 10px
8. 视觉 润色表单 input 的宽度
9. 视觉 移除 .rapfont统一改用 react-icons
10. 视觉 润色接口编辑器
11. 新增 仓库编辑器初始加载时显示动画
7. 修复 属性类型 Number 并且初始值为 '' 时,被解析为随机字符串
8. 完善 删除团队、仓库、模块、接口时的确认提示
9. 新增 导入器支持格式化输入的 JSON
10. 修复 导入器重复调用 handleAddMemoryProperty() 丢失临时属性
## 2017.05.2205.26
### 服务端 rap2-delos
1. 支持 迁移 RAP1 数据(开发和本地调试完成,待线上验证)
2. 完善 jQuery 插件、Mock 插件、插件文档 public/libs/README.md
3. 增加 检测和提示仓库中的重复接口
4. 修复 初始化新模块时创建了重复的示例接口
5. 支持 仓库协同(即 RAP1 的项目路由,用于指定与哪些项目共享 mock 数据)
6. 修复 creatorId 必须是当前登录用户,不需要前端传入
7. 修复 测试用例创建的临时仓库没有及时移除
8. 重构 IDB 结构设计
1. 清理 历史遗留表 user、repository、module、interface、property、organization、organization_members、logger、notification
2. 新建 仓库协同表 repositories_collaborators
3. 新建 账户通知表 notifications
4. 新增 字段 organizations.visibility用于支持私有团队待前端支持
5. 新增 字段 repositories.visibility用于支持私有仓库待前端支持
9. 调整 接口 /app/get 的位置,从 routes/mock.js 分散到 routes/account|organization|repository.js
10. 修复 当创建者或拥有者已经不存在时,仓库列表和组织列表的总记录数错误
11. 支持 转移团队 /organization/transfer待前端支持
12. 支持 转移仓库 /repository/transfer待前端支持
13. 优化 获取单个仓库完整数据的性能
14. 完善 示例接口初始化时填充更多的 Mock 规则示例
### 前端 rap2-dolores
1. 修复 /app/plugin/:repositories 接收到无效 repositoryId 时报错
2. 调整 导航栏,我的仓库=>仓库,团队仓库=>组织
3. 增加 仓库/全部仓库
4. 增加 检测和提示仓库中的重复接口
5. 修复 『我创建和加入的团队』不应该有分页
6. 协同 后端的『仓库协同』
7. 视觉 润色仓库编辑器
8. 修复 仓库编辑权限的判断逻辑
## 2017.05.1505.19
### 服务端 rap2-delos
1. 修复 团队测试用例的用户 id 不存在
2. 修复 接口 /repository/joined 不排除自己拥有的项目
3. 完善 模拟数据接口 /app/mock/:repository/:method/:url
1. 支持响应多个仓库的数据
2. 支持不同的 http method
3. 完善相应的测试用例
4. 支持过滤重复仓库 id
5. 完善注释内容,增加关于直接通过 interface id 获取模板和数据的说明
6. 增加请求属性和响应属性的 Mock 模板
4. 重构 IDB 结构设计,为迁移 RAP1 数据做准备
1. 清理历史遗留表 corporation、product、grouping、project、page、action
2. 清理历史遗留字段 property.template、property.page、property.project、module.project
3. 利用 Sequelize 重构所有表之间的关联关系(代码更精简)
4. 修改所有外键的命名,风格统一为 modelId减少歧义
5. 调整所有涉及的模型、路由、测试用例和初始数据
5. 清理 历史 API 示例 HTML已经全部改为测试用例
6. 新增 前端插件适配 jQuery、Mock待测试
7. 完善 SQL 日志格式
8. 完善 生成数据模板时的异常日志格式
9. 完善 测试用例:用户、组织、仓库
### 前端 rap2-dolores
1. 新增 测试器 Tester未完
2. 引入 react-icons因为 iconfont 的质量参差不齐,在 React 中使用不方便
3. 完善 仓库列表、组织列表的视觉:废弃 table 布局,类型文案改为 <select>
4. 修复 组件 Popover 定位错误
5. 修复 进入仓库编辑器时先展示上一次编辑过的仓库,直到当前仓库的数据返回后才会更新
6. 修复 仓库数据返回之前,会展示不完整的静态内容
7. 引入 RCodeMirror作为接口属性导入工具的编辑器
8. 协同 后端的『重构 IDB 结构设计』
9. 优化 没有搜索到匹配的组织、仓库时的提示
## 2017.05.12
### 服务端 rap2-delos
1. 新增 Sequelize 持久化时字段值校验
2. 重构 初始数据,测试用户从 100000000 开始
3. 新增 按 name 或 id 搜索用户、仓库、组织
4. 修复 插件中的接口字段
5. 新增 属性增加生成规则字段 rule
6. 新增 所有响应 JSON 增加字段 update_date前端可以当作版本号进行版本检测
7. 新增 用户日志 /account/logger未完
8. 修复 更新仓库、组织时意外变更 owner 和 creator
9. 修复 仓库的字段 members 没有成员时返回 [null] 导致前端渲染报错
10. 完善 格式化 /app/mock/:repository/:url 的响应,方便前端阅读和调试
### 前端 rap2-dolores
1. 重构 整站交互和样式,导航固定为首页、我的仓库、团队仓库、状态
2. 重构 组织相关功能,代码和逻辑更清爽条理
3. 修正 全局计数器 fetching
4. 新增 成员输入组件 MembersInput
5. 重构 仓库相关功能,代码和逻辑更清爽条理
6. 放弃 样式组件 .panel改为 .card
7. 新增 用户日志(未完)
8. 修复 组件 build 后初始 state 为 null
9. 新增 表单验证(未完)
10. 引入 NProgress
11. 完善 表单输入一律禁用 spellcheck
12. 完善 整站开屏动画
13. 完善 预览 JSON 模板和 JSON 数据
14. 新增 RModal 组件,用于替换繁琐的 DialogController
15. 修复 接口测试时仓库 id 错误
## 2017.04.28
### 服务端 rap2-delos
1. 重构整个项目:目录结构、依赖包、代码规范 standardjs、pre-commit 检测
2. 增加测试用例 Account、Organization、Worksapce、Mock
3. v2.1 需求和约定 http://gitlab.alibaba-inc.com/thx/rap2-delos/blob/master/docs/Design.md
4. 安全修复:属性为死循环函数导致服务宕掉
### 前端 rap2-dolores
1. 接入集团统一登陆
2. 增加代码规范 standardjs、pre-commit 检测
3. 支持编辑模块、页面、接口

@ -8,15 +8,21 @@
"main": "dist/dispatch.js",
"scripts": {
"build": "rimraf -rf dist/ && tsc",
"test": "cross-env NODE_ENV=development cross-env TEST_MODE=true nyc mocha --exit",
"test": "cross-env NODE_ENV=development cross-env TEST_MODE=true nyc mocha test/**/*.js",
"check": "echo \"Checking...\" && tsc && npm run lint",
"dev": "cross-env NODE_ENV=development nodemon --watch scripts --watch dist dist/scripts/dev.js",
"create-db": "cross-env NODE_ENV=development node dist/scripts/init",
"create-db": "cross-env NODE_ENV=development node dist/scripts/initSchema",
"start": "cross-env NODE_ENV=production pm2 start dist/dispatch.js --name=rap-server-delos",
"lint": "echo \"TSLint checking...\" && tslint -c tslint.json --fix 'src/**/*.ts' 'src/**/*.tsx'",
"start:redis": "pm2 start redis-server --name redis-server",
"clean": "pm2 delete all",
"build-docker": "docker build --rm -f \"Dockerfile\" -t rapteam/rap2-delos:rap2org ."
"build-docker": "docker build --rm -f \"Dockerfile\" -t rapteam/rap2-delos:latest ."
},
"mocha": {
"timeout": 8000,
"slow": 200,
"exit": true,
"allowUncaught": true
},
"author": "bosn, nuysoft",
"license": "ISC",
@ -48,14 +54,14 @@
"node-fetch": "^2.6.0",
"node-print": "0.0.4",
"node-schedule": "^1.3.2",
"nodemailer": "^6.2.1",
"nodemailer": "^6.4.10",
"notevil": "^1.3.3",
"path-to-regexp": "^3.1.0",
"redis": "^3.0.2",
"reflect-metadata": "^0.1.13",
"request": "^2.88.2",
"request-promise": "^4.2.5",
"sequelize": "^5.21.5",
"sequelize": "^5.22.3",
"sequelize-typescript": "^1.1.0",
"svg-captcha": "^1.4.0",
"treeify": "^1.1.0",
@ -74,13 +80,12 @@
"@types/koa-router": "^7.4.0",
"@types/koa-static": "^4.0.1",
"@types/lodash": "^4.14.149",
"@types/md5": "^2.1.33",
"@types/mocha": "^5.2.7",
"@types/mocha": "^8.0.0",
"@types/mockjs": "^1.0.2",
"@types/nanoid": "^2.1.0",
"@types/node": "^13.7.7",
"@types/node-schedule": "^1.3.0",
"@types/nodemailer": "^6.2.2",
"@types/nodemailer": "^6.4.0",
"@types/redis": "^2.8.16",
"@types/request": "^2.48.4",
"@types/request-promise": "^4.1.45",
@ -88,7 +93,7 @@
"@types/underscore": "^1.9.4",
"babel-eslint": "^10.1.0",
"chai": "^4.2.0",
"mocha": "^6.2.2",
"mocha": "^8.0.1",
"nodemon": "^2.0.2",
"npm-run-all": "^4.1.5",
"nyc": "^15.0.0",
@ -101,6 +106,7 @@
"typescript": "^3.8.3"
},
"pre-commit": [
"check"
"check",
"test"
]
}

@ -0,0 +1 @@
{ "isOk": false, "errMsg": "找不到接口,请检查您的配置。 Can not find your API, please check your configurations."}

@ -0,0 +1 @@
{ "isOk": false, "errMsg": "服务器内部错误,请联系管理员。 Server side internal error occurred, please contact the administrator of this system."}

@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="favicon.ico">
<title>RAP2 Delos</title>
</head>
<body>
Opps, 发生了错误.... 请联系管理员....
</body>
</html>

@ -6,17 +6,19 @@ const config: IConfigOptions = {
port: (process.env.SERVE_PORT && parseInt(process.env.SERVE_PORT)) || 8080,
path: '',
},
keys: ['some secret hurr'],
keys: ["some secret hurr"],
session: {
key: 'rap2:sess',
},
db: {
dialect: 'mysql',
host: process.env.MYSQL_URL ?? 'localhost',
// dialect: 'mysql',
// host: process.env.MYSQL_URL || 'tddl.daily2.alibaba.net',
dialect: "mysql",
host: process.env.MYSQL_URL ?? "localhost",
port: (process.env.MYSQL_PORT && parseInt(process.env.MYSQL_PORT)) || 3306,
username: process.env.MYSQL_USERNAME ?? 'root',
password: process.env.MYSQL_PASSWD ?? '',
database: process.env.MYSQL_SCHEMA ?? 'RAP2_DELOS_APP',
username: process.env.MYSQL_USERNAME ?? "root",
password: process.env.MYSQL_PASSWD ?? "29837DF983(*&34kfjD(*3kjf(",
database: process.env.MYSQL_SCHEMA ?? "RAP2_DELOS_APP",
pool: {
max: 10,
min: 0,
@ -24,18 +26,18 @@ const config: IConfigOptions = {
},
logging: false,
dialectOptions: {
connectTimeout: 20000,
},
connectTimeout: 20000
}
},
redis: {},
mail: {
host: process.env.MAIL_HOST ?? 'smtp.aliyun.com',
host: process.env.MAIL_HOST ?? "smtp.aliyun.com",
port: process.env.MAIL_PORT ?? 465,
secure: process.env.MAIL_SECURE ?? true,
auth: {
user: process.env.MAIL_USER ?? 'rap2org@service.alibaba.com',
pass: process.env.MAIL_PASS ?? '',
},
user: process.env.MAIL_USER ?? "rap2org@service.alibaba.com",
pass: process.env.MAIL_PASS ?? ""
}
},
mailSender: process.env.MAIL_SENDER ?? 'rap2org@service.alibaba.com',
}

@ -6,17 +6,17 @@ let config: IConfigOptions = {
port: (process.env.SERVE_PORT && parseInt(process.env.SERVE_PORT)) || 8080,
path: '',
},
keys: ['some secret hurr'],
keys: ["some secret hurr"],
session: {
key: 'rap2:sess',
},
db: {
dialect: 'mysql',
host: process.env.MYSQL_URL || 'localhost',
dialect: "mysql",
host: process.env.MYSQL_URL || "localhost",
port: (process.env.MYSQL_PORT && parseInt(process.env.MYSQL_PORT)) || 3306,
username: process.env.MYSQL_USERNAME || 'root',
password: process.env.MYSQL_PASSWD || '',
database: process.env.MYSQL_SCHEMA || 'RAP2_DELOS_APP_LOCAL',
username: process.env.MYSQL_USERNAME || "root",
password: process.env.MYSQL_PASSWD || "",
database: process.env.MYSQL_SCHEMA || "RAP2_DELOS_APP_LOCAL",
pool: {
max: 5,
min: 0,
@ -26,15 +26,15 @@ let config: IConfigOptions = {
},
redis: {},
mail: {
host: 'smtp-mail.outlook.com',
port: 587,
secure: false,
host: process.env.MAIL_HOST ?? "smtp-mail.outlook.com",
port: process.env.MAIL_PORT ?? 587,
secure: process.env.MAIL_SECURE ?? false,
auth: {
user: '',
pass: ''
user: process.env.MAIL_USER ?? "",
pass: process.env.MAIL_PASS ?? ""
}
},
mailSender: '',
mailSender: process.env.MAIL_SENDER ?? ""
}
export default config

@ -7,7 +7,7 @@ let config: IConfigOptions = {
port: (process.env.SERVE_PORT && parseInt(process.env.SERVE_PORT)) || 8080,
path: '',
},
keys: ['some secret hurr'],
keys: ["some secret hurr"],
session: {
key: 'rap2:sess',
},
@ -39,7 +39,7 @@ let config: IConfigOptions = {
pass: process.env.MAIL_PASS ?? '',
},
},
mailSender: process.env.MAIL_SENDER ?? 'rap2org@service.alibaba.com',
mailSender: process.env.MAIL_SENDER ?? "rap2org@service.alibaba.com"
}
export default config

@ -0,0 +1,55 @@
import { Table, Column, Model, AutoIncrement, PrimaryKey, DataType, AllowNull, ForeignKey, BelongsTo } from 'sequelize-typescript'
import { ENTITY_TYPE } from '../../routes/utils/const'
import { User } from '../../models'
@Table({ paranoid: true, freezeTableName: false, timestamps: true, tableName: 'history_log' })
export default class HistoryLog extends Model<HistoryLog> {
@AllowNull(false)
@PrimaryKey
@AutoIncrement
@Column
id: number
/**
* ENTITY_TYPE.INTERFACE:
* ENTITY_TYPE.REPOSITORY:
*/
@AllowNull(false)
@Column({ type: DataType.INTEGER }) // for extension, type to INT, code as enum
entityType: ENTITY_TYPE
@AllowNull(false)
@Column
entityId: number
/**
* MarkdownMarkDown
*/
@AllowNull(false)
@Column({ type: DataType.TEXT })
changeLog: string
/**
* Model JSON
*/
@Column({ type: DataType.TEXT })
relatedJSONData: string
@AllowNull(false)
@ForeignKey(() => User)
@Column
userId: number
@BelongsTo(() => User, 'userId')
user: User
jsonDataIsNull?: boolean
}
export const LOG_SEPERATOR = '.|.'
export const LOG_SUB_SEPERATOR = '@|@'

@ -11,3 +11,4 @@ export { default as OrganizationsMembers } from './bo/organizationsMembers'
export { default as RepositoriesCollaborators } from './bo/repositoriesCollaborators'
export { default as RepositoriesMembers } from './bo/repositoriesMembers'
export { default as DefaultVal } from './bo/defaultVal'
export { default as HistoryLog } from './bo/historyLog'

@ -34,8 +34,9 @@ sequelize.authenticate()
console.log('----------------------------------------')
MigrateService.checkAndFix()
})
// sequelize.sync()
.catch(err => {
console.log('Unable to connect to the database:', err)
})
export default sequelize
export default sequelize

@ -7,11 +7,13 @@ import { QueryInclude } from '../models'
import { Op } from 'sequelize'
import MailService from '../service/mail'
import * as md5 from 'md5'
import RedisService, { CACHE_KEY } from '../service/redis'
import { isLoggedIn } from './base'
import { AccessUtils } from './utils/access'
import { COMMON_ERROR_RES } from './utils/const'
import * as moment from 'moment'
import RedisService, { CACHE_KEY, DEFAULT_CACHE_VAL } from '../service/redis'
router.get('/app/get', async (ctx, next) => {
@ -59,16 +61,15 @@ router.get('/account/list', isLoggedIn, async (ctx) => {
let limit = Math.min(ctx.query.limit ?? 10, 100)
let pagination = new Pagination(total, ctx.query.cursor || 1, limit)
ctx.body = {
//@ts-ignore
data: await User.findAll(Object.assign(options, {
attributes: ['id', 'fullname', 'email'],
offset: pagination.start,
limit: pagination.limit,
order: [
['id', 'DESC'],
],
})),
pagination: pagination,
data: await User.findAll({
...options, ...{
attributes: ['id', 'fullname', 'email'],
offset: pagination.start,
limit: pagination.limit,
order: [['id', 'DESC']],
}
}),
pagination: pagination
}
})
@ -214,6 +215,37 @@ router.post('/account/setting', async (ctx) => {
}
})
router.post('/account/fetchUserSettings', isLoggedIn, async (ctx) => {
const keys: CACHE_KEY[] = ctx.request.body.keys
if (!keys || !keys.length) {
ctx.body = {
isOk: false,
errMsg: 'error'
}
return
}
const data: { [key: string]: string } = {}
for (const key of keys) {
data[key] = await RedisService.getCache(key, ctx.session.id) || DEFAULT_CACHE_VAL[key]
}
ctx.body = {
isOk: true,
data,
}
})
router.post('/account/updateUserSetting/:key', isLoggedIn, async (ctx) => {
const key: CACHE_KEY = ctx.params.key as CACHE_KEY
const value: string = ctx.request.body.value
await RedisService.setCache(key, value, ctx.session.id, 10 * 365 * 24 * 60 * 60)
ctx.body = {
isOk: true,
}
})
// TODO 2.3 账户通知
let NOTIFICATION_EXCLUDE_ATTRIBUTES: any = []
router.get('/account/notification/list', async (ctx) => {

@ -14,7 +14,10 @@ import { Op } from 'sequelize'
import { isLoggedIn } from './base'
import { initRepository, initModule } from './utils/helper'
import nanoid = require('nanoid')
import nanoid = require('nanoid')
import { LOG_SEPERATOR, LOG_SUB_SEPERATOR } from '../models/bo/historyLog'
import { ENTITY_TYPE } from './utils/const'
import { IPager } from '../types'
router.get('/app/get', async (ctx, next) => {
let data: any = {}
@ -24,6 +27,7 @@ router.get('/app/get', async (ctx, next) => {
module: Module,
interface: Interface,
property: Property,
user: User,
}
for (let name in hooks) {
if (!query[name]) continue
@ -504,7 +508,7 @@ router.post('/module/update', isLoggedIn, async (ctx, next) => {
return
}
let mod = await Module.findByPk(id)
await mod.update({name, description})
await mod.update({ name, description })
ctx.request.body.repositoryId = mod.repositoryId
ctx.body = {
data: {
@ -654,6 +658,7 @@ router.get('/interface/get', async (ctx) => {
}
let itf = await Interface.findByPk(id, {
include: [QueryInclude.Locker],
attributes: { exclude: [] },
})
@ -722,17 +727,28 @@ router.post('/interface/create', isLoggedIn, async (ctx, next) => {
})
router.post('/interface/update', isLoggedIn, async (ctx, next) => {
let body = ctx.request.body
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE_SET, ctx.session.id, +body.id)) {
let summary = ctx.request.body
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.INTERFACE_SET, ctx.session.id, +summary.id)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
await Interface.update(body, {
where: { id: body.id }
const itf = await Interface.findByPk(summary.id)
const itfChangeLog: string[] = []
itf.name !== summary.name && itfChangeLog.push(`接口名 \`${itf.name}\` => \`${summary.name}\``)
itf.url !== summary.url && itfChangeLog.push(`URL \`${itf.url || '空URL'}\` => \`${summary.url}\``)
itf.method !== summary.method && itfChangeLog.push(`METHOD \`${itf.method}\` => \`${summary.method}\``)
itfChangeLog.length && await RepositoryService.addHistoryLog({
entityId: itf.id,
entityType: Consts.ENTITY_TYPE.INTERFACE,
changeLog: `接口${itf.name}(${itf.url || '空URL'}) 变更${itfChangeLog.join(LOG_SEPERATOR)}`,
userId: ctx.session.id,
})
await Interface.update(summary, {
where: { id: summary.id }
})
ctx.body = {
data: {
itf: await Interface.findByPk(body.id),
itf: await Interface.findByPk(summary.id),
}
}
return next()
@ -772,6 +788,15 @@ router.get('/interface/remove', async (ctx, next) => {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
const itf = await Interface.findByPk(id)
const properties = await Property.findAll({ where: { interfaceId: id } })
await RepositoryService.addHistoryLog({
entityId: itf.repositoryId,
entityType: Consts.ENTITY_TYPE.REPOSITORY,
changeLog: `接口 ${itf.name} (${itf.url}) 被删除,数据已备份。`,
userId: ctx.session.id,
relatedJSONData: JSON.stringify({ "itf": itf, "properties": properties })
})
let result = await Interface.destroy({ where: { id } })
await Property.destroy({ where: { interfaceId: id } })
ctx.body = {
@ -939,7 +964,7 @@ router.post('/property/update', isLoggedIn, async (ctx) => {
router.post('/properties/update', isLoggedIn, async (ctx, next) => {
const itfId = +ctx.query.itf
let { properties, summary } = ctx.request.body // JSON.parse(ctx.request.body)
let { properties } = ctx.request.body as { properties: Property[], summary: Interface }
properties = Array.isArray(properties) ? properties : [properties]
let itf = await Interface.findByPk(itfId)
@ -948,20 +973,8 @@ router.post('/properties/update', isLoggedIn, async (ctx, next) => {
return
}
if (summary.name) {
itf.name = summary.name
}
if (summary.url) {
itf.url = summary.url
}
if (summary.method) {
itf.method = summary.method
}
if (summary.description) {
itf.description = summary.description
}
await itf.save()
const itfPropertiesChangeLog: string[] = []
// 删除不在更新列表中的属性
// DONE 2.2 清除幽灵属性:子属性的父属性不存在(原因:前端删除父属性后,没有一并删除后代属性,依然传给了后端)
@ -977,7 +990,22 @@ router.post('/properties/update', isLoggedIn, async (ctx, next) => {
) as p
)
*/
let existingProperties = properties.filter((item: any) => !item.memory)
const pLog = (p: Property, title: string) => `\`${title}\`${p.scope === 'request' ? '请求' : '响应'}参数\`${p.name}\`${p.description ? '(' + p.description + ')' : ''}`
const existingProperties = properties.filter((item: any) => !item.memory)
const existingPropertyIds = existingProperties.map(x => x.id)
const originalProperties = await Property.findAll({ where: { interfaceId: itfId } })
const deletedProperties = originalProperties.filter(x => existingPropertyIds.indexOf(x.id) === -1)
const deletedPropertyLog: string[] = []
for (const deletedProperty of deletedProperties) {
deletedPropertyLog.push(pLog(deletedProperty, '删除了'))
}
deletedPropertyLog.length && itfPropertiesChangeLog.push(deletedPropertyLog.join(LOG_SUB_SEPERATOR))
let result = await Property.destroy({
where: {
id: { [Op.notIn]: existingProperties.map((item: any) => item.id) },
@ -985,26 +1013,43 @@ router.post('/properties/update', isLoggedIn, async (ctx, next) => {
}
})
const updatedPropertyLog: string[] = []
// 更新已存在的属性
for (let item of existingProperties) {
const changed: string[] = []
const o = originalProperties.filter(x => x.id === item.id)[0]
if (o) {
if (o.name !== item.name) {
changed.push(`变量名${o.name} => ${item.name}`)
}
// mock rules 不记入日志
if (o.type !== item.type) {
changed.push(`类型${o.type} => ${item.type}`)
}
changed.length && updatedPropertyLog.push(`${pLog(item, '更新了')} ${changed.join(' ')}`)
}
let affected = await Property.update(item, {
where: { id: item.id },
})
result += affected[0]
}
updatedPropertyLog.length && itfPropertiesChangeLog.push(updatedPropertyLog.join(LOG_SUB_SEPERATOR))
// 插入新增加的属性
let newProperties = properties.filter((item: any) => item.memory)
let memoryIdsMap: any = {}
const addedPropertyLog: string[] = []
for (let item of newProperties) {
let created = await Property.create(Object.assign({}, item, {
id: undefined,
parentId: -1,
priority: item.priority || Date.now()
}))
addedPropertyLog.push(pLog(item, '新增了'))
memoryIdsMap[item.id] = created.id
item.id = created.id
result += 1
}
addedPropertyLog.length && itfPropertiesChangeLog.push(addedPropertyLog.join(LOG_SUB_SEPERATOR))
// 同步 parentId
for (let item of newProperties) {
let parentId = memoryIdsMap[item.parentId] || item.parentId
@ -1015,6 +1060,16 @@ router.post('/properties/update', isLoggedIn, async (ctx, next) => {
itf = await Interface.findByPk(itfId, {
include: (QueryInclude.RepositoryHierarchy as any).include[0].include,
})
if (itfPropertiesChangeLog.length) {
await RepositoryService.addHistoryLog({
entityId: itf.id,
entityType: Consts.ENTITY_TYPE.INTERFACE,
changeLog: `接口 ${itf.name}(${itf.url}) 参数变更: ${itfPropertiesChangeLog.join(LOG_SEPERATOR)}`,
userId: ctx.session.id,
})
}
ctx.body = {
data: {
result,
@ -1050,12 +1105,12 @@ router.get('/property/remove', isLoggedIn, async (ctx) => {
})
router.post('/repository/import', isLoggedIn, async (ctx) => {
const { docUrl, orgId } = ctx.request.body
const { docUrl, orgId, version, projectData } = ctx.request.body
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.ORGANIZATION_SET, ctx.session.id, orgId)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
return
}
const result = await MigrateService.importRepoFromRAP1DocUrl(orgId, ctx.session.id, docUrl)
const result = await MigrateService.importRepoFromRAP1DocUrl(orgId, ctx.session.id, docUrl, +version, projectData)
ctx.body = {
isOk: result,
message: result ? '导入成功' : '导入失败',
@ -1066,7 +1121,7 @@ router.post('/repository/import', isLoggedIn, async (ctx) => {
})
router.post('/repository/importswagger', isLoggedIn, async (ctx) => {
const { orgId, repositoryId, swagger, version = 1, mode = 'manual'} = ctx.request.body
const { orgId, repositoryId, swagger, version = 1, mode = 'manual' } = ctx.request.body
// 权限判断
if (!await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_SET, ctx.session.id, repositoryId)) {
ctx.body = Consts.COMMON_ERROR_RES.ACCESS_DENY
@ -1084,7 +1139,7 @@ router.post('/repository/importswagger', isLoggedIn, async (ctx) => {
}
})
router.post('/repository/importJSON', isLoggedIn , async ctx => {
router.post('/repository/importJSON', isLoggedIn, async ctx => {
const { data } = ctx.request.body
if (!(await AccessUtils.canUserAccess(ACCESS_TYPE.REPOSITORY_SET, ctx.session.id, data.id))) {
@ -1104,8 +1159,38 @@ router.post('/repository/importJSON', isLoggedIn , async ctx => {
isOk: false,
message: '服务器错误,导入失败'
}
throw(error)
throw (error)
}
})
router.get('/:type/history/:itfId', isLoggedIn, async ctx => {
const pager: IPager = {
limit: +ctx.query.limit || 10,
offset: +ctx.query.offset || 0,
}
let type: ENTITY_TYPE
if (ctx.params.type === 'interface') {
type = ENTITY_TYPE.INTERFACE
} else if (ctx.params.type === 'repository') {
type = ENTITY_TYPE.REPOSITORY
} else {
ctx.body = {
isOk: false,
errMsg: 'error path',
}
return
}
ctx.body = {
isOk: true,
data: await RepositoryService.getHistoryLog(+ctx.params.itfId, type, pager)
}
})
router.get('/interface/history/JSONData/:id', isLoggedIn, async ctx => {
const historyLogId = +ctx.params.id
ctx.set('Content-disposition', `attachment; filename=history_log_detail_data_${historyLogId}`)
ctx.set('Content-type', 'text/html; charset=UTF-8')
ctx.body = await RepositoryService.getHistoryLogJSONData(historyLogId)
})

@ -29,6 +29,18 @@ export class AccessUtils {
if (inTestMode) {
return true
}
// 内网全可读
const {
ORGANIZATION_GET,
REPOSITORY_GET,
MODULE_GET,
INTERFACE_GET,
PROPERTY_GET,
} = ACCESS_TYPE
const GET_TYPES = [ORGANIZATION_GET, REPOSITORY_GET, MODULE_GET, INTERFACE_GET, PROPERTY_GET]
if (GET_TYPES.includes(accessType)) {
return true
}
// 无 session 且无 toeken 时拒绝访问
if (!curUserId && !token) {

@ -17,8 +17,20 @@ export enum DATE_CONST {
YEAR = 1000 * 60 * 60 * 24 * 365,
}
export enum ENTITY_TYPE {
REPOSITORY,
INTERFACE,
PARAMETER,
REPOSITORY = 0,
INTERFACE = 1,
PARAMETER = 2,
}
export enum THEME_TEMPLATE_KEY {
RED = 'RED', // DEFAULT
BLACK = 'BLACK',
BLUE = 'BLUE',
GREEN = 'GREEN',
PINK = 'PINK',
ORANGE = 'ORANGE',
PURPLE = 'PURPLE',
CYAN = 'CYAN',
}

@ -46,7 +46,7 @@ export default class Tree {
public static TreeToTemplate(tree: any) {
const vm = new VM({
sandbox: {},
timeout: 1000
timeout: 3000
})
function parse(item: any, result: any) {
let rule = item.rule ? '|' + item.rule : ''
@ -137,7 +137,7 @@ export default class Tree {
// https://nodejs.org/dist/latest-v7.x/docs/api/vm.html
const vm = new VM({
sandbox: { mock: Mock.mock, template, },
timeout: 1000
timeout: 3000
})
try {
let data: any = vm.run('mock(template)')

@ -23,6 +23,12 @@ export default class UrlUtils {
return url
}
// 把 /pet/{id}/ 转换成 /pet/:id/
// https://regexr.com/537jp
public static convertBracePatternRestfulUrl(url: string) {
return url.replace(/\/{([^}]+)}/g, '/:$1')
}
public static urlMatchesPattern = (url: string, pattern: string) => {
url = UrlUtils.getRelative(url)
pattern = UrlUtils.getRelative(pattern)
@ -32,6 +38,7 @@ export default class UrlUtils {
public static getUrlPattern = (pattern: string) => {
pattern = UrlUtils.getRelative(pattern)
pattern = UrlUtils.convertBracePatternRestfulUrl(pattern)
return pathToRegexp(pattern)
}

@ -8,7 +8,7 @@ const start = () => {
let open = false
console.log('----------------------------------------')
app.listen(port, () => {
console.log(`rap2-delos is running as ${url}`)
console.log(`rap2-dolores is running as ${url}`)
if (!open) return
try {
execSync(`osascript openChrome.applescript ${url}`, { cwd: __dirname, stdio: 'ignore' })

@ -56,7 +56,7 @@ export default class PostmanService {
url: {
raw: `{{url}}${relativeUrl}`,
host: '{{url}}',
port: parseResult.port || '',
port: parseResult.port || undefined,
hash: parseResult.hash,
path: [parseResult.path],
query: getQuery(requestParams),

@ -2,7 +2,9 @@ import * as nodemailer from 'nodemailer'
import config from '../config'
export default class MailService {
public static async sendMail(mailOptions: nodemailer.SendMailOptions) {
public static async sendMail(mailOptions: any) {
const transporter = nodemailer.createTransport(config.mail)
return new Promise((resolve, reject) => {
@ -23,6 +25,7 @@ export default class MailService {
}
public static send(to: string | string[], subject: string, html: string) {
const transporter = nodemailer.createTransport(config.mail)
const mailOptions = {

@ -1,4 +1,4 @@
import { Repository, Module, Interface, Property, User, QueryInclude } from '../models'
import { Repository, Module, Interface, Property, QueryInclude, User } from '../models'
import { SCOPES } from '../models/bo/property'
import Tree from '../routes/utils/tree'
import * as JSON5 from 'json5'
@ -8,8 +8,12 @@ import { Op } from 'sequelize'
import RedisService, { CACHE_KEY } from './redis'
import MailService from './mail'
import * as md5 from 'md5'
const isMd5 = require('is-md5')
// import DingPushService from './ding.push'
// import sequelize from '../models/sequelize'
import * as _ from 'lodash'
const isMd5 = require('is-md5')
const safeEval = require('notevil')
const SWAGGER_VERSION = {
1: '2.0',
@ -425,6 +429,7 @@ export default class MigrateService {
let description = []
if (p.name) description.push(p.name)
if (p.remark && remarkWithoutMock) description.push(remarkWithoutMock)
const pCreated = await Property.create({
scope,
name,
@ -474,22 +479,28 @@ export default class MigrateService {
orgId: number,
curUserId: number,
docUrl: string,
version: number,
projectDataJSON: string
): Promise<boolean> {
const { projectId } = querystring.parse(docUrl.substring(docUrl.indexOf('?') + 1))
let domain = docUrl
if (domain.indexOf('http') === -1) {
domain = 'http://' + domain
}
domain = domain.substring(0, domain.indexOf('/', domain.indexOf('.')))
let result = await rp(`${domain}/api/queryRAPModel.do?projectId=${projectId}`, {
json: false,
})
result = JSON.parse(result)
let result: any = null
if (version === 1) {
const { projectId } = querystring.parse(docUrl.substring(docUrl.indexOf('?') + 1))
let domain = docUrl
if (domain.indexOf('http') === -1) {
domain = 'http://' + domain
}
domain = domain.substring(0, domain.indexOf('/', domain.indexOf('.')))
let result = await rp(`${domain}/api/queryRAPModel.do?projectId=${projectId}`, {
json: false,
})
result = JSON.parse(result)
// result = unescape(result.modelJSON)
result = result.modelJSON
const safeEval = require('notevil')
result = safeEval('(' + result + ')')
// result = unescape(result.modelJSON)
result = result.modelJSON
result = safeEval('(' + result + ')')
} else if (version === 2) {
result = safeEval('(' + projectDataJSON + ')')
}
return await this.importRepoFromRAP1ProjectData(orgId, curUserId, result)
}
@ -623,7 +634,7 @@ export default class MigrateService {
swagger: SwaggerData,
): Promise<boolean> {
checkSwaggerResult = []
if (!swagger.paths || !swagger.swagger || !swagger.host) return false
if (!swagger.paths || !swagger.swagger) return false
let mCounter = 1 // 模块优先级顺序
let iCounter = 1 // 接口优先级顺序
@ -647,7 +658,7 @@ export default class MigrateService {
const { rule, value, type, description } = transformRapParams(p)
const joinDescription = `${p.description || ''}${
(p.description || '') && (description || '') ? '|' : ''
}${description || ''}`
}${description || ''}`
const pCreated = await Property.create({
scope,
name: p.name,
@ -728,6 +739,7 @@ export default class MigrateService {
} else {
mod = repository.modules[findIndex]
}
for (const action in paths) {
const apiObj = paths[action][Object.keys(paths[action])[0]]
const method = Object.keys(paths[action])[0]
@ -783,7 +795,7 @@ export default class MigrateService {
moduleId: mod.id,
name: `${apiObj.summary}`,
description: apiObj.description,
url: `https//${host}${url.replace('-test', '')}`,
url: `${host ? `https://${host}` : ''}${url.replace('-test', '')}`,
priority: iCounter++,
creatorId: curUserId,
repositoryId: repositoryId,
@ -806,7 +818,7 @@ export default class MigrateService {
moduleId: mod.id,
name: `${apiObj.summary}`,
description: apiObj.description,
url: `https//${host}${url.replace('-test', '')}`,
url: `${host ? `https://${host}` : ''}${url.replace('-test', '')}`,
repositoryId: repositoryId,
method: method.toUpperCase(),
},
@ -880,7 +892,7 @@ export default class MigrateService {
const { type, description, rule, value } = transformRapParams(bValue)
const joinDescription = `${bValue.description || ''}${
(bValue.description || '') && (description || '') ? '|' : ''
}${description || ''}`
}${description || ''}`
if (index >= 0) {
// 属性存在 ---修改:类型;是否必填;属性说明;不修改规则和默认值(前端可能正在mock)
@ -903,7 +915,7 @@ export default class MigrateService {
// 描述信息变更
changeTip = `${changeTip}<br/>接口名称:${apiObj.summary} [更新属性:${
A_ExistsProperties[index].name
}${A_ExistsProperties[index].description ||
}${A_ExistsProperties[index].description ||
'无'}${joinDescription}]`
}
@ -924,9 +936,9 @@ export default class MigrateService {
} else {
changeTip = `${changeTip}<br/>接口名称:${apiObj.summary} [属性添加:${
bValue.name
}${type} : ${bValue.description || ''}${
}${type} : ${bValue.description || ''}${
bValue.description || '' ? '|' : ''
}${description || ''} ]`
}${description || ''} ]`
// 属性不存在
if (depth === 0) {
properties.push({
@ -1004,9 +1016,9 @@ export default class MigrateService {
// A 存在B不存在
changeTip = `${changeTip} <br/> 接口名称:${apiObj.summary} [属性删除:${
aValue.name
}${type} : ${aValue.description || ''} ${
}${type} : ${aValue.description || ''} ${
description ? `${aValue.description ? '|' : ''}${description}` : ''
} ]`
} ]`
}
}
}
@ -1133,8 +1145,8 @@ export default class MigrateService {
`仓库:${mailRepositoryName}(${mailRepositoryId})接口更新同步`,
sendMailTemplate(changeTip),
)
.then(() => {})
.catch(() => {})
.then(() => { })
.catch(() => { })
// 钉钉消息发送
// const dingMsg = {

@ -153,6 +153,7 @@ export class MockService {
}
} else if (listMatched.length === 0) {
ctx.body = { isOk: false, errMsg: '未匹配到任何接口,请检查请求类型是否一致。' }
ctx.status = 404
return
} else {
loadDataId = listMatched[0].id
@ -200,11 +201,10 @@ export class MockService {
}
}
if (!passed) {
ctx.body = {
isOk: false,
errMsg: `必选参数${pFailed.name}未传值。 Required parameter ${pFailed.name} has no value.`,
}
return
ctx.set(
'X-RAP-WARNING',
`Required parameter ${pFailed.name} has not be passed in.`,
)
}
}

@ -1,11 +1,19 @@
import * as redis from 'redis'
import config from '../config'
import * as ioredis from 'ioredis'
import { THEME_TEMPLATE_KEY } from '../routes/utils/const'
export enum CACHE_KEY {
REPOSITORY_GET = 'REPOSITORY_GET',
PWDRESETTOKEN_GET = 'PWDRESETTOKEN_GET',
REPOSITORY_GET_EXCLUDE_PROPERTY = 'REPOSITORY_GET_EXCLUDE_PROPERTY',
/** GLOBAL PERSONAL PREFERENCES */
THEME_ID = 'THEME_ID',
GUIDE_20200714 = 'GUIDE_20200714',
}
export const DEFAULT_CACHE_VAL = {
[CACHE_KEY.THEME_ID]: THEME_TEMPLATE_KEY.RED
}
export default class RedisService {

@ -1,8 +1,11 @@
import { Repository, RepositoriesMembers, Interface, Property, Module } from '../models'
import { Repository, RepositoriesMembers, Interface, Property, Module, HistoryLog, User } from '../models'
import { MoveOp } from '../models/bo/interface'
import RedisService, { CACHE_KEY } from '../service/redis'
import { AccessUtils, ACCESS_TYPE } from '../routes/utils/access'
import OrganizationService from './organization'
import { ENTITY_TYPE } from '../routes/utils/const'
import { Op, col, fn } from 'sequelize'
import { IPager } from '../types'
export default class RepositoryService {
public static async canUserAccessRepository(
@ -152,4 +155,42 @@ export default class RepositoryService {
RedisService.delCache(CACHE_KEY.REPOSITORY_GET, destRepoId),
])
}
public static async addHistoryLog(log: Partial<HistoryLog>) {
await HistoryLog.create(log)
}
public static async getHistoryLog(entityId: number, entityType: ENTITY_TYPE.INTERFACE | ENTITY_TYPE.REPOSITORY, pager: IPager) {
const { offset, limit } = pager
const baseCon = { entityType: entityType, entityId: entityId }
const isRepo = entityType === ENTITY_TYPE.REPOSITORY
let relatedInterfaceIds: number[] = []
if (isRepo) {
const interfaces = await Interface.findAll({ attributes: ['id'], where: { repositoryId: entityId } })
relatedInterfaceIds = interfaces.map(x => x.id)
}
return (await HistoryLog.findAndCountAll({
attributes: ['id', 'changeLog', 'entityId', 'entityType', 'userId', 'createdAt', [fn('isnull', col('relatedJSONData')), 'jsonDataIsNull']],
where: {
...relatedInterfaceIds.length === 0 ? baseCon : {
[Op.or]: [baseCon, {
entityType: ENTITY_TYPE.INTERFACE,
entityId: { [Op.in]: relatedInterfaceIds },
}]
},
},
include: [{
attributes: ['id', 'fullname', 'empId'],
model: User,
as: 'user',
}],
order: [['id', 'desc']],
offset,
limit,
}))
}
public static async getHistoryLogJSONData(id: number) {
return (await HistoryLog.findByPk(id))?.relatedJSONData
}
}

@ -21,9 +21,17 @@ declare interface IConfigOptions {
session: {
key: string
},
keycenter?: string | boolean
db: ISequelizeConfig
redis: RedisAndClusterOptions
mail: SMTPTransport
mailSender: string
keycenter?: string | boolean,
db: ISequelizeConfig,
redis: any,
mail: SMTPTransport,
mailSender: string,
}
declare interface IPager {
offset: number
limit: number
order?: TOrder
orderBy?: string
query?: string
}

@ -1,6 +0,0 @@
--allow-uncaught
--exit
--timeout 15000
--slow 200
test/**/*.js

@ -7,7 +7,9 @@ let Random = require('mockjs').Random
const { Interface } = require('../dist/models')
const { mockUsers, mockRepository, prepare } = require('./helper')
describe('Interface', () => {
let createdItfId = 1
let users = mockUsers()
let repository = mockRepository()
prepare(request, should, users, repository)
@ -26,9 +28,6 @@ describe('Interface', () => {
done()
})
let validInterface = (itf, extras = []) => {
itf.should.be.a('object').have.all.keys(
Object.keys(Interface.rawAttributes).concat(extras)
)
itf.creatorId.should.be.a('number')
itf.repositoryId.should.be.a('number')
itf.moduleId.should.be.a('number')
@ -41,8 +40,9 @@ describe('Interface', () => {
.expect(200)
.end((err, res) => {
should.not.exist(err)
validInterface(res.body.data.itf)
itf = res.body.data.itf
createdItfId = itf.id
validInterface(itf)
done()
})
})
@ -73,7 +73,7 @@ describe('Interface', () => {
})
it('/interface/get', done => {
request.get('/interface/get')
.query({ id: 1 })
.query({ id: createdItfId })
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {

@ -66,25 +66,24 @@ describe('Mock', () => {
done()
})
})
/**
it('/app/get', done => {
request.get('/app/get')
.query({ user: 100000000, organization: 1, repository: 1, module: 1, interface: 1, property: 1 })
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
should.not.exist(err)
let { user, organization, repository, property } = res.body.data
let mod = res.body.data.module
let itf = res.body.data.interface
user.should.be.a('object')
organization.should.be.a('object')
repository.should.be.a('object')
mod.should.be.a('object')
itf.should.be.a('object')
property.should.be.a('object')
done()
})
})
*/
// it('/app/get', done => {
// request.get('/app/get')
// .query({ user: 100000000, organization: 1, repository: 1, module: 1, interface: 1, property: 1 })
// .expect('Content-Type', /json/)
// .expect(200)
// .end((err, res) => {
// console.log(res.body.data)
// should.not.exist(err)
// let { user, organization, repository, property } = res.body.data
// let mod = res.body.data.module
// let itf = res.body.data.interface
// user.should.be.a('object')
// organization.should.be.a('object')
// repository.should.be.a('object')
// mod.should.be.a('object')
// itf.should.be.a('object')
// property.should.be.a('object')
// done()
// })
// })
})

@ -22,10 +22,10 @@ describe('Repository', () => {
done()
})
let validRepository = (repository, deep) => {
repository.should.be.a('object').have.all.keys(
[...Object.keys(Repository.rawAttributes), 'creator', 'owner', 'members', 'locker', 'organization', 'collaborators']
.concat(deep ? ['modules'] : [])
)
// repository.should.be.a('object').have.all.keys(
// [...Object.keys(Repository.rawAttributes), 'creator', 'owner', 'members', 'locker', 'organization', 'collaborators']
// .concat(deep ? ['modules'] : [])
// )
let { creator, owner, members } = repository
creator.should.be.a('object').have.all.keys(['id', 'fullname', 'email'])
owner.should.be.a('object').have.all.keys(['id', 'fullname', 'email'])

Loading…
Cancel
Save