fix many to many mapping errors, reconstruct BO layer

pull/76/head
Bosn 7 years ago
parent 8c0a2d311e
commit e57036712e

@ -0,0 +1,11 @@
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 ;

@ -50,7 +50,14 @@
},
"devDependencies": {
"@types/chai": "^4.0.10",
"@types/kcors": "^2.2.2",
"@types/koa": "^2.0.43",
"@types/koa-logger": "^3.1.0",
"@types/koa-router": "^7.0.27",
"@types/koa-session": "^3.0.6",
"@types/koa-static": "^4.0.0",
"@types/mocha": "^2.2.46",
"@types/mockjs": "^1.0.0",
"@types/node": "^8.5.2",
"babel-eslint": "^7.2.3",
"chai": "^3.5.0",

@ -21,7 +21,7 @@ let config: IConfigOptions = {
min: 0,
idle: 10000
},
logging: false
logging: true
}
}

@ -0,0 +1,15 @@
import { Table, Column, Model, ForeignKey, PrimaryKey } from 'sequelize-typescript'
import { Repository } from '../'
@Table({ freezeTableName: true, timestamps: true, tableName: 'repositories_collaborators' })
export default class RepositoriesCollaborators extends Model<RepositoriesCollaborators> {
@ForeignKey(() => Repository)
@PrimaryKey
@Column
repositoryId: number
@ForeignKey(() => Repository)
@PrimaryKey
@Column
collaboratorId: number
}

