import { BoosterConfig , GraphQLOperation , InvalidParameterError , Logger , NotAuthorizedError , NotFoundError , ReadModelInterface , ReadModelListResult , ReadModelRequestEnvelope , ReadOnlyNonEmptyArray , SubscriptionEnvelope , } from '@boostercloud/framework-types' ; import { Booster } from './booster' ; import { BoosterAuth } from './booster-auth' ; import { applyReadModelRequestBeforeFunctions } from './services/filter-helpers' ; export class BoosterReadModelsReader { public constructor ( readonly config , readonly logger ) { } public async findById ( readModelRequest ) { this . validateByIdRequest ( readModelRequest ) ; const readModelMetadata = this . config . readModels [ readModelRequest . class . name ] ; const readModelTransformedRequest = applyReadModelRequestBeforeFunctions ( readModelRequest , readModelMetadata . before ) ; const key = readModelTransformedRequest . key ; if ( ! key ) { throw 'Tried to run a findById operation without providing a key. An ID is required to perform this operation.' ; } return Booster . readModel ( readModelMetadata . class ) . findById ( key . id , key . sequenceKey ) ; } public async search ( readModelRequest ) { this . validateRequest ( readModelRequest ) ; const readModelMetadata = this . config . readModels [ readModelRequest . class . name ] ; const readModelTransformedRequest = applyReadModelRequestBeforeFunctions ( readModelRequest , readModelMetadata . before ) ; return Booster . readModel ( readModelMetadata . class ) . filter ( readModelTransformedRequest . filters ) . limit ( readModelTransformedRequest . limit ) . afterCursor ( readModelTransformedRequest . afterCursor ) . paginatedVersion ( readModelTransformedRequest . paginatedVersion ) . search ( ) ; } public async subscribe ( connectionID , readModelRequest , operation ) { this . validateRequest ( readModelRequest ) ; return this . processSubscription ( connectionID , readModelRequest , operation ) ; } public async unsubscribe ( connectionID , subscriptionID ) { return this . config . provider . readModels . deleteSubscription ( this . config , this . logger , connectionID , subscriptionID ) ; } public async unsubscribeAll ( connectionID ) { return this . config . provider . readModels . deleteAllSubscriptions ( this . config , this . logger , connectionID ) ; } private validateByIdRequest ( readModelByIdRequest ) { this . logger . debug ( 'Validating the following read model by id request: ' , readModelByIdRequest ) ; if ( ! readModelByIdRequest . version ) { throw new InvalidParameterError ( 'The required request "version" was not present' ) ; } const readModelMetadata = this . config . readModels [ readModelByIdRequest . class . name ] ; if ( ! readModelMetadata ) { throw new NotFoundError ( `Could not find read model ${ readModelByIdRequest . class . name }` ) ; } if ( ! BoosterAuth . isUserAuthorized ( readModelMetadata . authorizedRoles , readModelByIdRequest . currentUser ) ) { throw new NotAuthorizedError ( `Access denied for read model ${ readModelByIdRequest . class . name }` ) ; } if ( readModelByIdRequest ?. key ?. sequenceKey && readModelByIdRequest . key . sequenceKey . name !== this . config . readModelSequenceKeys [ readModelByIdRequest . class . name ] ) { throw new InvalidParameterError ( `Could not find a sort key defined for ${ readModelByIdRequest . class . name } named '${ readModelByIdRequest . key . sequenceKey . name }'.` ) ; } } private validateRequest ( readModelRequest ) { this . logger . debug ( 'Validating the following read model request: ' , readModelRequest ) ; if ( ! readModelRequest . version ) { throw new InvalidParameterError ( 'The required request "version" was not present' ) ; } const readModelMetadata = this . config . readModels [ readModelRequest . class . name ] ; if ( ! readModelMetadata ) { throw new NotFoundError ( `Could not find read model ${ readModelRequest . class . name }` ) ; } if ( ! BoosterAuth . isUserAuthorized ( readModelMetadata . authorizedRoles , readModelRequest . currentUser ) ) { throw new NotAuthorizedError ( `Access denied for read model ${ readModelRequest . class . name }` ) ; } } private async processSubscription ( connectionID , readModelRequest , operation ) { this . logger . info ( `Processing subscription of connection '${ connectionID }' to read model '${ readModelRequest . class . name }' with the following data: ` , readModelRequest ) ; const readModelMetadata = this . config . readModels [ readModelRequest . class . name ] ; const newReadModelRequest = applyReadModelRequestBeforeFunctions ( readModelRequest , readModelMetadata . before ) ; const nowEpoch = Math . floor ( new Date ( ) . getTime ( ) / 1000 ) ; const subscription = { ... newReadModelRequest , expirationTime : nowEpoch + this . config . subscriptions . maxDurationInSeconds , connectionID , operation , } ; return this . config . provider . readModels . subscribe ( this . config , this . logger , subscription ) ; } }