import { CancellableStream } from "google-gax" ; import { ClientOptions } from "./client" ; import { BaseMutationHookArgs , BaseRequestHookArgs , HookedCancellation , HookedResolution , Hooks , } from "./hooks" ; import { parse } from "./parser" ; import { services } from "./protos" ; import ServiceFactory from "./protos/autogen/serviceFactory" ; import { buildQuery } from "./query" ; import { CustomerOptions , MutateOperation , MutateOptions , PageToken , ReportOptions , RequestOptions , } from "./types" ; import { createNextChunkArrivedPromise } from "./utils" ; export class Customer extends ServiceFactory { constructor ( clientOptions , customerOptions , hooks ? ) { super ( clientOptions , customerOptions , hooks ?? { } ) ; } description hooks public async query < T = services . IGoogleAdsRow [ ] > ( gaqlQuery , requestOptions = { } ) { const { response } = await this . querier < T > ( gaqlQuery , requestOptions ) ; return response ; } description hooks public async report < T = services . IGoogleAdsRow [ ] > ( options ) { const { gaqlQuery , requestOptions } = buildQuery ( options ) ; const { response } = await this . querier < T > ( gaqlQuery , requestOptions , options ) ; return response ; } description hooks public async reportCount ( options ) { options . limit = 1 ; const { gaqlQuery , requestOptions } = buildQuery ( options ) ; requestOptions . return_total_results_count = true ; const useHooks = false ; const { totalResultsCount } = await this . querier ( gaqlQuery , requestOptions , options , useHooks ) ; return totalResultsCount ; } description hooks example public async * reportStream < T = services . IGoogleAdsRow > ( reportOptions ) { const { gaqlQuery , requestOptions } = buildQuery ( reportOptions ) ; const baseHookArguments = { credentials : this . credentials , query : gaqlQuery , reportOptions , } ; if ( this . hooks . onStreamStart ) { const queryStart = { cancelled : false } ; await this . hooks . onStreamStart ( { ... baseHookArguments , cancel : ( ) => { queryStart . cancelled = true ; } , editOptions : ( options ) => { Object . entries ( options ) . forEach ( ( [ key , val ] ) => { requestOptions [ key ] = val ; } ) ; } , } ) ; if ( queryStart . cancelled ) { return ; } } const { service , request } = this . buildSearchStreamRequestAndService ( gaqlQuery , requestOptions ) ; const stream = service . searchStream ( request , { otherArgs : { headers : this . callHeaders } , } ) ; let streamFinished = false ; const accumulator = [ ] ; let nextChunk = createNextChunkArrivedPromise ( ) ; stream . on ( "data" , ( chunk ) => { const results = chunk . summary_row ? [ chunk . summary_row ] : chunk . results ; const parsedResponse = this . clientOptions . disable_parsing ? results : parse ( { results , reportOptions } ) ; accumulator . push ( ... ( parsedResponse as T [ ] ) ) ; nextChunk . resolve ( ) ; nextChunk = createNextChunkArrivedPromise ( ) ; } ) ; stream . on ( "error" , ( searchError ) => { nextChunk . reject ( searchError ) ; } ) ; stream . on ( "end" , ( ) => { streamFinished = true ; nextChunk . resolve ( ) ; } ) ; try { while ( ! streamFinished || accumulator . length ) { if ( accumulator . length > 0 ) { const item = accumulator . shift ( ) ; if ( item === undefined ) { throw new Error ( "UNDEFINED_STREAM_ERROR" ) ; } yield item ; } else { await nextChunk . newPromise ; } } } catch ( searchError ) { const googleAdsError = this . getGoogleAdsError ( searchError ) ; if ( this . hooks . onStreamError ) { await this . hooks . onStreamError ( { ... baseHookArguments , error : googleAdsError , } ) ; } throw googleAdsError ; } finally { stream . destroy ( ) ; } } description hooks example public async reportStreamRaw ( reportOptions ) { const { gaqlQuery , requestOptions } = buildQuery ( reportOptions ) ; const baseHookArguments = { credentials : this . credentials , query : gaqlQuery , reportOptions , } ; const queryStart = { cancelled : false } ; if ( this . hooks . onStreamStart ) { await this . hooks . onStreamStart ( { ... baseHookArguments , cancel : ( ) => { queryStart . cancelled = true ; } , editOptions : ( options ) => { Object . entries ( options ) . forEach ( ( [ key , val ] ) => { requestOptions [ key ] = val ; } ) ; } , } ) ; if ( queryStart . cancelled ) { return ; } } const { service , request } = this . buildSearchStreamRequestAndService ( gaqlQuery , requestOptions ) ; return service . searchStream ( request , { otherArgs : { headers : this . callHeaders } , } ) ; } private async search ( gaqlQuery , requestOptions ) { const { service , request } = this . buildSearchRequestAndService ( gaqlQuery , requestOptions ) ; const searchResponse = await service . search ( request , { otherArgs : { headers : this . callHeaders } , autoPaginate : false , } ) ; const response = searchResponse [ 0 ] ; const summaryRow = searchResponse [ 2 ] . summary_row ; const nextPageToken = searchResponse [ 2 ] . next_page_token ; const totalResultsCount = searchResponse [ 2 ] . total_results_count ? + searchResponse [ 2 ] . total_results_count : undefined ; if ( summaryRow ) { response . unshift ( summaryRow ) ; } return { response , nextPageToken , totalResultsCount } ; } private async paginatedSearch ( gaqlQuery , requestOptions , parser ) { const response = [ ] ; let nextPageToken = undefined ; const initialSearch = await this . search ( gaqlQuery , requestOptions ) ; const totalResultsCount = initialSearch . totalResultsCount ; response . push ( ... parser ( initialSearch . response ) ) ; nextPageToken = initialSearch . nextPageToken ; while ( nextPageToken ) { const nextSearch = await this . search ( gaqlQuery , { ... requestOptions , page_token : nextPageToken , } ) ; response . push ( ... parser ( nextSearch . response ) ) ; nextPageToken = nextSearch . nextPageToken ; } return { response , totalResultsCount } ; } private async querier < T = services . IGoogleAdsRow [ ] > ( gaqlQuery , requestOptions = { } , reportOptions ? , useHooks = true ) { const baseHookArguments = { credentials : this . credentials , query : gaqlQuery , reportOptions , } ; if ( this . hooks . onQueryStart && useHooks ) { const queryCancellation = { cancelled : false } ; await this . hooks . onQueryStart ( { ... baseHookArguments , cancel : ( res ) => { queryCancellation . cancelled = true ; queryCancellation . res = res ; } , editOptions : ( options ) => { Object . entries ( options ) . forEach ( ( [ key , val ] ) => { requestOptions [ key ] = val ; } ) ; } , } ) ; if ( queryCancellation . cancelled ) { return { response : queryCancellation . res as T } ; } } try { const parsingWapper = ( rows ) => { return this . clientOptions . disable_parsing ? rows : reportOptions ? parse ( { results : rows , reportOptions } ) : parse ( { results : rows , gaqlString : gaqlQuery } ) ; } ; const { response , totalResultsCount } = await this . paginatedSearch ( gaqlQuery , requestOptions , parsingWapper ) ; if ( this . hooks . onQueryEnd && useHooks ) { const queryResolution = { resolved : false } ; await this . hooks . onQueryEnd ( { ... baseHookArguments , response , resolve : ( res ) => { queryResolution . resolved = true ; queryResolution . res = res ; } , } ) ; if ( queryResolution . resolved ) { return { response : queryResolution . res as T , totalResultsCount } ; } } return { response : response as unknown as T , totalResultsCount } ; } catch ( searchError ) { const googleAdsError = this . getGoogleAdsError ( searchError ) ; if ( this . hooks . onQueryError && useHooks ) { await this . hooks . onQueryError ( { ... baseHookArguments , error : googleAdsError , } ) ; } throw googleAdsError ; } } description hooks public async mutateResources < T > ( mutations , mutateOptions = { } ) { const baseHookArguments = { credentials : this . credentials , method : "GoogleAdsService.mutate" , mutations , isServiceCall : false , } ; if ( this . hooks . onMutationStart ) { const mutationCancellation = { cancelled : false } ; await this . hooks . onMutationStart ( { ... baseHookArguments , cancel : ( res ) => { mutationCancellation . cancelled = true ; mutationCancellation . res = res ; } , editOptions : ( options ) => { Object . entries ( options ) . forEach ( ( [ key , val ] ) => { mutateOptions [ key ] = val ; } ) ; } , } ) ; if ( mutationCancellation . cancelled ) { return mutationCancellation . res ; } } const { service , request } = this . buildMutationRequestAndService ( mutations , mutateOptions ) ; try { const response = ( await service . mutate ( request , { otherArgs : { headers : this . callHeaders } , } ) ) [ 0 ] as services . MutateGoogleAdsResponse ; const parsedResponse = request . partial_failure ? this . decodePartialFailureError ( response ) : response ; if ( this . hooks . onMutationEnd ) { const mutationResolution = { resolved : false } ; await this . hooks . onMutationEnd ( { ... baseHookArguments , response : parsedResponse , resolve : ( res ) => { mutationResolution . resolved = true ; mutationResolution . res = res ; } , } ) ; if ( mutationResolution . resolved ) { return mutationResolution . res ; } } return parsedResponse ; } catch ( mutateError ) { const googleAdsError = this . getGoogleAdsError ( mutateError ) ; if ( this . hooks . onMutationError ) { await this . hooks . onMutationError ( { ... baseHookArguments , error : googleAdsError , } ) ; } throw googleAdsError ; } } private get googleAdsFields ( ) { return { searchGoogleAdsFields : async ( request ) => { const service = await this . loadService ( "GoogleAdsFieldServiceClient" ) ; return service . searchGoogleAdsFields ( request , { otherArgs : { headers : this . callHeaders } , } ) ; } , } ; } }