import fetch from 'isomorphic-unfetch' ; import { Client , GraphQLRequest , createRequest as createRequestUrql , } from '@urql/core' ; import { DocumentNode } from 'graphql' ; import { pipe , subscribe } from 'wonka' ; import { ErrorConnection , ErrorUnexpectedResult } from '@aragon/connect-core' ; import { SubscriptionCallback , SubscriptionHandler , } from '@aragon/connect-types' ; import { ParseFunction , QueryResult } from '../types' ; const POLL_INTERVAL_DEFAULT = 13 * 1000 ; function createRequest ( query , args ) { if ( query . definitions ) { query = { ... query , definitions : query . definitions . map ( ( definition ) => ( { ... definition , operation : 'query' , } ) ) , } ; } return createRequestUrql ( query , args ) ; } type GraphQLWrapperOptions = { pollInterval ? : number ; verbose ? : boolean ; } ; export default class GraphQLWrapper { #client : Client ; #pollInterval : number ; #verbose : boolean ; constructor ( subgraphUrl , options = { } ) { if ( typeof options === 'boolean' ) { console . warn ( 'GraphQLWrapper: please use `new GraphQLWrapper(url, { verbose })` rather than `new GraphQLWrapper(url, verbose)`.' ) ; options = { verbose : options } ; } options = options as GraphQLWrapperOptions ; this . #verbose = options . verbose ?? false ; this . #pollInterval = options . pollInterval ?? POLL_INTERVAL_DEFAULT ; this . #client = new Client ( { maskTypename : true , url : subgraphUrl , fetch } ) ; } close ( ) { } subscribeToQuery ( query , args = { } , callback ) { const request = createRequest ( query , args ) ; return pipe ( this . #client . executeQuery ( request , { pollInterval : this . #pollInterval , requestPolicy : 'cache-and-network' , } ) , subscribe ( ( result ) => { if ( this . #verbose ) { console . log ( this . describeQueryResult ( result ) ) ; } if ( result . error ) { callback ( new Error ( 'Error performing subscription.\n' + `${ result . error . name }: ${ result . error . message } \n` + this . describeQueryResult ( result ) ) ) ; return ; } callback ( null , result ) ; } ) ) ; } subscribeToQueryWithParser < T > ( query , args = { } , callback , parser ) { return this . subscribeToQuery ( query , args , async ( error , result ? ) => { try { if ( error || result ?. error ) { throw new ErrorConnection ( result ? 'Error performing subscription.\n' + `${ result ?. error ?. name }: ${ result ?. error ?. message }\n` + this . describeQueryResult ( result ) : undefined ) ; } callback ( null , await this . parseQueryResult < T > ( parser , result as QueryResult ) ) ; } catch ( error ) { callback ( error ) ; } } ) ; } async performQuery ( query , args = { } ) { const result = await this . #client . query ( query , args ) . toPromise ( ) ; if ( this . #verbose ) { console . log ( this . describeQueryResult ( result ) ) ; } if ( result . error ) { throw new ErrorConnection ( this . describeQueryResultError ( result ) + this . describeQueryResult ( result ) ) ; } return result ; } async performQueryWithParser < T > ( query , args = { } , parser ) { const result = await this . performQuery ( query , args ) ; return this . parseQueryResult < T > ( parser , result ) ; } async parseQueryResult < T > ( parser , result ) { if ( result . error ) { throw new ErrorUnexpectedResult ( ) ; } return parser ( result ) ; } private describeQueryResultError ( result ) { if ( ! result . error ) { return '' ; } return `${ result . error . name }: ${ result . error . message }\n\n` ; } private describeQueryResult ( result ) { const queryStr = result . operation . query . loc ?. source . body ; const dataStr = JSON . stringify ( result . data , null , 2 ) ; const argsStr = JSON . stringify ( result . operation . variables , null , 2 ) ; const subgraphUrl = result . operation . context . url ; return ( `Subgraph: ${ subgraphUrl }\n\n` + `Arguments: ${ argsStr }\n\n` + `Query: ${ queryStr }\n\n` + `Returned data: ${ dataStr }\n\n` ) ; } }