import { DataSource } from 'apollo-datasource' ; import DataLoader from 'dataloader' ; import { Knex } from 'knex' ; import { EntityNotFoundError } from 'common/errors' ; import logger from 'common/logger' ; import { aws , es , knex } from 'connectors' ; import { Item , TableName } from 'definitions' ; interface InitLoaderInput { table : TableName ; mode : 'id' | 'uuid' ; } interface FindUniqueInput { table : TableName ; where : { id : string ; } ; } interface FindFirstInput { table : TableName ; select ? : string [ ] ; where : Record < string , any > ; whereIn ? : [ string , string [ ] ] ; orderBy ? : Array < { column : string ; order : 'asc' | 'desc' ; } > ; } interface FindManyInput { table : TableName ; select ? : string [ ] ; where ? : Record < string , any > ; whereIn ? : [ string , string [ ] ] ; orderBy ? : Array < { column : string ; order : 'asc' | 'desc' ; } > ; orderByRaw ? : string ; skip ? : number ; take ? : number ; } interface CreateInput { table : TableName ; data : Record < string , any > ; } interface UpdateInput { table : TableName ; where : Record < string , any > ; data : Record < string , any > ; } interface UpsertInput { table : TableName ; where ? : Record < string , any > ; create : Record < string , any > ; update : Record < string , any > ; } interface DeleteManyInput { table : TableName ; where ? : Record < string , any > ; whereIn ? : [ string , string [ ] ] ; } interface CountInput { table : TableName ; where : Record < string , any > ; whereIn ? : [ string , string [ ] ] ; } interface MaxInput { table : TableName ; where ? : Record < string , any > ; column : string ; } /**
 * This object is a container for data loaders or system wide services.
 */ export class AtomService extends DataSource { es : typeof es ; aws : typeof aws ; knex : Knex ; circleIdLoader : DataLoader < string , Item > ; draftIdLoader : DataLoader < string , Item > ; userIdLoader : DataLoader < string , Item > ; topicIdLoader : DataLoader < string , Item > ; chapterIdLoader : DataLoader < string , Item > ; constructor ( ) { super ( ) ; this . es = es ; this . aws = aws ; this . knex = knex ; this . circleIdLoader = this . initLoader ( { table : 'circle' , mode : 'id' } ) ; this . draftIdLoader = this . initLoader ( { table : 'draft' , mode : 'id' } ) ; this . userIdLoader = this . initLoader ( { table : 'user' , mode : 'id' } ) ; this . topicIdLoader = this . initLoader ( { table : 'topic' , mode : 'id' } ) ; this . chapterIdLoader = this . initLoader ( { table : 'chapter' , mode : 'id' } ) ; } /**
     * Initialize typical data loader.
     */ initLoader = ( { table , mode } ) => { const batchFn = async ( keys ) => { let records = await this . findMany ( { table , whereIn : [ mode , keys as string [ ] ] , } ) ; if ( records . findIndex ( ( item ) => ! item ) >= 0 ) { throw new EntityNotFoundError ( `Cannot find entity from ${ table }` ) ; } records = keys . map ( ( key ) => records . find ( ( r ) => r [ mode ] === key ) ) ; return records ; } ; return new DataLoader ( batchFn ) ; } ; /**
     * Find an unique record.
     *
     * A Prisma like method for retrieving a record by specified id.
     */ findUnique = async ( { table , where } ) => this . knex . select ( ) . from ( table ) . where ( where ) . first ( ) ; /**
     * Find the first record in rows.
     *
     * A Prisma like method for getting the first record in rows.
     */ findFirst = async ( { table , where , whereIn , orderBy } ) => { const query = this . knex . select ( ) . from ( table ) . where ( where ) ; if ( whereIn ) { query . whereIn ( ... whereIn ) ; } if ( orderBy ) { query . orderBy ( orderBy ) ; } return query . first ( ) ; } ; /**
     * Find multiple records by given clauses.
     *
     * A Prisma like mehtod for fetching records.
     */ findMany = async ( { table , select = [ '*' ] , where , whereIn , orderBy , orderByRaw , skip , take , } ) => { const query = this . knex . select ( select ) . from ( table ) ; if ( where ) { query . where ( where ) ; } if ( whereIn ) { query . whereIn ( ... whereIn ) ; } if ( orderBy ) { query . orderBy ( orderBy ) ; } if ( orderByRaw ) { query . orderByRaw ( orderByRaw ) ; } if ( skip ) { query . offset ( skip ) ; } if ( take || take === 0 ) { query . limit ( take ) ; } return query ; } ; /**
     * Create a new record by given data.
     *
     * A Prisma like method for creating one record.
     */ create = async ( { table , data } ) => { const [ record ] = await this . knex ( table ) . insert ( data ) . returning ( '*' ) ; return record ; } ; /**
     * Update an unique record.
     *
     * A Prisma like method for updating a record.
     */ update = async ( { table , where , data } ) => { const [ record ] = await this . knex . where ( where ) . update ( data ) . into ( table ) . returning ( '*' ) ; return record ; } ; /**
     * Update many records.
     *
     * A Prisma like method for updating many records.
     */ updateMany = async ( { table , where , data } ) => { const records = await this . knex . where ( where ) . update ( data ) . into ( table ) . returning ( '*' ) ; return records ; } ; /**
     * Upsert an unique record.
     *
     * A Prisma like method for updating or creating a record.
     */ upsert = async ( { table , where , create , update } ) => { const record = await this . knex ( table ) . select ( ) . where ( where as Record < string , any > ) . first ( ) ; if ( ! record ) { return this . knex ( table ) . insert ( create ) . returning ( '*' ) ; } const [ updatedRecord ] = await this . knex ( table ) . where ( where as Record < string , any > ) . update ( update ) . returning ( '*' ) ; return updatedRecord ; } ; /**
     * Delete records.
     *
     * A Prisma like method for deleting multiple records.
     */ deleteMany = async ( { table , where , whereIn } ) => { const action = this . knex ( table ) ; if ( where ) { action . where ( where as Record < string , any > ) ; } if ( whereIn ) { action . whereIn ( ... whereIn ) ; } await action . del ( ) ; } ; /**
     * Count records.
     *
     * A Prisma like method for counting records.
     */ count = async ( { table , where , whereIn } ) => { const action = this . knex . count ( ) . from ( table ) . where ( where ) ; if ( whereIn ) { action . whereIn ( ... whereIn ) ; } const record = await action . first ( ) ; return parseInt ( record ? ( record . count as string ) : '0' , 10 ) ; } ; /**
     * Max of given column.
     *
     * A Prisma like method for getting max.
     */ max = async ( { table , where , column } ) => { const record = await this . knex ( table ) . max ( column ) . where ( where as Record < string , any > ) . first ( ) ; return parseInt ( record ? ( record . count as string ) : '0' , 10 ) ; } ; /**
     * Find the url of an asset.
     */ findAssetUrl = async ( { where , } ) : Promise < string | null > => { const record = await this . findUnique ( { table : 'asset' , where , } ) ; return record && record . path ? `${ this . aws . s3Endpoint }/${ record . path }` : null ; } ; /**
     * Delete data stored in elastic search.
     */ deleteSearch = async ( { table , id } ) => { try { const result = await this . es . client . delete ( { index : table , id } ) ; return result ; } catch ( error ) { logger . error ( error ) ; } } ; }