UNPKG

@denizelderenbos/google-ads-api

Version:
365 lines 14.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Customer = void 0; const parser_1 = require("./parser"); const serviceFactory_1 = __importDefault(require("./protos/autogen/serviceFactory")); const query_1 = require("./query"); const utils_1 = require("./utils"); class Customer extends serviceFactory_1.default { constructor(clientOptions, customerOptions, hooks) { super(clientOptions, customerOptions, hooks !== null && hooks !== void 0 ? hooks : {}); } /** @description Single query using a raw GAQL string. @hooks onQueryStart, onQueryError, onQueryEnd */ async query(gaqlQuery, requestOptions = {}) { const { response } = await this.querier(gaqlQuery, requestOptions); return response; } /** @description Stream query using a raw GAQL string. If a generic type is provided, it must be the type of a single row. If a summary row is requested then this will be the last emitted row of the stream. @hooks onStreamStart, onStreamError @example const stream = queryStream<T>(gaqlQuery) for await (const row of stream) { ... } */ async *queryStream(gaqlQuery, requestOptions = {}) { const stream = this.streamer(gaqlQuery, requestOptions); for await (const row of stream) { yield row; } } /** @description Single query using ReportOptions. If a summary row is requested then this will be the first row of the results. @hooks onQueryStart, onQueryError, onQueryEnd */ async report(options) { const { gaqlQuery, requestOptions } = (0, query_1.buildQuery)(options); const { response } = await this.querier(gaqlQuery, requestOptions, options); return response; } /** @description Get the total row count of a report. @hooks none */ async reportCount(options) { const { gaqlQuery, requestOptions } = (0, query_1.buildQuery)({ ...options, limit: 1 }); // must get at least one row // @ts-expect-error we do not allow this field in reportOptions, however it is still a valid request option requestOptions.return_total_results_count = true; const useHooks = false; // to avoid cacheing conflicts const { totalResultsCount } = await this.querier(gaqlQuery, requestOptions, options, useHooks); return totalResultsCount; } /** @description Stream query using ReportOptions. If a generic type is provided, it must be the type of a single row. If a summary row is requested then this will be the last emitted row of the stream. @hooks onStreamStart, onStreamError @example const stream = reportStream<T>(reportOptions) for await (const row of stream) { ... } */ async *reportStream(reportOptions) { const { gaqlQuery, requestOptions } = (0, query_1.buildQuery)(reportOptions); const stream = this.streamer(gaqlQuery, requestOptions, reportOptions); for await (const row of stream) { yield row; } } /** @description Retreive the raw stream using ReportOptions. @hooks onStreamStart @example const stream = reportStreamRaw(reportOptions) stream.on('data', (chunk) => { ... }) // a chunk contains up to 10,000 un-parsed rows stream.on('error', (error) => { ... }) stream.on('end', () => { ... }) */ async reportStreamRaw(reportOptions) { const { gaqlQuery, requestOptions } = (0, query_1.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]) => { // @ts-ignore requestOptions[key] = val; }); }, }); if (queryStart.cancelled) { return; } } const { service, request } = this.buildSearchStreamRequestAndService(gaqlQuery, requestOptions); return service.searchStream(request, { otherArgs: { headers: this.callHeaders }, }); } async search(gaqlQuery, requestOptions) { const { service, request } = this.buildSearchRequestAndService(gaqlQuery, requestOptions); const searchResponse = await service.search(request, { otherArgs: { headers: this.callHeaders }, autoPaginate: false, // autoPaginate doesn't work }); 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 }; } 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 }; } async querier(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]) => { // @ts-ignore requestOptions[key] = val; }); }, }); if (queryCancellation.cancelled) { return { response: queryCancellation.res }; } } try { const parsingWapper = (rows) => { return this.clientOptions.disable_parsing ? rows : reportOptions ? (0, parser_1.parse)({ results: rows, reportOptions }) : (0, parser_1.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, totalResultsCount }; } } return { response: response, totalResultsCount }; } catch (searchError) { const googleAdsError = this.getGoogleAdsError(searchError); if (this.hooks.onQueryError && useHooks) { await this.hooks.onQueryError({ ...baseHookArguments, error: googleAdsError, }); } throw googleAdsError; } } async *streamer(gaqlQuery, requestOptions = {}, 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]) => { // @ts-expect-error 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 = (0, utils_1.createNextChunkArrivedPromise)(); stream.on("data", (chunk) => { const results = chunk.summary_row ? [chunk.summary_row] : chunk.results; const parsedResponse = this.clientOptions.disable_parsing ? results : reportOptions ? (0, parser_1.parse)({ results, reportOptions }) : (0, parser_1.parse)({ results, gaqlString: gaqlQuery }); accumulator.push(...parsedResponse); nextChunk.resolve(); nextChunk = (0, utils_1.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 Creates, updates, or removes resources. This method supports atomic transactions * with multiple types of resources. For example, you can atomically create a campaign and a * campaign budget, or perform up to thousands of mutates atomically. * @hooks onMutationStart, onMutationError, onMutationEnd */ async mutateResources(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]) => { // @ts-ignore 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]; 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; } } get googleAdsFields() { return { searchGoogleAdsFields: async (request) => { const service = await this.loadService("GoogleAdsFieldServiceClient"); return service.searchGoogleAdsFields(request, { // @ts-expect-error This method does support call headers otherArgs: { headers: this.callHeaders }, }); }, }; } } exports.Customer = Customer; //# sourceMappingURL=customer.js.map