import { removeUndef } from "@adpt/utils" ; import { DocumentNode as Query , ExecutionResult , GraphQLSchema , print as gqlPrint } from "graphql" ; import * as ld from "lodash" ; import { ObserverResponse } from "./plugin" ; import { adaptGqlExecute } from "./query_transforms" ; import { ObserverNameHolder } from "./registry" ; export interface Variables { [ n ] : any ; } interface PodExecutedQuery { query : string ; variables ? : Variables ; } export interface ObserversThatNeedData { [ name ] : PodExecutedQuery [ ] ; } export interface ExecutedQuery { query : Query ; variables ? : Variables ; } export interface Observations { [ observerName ] : { observations : ObserverResponse ; queries : ExecutedQuery [ ] ; } ; } export interface ObserverManagerDeployment { registerSchema ( name , schema , observations ) : void ; findObserverSchema ( observer ) : GraphQLSchema | undefined ; executedQueries ( ) : { [ name ] : ExecutedQuery [ ] ; } ; executedQueriesThatNeededData ( ) : { [ name ] : ExecutedQuery [ ] ; } ; executeQuery < R = any > ( observer , q , vars ? ) : Promise < ExecutionResult < R > > ; } export function createObserverManagerDeployment ( ) { return new ObserverManagerDeploymentImpl ( ) ; } type ExecutedQueryStorage = Map < string , { doc : Query ; vars : Set < Variables | undefined > ; } > ; interface Observable { schema : GraphQLSchema ; observations : ObserverResponse < any > ; executedQueries : ExecutedQueryStorage ; } function addExecutedQuery ( o , query ) { const key = gqlPrint ( query . query ) ; const entry = o . get ( key ) ; if ( entry === undefined ) { const vars = new Set < Variables | undefined > ( [ query . variables ] ) ; o . set ( key , { doc : query . query , vars : ld . cloneDeep ( vars ) } ) ; return ; } for ( const val of entry . vars ) { if ( ld . isEqual ( val , query . variables ) ) return ; } entry . vars . add ( query . variables ) ; } function flattenExecutedQueryStorage ( queries ) { const ret = [ ] ; for ( const val of queries . values ( ) ) { for ( const vars of val . vars . values ( ) ) { ret . push ( { query : val . doc , variables : vars } ) ; } } return ret ; } class ObserverManagerDeploymentImpl implements ObserverManagerDeployment { observable : { [ name ] : Observable ; } = { } ; needsData : { [ name ] : ExecutedQueryStorage ; } = { } ; registerSchema = < D = object > ( observer , schema , observations ) : void => { const name = observer . observerName ; if ( name in this . observable ) throw new Error ( "Cannot register schema with name: " + name ) ; this . observable [ name ] = { schema , observations , executedQueries : new Map < string , { doc : Query ; vars : Set < Variables | undefined > ; } > ( ) } ; } ; findObserverSchema = ( observer ) : GraphQLSchema | undefined => { return this . observable [ observer . observerName ] . schema ; } ; executedQueries = ( ) => { const ret = { } ; for ( const schemaName in this . observable ) { if ( ! Object . hasOwnProperty . call ( this . observable , schemaName ) ) continue ; ret [ schemaName ] = flattenExecutedQueryStorage ( this . observable [ schemaName ] . executedQueries ) ; } return ret ; } ; executedQueriesThatNeededData = ( ) => { const ret = { } ; for ( const schemaName in this . needsData ) { if ( ! Object . hasOwnProperty . call ( this . needsData , schemaName ) ) continue ; ret [ schemaName ] = flattenExecutedQueryStorage ( this . needsData [ schemaName ] ) ; } return ret ; } ; executeQuery = async < R = any > ( observer , q , vars ? ) : Promise < ExecutionResult < R > > => { const schemaName = observer . observerName ; if ( ! ( schemaName in this . observable ) ) throw new Error ( "Unknown observation schema queried: " + schemaName ) ; const { schema , observations , executedQueries } = this . observable [ schemaName ] ; const query = { query : q , variables : vars } ; addExecutedQuery ( executedQueries , query ) ; const ret = await Promise . resolve ( adaptGqlExecute < R > ( schema , q , observations . data , observations . context , vars ) ) ; if ( ret . errors ) { const needDataErr = ret . errors . find ( ( e ) => e . message . startsWith ( "Adapt Observer Needs Data:" ) ) ; if ( needDataErr !== undefined ) { if ( this . needsData [ schemaName ] === undefined ) { this . needsData [ schemaName ] = new Map < string , { doc : Query ; vars : Set < Variables | undefined > ; } > ( ) ; } addExecutedQuery ( this . needsData [ schemaName ] , query ) ; } } return ret ; } ; } type SimplifyReturns = ReturnType < typeof simplifyNeedsData > ; export function simplifyNeedsData ( nd ) { const ret = { } ; for ( const obsName in nd ) { if ( ! Object . hasOwnProperty . call ( nd , obsName ) ) continue ; ret [ obsName ] = nd [ obsName ] . map ( ( q ) => { const vars = q . variables ? JSON . parse ( JSON . stringify ( q . variables ) ) : undefined ; return removeUndef ( ( { query : gqlPrint ( q . query ) , variables : vars } ) ) ; } ) as { query : string ; variables ? : Variables ; } [ ] ; } return ret ; }