Use EntitySchema and interfaces over typeorm decorators

This commit is contained in:
Jerko Steiner 2019-09-18 15:17:42 +07:00
parent 9c9c41aeaf
commit d4378719c5
33 changed files with 774 additions and 410 deletions

View File

@ -21,9 +21,13 @@ rules:
exports: always-multiline
functions: always-multiline
# semi:
# - warn
# - never
semi:
- warn
- never
quotes:
- warn
- single
- allowTemplateLiterals: true
# interface-name-prefix:
'import/no-extraneous-dependencies': off
'@typescript-eslint/member-delimiter-style':

649
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,8 +22,6 @@
"@rondo.dev/db-typeorm": "file:packages/db-typeorm"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^2.2.0",
"@typescript-eslint/parser": "^2.2.0",
"@types/bcrypt": "^3.0.0",
"@types/body-parser": "^1.17.0",
"@types/cls-hooked": "^4.2.1",
@ -51,6 +49,8 @@
"@types/std-mocks": "^1.0.0",
"@types/supertest": "^2.0.7",
"@types/uuid": "^3.4.4",
"@typescript-eslint/eslint-plugin": "^2.2.0",
"@typescript-eslint/parser": "^2.2.0",
"axios": "^0.19.0",
"bloomer": "^0.6.5",
"browser-unpack": "^1.4.2",
@ -96,6 +96,7 @@
"ts-transformer-keys": "^0.3.5",
"tsify": "^4.0.1",
"ttypescript": "^1.5.7",
"typeorm": "^0.2.19",
"typescript": "^3.6.3",
"typescript-tslint-plugin": "^0.5.4",
"uglify-js": "^3.5.3",
@ -103,4 +104,4 @@
"watchify": "^3.11.1"
},
"name": "node"
}
}

View File

@ -63,4 +63,4 @@ export interface UserTeam {
id: number
createDate: string
updateDate: string
}
}

View File

@ -1,4 +1,5 @@
export * from './Config'
export * from './ConfigReader'
import { ConfigReader } from './ConfigReader'
export default ConfigReader

View File

