import { Injectable } from '@angular/core' ; import { HttpClient , HttpHeaders } from '@angular/common/http' ; import { ApolloLink , Observable as LinkObservable , Operation , FetchResult , } from '@apollo/client/core' ; import { BatchLink , BatchHandler } from '@apollo/client/link/batch' ; import { print } from 'graphql' ; import { Body , Context , Request , Options , OperationPrinter } from './types' ; import { createHeadersWithClientAwereness , fetch , mergeHeaders , prioritize , } from './utils' ; import { BatchOptions } from './types' ; const defaults = { batchInterval : 10 , batchMax : 10 , uri : 'graphql' , method : 'POST' , } ; export class HttpBatchLinkHandler extends ApolloLink { public batcher : ApolloLink ; private batchInterval : number ; private batchMax : number ; private print : OperationPrinter = print ; constructor ( private httpClient , private options ) { super ( ) ; this . batchInterval = options . batchInterval || defaults . batchInterval ; this . batchMax = options . batchMax || defaults . batchMax ; if ( this . options . operationPrinter ) { this . print = this . options . operationPrinter ; } const batchHandler = ( operations ) => { return new LinkObservable ( ( observer ) => { const body = this . createBody ( operations ) ; const headers = this . createHeaders ( operations ) ; const { method , uri , withCredentials } = this . createOptions ( operations ) ; if ( typeof uri === 'function' ) { throw new Error ( `Option 'uri' is a function, should be a string` ) ; } const req = { method , url : uri , body : body , options : { withCredentials , headers , } , } ; const sub = fetch ( req , this . httpClient , ( ) => { throw new Error ( 'File upload is not available when combined with Batching' ) ; } ) . subscribe ( { next : ( result ) => observer . next ( result . body ) , error : ( err ) => observer . error ( err ) , complete : ( ) => observer . complete ( ) , } ) ; return ( ) => { if ( ! sub . closed ) { sub . unsubscribe ( ) ; } } ; } ) ; } ; const batchKey = options . batchKey || ( ( operation ) => { return this . createBatchKey ( operation ) ; } ) ; this . batcher = new BatchLink ( { batchInterval : this . batchInterval , batchMax : this . batchMax , batchKey , batchHandler , } ) ; } private createOptions ( operations ) { const context = operations [ 0 ] . getContext ( ) ; return { method : prioritize ( context . method , this . options . method , defaults . method ) , uri : prioritize ( context . uri , this . options . uri , defaults . uri ) , withCredentials : prioritize ( context . withCredentials , this . options . withCredentials ) , } ; } private createBody ( operations ) { return operations . map ( ( operation ) => { const includeExtensions = prioritize ( operation . getContext ( ) . includeExtensions , this . options . includeExtensions , false ) ; const includeQuery = prioritize ( operation . getContext ( ) . includeQuery , this . options . includeQuery , true ) ; const body = { operationName : operation . operationName , variables : operation . variables , } ; if ( includeExtensions ) { body . extensions = operation . extensions ; } if ( includeQuery ) { body . query = this . print ( operation . query ) ; } return body ; } ) ; } private createHeaders ( operations ) { return operations . reduce ( ( headers , operation ) => { return mergeHeaders ( headers , operation . getContext ( ) . headers ) ; } , createHeadersWithClientAwereness ( { headers : this . options . headers , clientAwareness : operations [ 0 ] ?. getContext ( ) ?. clientAwareness , } ) ) ; } private createBatchKey ( operation ) { const context = operation . getContext ( ) ; if ( context . skipBatching ) { return Math . random ( ) . toString ( 36 ) . substr ( 2 , 9 ) ; } const headers = context . headers && context . headers . keys ( ) . map ( ( k ) => context . headers . get ( k ) ) ; const opts = JSON . stringify ( { includeQuery : context . includeQuery , includeExtensions : context . includeExtensions , headers , } ) ; return prioritize ( context . uri , this . options . uri ) + opts ; } public request ( op ) { return this . batcher . request ( op ) ; } } @ Injectable ( { providedIn : 'root' , } ) export class HttpBatchLink { constructor ( private httpClient ) { } public create ( options ) { return new HttpBatchLinkHandler ( this . httpClient , options ) ; } }