@ -1,10 +1,10 @@
import { Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, BelongsTo, ForeignKey } from 'sequelize-typescript'
import { User, Module, Repository, Property } from './index';
import { User, Module, Repository, Property } from '../';
enum methods { GET= 'GET', POST= 'POST', PUT= 'PUT', DELETE= 'DELETE' }
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
export class Interface extends Model<Interface> {
export default class Interface extends Model<Interface> {
public static METHODS= methods
@ -29,7 +29,7 @@ export class Interface extends Model<Interface> {
method: string
@Column(DataType.TEXT)
description
description: string
@AllowNull(false)
@Default(1)

@ -1,5 +1,5 @@
import { Table, Column, Model, AutoIncrement, PrimaryKey, AllowNull, DataType, BelongsTo, ForeignKey } from 'sequelize-typescript'
import { User, Repository, Organization, Module, Interface } from './index'
import { User, Repository, Organization, Module, Interface } from '../'
enum types {
CREATE = 'create', UPDATE = 'update', DELETE = 'delete',
@ -7,7 +7,7 @@ enum types {
}
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
export class Logger extends Model<Logger> {
export default class Logger extends Model<Logger> {
public static TYPES = types
@AutoIncrement

@ -1,8 +1,8 @@
import { Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, BelongsTo, ForeignKey } from 'sequelize-typescript'
import { User, Repository, Interface } from './index'
import { User, Repository, Interface } from '../'
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
export class Module extends Model<Module> {
export default class Module extends Model<Module> {
@AutoIncrement
@PrimaryKey

@ -1,7 +1,7 @@
import { Table, Column, Model, AutoIncrement, PrimaryKey, AllowNull, DataType, Default } from 'sequelize-typescript'
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
export class Notification extends Model<Notification> {
export default class Notification extends Model<Notification> {
@AutoIncrement
@PrimaryKey
@ -16,7 +16,7 @@ export class Notification extends Model<Notification> {
toId: number
@AllowNull(false)
@Column({ type: DataType.STRING(128), comment: 'msg type' })
@Column({ comment: 'msg type' })
type: string
@Column(DataType.STRING(128))

@ -1,8 +1,8 @@
import { Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, BelongsTo, BelongsToMany, ForeignKey } from 'sequelize-typescript'
import { User, Repository } from './index'
import { User, Repository, OrganizationsMembers } from '../'
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
export class Organization extends Model<Organization> {
export default class Organization extends Model<Organization> {
@AutoIncrement
@PrimaryKey
@ -38,9 +38,9 @@ export class Organization extends Model<Organization> {
@BelongsTo(() => User, 'ownerId')
owner: User
@BelongsToMany(() => User, 'organizations_members', 'userId', 'organizationId')
@BelongsToMany(() => User, () => OrganizationsMembers)
members: User[]
@HasMany(() => Repository, 'organizationId')
repositories: Repository[]
}
}

@ -0,0 +1,15 @@
import { Table, Column, Model, ForeignKey, PrimaryKey } from 'sequelize-typescript'
import { User, Organization } from '../'
@Table({ freezeTableName: true, timestamps: true, tableName: 'organizations_members' })
export default class OrganizationsMembers extends Model<OrganizationsMembers> {
@ForeignKey(() => User)
@PrimaryKey
@Column
userId: number
@ForeignKey(() => Organization)
@PrimaryKey
@Column
organizationId: number
}

@ -1,11 +1,11 @@
import { Table, Column, Model, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, BelongsTo, ForeignKey } from 'sequelize-typescript'
import { User, Interface, Module, Repository } from './index'
import { User, Interface, Module, Repository } from '../'
enum SCOPES { REQUEST = 'request', RESPONSE = 'response' }
enum TYPES { STRING = 'String', NUMBER = 'Number', BOOLEAN = 'Boolean', OBJECT = 'Object', ARRAY = 'Array', FUNCTION = 'Function', REGEXP = 'RegExp' }
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
export class Property extends Model<Property> {
export default class Property extends Model<Property> {
public static TYPES = TYPES
public static SCOPES = SCOPES

@ -0,0 +1,15 @@
import { Table, Column, Model, ForeignKey, PrimaryKey } from 'sequelize-typescript'
import { Repository, User } from '../'
@Table({ freezeTableName: true, timestamps: true, tableName: 'repositories_members' })
export default class RepositoriesMembers extends Model<RepositoriesMembers> {
@ForeignKey(() => User)
@PrimaryKey
@Column
userId: number
@ForeignKey(() => Repository)
@PrimaryKey
@Column
repositoryId: number
}

@ -1,8 +1,8 @@
import { Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Default, BelongsTo, BelongsToMany, ForeignKey } from 'sequelize-typescript'
import { User, Organization, Module, Interface } from './index'
import { User, Organization, Module, Interface, RepositoriesCollaborators } from '../'
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
export class Repository extends Model<Repository> {
export default class Repository extends Model<Repository> {
@AutoIncrement
@PrimaryKey
@ -61,7 +61,7 @@ export class Repository extends Model<Repository> {
@HasMany(() => Module, 'repositoryId')
interfaces: Interface[]
@BelongsToMany(() => Repository, 'repositories_collaborators', 'repositoryId', 'collaboratorId')
@BelongsToMany(() => Repository, () => RepositoriesCollaborators)
collaborators: Repository[]
}

@ -1,8 +1,8 @@
import { Table, Column, Model, HasMany, AutoIncrement, PrimaryKey, AllowNull, DataType, Unique, BelongsToMany } from 'sequelize-typescript'
import { Organization, Repository } from './index'
import { Organization, Repository, OrganizationsMembers, RepositoriesMembers } from '../'
@Table({ paranoid: true, freezeTableName: false, timestamps: true })
export class User extends Model<User> {
export default class User extends Model<User> {
@AutoIncrement
@PrimaryKey
@ -24,13 +24,13 @@ export class User extends Model<User> {
@HasMany(() => Organization, 'ownerId')
ownedOrganizations: Organization[]
@BelongsToMany(() => Organization, 'organizations_members', 'organizationId', 'userId')
@BelongsToMany(() => Organization, () => OrganizationsMembers)
joinedOrganizations: Organization[]
@HasMany(() => Repository, 'ownerId')
ownedRepositories: Repository[]
@BelongsToMany(() => Repository, 'repositories_members', 'userId', 'repositoryId')
@BelongsToMany(() => Repository, () => RepositoriesMembers)
joinedRepositories: Repository[]
}

@ -1,4 +1,4 @@
export let Helper = {
export let Helper: any = {
include: [],
exclude: {
generalities: ['createdAt', 'updatedAt', 'deletedAt', 'reserve']

@ -1,9 +1,12 @@
export { QueryInclude } from './queryInclude'
export { Interface } from './interface'
export { Logger } from './logger'
export { Module } from './module'
export { Notification } from './notification'
export { Organization } from './organization'
export { Property } from './property'
export { Repository } from './repository'
export { User } from './user'
export { default as QueryInclude } from './util/queryInclude'
export { default as Interface } from './bo/interface'
export { default as Logger } from './bo/logger'
export { default as Module } from './bo/module'
export { default as Notification } from './bo/notification'
export { default as Organization } from './bo/organization'
export { default as Property } from './bo/property'
export { default as Repository } from './bo/repository'
export { default as User } from './bo/user'
export { default as OrganizationsMembers } from './bo/organizationsMembers'
export { default as RepositoriesCollaborators } from './bo/repositoriesCollaborators'
export { default as RepositoriesMembers } from './bo/repositoriesMembers'

@ -1,58 +0,0 @@
// require('colors')
const Sequelize = require('sequelize')
const config = require('../../config')
const chalk = require('chalk')
const now = () => new Date().toISOString().replace(/T/, ' ').replace(/Z/, '')
const logging = process.env.NODE_ENV === 'development'
? (sql) => {
sql = sql.replace('Executing (default): ', '')
console.log(`${chalk.bold('SQL')} ${now()} ${chalk.gray(sql)}`)
}
: console.log
const sequelize = new Sequelize({
database: config.db.database,
username: config.db.username,
password: config.db.password,
host: config.db.host,
port: config.db.port,
dialect: config.db.dialect,
pool: config.db.pool,
logging: config.db.logging ? logging : false
})
sequelize.authenticate()
.then((/* err */) => {
// console.log('Connection has been established successfully.');
console.log('----------------------------------------')
console.log('DATABASE √')
console.log(' HOST %s', config.db.host)
console.log(' PORT %s', config.db.port)
console.log(' DATABASE %s', config.db.database)
console.log('----------------------------------------')
})
.catch(err => {
console.log('Unable to connect to the database:', err)
})
module.exports = sequelize
// module.exports = {
// Sequelize,
// sequelize,
// id: { type: Sequelize.BIGINT(11).UNSIGNED, primaryKey: true, allowNull: false, autoIncrement: true },
// attributes: {
// create_date: { type: Sequelize.DATE, allowNull: false, defaultValue: Sequelize.NOW, comment: '创建时间' },
// update_date: { type: Sequelize.DATE, allowNull: false, defaultValue: Sequelize.NOW, comment: '更新时间' },
// delete_date: { type: Sequelize.DATE, allowNull: true, comment: '删除时间' },
// reserve: { type: Sequelize.STRING, allowNull: true, comment: '备用' }
// },
// options: {
// // freezeTableName: true,
// // createdAt: 'create_date',
// // updatedAt: 'update_date',
// // deletedAt: 'delete_date',
// paranoid: true
// },
// exclude: ['password', 'create_date', 'delete_date', 'reserve'] // 'update_date',
// }

@ -1,11 +1,11 @@
import { Sequelize } from 'sequelize-typescript'
import { Interface, Logger, Module, Notification, Organization, Property, User, Repository } from './index'
import config from '../config'
import { Organization, Logger } from '.';
const chalk = require('chalk')
const now = () => new Date().toISOString().replace(/T/, ' ').replace(/Z/, '')
const logging = process.env.NODE_ENV === 'development'
? (sql) => {
? (sql: string) => {
sql = sql.replace('Executing (default): ', '')
console.log(`${chalk.bold('SQL')} ${now()} ${chalk.gray(sql)}`)
}
@ -22,12 +22,12 @@ const sequelize = new Sequelize({
logging: config.db.logging ? logging : false
})
sequelize.addModels([Interface, Logger, Module, Notification, Organization, Property, Repository, User])
sequelize.addModels([__dirname + '/bo'])
sequelize.authenticate()
.then((/* err */) => {
// initialize hooks
Organization.hook('afterCreate', async(instance) => {
Organization.hook('afterCreate', async(instance: Organization) => {
await Logger.create({
userId: instance.creatorId,
type: 'create',

@ -1,12 +0,0 @@
const Sequelize = require('sequelize')
const sequelize = require('./sequelize')
const { id } = require('./helper')
module.exports = sequelize.define('user', {
id,
fullname: { type: Sequelize.STRING(32), allowNull: false, comment: '姓名' },
password: { type: Sequelize.STRING(32), allowNull: true, comment: '密码' },
email: { type: Sequelize.STRING(128), allowNull: false, unique: true, comment: '邮箱' }
}, {
paranoid: true,
comment: '用户'
})

@ -0,0 +1,12 @@
declare interface IHelper {
include: string[],
exclude: {
generalities: string[]
}
}
export let Helper: IHelper = {
include: [],
exclude: {
generalities: ['createdAt', 'updatedAt', 'deletedAt', 'reserve']
}
}

@ -1,13 +1,29 @@
// TODO 2.2 如何缓存重复查询https://github.com/rfink/sequelize-redis-cache
import { Helper } from './helper'
import { User } from './user'
import { Repository } from './repository';
import { Organization } from './organization'
import { Module } from './module'
import { Interface } from './interface'
import { Property } from './property'
import User from '../bo/user';
import Repository from '../bo/repository';
import Module from '../bo/module';
import Organization from '../bo/organization';
import Interface from '../bo/interface';
import Property from '../bo/property';
export const QueryInclude = {
declare interface IQueryIncludeItem {
model?: object,
'as'?: string,
include?: any,
attributes?: any,
required?: boolean,
through?: any,
paranoid?: boolean,
separate?: boolean,
order?: [string[]]
}
declare interface IQueryInclude {
[key: string]: IQueryIncludeItem
}
const QueryInclude: IQueryInclude = {
User: { model: User, as: 'user', attributes: { exclude: ['password', ...Helper.exclude.generalities] }, required: true },
UserForSearch: { model: User, as: 'user', attributes: { include: ['id', 'fullname'] }, required: true },
Creator: { model: User, as: 'creator', attributes: { exclude: ['password', ...Helper.exclude.generalities] }, required: true },
@ -57,4 +73,6 @@ export const QueryInclude = {
attributes: { exclude: [] },
required: false
}
}
}
export default QueryInclude

@ -6,9 +6,9 @@ import Pagination from './utils/pagination'
import { QueryInclude } from '../models'
router.get('/app/get', async (ctx, next) => {
let data = {}
let data: any = {}
let query = ctx.query
let hooks = {
let hooks: any = {
user: User
}
for (let name in hooks) {
@ -167,7 +167,7 @@ router.post('/account/setting', async(ctx) => {
})
// TODO 2.3 账户通知
let NOTIFICATION_EXCLUDE_ATTRIBUTES = []
let NOTIFICATION_EXCLUDE_ATTRIBUTES: any = []
router.get('/account/notification/list', async(ctx) => {
let total = await Notification.count()
let pagination = new Pagination(total, ctx.query.cursor || 1, ctx.query.limit || 10)
@ -234,7 +234,7 @@ router.get('/account/logger', async(ctx) => {
['id', 'DESC']
],
paranoid: false
})
} as any)
ctx.body = {
data: logs,

@ -21,7 +21,7 @@ router.get('/app/analytics/repositories/created', async (ctx) => {
ORDER BY label ASC;
`
let result = await sequelize.query(sql, SELECT)
result = result.map(item => ({
result = result.map((item: any) => ({
label: moment(item.label).format(YYYY_MM_DD),
value: item.value
}))
@ -46,7 +46,7 @@ router.get('/app/analytics/repositories/updated', async (ctx) => {
ORDER BY label ASC;
`
let result = await sequelize.query(sql, SELECT)
result = result.map(item => ({
result = result.map((item: any) => ({
label: moment(item.label).format(YYYY_MM_DD),
value: item.value
}))

@ -3,13 +3,13 @@ import { Repository, Interface, Property } from '../models'
import { QueryInclude } from '../models';
import Tree from './utils/tree'
import urlUtils from './utils/url'
const attributes = { exclude: [] }
const attributes: any = { exclude: [] }
const pt = require('node-print').pt
const beautify = require('js-beautify').js_beautify
// 检测是否存在重复接口,会在返回的插件 JS 中提示。同时也会在编辑器中提示。
const parseDuplicatedInterfaces = (repository) => {
let counter = {}
const parseDuplicatedInterfaces = (repository: Repository) => {
let counter: any = {}
for (let itf of repository.interfaces) {
let key = `${itf.method} ${itf.url}`
counter[key] = [...(counter[key] || []), { id: itf.id, method: itf.method, url: itf.url }]
@ -22,7 +22,7 @@ const parseDuplicatedInterfaces = (repository) => {
}
return duplicated
}
const generatePlugin = (protocol, host, repository) => {
const generatePlugin = (protocol: any, host: any, repository: Repository) => {
// DONE 2.3 protocol 错误,应该是 https
let duplicated = parseDuplicatedInterfaces(repository)
let editor = `${protocol}://rap2.taobao.org/repository/editor?id=${repository.id}` // [TODO] replaced by cur domain
@ -38,7 +38,7 @@ const generatePlugin = (protocol, host, repository) => {
;(function(){
let repositoryId = ${repository.id}
let interfaces = [
${repository.interfaces.map(itf =>
${repository.interfaces.map((itf: Interface) =>
`{ id: ${itf.id}, name: '${itf.name}', method: '${itf.method}', url: '${itf.url}',
request: ${JSON.stringify(itf.request)},
response: ${JSON.stringify(itf.response)} }`
@ -57,7 +57,7 @@ const generatePlugin = (protocol, host, repository) => {
}
router.get('/app/plugin/:repositories', async (ctx) => {
let repositoryIds = new Set<number>(ctx.params.repositories.split(',').map(item => +item).filter(item => item)) // _.uniq() => Set
let repositoryIds = new Set<number>(ctx.params.repositories.split(',').map((item: string) => +item).filter((item: any) => item)) // _.uniq() => Set
let result = []
for (let id of repositoryIds) {
let repository = await Repository.findById(id, {
@ -70,7 +70,7 @@ router.get('/app/plugin/:repositories', async (ctx) => {
QueryInclude.Organization,
QueryInclude.Collaborators
]
})
} as any)
if (!repository) continue
if (repository.collaborators) {
repository.collaborators.map(item => {
@ -85,7 +85,7 @@ router.get('/app/plugin/:repositories', async (ctx) => {
include: [
QueryInclude.Properties
]
})
} as any)
repository.interfaces.forEach(itf => {
itf.request = Tree.ArrayToTreeToTemplate(itf.properties.filter(item => item.scope === 'request'))
itf.response = Tree.ArrayToTreeToTemplate(itf.properties.filter(item => item.scope === 'response'))

@ -5,9 +5,9 @@ import * as _ from 'lodash'
import Pagination from './utils/pagination'
router.get('/app/get', async (ctx, next) => {
let data = {}
let data: any = {}
let query = ctx.query
let hooks = {
let hooks: any = {
organization: Organization
}
for (let name in hooks) {
@ -45,7 +45,7 @@ router.get('/organization/list', async(ctx) => {
QueryInclude.Creator,
QueryInclude.Owner
]
})
} as any)
let pagination = new Pagination(total, ctx.query.cursor || 1, ctx.query.limit || 100)
let organizations = await Organization.findAll({
where,
@ -58,7 +58,7 @@ router.get('/organization/list', async(ctx) => {
offset: pagination.start,
limit: pagination.limit,
order: [['updatedAt', 'DESC']]
})
} as any)
ctx.body = {
data: organizations,
pagination: pagination
@ -120,7 +120,7 @@ router.get('/organization/get', async(ctx) => {
let organization = await Organization.findById(ctx.query.id, {
attributes: { exclude: [] },
include: [QueryInclude.Creator, QueryInclude.Owner, QueryInclude.Members]
})
} as any)
ctx.body = {
data: organization
}
@ -136,7 +136,7 @@ router.post('/organization/create', async(ctx) => {
let filled = await Organization.findById(created.id, {
attributes: { exclude: [] },
include: [QueryInclude.Creator, QueryInclude.Owner, QueryInclude.Members]
})
} as any)
ctx.body = {
data: filled
}
@ -168,8 +168,8 @@ router.post('/organization/update', async(ctx, next) => {
})
// 加入 & 退出
if (!ctx.prevAssociations || !ctx.nextAssociations) return
let prevIds = ctx.prevAssociations.map(item => item.id)
let nextIds = ctx.nextAssociations.map(item => item.id)
let prevIds = ctx.prevAssociations.map((item: any) => item.id)
let nextIds = ctx.nextAssociations.map((item: any) => item.id)
let joined = _.difference(nextIds, prevIds)
let exited = _.difference(prevIds, nextIds)
let creatorId = ctx.session.id

@ -7,9 +7,9 @@ import Tree from './utils/tree'
const { initRepository, initModule } = require('./utils/helper')
router.get('/app/get', async (ctx, next) => {
let data = {}
let data: any = {}
let query = ctx.query
let hooks = {
let hooks: any = {
repository: Repository,
module: Module,
interface: Interface,
@ -52,7 +52,7 @@ router.get('/repository/list', async(ctx) => {
QueryInclude.Owner,
QueryInclude.Locker
]
})
} as any)
let pagination = new Pagination(total, ctx.query.cursor || 1, ctx.query.limit || 100)
let repositories = await Repository.findAll({
where,
@ -68,7 +68,7 @@ router.get('/repository/list', async(ctx) => {
offset: pagination.start,
limit: pagination.limit,
order: [['updatedAt', 'DESC']]
})
} as any)
ctx.body = {
data: repositories,
pagination: pagination
@ -155,7 +155,7 @@ router.get('/repository/get', async(ctx) => {
QueryInclude.RepositoryHierarchy,
QueryInclude.Collaborators
]
})
} as any)
ctx.body = {
data: repository
}
@ -185,7 +185,7 @@ router.post('/repository/create', async(ctx, next) => {
QueryInclude.RepositoryHierarchy,
QueryInclude.Collaborators
]
})
} as any)
}
return next()
}, async(ctx) => {
@ -227,8 +227,8 @@ router.post('/repository/update', async(ctx, next) => {
})
// 加入 & 退出
if (!ctx.prevAssociations || !ctx.nextAssociations) return
let prevIds = ctx.prevAssociations.map(item => item.id)
let nextIds = ctx.nextAssociations.map(item => item.id)
let prevIds = ctx.prevAssociations.map((item: any) => item.id)
let nextIds = ctx.nextAssociations.map((item: any) => item.id)
let joined = _.difference(nextIds, prevIds)
let exited = _.difference(prevIds, nextIds)
let creatorId = ctx.session.id
@ -448,10 +448,7 @@ router.get('/interface/get', async (ctx) => {
}
ctx.type = 'json'
let result = Tree.stringifyWithFunctonAndRegExp({ data: itf })
console.log('result:')
console.log(result)
ctx.body = result
ctx.body = Tree.stringifyWithFunctonAndRegExp({ data: itf })
})
router.post('/interface/create', async (ctx, next) => {
let creatorId = ctx.session.id
@ -639,10 +636,10 @@ router.post('/properties/update', async (ctx, next) => {
) as p
)
*/
let existingProperties = properties.filter(item => !item.memory)
let existingProperties = properties.filter((item: any) => !item.memory)
let result = await Property.destroy({
where: {
id: { $notIn: existingProperties.map(item => item.id) },
id: { $notIn: existingProperties.map((item: any) => item.id) },
interfaceId: itf
}
})
@ -654,8 +651,8 @@ router.post('/properties/update', async (ctx, next) => {
result += affected[0]
}
// 插入新增加的属性
let newProperties = properties.filter(item => item.memory)
let memoryIdsMap = {}
let newProperties = properties.filter((item: any) => item.memory)
let memoryIdsMap: any = {}
for (let item of newProperties) {
let created = await Property.create(Object.assign({}, item, {
id: undefined,
@ -697,4 +694,4 @@ router.get('/property/remove', async (ctx) => {
where: { id }
})
}
})
})

@ -1,12 +1,13 @@
import { Module, Interface, Property } from '../../models'
import { Repository } from '../../models'
const genExampleModule = (extra) => Object.assign({
const genExampleModule = (extra: any) => Object.assign({
name: '示例模块',
description: '示例模块',
creatorId: undefined,
repositoryId: undefined
}, extra)
const genExampleInterface = (extra) => Object.assign({
const genExampleInterface = (extra: any) => Object.assign({
name: '示例接口',
url: `/example/${Date.now()}`,
method: 'GET',
@ -16,13 +17,13 @@ const genExampleInterface = (extra) => Object.assign({
moduleId: undefined,
repositoryId: undefined
}, extra)
const genExampleProperty = (extra) => Object.assign({
const genExampleProperty = (extra: any) => Object.assign({
scope: undefined,
name: 'foo',
type: 'String',
rule: '',
value: '@ctitle',
description: { request: '请求属性示例', response: '响应属性示例' }[extra.scope],
description: ({ request: '请求属性示例', response: '响应属性示例' } as any)[extra.scope],
parentId: -1,
creatorId: undefined,
interfaceId: undefined,
@ -31,7 +32,7 @@ const genExampleProperty = (extra) => Object.assign({
}, extra)
// 初始化仓库
const initRepository = async (repository) => {
const initRepository = async (repository: Repository) => {
let mod = await Module.create(genExampleModule({
creatorId: repository.creatorId,
repositoryId: repository.id
@ -39,7 +40,7 @@ const initRepository = async (repository) => {
await initModule(mod)
}
// 初始化模块
const initModule = async (mod) => {
const initModule = async (mod: Module) => {
let itf = await Interface.create(genExampleInterface({
creatorId: mod.creatorId,
moduleId: mod.id,
@ -48,7 +49,7 @@ const initModule = async (mod) => {
await initInterface(itf)
}
// 初始化接口
const initInterface = async (itf) => {
const initInterface = async (itf: Interface) => {
let { creatorId, repositoryId, moduleId } = itf
let interfaceId = itf.id
await Property.create(genExampleProperty({

@ -51,19 +51,19 @@ export default class Pagination {
public cursor: number
public limit: number
public focus: number
public pages
public start
public end
public hasPrev
public hasNext
public hasFirst
public hasLast
public prev
public next
public first
public last
constructor(data, cursor, limit) {
public pages: any
public start: any
public end: any
public hasPrev: any
public hasNext: any
public hasFirst: any
public hasLast: any
public prev: any
public next: any
public first: any
public last: any
constructor(data: any, cursor: any, limit: any) {
this.data = (typeof data === 'number' || typeof data === 'string') ? undefined : data
this.total = this.data ? this.data.length : parseInt(data, 10)
this.cursor = parseInt(cursor, 10)
@ -108,7 +108,7 @@ export default class Pagination {
return this
}
public moveTo(cursor) {
public moveTo(cursor: any) {
this.cursor = parseInt(cursor, 10)
return this.calc()
}
@ -129,27 +129,27 @@ export default class Pagination {
return this.moveTo(this.pages)
}
public fetch(arr) {
public fetch(arr: any) {
return (arr || this.data).slice(this.start, this.end)
}
public setData(data) {
public setData(data: any) {
this.data = data
this.total = data.length
return this.calc()
}
public setTotal(total) {
public setTotal(total: any) {
this.total = parseInt(total, 10)
return this.calc()
}
public setCursor(cursor) {
public setCursor(cursor: any) {
this.cursor = parseInt(cursor, 10)
return this.calc()
}
public setFocus(focus) {
public setFocus(focus: any) {
this.focus = parseInt(focus, 10)
if (this.focus < 0) this.focus += this.total
if (this.focus >= this.total) this.focus -= this.total
@ -157,12 +157,12 @@ export default class Pagination {
return this.calc()
}
public setLimit(limit) {
public setLimit(limit: any) {
this.limit = parseInt(limit, 10)
return this.calc()
}
public get(focus) {
public get(focus: any) {
if (focus !== undefined) return this.data[focus % this.data.length]
else return this.data[this.focus]
}

@ -1,22 +1,23 @@
import { Property } from "../../models";
const vm = require('vm')
const _ = require('underscore')
const Mock = require('mockjs')
import * as Mock from 'mockjs'
const { RE_KEY } = require('mockjs/src/mock/constant')
export default class Tree {
public static ArrayToTree (list) {
let result = {
public static ArrayToTree (list: Property[]) {
let result: any = {
name: 'root',
children: [],
depth: 0
}
let mapped = {}
let mapped: any = {}
list.forEach(item => { mapped[item.id] = item })
function _parseChildren (parentId, children, depth) {
function _parseChildren (parentId: any, children: any, depth: any) {
for (let id in mapped) {
let item = mapped[id]
if (typeof parentId === 'function' ? parentId(item.parentId) : item.parentId === parentId) {
@ -29,9 +30,10 @@ export default class Tree {
}
_parseChildren(
(parentId) => {
(parentId: number) => {
// 忽略 parentId 为 0 的根属性(历史遗留),现为 -1
if (parentId === -1) return true
return false
},
result.children,
result.depth
@ -41,8 +43,8 @@ export default class Tree {
}
// TODO 2.x 和前端重复了
public static TreeToTemplate (tree) {
function parse (item, result) {
public static TreeToTemplate (tree: any) {
function parse (item: any, result: any) {
let rule = item.rule ? ('|' + item.rule) : ''
let value = item.value
switch (item.type) {
@ -80,7 +82,7 @@ export default class Tree {
}
} else {
result[item.name + rule] = {}
item.children.forEach((child) => {
item.children.forEach((child: any) => {
parse(child, result[item.name + rule])
})
}
@ -94,7 +96,7 @@ export default class Tree {
}
} else {
result[item.name + rule] = item.children.length ? [{}] : []
item.children.forEach((child) => {
item.children.forEach((child: any) => {
parse(child, result[item.name + rule][0])
})
}
@ -102,13 +104,13 @@ export default class Tree {
}
}
let result = {}
tree.children.forEach((child) => {
tree.children.forEach((child: any) => {
parse(child, result)
})
return result
}
public static TemplateToData (template) {
public static TemplateToData (template: any) {
// 数据模板 template 中可能含有攻击代码,例如死循环,所以在沙箱中生成最终数据
// https://nodejs.org/dist/latest-v7.x/docs/api/vm.html
const sandbox = { Mock, template, data: {} }
@ -127,13 +129,13 @@ export default class Tree {
}
}
public static ArrayToTreeToTemplate(list) {
public static ArrayToTreeToTemplate(list: Property[]) {
let tree = Tree.ArrayToTree(list)
let template = Tree.TreeToTemplate(tree)
return template
}
public static ArrayToTreeToTemplateToData(list, extra?: any) {
public static ArrayToTreeToTemplateToData(list: Property[], extra?: any) {
let tree = Tree.ArrayToTree(list)
let template = Tree.TreeToTemplate(tree)
let data
@ -153,7 +155,7 @@ export default class Tree {
return data
}
public static ArrayToTreeToTemplateToJSONSchema(list) {
public static ArrayToTreeToTemplateToJSONSchema(list: Property[]) {
let tree = Tree.ArrayToTree(list)
let template = Tree.TreeToTemplate(tree)
let schema = Mock.toJSONSchema(template)
@ -163,7 +165,7 @@ export default class Tree {
// TODO 2.2 执行 JSON.stringify() 序列化时会丢失正则和函数。需要转为字符串或者函数。
// X Function.protytype.toJSON = Function.protytype.toString
// X RegExp.protytype.toJSON = RegExp.protytype.toString
public static stringifyWithFunctonAndRegExp(json) {
public static stringifyWithFunctonAndRegExp(json: object) {
return JSON.stringify(json, (k, v) => {
k
if (typeof v === 'function') return v.toString()
@ -172,4 +174,4 @@ export default class Tree {
}, 2)
}
}
}

@ -2,8 +2,7 @@ let pathToRegexp = require('path-to-regexp')
export default class UrlUtils {
public static getRelative = url => {
if (!url || typeof url !== 'string') return
public static getRelative = (url: string) => {
url = url.toLowerCase()
const prefixes = ['https://', 'http://']
for (let item of prefixes) {
@ -23,11 +22,11 @@ export default class UrlUtils {
return url
}
public static urlMatchesPattern = (url, pattern) => {
public static urlMatchesPattern = (url: string, pattern: string) => {
url = UrlUtils.getRelative(url)
pattern = UrlUtils.getRelative(pattern)
let re = pathToRegexp(pattern)
return re.test(url)
}
}
}

@ -8,8 +8,9 @@ import * as cors from 'kcors'
import router from '../routes'
import config from '../config'
const app: any = new Koa()
app.counter = { users: {}, mock: 0 }
const app = new Koa()
let appAny: any = app
appAny.counter = { users: {}, mock: 0 }
app.keys = config.keys
app.use(session(config.session, app))
@ -18,7 +19,8 @@ app.use(async(ctx, next) => {
await next()
if (ctx.path === '/favicon.ico') return
ctx.session.views = (ctx.session.views || 0) + 1
if (ctx.session.fullname) ctx.app.counter.users[ctx.session.fullname] = true
let app: any = ctx.app
if (ctx.session.fullname) app.counter.users[ctx.session.fullname] = true
})
app.use(cors({
credentials: true

@ -1,101 +0,0 @@
// let Random = require('mockjs').Random
const { mock } = require('mockjs')
const scopes = ['request', 'response']
const methods = ['GET', 'POST', 'PUT', 'DELETE']
const types = ['String', 'Number', 'Boolean', 'Object', 'Array', 'Function', 'RegExp']
const values = ['@INT', '@FLOAT', '@TITLE', '@NAME']
let USER_ID = 100000000
let ORGANIZATION_ID = 1
let REPOSITORY_ID = 1
let MODULE_ID = 1
let INTERFACE_ID = 1
let PROPERTY_ID = 1
module.exports = {
BO_ADMIN: { id: USER_ID++, fullname: 'admin', email: 'admin@rap2.com', password: 'admin' },
BO_MOZHI: { id: USER_ID++, fullname: '墨智', email: 'mozhi@rap2.com', password: 'mozhi' },
BO_USER_COUNT: 10,
BO_USER_FN: () => mock({
id: USER_ID++,
fullname: '@cname',
email: '@email',
password: '@word(6)'
}),
BO_ORGANIZATION_COUNT: 3,
BO_ORGANIZATION_FN: (source) => {
return Object.assign(
mock({
id: ORGANIZATION_ID++,
name: '组织@ctitle(5)',
description: '@cparagraph',
logo: '@url',
creatorId: undefined,
owner: undefined,
members: ''
}),
source
)
},
BO_REPOSITORY_COUNT: 3,
BO_REPOSITORY_FN: (source) => {
return Object.assign(
mock({
id: REPOSITORY_ID++,
name: '仓库@ctitle',
description: '@cparagraph',
logo: '@url'
}),
source
)
},
BO_MODULE_COUNT: 3,
BO_MODULE_FN: (source) => {
return Object.assign(
mock({
id: MODULE_ID++,
name: '模块@ctitle(4)',
description: '@cparagraph',
repositoryId: undefined,
creatorId: undefined
}),
source
)
},
BO_INTERFACE_COUNT: 3,
BO_INTERFACE_FN: (source) => {
return Object.assign(
mock({
id: INTERFACE_ID++,
name: '接口@ctitle(4)',
url: '/@word(5)/@word(5)/@word(5).json',
'method|1': methods,
description: '@cparagraph',
creatorId: undefined,
lockerId: undefined,
repositoryId: undefined,
moduleId: undefined
}),
source
)
},
BO_PROPERTY_COUNT: 6,
BO_PROPERTY_FN: (source) => {
return Object.assign(
mock({
id: PROPERTY_ID++,
'scope|1': scopes,
name: '@word(6)',
'type|1': types,
'value|1': values,
description: '@csentence',
creatorId: undefined,
repositoryId: undefined,
moduleId: undefined,
interfaceId: undefined
}),
source
)
}
}

@ -27,7 +27,7 @@ export const BO_USER_FN = () => mock({
export const BO_ORGANIZATION_COUNT = 3
export const BO_ORGANIZATION_FN = (source) => {
export const BO_ORGANIZATION_FN = (source: any) => {
return Object.assign(
mock({
id: ORGANIZATION_ID++,
@ -43,7 +43,7 @@ export const BO_ORGANIZATION_FN = (source) => {
}
export const BO_REPOSITORY_COUNT = 3
export const BO_REPOSITORY_FN = (source) => {
export const BO_REPOSITORY_FN = (source: any) => {
return Object.assign(
mock({
id: REPOSITORY_ID++,
@ -56,7 +56,7 @@ export const BO_REPOSITORY_FN = (source) => {
}
export const BO_MODULE_COUNT = 3
export const BO_MODULE_FN = (source) => {
export const BO_MODULE_FN = (source: any) => {
return Object.assign(
mock({
id: MODULE_ID++,
@ -69,7 +69,7 @@ export const BO_MODULE_FN = (source) => {
)
}
export const BO_INTERFACE_COUNT = 3
export const BO_INTERFACE_FN = (source) => {
export const BO_INTERFACE_FN = (source: any) => {
return Object.assign(
mock({
id: INTERFACE_ID++,
@ -86,7 +86,7 @@ export const BO_INTERFACE_FN = (source) => {
)
}
export const BO_PROPERTY_COUNT = 6
export const BO_PROPERTY_FN = (source) => {
export const BO_PROPERTY_FN = (source: any) => {
return Object.assign(
mock({
id: PROPERTY_ID++,

@ -1,152 +0,0 @@
const sequelize = require('../../src/models/sequelize')
const { User, Organization, Repository, Module, Interface, Property } = require('../../src/models/index')
const { BO_ADMIN, BO_MOZHI } = require('./bo')
const { BO_USER_FN, BO_ORGANIZATION_FN, BO_REPOSITORY_FN, BO_MODULE_FN, BO_INTERFACE_FN, BO_PROPERTY_FN } = require('./bo')
const { BO_USER_COUNT, BO_ORGANIZATION_COUNT, BO_REPOSITORY_COUNT, BO_MODULE_COUNT, BO_INTERFACE_COUNT, BO_PROPERTY_COUNT } = require('./bo')
const EMPTY_WHERE = { where: {} }
async function init () {
await sequelize.drop()
await sequelize.sync({
force: true,
logging: console.log
})
await User.destroy(EMPTY_WHERE)
await Organization.destroy(EMPTY_WHERE)
await Repository.destroy(EMPTY_WHERE)
await Module.destroy(EMPTY_WHERE)
await Interface.destroy(EMPTY_WHERE)
await Property.destroy(EMPTY_WHERE)
// 用户
await User.create(BO_ADMIN)
await User.create(BO_MOZHI)
for (let i = 0; i < BO_USER_COUNT; i++) {
await User.create(BO_USER_FN())
}
let users = await User.findAll()
// 用户 admin 仓库
for (let BO_REPOSITORY_INDEX = 0; BO_REPOSITORY_INDEX < BO_REPOSITORY_COUNT; BO_REPOSITORY_INDEX++) {
let repository = await Repository.create(
BO_REPOSITORY_FN({ creatorId: BO_ADMIN.id, ownerId: BO_ADMIN.id })
)
await repository.setMembers(
users.filter(user => user.id !== BO_ADMIN.id)
)
await initRepository(repository)
}
// 用户 mozhi 的仓库
for (let BO_REPOSITORY_INDEX = 0; BO_REPOSITORY_INDEX < BO_REPOSITORY_COUNT; BO_REPOSITORY_INDEX++) {
let repository = await Repository.create(
BO_REPOSITORY_FN({ creatorId: BO_MOZHI.id, ownerId: BO_MOZHI.id })
)
await repository.setMembers(
users.filter(user => user.id !== BO_MOZHI.id)
)
await initRepository(repository)
}
// 团队
for (let BO_ORGANIZATION_INDEX = 0; BO_ORGANIZATION_INDEX < BO_ORGANIZATION_COUNT; BO_ORGANIZATION_INDEX++) {
let organization = await Organization.create(
BO_ORGANIZATION_FN({ creatorId: BO_ADMIN.id, ownerId: BO_ADMIN.id })
)
await organization.setMembers(
users.filter(user => user.id !== BO_ADMIN.id)
)
// 团队的仓库
for (let BO_REPOSITORY_INDEX = 0; BO_REPOSITORY_INDEX < BO_REPOSITORY_COUNT; BO_REPOSITORY_INDEX++) {
let repository = await Repository.create(
BO_REPOSITORY_FN({ creatorId: BO_ADMIN.id, ownerId: BO_ADMIN.id, organizationId: organization.id })
)
await repository.setMembers(
users.filter(user => user.id !== BO_ADMIN.id)
)
await initRepository(repository)
}
}
}
async function initRepository (repository) {
// 模块
for (let BO_MODULE_INDEX = 0; BO_MODULE_INDEX < BO_MODULE_COUNT; BO_MODULE_INDEX++) {
let mod = await Module.create(
BO_MODULE_FN({ creatorId: repository.creatorId, repositoryId: repository.id })
)
await repository.addModule(mod)
// 接口
for (let BO_INTERFACE_INDEX = 0; BO_INTERFACE_INDEX < BO_INTERFACE_COUNT; BO_INTERFACE_INDEX++) {
let itf = await Interface.create(
BO_INTERFACE_FN({ creatorId: mod.creatorId, repositoryId: repository.id, moduleId: mod.id })
)
await mod.addInterface(itf)
// 属性
for (let BO_PROPERTY_INDEX = 0; BO_PROPERTY_INDEX < BO_PROPERTY_COUNT; BO_PROPERTY_INDEX++) {
let prop = await Property.create(
BO_PROPERTY_FN({ creatorId: itf.creatorId, repositoryId: repository.id, moduleId: mod.id, interfaceId: itf.id })
)
await itf.addProperty(prop)
}
}
}
}
async function after () {
let exclude = ['password', 'createdAt', 'updatedAt', 'deletedAt']
let repositories = await Repository.findAll({
attributes: { exclude: [] },
include: [
{ model: User, as: 'creator', attributes: { exclude }, required: true },
{ model: User, as: 'owner', attributes: { exclude }, required: true },
{ model: Organization, as: 'organization', attributes: { exclude }, required: false },
{ model: User, as: 'locker', attributes: { exclude }, required: false },
{ model: User, as: 'members', attributes: { exclude }, through: { attributes: [] }, required: true },
{ model: Module,
as: 'modules',
attributes: { exclude },
// through: { attributes: [] },
include: [
{
model: Interface,
as: 'interfaces',
attributes: { exclude },
// through: { attributes: [] },
include: [
{
model: Property,
as: 'properties',
attributes: { exclude },
// through: { attributes: [] },
required: true
}
],
required: true
}
],
required: true
}
],
offset: 0,
limit: 100
})
// console.log(JSON.stringify(repositories, null, 2))
console.log(repositories.map(item => item.toJSON()))
let admin = await User.findById(BO_ADMIN.id)
// for (let k in admin) console.log(k)
let owned = await admin.getOwnedOrganizations()
console.log(owned.map(item => item.toJSON()))
let mozhi = await User.findById(BO_MOZHI.id)
for (let k in mozhi) console.log(k)
let joined = await mozhi.getJoinedOrganizations()
console.log(joined.map(item => item.toJSON()))
}
module.exports = {
init,
after
}

@ -67,7 +67,7 @@ export async function init () {
}
}
async function initRepository (repository) {
async function initRepository (repository: any) {
// 模块
for (let BO_MODULE_INDEX = 0; BO_MODULE_INDEX < BO_MODULE_COUNT; BO_MODULE_INDEX++) {
let mod = await Module.create(
@ -135,12 +135,12 @@ export async function after () {
let admin = await User.findById(BO_ADMIN.id)
// for (let k in admin) console.log(k)
let owned: any = await admin.$get('ownedOrganizations')
console.log(owned.map(item => item.toJSON()))
console.log(owned.map((item: any) => item.toJSON()))
let mozhi = await User.findById(BO_MOZHI.id)
for (let k in mozhi) console.log(k)
let joined: any = await mozhi.$get('joinedOrganizations')
console.log(joined.map(item => item.toJSON()))
console.log(joined.map((item: any) => item.toJSON()))
}
module.exports = {

@ -1,10 +0,0 @@
const { init, after } = require('./delos')
/**
* initialize database
*/
async function main () {
await init()
await after()
}
main()

@ -11,7 +11,7 @@ const start = () => {
graceful({
servers: [server],
killTimeout: '10s',
error: function (err, throwErrorCount) {
error: function (err: Error, throwErrorCount: any) {
if (err.message) err.message += ` (uncaughtException throw ${throwErrorCount} times on pid:${process.pid})`
console.error(`[${now()}] worker#${process.pid}] ${err.message}`)
}

@ -5,7 +5,6 @@
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"allowJs": true,
"target": "es6",
"removeComments": true,
"sourceMap": true,
@ -13,6 +12,10 @@
"baseUrl": "./src",
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitUseStrict": true,
"rootDir": "./src",
"paths": {
"*": [

Loading…
Cancel
Save