@ -69,7 +69,7 @@ export async function exec(...argv: string[]) {
export async function createMigration(...argv: string[]) {
const args = argparse({
name: arg('string', {required: true, positional: true}),
project: arg('string', {default: '.'}),
project: arg('string', {alias: 'p', default: '.'}),
help: arg('boolean', {alias: 'h'}),
})
.parse(argv)
@ -82,7 +82,7 @@ export async function createMigration(...argv: string[]) {
if (!typeorm) {
throw new Error('typeorm not found')
}
await run('ts-node', [typeorm, 'migration:create', '--name', name], project)
await run('ts-node', [typeorm, 'migration:generate', '--name', name], project)
}
function findTsConfig(file: string): string {

View File

@ -19,9 +19,9 @@ app:
migrationsRun: false
logging: true
entities:
- src/entities/index.ts
- src/entity-schemas/*Entity.ts
migrations:
- src/migrations/index.ts
cli:
migrationsDir: src/migrations
entitiesDir: src/entities
entitiesDir: src/entity-schemas

View File

@ -1,3 +1,3 @@
const {ConfigReader} = require('@rondo.dev/server')
const {ConfigReader} = require('@rondo.dev/config')
const config = new ConfigReader(__dirname).read()
module.exports = JSON.parse(JSON.stringify(config.get('app.db')))

View File

@ -20,10 +20,10 @@ export interface ServerBootstrapParams {
}
// tslint:disable-next-line
function getFunctions(obj: object): Function[] {
function objToArray(obj: object): Function[] {
return Object.keys(obj)
.filter(k => !k.startsWith('_'))
.map(k => (obj as any)[k])
.filter(f => typeof f === 'function')
}
export class ServerBootstrap implements Bootstrap {
@ -45,10 +45,10 @@ export class ServerBootstrap implements Bootstrap {
db: {
...params.config.app.db,
entities: params.entities
? getFunctions(params.entities)
? objToArray(params.entities)
: params.config.app.db.entities,
migrations: params.migrations
? getFunctions(params.migrations)
? objToArray(params.migrations)
: params.config.app.db.migrations,
},
},

View File

@ -1,7 +1,7 @@
import { ServerBootstrap } from './application'
import { configureServer } from './application/configureServer'
import { config } from './config'
import * as entities from './entities'
import * as entities from './entity-schemas'
import * as migrations from './migrations'
export default new ServerBootstrap({

View File

@ -1,17 +1,17 @@
import { CreateDateColumn, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
// import { CreateDateColumn, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
const transformer = {
from: (value: Date) => !isNaN(value.getTime()) ? value.toISOString() : value,
to: (value: undefined | null | string) => value ? new Date(value) : value,
}
// const transformer = {
// from: (value: Date) => !isNaN(value.getTime()) ? value.toISOString() : value,
// to: (value: undefined | null | string) => value ? new Date(value) : value,
// }
export abstract class BaseEntity {
@PrimaryGeneratedColumn({type: 'bigint'})
id!: number
// export abstract class BaseEntity {
// @PrimaryGeneratedColumn({type: 'bigint'})
// id!: number
@CreateDateColumn({transformer, type: 'datetime'})
createDate!: string
// @CreateDateColumn({transformer, type: 'datetime'})
// createDate!: string
@UpdateDateColumn({transformer, type: 'datetime'})
updateDate!: string
}
// @UpdateDateColumn({transformer, type: 'datetime'})
// updateDate!: string
// }

View File

@ -1,8 +1,8 @@
import { Column, Entity } from 'typeorm'
import { BaseEntity } from './BaseEntity'
// import { Column, Entity } from 'typeorm'
// import { BaseEntity } from './BaseEntity'
@Entity()
export class Role extends BaseEntity {
@Column({ unique: true })
name!: string
}
// @Entity()
// export class Role extends BaseEntity {
// @Column({ unique: true })
// name!: string
// }

View File

@ -1,22 +1,22 @@
import { Column, Entity, Index, ManyToOne, PrimaryColumn } from 'typeorm'
import { DefaultSession } from '../session/DefaultSession'
import { User } from './User'
// import { Column, Entity, Index, ManyToOne, PrimaryColumn } from 'typeorm'
// import { DefaultSession } from '../session/DefaultSession'
// import { User } from './User'
@Entity()
export class Session implements DefaultSession {
@PrimaryColumn()
id!: string
// @Entity()
// export class Session implements DefaultSession {
// @PrimaryColumn()
// id!: string
@Index()
@Column({type: 'bigint'})
expiredAt: number = Date.now()
// @Index()
// @Column({type: 'bigint'})
// expiredAt: number = Date.now()
@ManyToOne(type => User, user => user.sessions)
user?: User
// @ManyToOne(type => User, user => user.sessions)
// user?: User
@Column({ nullable: true })
userId!: number
// @Column({ nullable: true })
// userId!: number
@Column('text')
json = ''
}
// @Column('text')
// json = ''
// }

View File

@ -1,20 +1,20 @@
import { Column, Entity, Index, ManyToOne, OneToMany } from 'typeorm'
import { BaseEntity } from './BaseEntity'
import { User } from './User'
import { UserTeam } from './UserTeam'
// import { Column, Entity, Index, ManyToOne, OneToMany } from 'typeorm'
// import { BaseEntity } from './BaseEntity'
// import { User } from './User'
// import { UserTeam } from './UserTeam'
@Entity()
export class Team extends BaseEntity {
@Column()
name!: string
// @Entity()
// export class Team extends BaseEntity {
// @Column()
// name!: string
@Column()
@Index()
userId!: number
// @Column()
// @Index()
// userId!: number
@ManyToOne(type => User)
user?: User
// @ManyToOne(type => User)
// user?: User
@OneToMany(type => UserTeam, userTeam => userTeam.team)
userTeams!: UserTeam[]
}
// @OneToMany(type => UserTeam, userTeam => userTeam.team)
// userTeams!: UserTeam[]
// }

View File

@ -1,27 +1,27 @@
import { Column, Entity, OneToMany } from 'typeorm'
import { BaseEntity } from './BaseEntity'
import { Session } from './Session'
import { UserEmail } from './UserEmail'
import { UserTeam } from './UserTeam'
// import { Column, Entity, OneToMany } from 'typeorm'
// import { BaseEntity } from './BaseEntity'
// import { Session } from './Session'
// import { UserEmail } from './UserEmail'
// import { UserTeam } from './UserTeam'
@Entity()
export class User extends BaseEntity {
@Column()
firstName!: string
// @Entity()
// export class User extends BaseEntity {
// @Column()
// firstName!: string
@Column()
lastName!: string
// @Column()
// lastName!: string
@OneToMany(type => UserEmail, email => email.user)
emails!: UserEmail[]
// @OneToMany(type => UserEmail, email => email.user)
// emails!: UserEmail[]
// bcrypt encoded password
@Column({nullable: true, length: '60', select: false})
password?: string
// // bcrypt encoded password
// @Column({nullable: true, length: '60', select: false})
// password?: string
@OneToMany(type => Session, session => session.user)
sessions!: Session[]
// @OneToMany(type => Session, session => session.user)
// sessions!: Session[]
@OneToMany(type => UserTeam, userTeam => userTeam.user)
userTeams!: UserTeam[]
}
// @OneToMany(type => UserTeam, userTeam => userTeam.user)
// userTeams!: UserTeam[]
// }

View File

@ -1,15 +1,15 @@
import { Column, Entity, ManyToOne } from 'typeorm'
import { BaseEntity } from './BaseEntity'
import { User } from './User'
// import { Column, Entity, ManyToOne } from 'typeorm'
// import { BaseEntity } from './BaseEntity'
// import { User } from './User'
@Entity()
export class UserEmail extends BaseEntity {
@Column({unique: true})
email!: string
// @Entity()
// export class UserEmail extends BaseEntity {
// @Column({unique: true})
// email!: string
@ManyToOne(type => User, user => user.emails)
user?: User
// @ManyToOne(type => User, user => user.emails)
// user?: User
@Column()
userId?: number = undefined
}
// @Column()
// userId?: number = undefined
// }

View File

@ -1,26 +1,26 @@
import { Column, Entity, ManyToOne } from 'typeorm'
import { BaseEntity } from './BaseEntity'
import { Role } from './Role'
import { Team } from './Team'
import { User } from './User'
// import { Column, Entity, ManyToOne } from 'typeorm'
// import { BaseEntity } from './BaseEntity'
// import { Role } from './Role'
// import { Team } from './Team'
// import { User } from './User'
@Entity()
export class UserTeam extends BaseEntity {
@ManyToOne(type => User, user => user.userTeams)
user!: User
// @Entity()
// export class UserTeam extends BaseEntity {
// @ManyToOne(type => User, user => user.userTeams)
// user!: User
@Column()
userId!: number
// @Column()
// userId!: number
@ManyToOne(type => Team, team => team.userTeams)
team?: Team
// @ManyToOne(type => Team, team => team.userTeams)
// team?: Team
@Column()
teamId!: number
// @Column()
// teamId!: number
@ManyToOne(type => Role)
role?: Role
// @ManyToOne(type => Role)
// role?: Role
@Column()
roleId!: number
}
// @Column()
// roleId!: number
// }

View File

@ -1,7 +1,7 @@
export * from './BaseEntity'
export * from './Role'
export * from './Session'
export * from './Team'
export * from './User'
export * from './UserEmail'
export * from './UserTeam'
// export * from './BaseEntity'
// export * from './Role'
// export * from './Session'
// export * from './Team'
// export * from './User'
// export * from './UserEmail'
// export * from './UserTeam'

View File

@ -0,0 +1,25 @@
import { EntitySchemaColumnOptions } from 'typeorm'
const transformer = {
from: (value: Date) => !isNaN(value.getTime()) ? value.toISOString() : value,
to: (value: undefined | null | string) => value ? new Date(value) : value,
}
export const BaseEntitySchemaPart: Record<
'id' | 'createDate' | 'updateDate', EntitySchemaColumnOptions> = {
id: {
type: 'integer',
primary: true,
generated: true,
},
createDate: {
type: 'datetime',
createDate: true,
transformer,
},
updateDate: {
type: 'datetime',
updateDate: true,
transformer,
},
}

View File

@ -0,0 +1,14 @@
import { EntitySchema } from 'typeorm'
import { Role } from '@rondo.dev/common'
import { BaseEntitySchemaPart } from './BaseEntitySchemaPart'
export const RoleEntity = new EntitySchema<Role>({
name: 'role',
columns: {
...BaseEntitySchemaPart,
name: {
type: String,
unique: true,
},
},
})

View File

@ -0,0 +1,34 @@
import { Session } from '@rondo.dev/common'
import { EntitySchema } from 'typeorm'
export const SessionEntity = new EntitySchema<Session>({
name: 'session',
columns: {
id: {
type: String,
primary: true,
},
expiredAt: {
type: Number,
// default: () => Date.now(),
},
userId: {
type: Number,
nullable: true,
},
json: {
type: 'text',
// default: '',
},
},
relations: {
user: {
type: 'many-to-one',
target: 'user',
inverseSide: 'sessions',
},
},
indices: [{
columns: ['expiredAt'],
}],
})

View File

@ -0,0 +1,29 @@
import { EntitySchema } from 'typeorm'
import { Team } from '@rondo.dev/common'
import { BaseEntitySchemaPart } from './BaseEntitySchemaPart'
export const TeamEntity = new EntitySchema<Team>({
name: 'team',
columns: {
...BaseEntitySchemaPart,
name: {
type: String,
},
userId: {
type: Number,
},
},
relations: {
user: {
type: 'many-to-one',
target: 'user',
},
userTeams: {
type: 'one-to-many',
target: 'user_team',
},
},
indices: [{
columns: ['userId'],
}],
})

View File

@ -0,0 +1,23 @@
import { BaseEntitySchemaPart } from './BaseEntitySchemaPart'
import { EntitySchema } from 'typeorm'
import { UserEmail } from '@rondo.dev/common'
export const UserEmailEntity = new EntitySchema<UserEmail>({
name: 'user_email',
columns: {
...BaseEntitySchemaPart,
email: {
type: String,
unique: true,
},
userId: {
type: Number,
},
},
relations: {
user: {
type: 'many-to-one',
target: 'user',
},
},
})

View File

@ -0,0 +1,36 @@
import { BaseEntitySchemaPart } from './BaseEntitySchemaPart'
import { EntitySchema } from 'typeorm'
import { User } from '@rondo.dev/common'
export const UserEntity = new EntitySchema<User>({
name: 'user',
columns: {
...BaseEntitySchemaPart,
firstName: {
type: String,
},
lastName: {
type: String,
},
password: {
type: String,
length: 60,
select: false,
nullable: true,
},
},
relations: {
emails: {
type: 'one-to-many',
target: 'user_email',
},
sessions: {
type: 'one-to-many',
target: 'session',
},
userTeams: {
type: 'one-to-many',
target: 'team',
},
},
})

View File

@ -0,0 +1,33 @@
import { BaseEntitySchemaPart } from './BaseEntitySchemaPart'
import { EntitySchema } from 'typeorm'
import { UserTeam } from '@rondo.dev/common'
export const UserTeamEntity = new EntitySchema<UserTeam>({
name: 'user_team',
columns: {
...BaseEntitySchemaPart,
userId: {
type: Number,
},
teamId: {
type: Number,
},
roleId: {
type: Number,
},
},
relations: {
user: {
type: 'many-to-one',
target: 'user',
},
team: {
type: 'many-to-one',
target: 'team',
},
role: {
type: 'many-to-one',
target: 'role',
},
},
})

View File

@ -0,0 +1,6 @@
export * from './RoleEntity'
export * from './SessionEntity'
export * from './TeamEntity'
export * from './UserEmailEntity'
export * from './UserEntity'
export * from './UserTeamEntity'

View File

@ -5,7 +5,7 @@ if (require.main === module) {
}
export * from './application'
export * from './cli'
export * from './entities'
export * from './entity-schemas'
export * from './error'
export * from './logger'
export * from './middleware'

View File

@ -1,12 +1,13 @@
import { Session } from '@rondo.dev/common'
import { TypeORMTransactionManager } from '@rondo.dev/db-typeorm'
import ExpressSession from 'express-session'
import { UrlWithStringQuery } from 'url'
import { Session as SessionEntity } from '../entities/Session'
import { SessionEntity } from '../entity-schemas'
import { apiLogger } from '../logger'
import { DefaultSession } from '../session'
import { SessionStore } from '../session/SessionStore'
import { Handler } from './Handler'
import { Middleware } from './Middleware'
import { DefaultSession } from '../session'
import { TypeORMTransactionManager } from '@rondo.dev/db-typeorm'
export interface SessionMiddlewareParams {
transactionManager: TypeORMTransactionManager
@ -45,7 +46,7 @@ export class SessionMiddleware implements Middleware {
protected buildSession = (
sessionData: Express.SessionData,
sess: DefaultSession,
): SessionEntity => {
): Session => {
return {...sess, userId: sessionData.userId }
}

View File

@ -1,10 +1,9 @@
import { TeamAddUserParams, TeamCreateParams, TeamRemoveParams, TeamService, TeamUpdateParams, trim, UserPermissions } from '@rondo.dev/common'
import { TeamAddUserParams, TeamCreateParams, TeamRemoveParams, TeamService, TeamUpdateParams, trim, UserPermissions, UserTeam } from '@rondo.dev/common'
import { UserInTeam } from '@rondo.dev/common/lib/team/UserInTeam'
import Validator from '@rondo.dev/validator'
import { Team } from '../entities/Team'
import { UserTeam } from '../entities/UserTeam'
import { ensureLoggedIn, Context, RPC } from './RPC'
import { TypeORMDatabase } from '@rondo.dev/db-typeorm'
import { TeamEntity, UserTeamEntity } from '../entity-schemas'
@ensureLoggedIn
export class SQLTeamService implements RPC<TeamService> {
@ -22,7 +21,7 @@ export class SQLTeamService implements RPC<TeamService> {
.ensure('userId')
.throw()
const team = await this.db.getRepository(Team).save({
const team = await this.db.getRepository(TeamEntity).save({
name,
userId,
})
@ -45,10 +44,10 @@ export class SQLTeamService implements RPC<TeamService> {
userId,
})
await this.db.getRepository(UserTeam)
await this.db.getRepository(UserTeamEntity)
.delete({teamId: id, userId})
await this.db.getRepository(Team)
await this.db.getRepository(TeamEntity)
.delete(id)
return {id}
@ -62,7 +61,7 @@ export class SQLTeamService implements RPC<TeamService> {
userId,
})
await this.db.getRepository(Team)
await this.db.getRepository(TeamEntity)
.update({
id,
}, {
@ -74,7 +73,7 @@ export class SQLTeamService implements RPC<TeamService> {
async addUser(context: Context, params: TeamAddUserParams) {
const {userId, teamId, roleId} = params
await this.db.getRepository(UserTeam)
await this.db.getRepository(UserTeamEntity)
.save({userId, teamId, roleId})
const userTeam = await this._createFindUserInTeamQuery()
@ -97,20 +96,20 @@ export class SQLTeamService implements RPC<TeamService> {
})
// TODO check if this is the last admin team member
await this.db.getRepository(UserTeam)
await this.db.getRepository(UserTeamEntity)
.delete({userId, teamId, roleId})
return {teamId, userId, roleId}
}
async findOne(context: Context, id: number) {
return this.db.getRepository(Team).findOne(id)
return this.db.getRepository(TeamEntity).findOne(id)
}
async find(context: Context) {
const userId = context.user!.id
return this.db.getRepository(Team)
return this.db.getRepository(TeamEntity)
.createQueryBuilder('team')
.select('team')
.innerJoin('team.userTeams', 'ut')
@ -149,7 +148,7 @@ export class SQLTeamService implements RPC<TeamService> {
}
protected _createFindUserInTeamQuery() {
return this.db.getRepository(UserTeam)
return this.db.getRepository(UserTeamEntity)
.createQueryBuilder('ut')
.select('ut')
.innerJoinAndSelect('ut.user', 'user')

View File

@ -1,9 +1,8 @@
import { UserService } from '@rondo.dev/common'
import { TypeORMDatabase } from '@rondo.dev/db-typeorm'
import { hash } from 'bcrypt'
import { User } from '../entities/User'
import { UserEmail } from '../entities/UserEmail'
import { Context, ensureLoggedIn, RPC } from './RPC'
import { UserEntity, UserEmailEntity } from '../entity-schemas'
const SALT_ROUNDS = 10
// const MIN_PASSWORD_LENGTH = 10
@ -16,7 +15,7 @@ export class SQLUserService implements RPC<UserService> {
const userId = context.user!.id
// current user should always exist in the database
const user = (await this.db.getRepository(User).findOne(userId, {
const user = (await this.db.getRepository(UserEntity).findOne(userId, {
relations: ['emails'],
}))!
@ -29,7 +28,7 @@ export class SQLUserService implements RPC<UserService> {
}
async findUserByEmail(context: Context, email: string) {
const userEmail = await this.db.getRepository(UserEmail)
const userEmail = await this.db.getRepository(UserEmailEntity)
.findOne({ email }, {
relations: ['user'],
})

View File

@ -4,8 +4,7 @@ import Validator from '@rondo.dev/validator'
import { compare, hash } from 'bcrypt'
import { validate as validateEmail } from 'email-validator'
import createError from 'http-errors'
import { User } from '../entities/User'
import { UserEmail } from '../entities/UserEmail'
import { UserEntity, UserEmailEntity } from '../entity-schemas'
const SALT_ROUNDS = 10
const MIN_PASSWORD_LENGTH = 10
@ -35,11 +34,11 @@ export class SQLAuthService implements AuthService {
.throw()
const password = await this.hash(payload.password)
const user = await this.db.getRepository(User).save({
const user = await this.db.getRepository(UserEntity).save({
...newUser,
password,
})
await this.db.getRepository(UserEmail).save({
await this.db.getRepository(UserEmailEntity).save({
email: newUser.username,
userId: user.id,
})
@ -50,7 +49,7 @@ export class SQLAuthService implements AuthService {
}
async findOne(id: number) {
const user = await this.db.getRepository(User).findOne(id, {
const user = await this.db.getRepository(UserEntity).findOne(id, {
relations: ['emails'],
})
@ -67,7 +66,7 @@ export class SQLAuthService implements AuthService {
}
async findUserByEmail(email: string) {
const userEmail = await this.db.getRepository(UserEmail)
const userEmail = await this.db.getRepository(UserEmailEntity)
.findOne({ email }, {
relations: ['user'],
})
@ -92,7 +91,7 @@ export class SQLAuthService implements AuthService {
newPassword: string
}) {
const {userId, oldPassword, newPassword} = params
const userRepository = this.db.getRepository(User)
const userRepository = this.db.getRepository(UserEntity)
const user = await userRepository
.createQueryBuilder('user')
.select('user')
@ -110,7 +109,7 @@ export class SQLAuthService implements AuthService {
async validateCredentials(credentials: Credentials) {
const {username, password} = credentials
const user = await this.db.getRepository(User)
const user = await this.db.getRepository(UserEntity)
.createQueryBuilder('user')
.select('user')
.addSelect('user.password')
@ -132,7 +131,7 @@ export class SQLAuthService implements AuthService {
}
async findUserEmails(userId: number) {
return this.db.getRepository(UserEmail).find({ userId })
return this.db.getRepository(UserEmailEntity).find({ userId })
}
protected async hash(password: string): Promise<string> {

View File

@ -1,14 +1,14 @@
import { UserPermissions } from '@rondo.dev/common'
import createError from 'http-errors'
import { UserTeam } from '../entities/UserTeam'
import { TypeORMDatabase } from '@rondo.dev/db-typeorm'
import { UserTeamEntity } from '../entity-schemas'
export class SQLUserPermissions implements UserPermissions {
constructor(protected readonly db: TypeORMDatabase) {}
async belongsToTeam(params: {userId: number, teamId: number}) {
const {userId, teamId} = params
const result = await this.db.getRepository(UserTeam)
const result = await this.db.getRepository(UserTeamEntity)
.findOne({
where: {userId, teamId},
})

View File

@ -8,7 +8,6 @@ import supertest from 'supertest'
import { Connection, QueryRunner } from 'typeorm'
import { AppServer } from '../application/AppServer'
import { Bootstrap } from '../application/Bootstrap'
import { Role } from '../entities/Role'
import { RequestTester } from './RequestTester'
import { TypeORMTransactionManager } from '@rondo.dev/db-typeorm'
import { TRANSACTION_ID, TRANSACTION } from '@rondo.dev/db'
@ -74,12 +73,6 @@ export class TestUtils<T extends Routes> {
})
}
async createRole(name: string) {
return this.transactionManager
.getRepository(Role)
.save({name})
}
async getError(promise: Promise<unknown>): Promise<Error> {
let error!: Error
try {