UNPKG

obrigado-react-admin-backend-utils

Version:
432 lines (414 loc) 15.3 kB
import { Arg, Authorized, ClassType, Ctx, Field, Int, Mutation, ObjectType, Query, Resolver, } from 'type-graphql' import { BaseEntity, createQueryBuilder, EntityMetadata, EntitySchema, getRepository, In, ObjectLiteral, SelectQueryBuilder, ObjectType as TypeOrmObjectType } from 'typeorm' import { ApolloError } from 'apollo-server-errors' import {EntityUpdateHelper, EntityUpdateHelperOptions} from './EntityUpdateHelper' import {GQLReactAdminListParams} from "./types/GQLReactAdminListParams"; import {GQLReactAdminGetManyReferenceParams} from "./types/GQLReactAdminGetManyReferenceParams"; import {IdsList} from "./types/IdsList"; import {ReactAdminDataProvider} from "./types/ReactAdminDataProvider"; import {ReturnTypeFuncValue} from "type-graphql/dist/decorators/types" export function createBaseCrudResolver< ReturnType extends ReturnTypeFuncValue, CreateType extends ReturnTypeFuncValue , EntityType extends ClassType >(objectTypeCls: ClassType<ReturnType>, inputTypeCls: ClassType<CreateType>, ORMEntity: EntityType, updateHelperOptions?:Partial<EntityUpdateHelperOptions>):ClassType<ReactAdminDataProvider<ReturnType,CreateType,CreateType>> { console.warn("createBaseCrudResolver is deprececated") return createAdminResolver({ create:inputTypeCls, update:inputTypeCls, return:objectTypeCls, entity:ORMEntity, updateHelperOptions }) } /** * Generates base class for your resolver * @param config Resolver config */ export function createAdminResolver<ReturnType extends ReturnTypeFuncValue, CreateType extends ReturnTypeFuncValue, UpdateType extends ReturnTypeFuncValue, EntityType extends EntitySchema<any>>(config:{ /** GQL type which resolver returns*/ return:ReturnType, /** name part of all resolver methods*/ name?:string, /** GQL input type for entity creation*/ create:ClassType<CreateType>, /** GQL input type for entity update,optional*/ update?: ClassType<UpdateType>, /** TypeOrm Entity class*/ entity:ClassType, updateHelperOptions?:Partial<EntityUpdateHelperOptions> }): ClassType<ReactAdminDataProvider<EntityType,CreateType,UpdateType extends undefined?CreateType:UpdateType>>{ const ORMEntity=config.entity const ReturnGQLClass=config.return const CreateGQLClass=config.create const UpdateGQLClass=config.update || config.create // @ts-ignore const suffix =config.name || ORMEntity.name const updateHelperOptions=config.updateHelperOptions //@ts-ignore let entityAlias = ORMEntity.name.toLowerCase() @ObjectType(`${suffix}List`) class OutList { @Field(type => [ReturnGQLClass], { nullable: true }) data: any @Field(type => Int) total: number } @Resolver({ isAbstract: true }) class BaseResolver extends ReactAdminDataProvider<EntityType,CreateType,UpdateType>{ @Authorized('admin') @Query(type => OutList, { name: `admin${suffix}List`, }) // GET LIST async getListQuery( @Arg('params', type => GQLReactAdminListParams) params: GQLReactAdminListParams, @Ctx() context:any ) { return this.getList(params,context) } async getList( params: GQLReactAdminListParams, @Ctx() context:any){ const metadata =getRepository( ORMEntity) .metadata let query =getRepository( ORMEntity).createQueryBuilder( entityAlias) if (params.filter) { this.applyFilterToQuery(query, params, metadata,context) } await this.alterGetListQuery(query, params) let total = await query.getCount() if (params.pagination) { query .take(params.pagination.perPage) .skip( params.pagination.perPage * (params.pagination.page - 1), ) } if (params.sort) { query.orderBy( `${entityAlias}.${params.sort.field}`, //@ts-ignore params.sort.order, ) } let data = await query.getMany() // @ts-ignore return { data: data || [], total } } // GET ONE @Authorized('admin') @Query(type => ReturnGQLClass, { name: `admin${suffix}GetOne` }) async getOneQuery( @Arg('id') id: string, @Ctx() context:any ) { return this.getOne(id,context) } async getOne(id:string,context:any) { let where:ObjectLiteral={} where[this.primaryKey]=id // @ts-ignore return await getRepository(ORMEntity).findOne({ where }) } // GET_MANY @Authorized('admin') @Query(type => [ReturnGQLClass], { name: `admin${suffix}GetMany`, nullable: true, }) async getManyQuery( @Arg('ids', type => [Int]) ids: number[], @Ctx() context:any ){ return this.getMany(ids,context) } async getMany( ids: number[],context:any){ let where:ObjectLiteral={} where[this.primaryKey]=In(ids) // @ts-ignore return await getRepository(ORMEntity).find({where}) } // GET_MANY_REFERENCE @Authorized('admin') @Query(type => OutList, { name: `admin${suffix}GetManyReference`, nullable: true, }) async getManyReferenceQuery( @Arg('params', type => GQLReactAdminGetManyReferenceParams) params: GQLReactAdminGetManyReferenceParams, @Ctx() context:any ) { return this.getManyReference(params,context) } async getManyReference(params: GQLReactAdminGetManyReferenceParams,context:any){ let query =getRepository(ORMEntity).createQueryBuilder('entity').where( `entity.${params.target}=:id`, {id:params.id}, ) let total = await query.getCount() if (params.pagination) { query .take(params.pagination.perPage) .skip( params.pagination.perPage * (params.pagination.page - 1), ) } if (params.sort) { //@ts-ignore query.orderBy(params.sort.field, params.sort.order) } return await { total, data: await query.getMany() } } // UPDATE @Authorized('admin') @Mutation(type => ReturnGQLClass, { name: `admin${suffix}Update` }) async updateMutation( @Arg('id', type => Int) id: number, @Arg('data', type => UpdateGQLClass) data: UpdateType, @Ctx() context:any ) { return await this.update(id,data,context) } async update(id: number, data: UpdateType, context:any) { let where:ObjectLiteral={} where[this.primaryKey]=id let entity = await getRepository(ORMEntity).findOne({ where }) if (!entity) throw new ApolloError( 'Entity not found for id ' + id, 'NOT_FOUND', ) await EntityUpdateHelper.update(entity, data,updateHelperOptions) await getRepository(ORMEntity).save(entity) return entity } //UPDATE_MANY @Authorized('admin') @Mutation(type => IdsList, { name: `admin${suffix}UpdateMany` }) async updateManyMutation( @Arg('ids', type => [Int]) ids: number[], @Arg('data', type => UpdateGQLClass) data: UpdateType, @Ctx() context:any ) { return this.updateMany(ids,data,context) } async updateMany( ids: number[],data: UpdateType,context:any){ let list = await getRepository(ORMEntity).createQueryBuilder( entityAlias) .whereInIds(ids) .getMany() for (let entity of list) { await EntityUpdateHelper.update(entity, data,updateHelperOptions) await getRepository(ORMEntity).save(entity) } return { ids } } //CREATE @Authorized('admin') @Mutation(type => ReturnGQLClass, { name: `admin${suffix}Create` }) async createMutation(@Arg('data', type => CreateGQLClass) data: CreateType, @Ctx() context:any) { return await this.create(data,context) } async create( data: CreateType, context:any) { let entity = getRepository(ORMEntity).create() await EntityUpdateHelper.update(entity, data,updateHelperOptions) await getRepository(ORMEntity).save(entity) return entity } // DELETE @Authorized('admin') @Mutation(type => ReturnGQLClass, { name: `admin${suffix}Delete` }) async deleteMutation( @Arg('id', type => Int) id: number, @Ctx() context:any ) { return this.delete(id,context) } async delete(id:number,context:any){ // @ts-ignore const entity = await validateEntityRelations(ORMEntity, id,this.primaryKey) let clonedEntity={...entity} // @ts-ignore await getRepository(ORMEntity).remove(entity) return clonedEntity } // DELETE_MANY @Authorized('admin') @Mutation(type => IdsList, { name: `admin${suffix}DeleteMany` }) async deleteManyMutation( @Arg('ids', type => [Int]) ids: number[], @Ctx() context:any ) { return this.deleteMany(ids,context) } async deleteMany( ids: number[],context:any){ let errors:any[] = [] let removedIds:any[] = [] for (let id of ids) { try { // @ts-ignore let entity = await validateEntityRelations(ORMEntity, id,this.primaryKey) //@ts-ignore await getRepository(ORMEntity).remove(entity) removedIds.push(id) } catch (e) { errors.push(e.message) } } if (errors.length > 0) { throw new ApolloError(errors.join(';'), 'DELETION_FAILED') } return { ids: removedIds } } async alterGetListQuery( qb: SelectQueryBuilder<EntityType>, params: GQLReactAdminListParams, ) {} applyFilterToQuery( qb: SelectQueryBuilder<any>, params: GQLReactAdminListParams, metadata: EntityMetadata, context:any ) { if (params.filter) { let columnNames = metadata.columns.map(c => c.propertyName) for (let f of params.filter) { if (columnNames.includes(f.field)) { let value = {} as ObjectLiteral value[f.field] = f.value qb.andWhere( `${entityAlias}.${f.field}=:${f.field}`, value, ) } else if (f.field === 'q') { // build full text query let ftColumnNames = [] as string[] for (let ind of metadata.indices) { if (ind.isFulltext) { for (let column of ind.columns) { ftColumnNames.push( `${entityAlias}.${column.propertyName}`, ) } } } let searchValue = f.value const specialCharacters = ['+', '*', '@', '%', '-', '(', ')', '"'] specialCharacters.forEach(char => { searchValue = searchValue.replace(char, '') }) searchValue.length === 0 ? searchValue : searchValue = `${searchValue}*` if (ftColumnNames.length > 0) qb.andWhere( `match(${ftColumnNames.join( ',', )}) against (:ftQuery IN BOOLEAN MODE)`, { ftQuery: searchValue }, ) } } } } get primaryKey(){ const metadata = getRepository(ORMEntity).metadata return metadata.primaryColumns[0].databaseName } } //@ts-ignore return BaseResolver } export async function validateEntityRelations<ORM extends BaseEntity>( entityClass: any, id: number, primaryKey:string ): Promise<ORM> { const metadata =getRepository(entityClass).metadata as EntityMetadata let errors:any[] = [] let rQuery = await createQueryBuilder(entityClass, 'entity').where( `entity.${primaryKey}=:id`, { id }, ) for (let r of metadata.relations) { if ( r.onDelete !== 'CASCADE' && !r.isCascadeRemove && !r.isManyToOne ) { rQuery.loadRelationCountAndMap( `entity.${r.propertyName}_count`, `entity.${r.propertyName}`, ) } } let dataCounts = await rQuery.getOne() for (let r of metadata.relations) { //@ts-ignore if ( !r.isCascadeRemove && dataCounts && // @ts-ignore dataCounts[`${r.propertyName}_count`] > 0 ) { //@ts-ignore let count = dataCounts[`${r.propertyName}_count`] //@ts-ignore errors.push(`${r.type.name} (${count})`) } } if (errors.length > 0) { throw new ApolloError( `Entity ${id} has linked data : ${errors.join( ',', )}, unlink it first`, 'DELETION_FAILED', ) } //@ts-ignore return dataCounts } /* import {GQLAdministrator} from "./types/GQLAdministrator" import {GQLAdministratorInput} from "./types/GQLAdministratorInput" import {Administrator} from "./models/Administrator" const ZZZ=createAdminResolver({ return:GQLAdministrator, create:GQLAdministratorInput, update:GQLAdministrator, entity:Administrator }) class YYYY extends ZZZ{ async update(id: number, data: GQLAdministrator extends undefined ? GQLAdministratorInput : GQLAdministrator, context?: any): Promise<any> { return super.update(id, data, context) } }*/