Coder Social home page Coder Social logo

opteo / google-ads-api Goto Github PK

View Code? Open in Web Editor NEW
244.0 25.0 87.0 6.21 MB

Google Ads API client library for Node.js

Home Page: https://opteo.com

License: MIT License

TypeScript 99.98% JavaScript 0.02%
google-ads typescript nodejs adwords adwords-api

google-ads-api's Introduction

Google Ads API

Unofficial Google Ads API client library for Node.js

Features

  • Simple and easy to use API
  • Uses gRPC and Protocol Buffers internally (recommended by Google)
  • Typescript definitions for all resources, enums, errors and services
  • Provides all API functionality

Installation

npm install google-ads-api

Usage

Create a client

import { GoogleAdsApi } from "google-ads-api";

const client = new GoogleAdsApi({
  client_id: "<CLIENT-ID>",
  client_secret: "<CLIENT-SECRET>",
  developer_token: "<DEVELOPER-TOKEN>",
});

Create a customer instance

const customer = client.Customer({
  customer_id: "1234567890",
  refresh_token: "<REFRESH-TOKEN>",
});

// Also supports login & linked customer ids
const customer = client.Customer({
  customer_id: "1234567890",
  login_customer_id: "<LOGIN-CUSTOMER-ID>",
  linked_customer_id: "<LINKED-CUSTOMER-ID>",
  refresh_token: "<REFRESH-TOKEN>",
});

List accessible customers

This is a special client method for listing the accessible customers for a given refresh token, and is equivalent to CustomerService.listAccessibleCustomers. It returns the resource names of available customer accounts.

const client = new GoogleAdsApi({
  client_id: "<CLIENT-ID>",
  client_secret: "<CLIENT-SECRET>",
  developer_token: "<DEVELOPER-TOKEN>",
});

const refreshToken = "<REFRESH-TOKEN>";

const customers = await client.listAccessibleCustomers(refreshToken);

Retrieve Campaigns with metrics

import { enums } from "google-ads-api";

const campaigns = await customer.report({
  entity: "campaign",
  attributes: [
    "campaign.id",
    "campaign.name",
    "campaign.bidding_strategy_type",
    "campaign_budget.amount_micros",
  ],
  metrics: [
    "metrics.cost_micros",
    "metrics.clicks",
    "metrics.impressions",
    "metrics.all_conversions",
  ],
  constraints: {
    "campaign.status": enums.CampaignStatus.ENABLED,
  },
  limit: 20,
});

Retrieve Campaigns using GAQL

If you prefer to use the Google Ads Query Language (GAQL) the query method is available. Internally report uses this function. More GAQL examples can be found here.

const campaigns = await customer.query(`
  SELECT 
    campaign.id, 
    campaign.name,
    campaign.bidding_strategy_type,
    campaign_budget.amount_micros,
    metrics.cost_micros,
    metrics.clicks,
    metrics.impressions,
    metrics.all_conversions
  FROM 
    campaign
  WHERE
    campaign.status = "ENABLED"
  LIMIT 20
`);

Retrieve Ad Group metrics by date

import { enums } from "google-ads-api";

const campaigns = await customer.report({
  entity: "ad_group",
  metrics: [
    "metrics.cost_micros",
    "metrics.clicks",
    "metrics.impressions",
    "metrics.all_conversions",
  ],
  segments: ["segments.date"],
  from_date: "2021-01-01",
  to_date: "2021-02-01",
});

Retrieve Keywords with an async iterator

Calls searchStream internally but returns the rows one by one in an async iterator.

import { enums } from "google-ads-api";

const stream = customer.reportStream({
  entity: "ad_group_criterion",
  attributes: [
    "ad_group_criterion.keyword.text", 
    "ad_group_criterion.status",
  ],
  constraints: {
    "ad_group_criterion.type": enums.CriterionType.KEYWORD,
  },
});

// Rows are streamed in one by one
for await (const row of stream) {
    // Break the loop to stop streaming
    if (someLogic) {
        break
    }
}

Or use a GAQL query.

const stream = customer.queryStream(`
  SELECT
    ad_group_criterion.keyword.text,
    ad_group_criterion.status
  FROM
    ad_group_criterion
  WHERE
    ad_group_criterion.type = "KEYWORD"
`);

// Rows are streamed in one by one
for await (const row of stream) {
    // Break the loop to stop streaming
    if (someLogic) {
        break
    }
}

Retrieve Keywords with a raw stream

Returns the raw stream so that events can be handled manually. For more information on Google's stream methods please consult their docs.

import { enums, parse } from "google-ads-api";

const reportOptions = {
  entity: "ad_group_criterion",
  attributes: [
    "ad_group_criterion.keyword.text", 
    "ad_group_criterion.status",
  ],
  constraints: {
    "ad_group_criterion.type": enums.CriterionType.KEYWORD,
  },
};

const stream = customer.reportStreamRaw(reportOptions);

// Rows are streamed in 10,000 row chunks
stream.on("data", (chunk) => {
  const parsedResults = parse({
    results: chunk.results,
    reportOptions,
  });
});

stream.on("error", (error) => {
  throw new Error(error);
});

stream.on("end", () => {
  console.log("stream has finished");
});

Create an expanded text ad

import { resources, enums, ResourceNames } from "google-ads-api";

const ad = new resources.Ad({
  expanded_text_ad: {
    headline_part1: "Cruise to Mars",
    headline_part2: "Best Space Cruise Line",
    description: "Buy your tickets now!",
    path1: "cruises",
    path2: "mars",
  },
  final_urls: ["https://example.com"],
  type: enums.AdType.EXPANDED_TEXT_AD,
});

const adGroup = ResourceNames.adGroup(cus.credentials.customerId, "123");

const adGroupAd = new resources.AdGroupAd({
  status: enums.AdGroupAdStatus.PAUSED,
  ad_group,
  ad,
});

// Returns an array of newly created resource names if successful
const { results } = await cus.adGroupAds.create([adGroupAd]);

Create a Campaign & Budget atomically

import {
  resources,
  enums,
  toMicros,
  ResourceNames,
  MutateOperation,
} from "google-ads-api";

// Create a resource name with a temporary resource id (-1)
const budgetResourceName = ResourceNames.campaignBudget(
  customer.credentials.customer_id,
  "-1"
);

const operations: MutateOperation<
  resources.ICampaignBudget | resources.ICampaign
>[] = [
  {
    entity: "campaign_budget",
    operation: "create",
    resource: {
      // Create a budget with the temporary resource id
      resource_name: budgetResourceName,
      name: "Planet Express Budget",
      delivery_method: enums.BudgetDeliveryMethod.STANDARD,
      amount_micros: toMicros(500),
    },
  },
  {
    entity: "campaign",
    operation: "create",
    resource: {
      name: "Planet Express",
      advertising_channel_type: enums.AdvertisingChannelType.SEARCH,
      status: enums.CampaignStatus.PAUSED,
      manual_cpc: {
        enhanced_cpc_enabled: true,
      },
      // Use the temporary resource id which will be created in the previous operation
      campaign_budget: budgetResourceName,
      network_settings: {
        target_google_search: true,
        target_search_network: true,
      },
    },
  },
];

const result = await customer.mutateResources(operations);

Add Policy Exemption Rules

import {
  resources,
  enums,
  toMicros,
  ResourceNames,
  MutateOperation,
} from "google-ads-api";

// Ad Group to which you want to add the keyword
const adGroupResourceName = 'customers/123/adGroups/456'

const keyword = '24 hour locksmith harlem'

const operations: MutateOperation<
  resources.IAdGroupCriterion & { exempt_policy_violation_keys?: google.ads.googleads.v16.common.IPolicyViolationKey[]}
>[] = [
  {
    entity: 'ad_group_criterion',
    operation: "create",
    resource: {
      // Keyword with policy violation exemptions
        ad_group: adGroupResourceName,
        keyword: {
            text: '24 hour locksmith harlem',
            match_type: enums.KeywordMatchType.PHRASE,
        },
        status: enums.AdGroupStatus.ENABLED ,
    },
    exempt_policy_violation_keys: [
        {
            policy_name: 'LOCAL_SERVICES',
            violating_text: keyword,
        },
    ],
  }
];

const result = await customer.mutateResources(operations);

Uploading Click Conversions

const clickConversion = {
  gclid: "<GOOGLE-CLICK-ID>",
  conversion_action: "customers/1234567890/conversionActions/111222333",
  conversion_date_time: "2022-01-11 00:00:00",
  conversion_value: 123,
  currency_code: "GBP",
};

const request = new services.UploadClickConversionsRequest({
  customer_id: customerId,
  conversions: [clickConversion],
});

await customer.conversionUploads.uploadClickConversions(request);

Summary Row

If a summary row is requested in the report method, it will be included as the first row of the results.

const [summaryRow, ...response] = await customer.report({
  entity: "campaign",
  metrics: ["metrics.clicks", "metrics.all_conversions"],
  summary_row_setting: enums.SummaryRowSetting.SUMMARY_ROW_WITH_RESULTS,
});

If a summery row is requested in the reportStream method, it will be included as the final iterated row of the results.

const stream = customer.reportStream({
  entity: "campaign",
  metrics: ["metrics.clicks", "metrics.all_conversions"],
  summary_row_setting: enums.SummaryRowSetting.SUMMARY_ROW_WITH_RESULTS,
});

const accumulator = [];
for await (const row of stream) {
  accumulator.push(row);
}

const summaryRow = accumulator.slice(-1)[0];

Total Results Count

The reportCount method acts like report but returns the total number of rows that the query would have returned (ignoring the limit). This replaces the return_total_results_count report option. Hooks are not called in this function to avoid cacheing conflicts.

const totalRows = await customer.reportCount({
  entity: "search_term_view",
  attributes: ["search_term_view.resource_name"],
});

Report Results Order

There are 2 methods of sorting the results of report. The prefered method is to use the order key, which should be an array of objects with a field key and an optional sort_order key. The order of the items in the array will map to the order of the sorting keys in the GAQL query, and hence the priorities of the sorts.

const response = await customer.report({
  entity: "campaign",
  attributes: ["campaign.id"],
  metrics: ["metrics.clicks"],
  segments: ["segments.date"],
  order: [
    { field: "metrics.clicks", sort_order: "DESC" },
    { field: "segments.date", sort_order: "ASC" },
    { field: "campaign.id" }, // default sort_order is descending
  ],
});

The other method is to use the order_by and sort_order keys, however this will be deprecated in a future version of the API.

const response = await customer.report({
  entity: "campaign",
  attributes: ["campaign.id"],
  metrics: ["metrics.clicks"],
  segments: ["segments.date"],
  order_by: "metrics.clicks",
  sort_order: "DESC",
});

Resource Names

The library provides a set of helper methods under the ResourceNames export. These are used for compiling resource names from ids. Arguments can be of the type string, number, or a mix of both. If you have a client.Customer instance available, you can get the customer id with customer.credentials.customerId.

import { ResourceNames } from "google-ads-api";

const customerId = "1234567890";
const campaignId = "3218318373";

ResourceNames.campaign(customerId, campaignId);
// "customers/1234567890/campaigns/3218318373"

ResourceNames.adGroup(123, 123);
// "customers/123/adGroups/123"

ResourceNames.adGroupAd("1", "2", "3");
// "customers/1/adGroupAds/2~3"

const amsterdamLocationId = 1010543;
ResourceNames.geoTargetConstant(amsterdamLocationId);
// "geoTargetConstants/1010543"

ResourceNames.accountBudget(customer.credentials.customer_id, 123);
// "customers/1234567890/accountBudgets/123"

Hooks

The library provides hooks that can be executed before, after or on error of a query, stream or a mutation.

Query/stream hooks:

  • onQueryStart
  • onQueryError
  • onQueryEnd
  • onStreamStart
  • onStreamError

These hooks have access to the customerCredentials argument, containing the customer_id, login_customer_id and linked_customer_id.

These hooks also have access to the query argument, containing the GAQL query as a string.

These hooks also have access the the reportOptions argument. This will be undefined when using the query method.

Mutation hooks:

  • onMutationStart
  • onMutationError
  • onMutationEnd

These hooks have access to the customerCredentials argument, containing the customer_id, login_customer_id and linked_customer_id.

These hooks also have access to the method argument, containing the mutation method as a string.

Service hooks:

  • onServiceStart
  • onServiceError
  • onServiceEnd

These hooks have access to the customerCredentials argument, containing the customer_id, login_customer_id and linked_customer_id.

These hooks also have access to the method argument, containing the mutation method as a string.

Pre-request hooks:

  • onQueryStart - query and report
  • onStreamStart - reportStream and reportStreamRaw
  • onMutationStart
  • onServiceStart

These hooks are executed before a query/stream/mutation/service.

These hooks have access to the cancel method, which can cancel the action before it is done. The query and mutation pre-request hooks allow an optional argument to be passed into the cancel method, which will be used as an alternative return value for the query/mutation. A good use case for this method would be to cancel with a cached result.

These hooks also have access to the editOptions method which allows the request options to be changed before the request is sent. Keys included in the object passed to editOptions will be changed, and the rest will be maintained. A good use case for this method would be to set validateOnly as true when not in production.

import { OnQueryStart } from "google-ads-api";

const onQueryStart: OnQueryStart = async ({ cancel, editOptions }) => {
  if (env.mode === "test") {
    cancel([]); // Cancels the request. The supplied argument will become the alternative return value in query and mutation hooks
  }
  if (env.mode === "dev") {
    editOptions({ validate_only: true }); // Edits the request options
  }
};

const customer = client.Customer(
  {
    clientOptions,
    customerOptions,
  },
  { onQueryStart }
);

On error hooks:

  • onQueryError - query and report
  • onStreamError - reportStream (but not reportStreamRaw)
  • onMutationError
  • onServiceStart

These hooks are executed when a query/stream/mutation/service throws an error. If the error is a Google Ads failure then it will be converted to a GoogleAdsFailure first. The error can be accessed in these hooks with the error argument. Note that the onStreamError hook will not work with the reportStreamRaw method to avoid blocking the thread.

import { OnQueryError } from "google-ads-api";

const onQueryError: OnQueryError = async ({ error }) => {
  console.log(error.message); // An Error or a GoogleAdsFailure
};

const customer = client.Customer(
  {
    clientOptions,
    customerOptions,
  },
  { onQueryError }
);

Post-request hooks:

  • onQueryEnd - query and report
  • onMutationEnd
  • onServiceEnd

These hooks are executed after a query, mutation or service. This library does not contain an onStreamEnd hook to avoid accumulating the results of streams, and also so that we don't block the thread by waiting for the end event to be emitted.

import { OnQueryEnd } from "google-ads-api";

const onQueryEnd: OnQueryEnd = async ({ response, resolve }) => {
  const [first] = response; // The results of the query/mutation
  resolve([first]); // The supplied argument will become the alternative return value
};

const customer = client.Customer(
  {
    clientOptions,
    customerOptions,
  },
  { onQueryEnd }
);

Error handling

All errors, apart from GRPC specific cases (such as a connection problem or timeout, see more here), are instances of a GoogleAdsFailure.

You can find a list of all error types for a specific version in the official documentation, as well as more information about handling errors here.

import { errors } from "google-ads-api";

try {
  await customer.query(`
      SELECT campaign.bad_field FROM campaign
    `);
} catch (err) {
  if (err instanceof errors.GoogleAdsFailure) {
    console.log(err.errors); // Array of errors.GoogleAdsError instances

    // Get the first one and explicitly check for a certain error type
    const [firstError] = err.errors;
    if (
      firstError.error_code ===
      errors.QueryErrorEnum.QueryError.UNRECOGNIZED_FIELD
    ) {
      console.log(
        `Error: using invalid field "${firstError.trigger}" in query`
      );
    }
  }
}

google-ads-api's People

Contributors

aaronhayes avatar anaphade avatar angelbt91 avatar avermeil avatar cn-d avatar dependabot[bot] avatar eliotik avatar jafaircl avatar joel-valentine avatar josecamero avatar kritzware avatar loicadolphe avatar mateuszjanusz avatar mennotammens avatar nsberndt avatar wcoots avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

google-ads-api's Issues

Atomic Mutations

The REST API has a way to create multiple objects atomically in a single request by using temporary IDs. That way, if one of the requests fails, they all fail and you won't have to go back and delete the ones that worked to maintain consistency. One example is creating campaign budgets and campaigns at the same time. The request would look something like this:

// POST /v1/customer/123456789/googleAds:mutate

mutate_operations: [
  {
    campaign_budget_operation: {
      create: { name: "test budget", resource_name: "customers/123456789/campaignBudgets/-1" }
    },
    campaign_operation: {
      create: { name: "test campaign", campaign_budget: "customers/123456789/campaignBudgets/-1", resource_name: "customers/123456789/campaigns/-2" }
    }
  },
  ...etc
]

I don't know that there's a way to handle such a request in gRPC. But, it might not be a terrible idea to expose a mutate method that allows raw mutations such as this that otherwise wouldn't be possible. Would you be open to having such a method in the new API?

Incorrect attribution model enum values in documentation

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

Documentation for The ConversionAction.attribution_model_settings.attribution_model enums cite that each enum is sequential from 0 whereas in the the google ads api it uses different enum values for each attribution model.

Not actually a bug with the repo, the documentation just needs updating.

Other information

  • N/A

expose GenerateForecastMetricsRequest

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

Is there a timeline for exposing GenerateForecastMetricsRequest for KeywordPlans in the API? I can see that it's generated in google-ads-node so I'm guessing it's possible to use it straight from that library. But, it would be nice if it would be exposed here as well. I'm happy to help out if anyone can point to an example of how to get a service from google-ads-node into this library.

Other information

N/A

Skip, Offset in fetching

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

I'm not able to find any functionality about skipping the first x amount of items. I do see that campaigns has a LIMIT key but it would be nice to have something like SKIP or OFFSET too like in SQL. e.g:

customer.campaign.list({
    skip: 10,
    limit: 10,
})

โฌ† Gets result 10 to 20.

Issue with nested objects/arrays - Related to closed issue #59

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

It appears there is an issue with the interpretation/parsing of deeply nested objects/arrays. I am finding this issue specifically in the structure of AdGroup -> targeting_setting but it may also exist elsewhere in the API.

Overall this seems to be related to but not necessarily the same as the bug/discussion in closed issue #59. I have observed the following potential issues:

  1. targeting_restrictions: undefined in the struct of an AdGroup

  2. nested_path for deeply nested array is being generated as targeting_setting.target_restrictions.targetingDimension when it appears this should actually be targeting_setting.target_restrictions[<INDEX>].targetingDimension

Other information

This bug is being encountered specifically when trying to build AdGroup via API per the structure of the google-ads-api documentation and the native Google Ads API documentation. It appears at least part of the issue is within the handling of the unroll(v) function on line 84 of google-ads-node/build/lib/utils.js -- I believe this may need to have the index passed in from the isArray portion above it and a determination made as to how to structure the nested_path accordingly.

The following console logs may be helpful:

(Occurs when attempting to build/create AdGroup)

Error: Attempted to set value "3" on invalid path "targeting_setting.target_restrictions.targetingDimension" in resource
    at toProtoValueFormat (~/google-ads-api-test/node_modules/google-ads-node/build/lib/utils.js:96:15)
    at unroll (~/google-ads-api-test/node_modules/google-ads-node/build/lib/utils.js:87:19)
    at convertToProtoFormat (~/google-ads-api-test/node_modules/google-ads-node/build/lib/utils.js:83:26)
    at unroll (~/google-ads-api-test/node_modules/google-ads-node/build/lib/utils.js:86:19)
    at pb.(anonymous function).value.map (~/google-ads-api-test/node_modules/google-ads-node/build/lib/utils.js:79:24)
    at Array.map (<anonymous>)
    at convertToProtoFormat (~/google-ads-api-test/node_modules/google-ads-node/build/lib/utils.js:78:36)
    at unroll (~/google-ads-api-test/node_modules/google-ads-node/build/lib/utils.js:86:19)
    at Object.convertToProtoFormat (~/google-ads-api-test/node_modules/google-ads-node/build/lib/utils.js:83:26)
    at GoogleAdsClient.buildResource (~/google-ads-api-test/node_modules/google-ads-node/build/lib/client.js:82:41)

(Log of the struct for AdGroup -- note target_restrictions: undefined which should be an array of objects with targeting_dimension and bid_only properties )

{ struct:
   { resource_name: 'string',
     id: 'number',
     name: 'string',
     status: 'enum_AdGroupStatus',
     type: 'enum_AdGroupType',
     ad_rotation_mode: 'enum_AdGroupAdRotationMode',
     base_ad_group: 'string',
     tracking_url_template: 'string',
     url_custom_parameters: { key: 'string', value: 'string' },
     campaign: 'string',
     cpc_bid_micros: 'number',
     cpm_bid_micros: 'number',
     target_cpa_micros: 'number',
     cpv_bid_micros: 'number',
     target_cpm_micros: 'number',
     target_roas: 'number',
     percent_cpc_bid_micros: 'number',
     explorer_auto_optimizer_setting: { opt_in: 'boolean' },
     display_custom_bid_dimension: 'enum_TargetingDimension',
     final_url_suffix: 'string',
     targeting_setting: { target_restrictions: undefined },
     effective_target_cpa_micros: 'number',
     effective_target_cpa_source: 'enum_BiddingSource',
     effective_target_roas: 'number',
     effective_target_roas_source: 'enum_BiddingSource',
     labels: 'string' } }

I am actively working on a suitable resolution but would appreciate feedback/assistance from the community and authors in the meantime. I will comment/update/PR if I find a suitable fix that does not seem to cause breaking changes elsewhere in the API

Support for ConversionUploadService

The google ads documentation shows the ConversionUploadService as having endpoints for UploadCallConversions() and UploadClickConversions() which accept an array of conversions and support partial_failures. The typescript definitions for this client library suggest that only one conversion is accepted at a time:

customer.conversionUploads.uploadClickConversion(conversion: ClickConversion)
customer.conversionUploads.uploadCallConversion(conversion: CallConversion)

Should these not be more like as follows?

customer.conversionUploads.uploadClickConversions(conversions: Array, options?: ServiceCreateOptions)

Can report results be returned per page?

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

Is there a way to get the pages of a report instead of waiting for all the pages to come back? One of our clients has a couple hundred thousand keywords. Trying to wait for every page to return takes up a ton of memory, and takes quite a while causing time outs in lambda. I was wondering if there was a way to collect/return the results as they are returned from the api instead of after every page has completed. Maybe a callback that can be inserting into the page looping process?

Other information

Refresh Token

How does one get a refresh token?

Refresh token: You'll get this token when somebody authorises you to query their adwords account via OAuth. Is not thorough documentation.

UnhandledPromiseRejectionWarning: Error: Unauthorized CREATE operation in invoking a service's mutate method.

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

i was Trying to make Campagin i had Create Campagin Budget Successfully Before , but now i had that error and i can't pass it .

code :

const campaign = {
  ad_serving_optimization_status: 2, // Ad serving is optimized based on CTR for the campaign.
  advertising_channel_type: 7, // Multi Channel 
  bidding_strategy_type: 12, // Target impression Share
  campaign_budget: 'customers/7352765242/campaignBudgets/2082563969',
  end_date: '2037-12-30',
  start_date: '2019-06-09',
  experiment_type: 2, // Regular Campagin not testone
  geo_target_type_setting: { negative_geo_target_type: 2, positive_geo_target_type: 2 },//Dont Care
  labels: [],
  name: 'Loai abdalslam Pin-offer #1',
  network_settings: {
    target_content_network: true,
    target_google_search: false,
    target_partner_search_network: false,
    target_search_network: false,
  },
  payment_mode: 2, // By Click
  selective_optimization: { conversion_actions: [] },
  serving_status: 2,//Serving
  status: 2,	
}

const result =  customer.campaigns.create(campaign)
result.then(function(){
	console.log(result)
})

and error :

UnhandledPromiseRejectionWarning: Error: Unauthorized CREATE operation in invoking a service's mutate method.
    at CampaignService.<anonymous> (/home/loaii/personal/pin-offer/tasks/google-ads-api/google-ads/node_modules/google-ads-api/build/services/service.js:171:23)
    at Generator.throw (<anonymous>)
    at rejected (/home/loaii/personal/pin-offer/tasks/google-ads-api/google-ads/node_modules/google-ads-api/build/services/service.js:5:65)
(node:16906) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:16906) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Other information

letset version of google ads api
all things is working successfully but please help me to pass this error today ..
Screenshot from 2019-06-08 14-21-40

order_by only partially sorting

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

When querying the search term performance report and ordering by impressions, the results are only partially sorted. The values do decrease in net value, but they are not completely sorted

Screenshot 2019-11-06 at 14 56 23

Screenshot 2019-11-06 at 14 57 06

Call method from child threads

I got the bug when call method from Child threads (worker threads),
I'm running nodejs 12.13.0 and when I call Google Api with Protobuf (may relate with C++ native layer),
OS is Windows 10.

FATAL ERROR: v8::HandleScope::CreateHandle() Cannot create a handle without a HandleScope 1: 00007FF66DBE094F napi_wrap+124431 2: 00007FF66DB82696 v8::base::CPU::has_sse+34502 3: 00007FF66DB83356 v8::base::CPU::has_sse+37766 4: 00007FF66E386EBC v8::FunctionTemplate::RemovePrototype+284 5: 00007FF66E26DB95 v8::internal::HandleScope::Extend+53 6: 00007FF66E0E6580 v8::internal::JSReceiver::GetCreationContext+288 7: 00007FF66E36C030 v8::Object::CreationContext+32 8: 00007FF66DBFCE58 node::MakeCallback+40 10: 00007FFE4005ED30 12: 00007FF66DC1FC42 uv_prepare_init+114 13: 00007FF66DC29426 uv_run+262 14: 00007FF66DB4B892 EVP_CIPHER_CTX_buf_noconst+30946 15: 00007FF66DBA9263 node::Start+275

Documentation bug

When running the default examples, I get an error "customer.search is not a function"

However, customer.query works fine.

I'm not sure if this is a documentation bug or a software bug...

Can't create an Image Ad

We are having an issue creating a different type of ad (see #30): this time an image ad.

Using these params:

const b64 = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
  const adId = await createAd({
    ad_group: adGroup,
    status: AdGroupAdStatus.PAUSED,
    ad: {
      name: "Image Ag",
      final_urls: [ "https://my.url", ],
      type: AdType.IMAGE_AD,
      image_ad: {
        image_url: "https://image.com/image.gif",
        pixel_width: 600,
        pixel_height: 600,
        preview_image_url: "https://image.com/preview.gif",
        preview_pixel_width: 600,
        preview_pixel_height: 600,
        name: "img.gif",
        mime_type: MimeType.IMAGE_GIF,
        data: b64
      }
    }

We get:

[ { wrappers_:
     { '1':
        { wrappers_:
           { '4':
              { wrappers_: null,
                messageId_: undefined,
                arrayIndexOffset_: -1,
                array: [ 'customers/4674531019/adGroups/74761353488' ],
                pivot_: 1.7976931348623157e+308,
                convertedFloatingPointFields_: {} },
             '5':
              { wrappers_:
                 { '2':
                    [ { wrappers_: null,
                        messageId_: undefined,
                        arrayIndexOffset_: -1,
                        array: [ 'https://www.realestate.com.au/agent/lucas-harwood-1227455?rsf=cid:reach:lucasharwood:recentlysoldengage' ],
                        pivot_: 1.7976931348623157e+308,
                        convertedFloatingPointFields_: {} } ],
                   '22':
                    { wrappers_:
                       { '2':
                          { wrappers_: null,
                            messageId_: undefined,
                            arrayIndexOffset_: -1,
                            array:
                             [ Uint8Array [
                                 255,
                                 216,
                                 255,
                                 224,
                                 0,
                                 16,
                                 74,
                                 70,
                                 73,
                                 70,
                                 0,
                                 1,
                                 1,
                                 0,
                                 0,
                                 1,
                                 0,
                                 1,
                                 0,
                                 0,
                                 255,
                                 219,
                                 0,
                                 132,
                                 0,
                                 5,
                                 3,
                                 4,
                                 13,
                                 13,
                                 14,
                                 10,
                                 13,
                                 13,
                                 13,
                                 16,
                                 13,
                                 14,
                                 10,
                                 13,
                                 13,
                                 13,
                                 13,
                                 14,
                                 13,
                                 13,
                                 10,
                                 13,
                                 14,
                                 13,
                                 8,
                                 13,
                                 8,
                                 13,
                                 8,
                                 8,
                                 8,
                                 8,
                                 8,
                                 8,
                                 13,
                                 16,
                                 13,
                                 8,
                                 8,
                                 14,
                                 13,
                                 13,
                                 8,
                                 13,
                                 21,
                                 13,
                                 14,
                                 17,
                                 17,
                                 19,
                                 19,
                                 19,
                                 8,
                                 13,
                                 22,
                                 24,
                                 22,
                                 18,
                                 24,
                                 16,
                                 18,
                                 19,
                                 18,
                                 1,
                                 5,
                                 5,
                                 5,
                                 8,
                                 7,
                                 8,
                                 15,
                                 9,
                                 9,
                                 15,
                                 ... 14097 more items ] ],
                            pivot_: 1.7976931348623157e+308,
                            convertedFloatingPointFields_: {} },
                         '4':
                          { wrappers_: null,
                            messageId_: undefined,
                            arrayIndexOffset_: -1,
                            array: [ 600 ],
                            pivot_: 1.7976931348623157e+308,
                            convertedFloatingPointFields_: {} },
                         '5':
                          { wrappers_: null,
                            messageId_: undefined,
                            arrayIndexOffset_: -1,
                            array: [ 600 ],
                            pivot_: 1.7976931348623157e+308,
                            convertedFloatingPointFields_: {} },
                         '6':
                          { wrappers_: null,
                            messageId_: undefined,
                            arrayIndexOffset_: -1,
                            array: [ 'https://s3-ap-southeast-2.amazonaws.com/agent-reach-content-generator-staging/240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
                            pivot_: 1.7976931348623157e+308,
                            convertedFloatingPointFields_: {} },
                         '7':
                          { wrappers_: null,
                            messageId_: undefined,
                            arrayIndexOffset_: -1,
                            array: [ 600 ],
                            pivot_: 1.7976931348623157e+308,
                            convertedFloatingPointFields_: {} },
                         '8':
                          { wrappers_: null,
                            messageId_: undefined,
                            arrayIndexOffset_: -1,
                            array: [ 600 ],
                            pivot_: 1.7976931348623157e+308,
                            convertedFloatingPointFields_: {} },
                         '9':
                          { wrappers_: null,
                            messageId_: undefined,
                            arrayIndexOffset_: -1,
                            array: [ 'https://s3-ap-southeast-2.amazonaws.com/agent-reach-content-generator-staging/240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
                            pivot_: 1.7976931348623157e+308,
                            convertedFloatingPointFields_: {} },
                         '11':
                          { wrappers_: null,
                            messageId_: undefined,
                            arrayIndexOffset_: -1,
                            array: [ '240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
                            pivot_: 1.7976931348623157e+308,
                            convertedFloatingPointFields_: {} } },
                      messageId_: undefined,
                      arrayIndexOffset_: -1,
                      array:
                       [ <1 empty item>,
                         [ Uint8Array [
                             255,
                             216,
                             255,
                             224,
                             0,
                             16,
                             74,
                             70,
                             73,
                             70,
                             0,
                             1,
                             1,
                             0,
                             0,
                             1,
                             0,
                             1,
                             0,
                             0,
                             255,
                             219,
                             0,
                             132,
                             0,
                             5,
                             3,
                             4,
                             13,
                             13,
                             14,
                             10,
                             13,
                             13,
                             13,
                             16,
                             13,
                             14,
                             10,
                             13,
                             13,
                             13,
                             13,
                             14,
                             13,
                             13,
                             10,
                             13,
                             14,
                             13,
                             8,
                             13,
                             8,
                             13,
                             8,
                             8,
                             8,
                             8,
                             8,
                             8,
                             13,
                             16,
                             13,
                             8,
                             8,
                             14,
                             13,
                             13,
                             8,
                             13,
                             21,
                             13,
                             14,
                             17,
                             17,
                             19,
                             19,
                             19,
                             8,
                             13,
                             22,
                             24,
                             22,
                             18,
                             24,
                             16,
                             18,
                             19,
                             18,
                             1,
                             5,
                             5,
                             5,
                             8,
                             7,
                             8,
                             15,
                             9,
                             9,
                             15,
                             ... 14097 more items ] ],
                         <1 empty item>,
                         [ 600 ],
                         [ 600 ],
                         [ 'https://s3-ap-southeast-2.amazonaws.com/agent-reach-content-generator-staging/240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
                         [ 600 ],
                         [ 600 ],
                         [ 'https://s3-ap-southeast-2.amazonaws.com/agent-reach-content-generator-staging/240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
                         4,
                         [ '240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ] ],
                      pivot_: 1.7976931348623157e+308,
                      convertedFloatingPointFields_: {} },
                   '23':
                    { wrappers_: null,
                      messageId_: undefined,
                      arrayIndexOffset_: -1,
                      array: [ '240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
                      pivot_: 1.7976931348623157e+308,
                      convertedFloatingPointFields_: {} } },
                messageId_: undefined,
                arrayIndexOffset_: -1,
                array:
                 [ <1 empty item>,
                   [ [ 'https://www.realestate.com.au/agent/lucas-harwood-1227455?rsf=cid:reach:lucasharwood:recentlysoldengage' ] ],
                   <2 empty items>,
                   14,
                   <4 empty items>,
                   [],
                   <5 empty items>,
                   [],
                   <5 empty items>,
                   [ <1 empty item>,
                     [ Uint8Array [
                         255,
                         216,
                         255,
                         224,
                         0,
                         16,
                         74,
                         70,
                         73,
                         70,
                         0,
                         1,
                         1,
                         0,
                         0,
                         1,
                         0,
                         1,
                         0,
                         0,
                         255,
                         219,
                         0,
                         132,
                         0,
                         5,
                         3,
                         4,
                         13,
                         13,
                         14,
                         10,
                         13,
                         13,
                         13,
                         16,
                         13,
                         14,
                         10,
                         13,
                         13,
                         13,
                         13,
                         14,
                         13,
                         13,
                         10,
                         13,
                         14,
                         13,
                         8,
                         13,
                         8,
                         13,
                         8,
                         8,
                         8,
                         8,
                         8,
                         8,
                         13,
                         16,
                         13,
                         8,
                         8,
                         14,
                         13,
                         13,
                         8,
                         13,
                         21,
                         13,
                         14,
                         17,
                         17,
                         19,
                         19,
                         19,
                         8,
                         13,
                         22,
                         24,
                         22,
                         18,
                         24,
                         16,
                         18,
                         19,
                         18,
                         1,
                         5,
                         5,
                         5,
                         8,
                         7,
                         8,
                         15,
                         9,
                         9,
                         15,
                         ... 14097 more items ] ],
                     <1 empty item>,
                     [ 600 ],
                     [ 600 ],
                     [ 'https://s3-ap-southeast-2.amazonaws.com/agent-reach-content-generator-staging/240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
                     [ 600 ],
                     [ 600 ],
                     [ 'https://s3-ap-southeast-2.amazonaws.com/agent-reach-content-generator-staging/240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
                     4,
                     [ '240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ] ],
                   [ '240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
                   <2 empty items>,
                   [] ],
                pivot_: 1.7976931348623157e+308,
                convertedFloatingPointFields_: {} } },
          messageId_: undefined,
          arrayIndexOffset_: -1,
          array:
           [ <2 empty items>,
             3,
             [ 'customers/4674531019/adGroups/74761353488' ],
             [ <1 empty item>,
               [ [ 'https://www.realestate.com.au/agent/lucas-harwood-1227455?rsf=cid:reach:lucasharwood:recentlysoldengage' ] ],
               <2 empty items>,
               14,
               <4 empty items>,
               [],
               <5 empty items>,
               [],
               <5 empty items>,
               [ <1 empty item>,
                 [ Uint8Array [
                     255,
                     216,
                     255,
                     224,
                     0,
                     16,
                     74,
                     70,
                     73,
                     70,
                     0,
                     1,
                     1,
                     0,
                     0,
                     1,
                     0,
                     1,
                     0,
                     0,
                     255,
                     219,
                     0,
                     132,
                     0,
                     5,
                     3,
                     4,
                     13,
                     13,
                     14,
                     10,
                     13,
                     13,
                     13,
                     16,
                     13,
                     14,
                     10,
                     13,
                     13,
                     13,
                     13,
                     14,
                     13,
                     13,
                     10,
                     13,
                     14,
                     13,
                     8,
                     13,
                     8,
                     13,
                     8,
                     8,
                     8,
                     8,
                     8,
                     8,
                     13,
                     16,
                     13,
                     8,
                     8,
                     14,
                     13,
                     13,
                     8,
                     13,
                     21,
                     13,
                     14,
                     17,
                     17,
                     19,
                     19,
                     19,
                     8,
                     13,
                     22,
                     24,
                     22,
                     18,
                     24,
                     16,
                     18,
                     19,
                     18,
                     1,
                     5,
                     5,
                     5,
                     8,
                     7,
                     8,
                     15,
                     9,
                     9,
                     15,
                     ... 14097 more items ] ],
                 <1 empty item>,
                 [ 600 ],
                 [ 600 ],
                 [ 'https://s3-ap-southeast-2.amazonaws.com/agent-reach-content-generator-staging/240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
                 [ 600 ],
                 [ 600 ],
                 [ 'https://s3-ap-southeast-2.amazonaws.com/agent-reach-content-generator-staging/240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
                 4,
                 [ '240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ] ],
               [ '240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
               <2 empty items>,
               [] ] ],
          pivot_: 1.7976931348623157e+308,
          convertedFloatingPointFields_: {} } },
    messageId_: undefined,
    arrayIndexOffset_: -1,
    array:
     [ [ <2 empty items>,
         3,
         [ 'customers/4674531019/adGroups/74761353488' ],
         [ <1 empty item>,
           [ [ 'https://www.realestate.com.au/agent/lucas-harwood-1227455?rsf=cid:reach:lucasharwood:recentlysoldengage' ] ],
           <2 empty items>,
           14,
           <4 empty items>,
           [],
           <5 empty items>,
           [],
           <5 empty items>,
           [ <1 empty item>,
             [ Uint8Array [
                 255,
                 216,
                 255,
                 224,
                 0,
                 16,
                 74,
                 70,
                 73,
                 70,
                 0,
                 1,
                 1,
                 0,
                 0,
                 1,
                 0,
                 1,
                 0,
                 0,
                 255,
                 219,
                 0,
                 132,
                 0,
                 5,
                 3,
                 4,
                 13,
                 13,
                 14,
                 10,
                 13,
                 13,
                 13,
                 16,
                 13,
                 14,
                 10,
                 13,
                 13,
                 13,
                 13,
                 14,
                 13,
                 13,
                 10,
                 13,
                 14,
                 13,
                 8,
                 13,
                 8,
                 13,
                 8,
                 8,
                 8,
                 8,
                 8,
                 8,
                 13,
                 16,
                 13,
                 8,
                 8,
                 14,
                 13,
                 13,
                 8,
                 13,
                 21,
                 13,
                 14,
                 17,
                 17,
                 19,
                 19,
                 19,
                 8,
                 13,
                 22,
                 24,
                 22,
                 18,
                 24,
                 16,
                 18,
                 19,
                 18,
                 1,
                 5,
                 5,
                 5,
                 8,
                 7,
                 8,
                 15,
                 9,
                 9,
                 15,
                 ... 14097 more items ] ],
             <1 empty item>,
             [ 600 ],
             [ 600 ],
             [ 'https://s3-ap-southeast-2.amazonaws.com/agent-reach-content-generator-staging/240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
             [ 600 ],
             [ 600 ],
             [ 'https://s3-ap-southeast-2.amazonaws.com/agent-reach-content-generator-staging/240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
             4,
             [ '240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ] ],
           [ '240751-d18f6aed478f069039ba73fe5933e2b6e2041977.png' ],
           <2 empty items>,
           [] ] ] ],
    pivot_: 1.7976931348623157e+308,
    convertedFloatingPointFields_: {} } ]

The Mime Type field (id: 10) is missing.

Also: I'm not sure the best way to send that byte stream - as a decoded string?

Missing descriptive_name for customer_client entities

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

I'm running client.customerClients.list(), but it's entities missing descriptive_name property.
I'm getting only these properties in response: resource_name, client_customer, hidden, level

PHP example shows that the fields should be there:
https://github.com/googleads/google-ads-php/blob/master/examples/AccountManagement/GetAccountHierarchy.php#L128

customer.campaigns.list() is returning "Process finished with exit code 0"

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

I am trying to implement the google-ads-api in my Node Express app. I downloaded the library (npm i google-ads-api), and I am trying to get started with the code below.

const GoogleApiNodeLibrary = require('google-ads-api');

const client = new GoogleApiNodeLibrary.GoogleAdsApi({
    client_id: 'XXX',
    client_secret: 'XXX',
    developer_token: 'XXXX',
});

const customer = client.Customer({
    customer_account_id: 'XXX',
    login_customer_id: 'XXX',
    refresh_token: 'XXX'
});

customer.campaigns.list();

The values I am using for Authentication are all correct. The "login_customer_id:" uses the MCC ID, and the "customer_account_id:" uses one of the accounts ID linked to the MCC account.

When I run this code, I don't get an error. It just take a few seconds and then return "Process finished with exit code 0".

How can I fix this?

Error handling / example doesn't work

Hi guys,

I'm struggling to add a keyword.
Even the example doesn't work for me.

My code:

const { GoogleAdsApi, enums } = require('google-ads-api')

// Make sure you pass in valid authentication details!
const client = new GoogleAdsApi({
    client_id: '79573XXXXXXXXXXXXXXXXXXXX3.apps.googleusercontent.com',
    client_secret: 'sGCJRDXXXXXXXXXXXXXXXXXXXXQN1pq4',
    developer_token: 'hquXXXXXXXXXXXXXXXXXXXXB3jzPg',
})

async function main() {
    const customer = client.Customer({
        customer_account_id: '111222333',
        login_customer_id : '4445556666', // MCC
        refresh_token: "1/oWh6IPXXXXXXXXXXXXXXXq4P1X-M"
    })

    // Bonus: If you're using Typescript, set the type here to "types.Keyword" for autocomplete!
    const keyword = {
        ad_group: 'customers/111222333/adGroups/78901234567',
        status: enums.AdGroupCriterionStatus.ENABLED,
        text: 'hotels london',
        match_type: enums.KeywordMatchType.EXACT,
    }

    try {
        const { results } = await customer.adGroupCriterion.create(keyword)
        /*
            The newly created ad group criterion will have a resource name in the following format:
            "customers/{customer_id}/adGroupCriteria/{ad_group_id}~{criterion_id}"
        */
    } catch (err) {
        if (err.code.adGroupCriterionError === enums.AdGroupCriterionError.INVALID_KEYWORD_TEXT) {
            console.log(`Keyword with text "${keyword.text}" is invalid!`)
        } else {
            console.log(err);
        }
    }
}

main()

My output:

{ Error: The required field was not present in the resource.
    at AdGroupCriterionService.<anonymous> (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/google-ads-api/build/services/service.js:142:23)
    at Generator.throw (<anonymous>)
    at rejected (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/google-ads-api/build/services/service.js:5:65)
    at <anonymous>
  code: 
   { requestError: 0,
     biddingStrategyError: 0,
     urlFieldError: 0,
     listOperationError: 0,
     queryError: 0,
     mutateError: 0,
     fieldMaskError: 0,
     authorizationError: 0,
     internalError: 0,
     quotaError: 0,
     adError: 0,
     adGroupError: 0,
     campaignBudgetError: 0,
     campaignError: 0,
     authenticationError: 0,
     adGroupCriterionError: 0,
     adCustomizerError: 0,
     adGroupAdError: 0,
     adSharingError: 0,
     adxError: 0,
     assetError: 0,
     biddingError: 0,
     campaignCriterionError: 0,
     collectionSizeError: 0,
     countryCodeError: 0,
     criterionError: 0,
     customerError: 0,
     dateError: 0,
     dateRangeError: 0,
     distinctError: 0,
     feedAttributeReferenceError: 0,
     functionError: 0,
     functionParsingError: 0,
     idError: 0,
     imageError: 0,
     languageCodeError: 0,
     mediaBundleError: 0,
     mediaFileError: 0,
     multiplierError: 0,
     newResourceCreationError: 0,
     notEmptyError: 0,
     nullError: 0,
     operatorError: 0,
     rangeError: 0,
     recommendationError: 0,
     regionCodeError: 0,
     settingError: 0,
     stringFormatError: 0,
     stringLengthError: 0,
     operationAccessDeniedError: 0,
     resourceAccessDeniedError: 0,
     resourceCountLimitExceededError: 0,
     adGroupBidModifierError: 0,
     contextError: 0,
     fieldError: 2,
     sharedSetError: 0,
     sharedCriterionError: 0,
     campaignSharedSetError: 0,
     conversionActionError: 0,
     conversionAdjustmentUploadError: 0,
     conversionUploadError: 0,
     headerError: 0,
     databaseError: 0,
     policyFindingError: 0,
     enumError: 0,
     keywordPlanError: 0,
     keywordPlanCampaignError: 0,
     keywordPlanNegativeKeywordError: 0,
     keywordPlanAdGroupError: 0,
     keywordPlanKeywordError: 0,
     keywordPlanIdeaError: 0,
     accountBudgetProposalError: 0,
     userListError: 0,
     changeStatusError: 0,
     feedError: 0,
     geoTargetConstantSuggestionError: 0,
     feedItemError: 0,
     labelError: 0,
     billingSetupError: 0,
     customerClientLinkError: 0,
     customerManagerLinkError: 0,
     feedMappingError: 0,
     customerFeedError: 0,
     adGroupFeedError: 0,
     campaignFeedError: 0,
     customInterestError: 0,
     extensionFeedItemError: 0,
     adParameterError: 0,
     feedItemValidationError: 0,
     extensionSettingError: 0,
     feedItemTargetError: 0,
     policyViolationError: 0,
     mutateJobError: 0,
     partialFailureError: 0,
     policyValidationParameterError: 0 },
  request: 
   { customerId: '8849161347',
     operationsList: [ [Object] ],
     partialFailure: false,
     validateOnly: false },
  request_id: 'P0zzq9JTK3piHQocRGo5zg',
  failure: 
   { Error: 3 INVALID_ARGUMENT: The required field was not present in the resource.
    at Object.exports.createStatusError (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/grpc/src/common.js:91:15)
    at Object.onReceiveStatus (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/grpc/src/client_interceptors.js:1204:28)
    at InterceptingListener._callNext (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/grpc/src/client_interceptors.js:568:42)
    at InterceptingListener.onReceiveStatus (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/grpc/src/client_interceptors.js:618:8)
    at Object.grpc_1.default.ListenerBuilder.withOnReceiveStatus [as onReceiveStatus] (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/google-ads-node/build/lib/interceptor.js:68:17)
    at InterceptingListener._callNext (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/grpc/src/client_interceptors.js:568:42)
    at InterceptingListener.onReceiveStatus (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/grpc/src/client_interceptors.js:618:8)
    at Object.grpc_1.default.ListenerBuilder.withOnReceiveStatus [as onReceiveStatus] (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/google-ads-node/build/lib/interceptor.js:151:13)
    at InterceptingListener._callNext (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/grpc/src/client_interceptors.js:568:42)
    at InterceptingListener.onReceiveStatus (/Users/<USER>/Development/WebstormProjects/<PROJECT>/node_modules/grpc/src/client_interceptors.js:618:8)
     code: 3,
     metadata: Metadata { _internal_repr: [Object] },
     details: 'The required field was not present in the resource.' } }

How to get a helpful error message?
Just received

Error: 3 INVALID_ARGUMENT: The required field was not present in the resource.

So which required field is missing?

BTW: If I comment out the status row

const keyword = {
        ad_group: 'customers/8849161347/adGroups/68449099820',
        // status: enums.AdGroupCriterionStatus.ENABLED,
        text: 'hotels london',
        match_type: enums.KeywordMatchType.EXACT,
    }

The example works. The keyword is created.

I'm looking forward to any hint.

Thanks a lot.

Best,
Christian

Support sorting by multiple fields

Currently the query builder does not support sorting query results by multiple fields.
It should allow ordering by multiple fields with different sort orders like specified in the documentation : https://developers.google.com/google-ads/api/docs/query/ordering-limiting

Moreover, by default DESC sort_order is appended to the order part of the query although the official documentation says that sort order is not mandatory and the default behavior is to sort results ascending.

const formatOrderBy = (entity: string, order_by: string | Array<string>, sort_order?: string): string => {

Error on using CustomerClientLinks service

Hi
I tried to use the library for creating new Manager-Client link.
I did it this way:

const googleAdsJs = new GoogleAdsApi({
    client_id: 'xxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
    client_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxx',
    developer_token: 'xxxxxxxxxxxxxxxxxxxx'
})

const manager = googleAdsJs.Customer({
	customer_account_id: 'xxx-xxx-xxxx',
	refresh_token: '1/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
})

manager.customerClientLinks.create({
	client_customer: 'customers/77777777777',
	status: ManagerLinkStatus.PENDING
})

I've got error before call: TypeError: grpc[options.request] is not a constructor
I found couple possible typos in build/services/customer_client_link.js:

const MUTATE_METHOD = 'mutateCustomerClientLinks';
const MUTATE_REQUEST = 'MutateCustomerClientLinksRequest';

and replace it with:

const MUTATE_METHOD = 'mutateCustomerClientLink';
const MUTATE_REQUEST = 'MutateCustomerClientLinkRequest';

This fix let me to go further and I got error in service.ts:

TypeError: request.setOperationsList is not a function
    at CustomerClientLinkService.<anonymous> (.../node_modules/google-ads-api/build/services/service.js:103:25)

Turned out CustomerClientLinkService doesn't have setOperationsList method, but has setOperation. I monkey-pathed that part as well and make call to API successfully.

            if (!request.setOperationsList) {
                request.setOperation(operations[0]);
            }
            else {
                request.setOperationsList(operations);
            }

I wish I fill PR with this patch, but I'm afraid I don't understand internal machinery of the library well and I'm not sure if my fix make sense for all cases.

Best,
Ilya

url_custom_parameters missing keys

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

Hi there,

thanks for the great library! Sadly the when querying for any resource that uses url_custom_parameters I get back an array with objects of values. The official google API should return the keys of the custom parameter aswell. Only having a value without the key is not very helpful. Do I have to specify something to get it?
Here you can find the google docs: https://developers.google.com/adwords/api/docs/reference/v201809/AdGroupService.CustomParameter.html

Here is a sample query:

const results = await customer.report({
    entity: 'ad_group_criterion',
    attributes: ['ad_group_criterion.url_custom_parameters'],
});

and the result:

[
  {
    "ad_group_criterion": {
      "resource_name": "customers/<my-customer-id>/adGroupCriteria/<ad-group-criterion-id",
      "url_custom_parameters": [
        {
          "value": "value1"
        },
        {
          "value": "value2"
        }
      ]
    }
  }
]

How to get this initially setup and working?

I already have all my oauth credentials ready, I may be a complete brainlet because I can't figure out how to get a successful request/response through...

This repo only shows basic use case but no real setup and implementation example...

Is there a NodeJS example floating around somewhere that I can look at? Can I "gift" someone fifty bucks for an hour of their time to help me get a request through?

This is a seemingly esoteric subject as there are no guides online, and the official google docs are much less than helpful for a beginner, there's really quite sparse information over there.

Constraints error message is misleading when "val" is undefined

When using the constraints key-op-val syntax and the val is undefined, the error message we give is misleading:

(node:9568) UnhandledPromiseRejectionWarning: Error: must specify { key, op, val } when using object-style constraints
const campaign = {
  id: undefined,
};

const campaigns = await customer.campaigns.list({
  constraints: [
    {
      key: "campaign.id",
      op: "=",
      val: campaign.id,
    },
  ],
});

In this case, we should raise an error message that the val property is undefined. Something along the lines of "val" field is an undefined value. Alternatively, we could use TypeScript and make sure val is typeof not undefined, to give a compile time warning.

Listing customer_account_id when we don't know it

Hi there,

I'm using oAuth so my customers can provide access to their Google Ads accounts, which is working fine to get the refresh token for the Customer function, but I can't find a way to list the customer_account_id without them specifying it manually.

customer.Customer({
        refresh_token: GOTTHIS,
        customer_account_id: NOTGOTTHIS
});

Any ideas?

TypeError: operation.setUpdate is not a function

Hey there,

I'm wanting to update an existing billing setup and get back the following error:
TypeError: operation.setUpdate is not a function at BillingSetupService.<anonymous>

I run the following code (using version 3.2.0):

await customer.billingSetups.update({
    resource_name: "customers/*****/billingSetups/*****",
    start_time_type: TimeType.NOW
  });

Not too sure why I can't update, would be amazing to get a hand :)

Error: grpc: received message larger than max...

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

I've run into scenarios when running a query returns the following error "Error: Received message larger than max (9154892 vs. 4194304)"

Other information

t CustomerService. (C:\prj\node_modules\google-ads-api\build\services\service.js:205:23)
at Generator.throw ()
at rejected (C:\prj\node_modules\google-ads-api\build\services\service.js:5:65)

How to get campaign budget

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

For each campaign in my account, I wish to extract campaign_budget.amount_micros. However, I cannot figure out a way to do this. If I list all my campaigns, I see that campaign_budget property is a URL. However, I am stuck at trying to figure out how I could query it.

Also, I will need to update it once I receive the value. I assume that I would struggle with it as well :)

AssetService not in the documentation

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

I wasn't able to find the AssetService inside the documentation but I did find it in the code. I was wondering if this was done on purpose.

Other information

Missing fields when using any type of simulations

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

When querying any type of simulations (i.e. ad_group_simulation or campaign_criterion_simulation) with the report() method any fields ending in list are not shown.

This is the list of all the missing fields :

  • campaign_criterion_simulation.bid_modifier_point_list
  • ad_group_simulation.cpc_bid_point_list
  • ad_group_simulation.target_cpa_point_list
  • ad_group_criterion_simulation.cpc_bid_point_list

Creating Location in Campaign

Hey, I see that you do provide support to add location in code? (probably in customer.ts file) but I can't see anything in docs and am unable to comprehend how I can set location.

I understand that docs are in the process of being updated, but can someone please help me in finding the way to add location till that time.

Issue with ad_group_ad.policy_summary field

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

The policy_summary in AdGroupAd resolved to undefined.
While debugging protobufs I see the data for this attribute, but it's lost somewhere during converting to JSON.

https://developers.google.com/adwords/api/docs/reference/v201809/AdGroupAdService.AdGroupAdPolicySummary

Other information

return client
  .report({
    entity: 'ad_group_ad',
    attributes: ['ad_group_ad.status', 'ad_group_ad.policy_summary'],
    constraints
  })

Results are returned as objects instead of raw values

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

I noticed that the library wraps some results into objects with key value, instead of returning just raw values.

For instance, when I queried final urls for an ad group ad, I received this array of objects:
[ { value: 'https://www.geoteam.be/nl/steden/aalst' } ]
According to the API documentation, I should receive array of strings that looks like this:
['https://www.geoteam.be/nl/steden/aalst' ].

Other information

Another example of the issue:

[ { ad_group:
     { resource_name: 'customers/xxx/adGroups/xxx',
       cpc_bid_micros: 1000000,
       targeting_setting:
        { target_restrictions:
           [ { targetingDimension: 3, bidOnly: { value: true } },
             { targetingDimension: 4, bidOnly: { value: false } },
             { targetingDimension: 5, bidOnly: { value: true } },
             { targetingDimension: 6, bidOnly: { value: true } },
             { targetingDimension: 7, bidOnly: { value: false } },
             { targetingDimension: 8, bidOnly: { value: true } },
             { targetingDimension: 9, bidOnly: { value: true } } ] } } } ]
}

Here the library should return booleans for bidOnly.

Allow "constraints" to be a single object

It would be nice to optionally pass a single constraints object, as opposed to an array of multiple constraint objects.

Before:

const audiences = await gads.report({
    entity: 'campaign_criterion',
    attributes: ['campaign.id', 'campaign.advertising_channel_type'],
    constraints: [
        {
            'campaign.status': wasd.enums.CampaignStatus.ENABLED,
        },
        {
            'campaign.advertising_channel_type': wasd.enums.AdvertisingChannelType.DISPLAY,
        },
        {
            'campaign.id': 123
        }
    ],
})

After:

const audiences = await gads.report({
    entity: 'campaign_criterion',
    attributes: ['campaign.id', 'campaign.advertising_channel_type'],
    constraints: {
        'campaign.status': wasd.enums.CampaignStatus.ENABLED,
        'campaign.advertising_channel_type': wasd.enums.AdvertisingChannelType.DISPLAY,
        'campaign.id': 123,
    },
})

Mutation fails on CircleCI

I'm submitting a ...

  • bug report

Summary

When testing updating a campaign within a test I run into an error with grpc

Other information

image

gRPC max message size (#43 continued..)

I can't seem to add comments to #43 - not sure if it's because of the way you guys set up workflow on issues or if something is b0rked on my end... Would appreciate if someone could check into that and reply here, and otherwise try to resume the conversation for #43 on the thread for #43 :)

In any case, I wanted to say there that it happens for a query that is returning (or trying to return) 9MB of data

For me it was enough to run a 30 day report on a busy account with lots of ads, and asking for lots of user-presentable strings, like the ad text, search keywords, search terms, and account/campaign/adgroup names (but YMMV based on your account activity.)

Regarding a solution, I'm far from a gRPC maven, but I've been seeing lots about a stream interface which allows a response (or request) to be split into several payloads...

Would this be relevant? https://grpc.github.io/grpc/node/grpc-ClientReadableStream.html

Account Budget Proposal Error

Hey there,

I'm wanting to update an existing account budget and get back the following error:
TypeError: operation.setUpdate is not a function
at AccountBudgetProposalService.

I run the following code (using version 3.2.0):

customer.accountBudgetProposals.update({
    resource_name: accountBudget,
    proposal_type: 4
});

Not too sure why I can't update, I had a similar issue when trying to update billing setups (was not possible in the api). Looking at the docs this should be possible :)

https://developers.google.com/google-ads/api/docs/billing/account-budgets#terminating_account_budgets

Am I missing something here?
Thanks

Sharing OAuth with googleapis client?

Hi!

My application also uses other Google APIs. Ever since starting to use this library, I'm getting 401 errors from other Google libraries using the official Google API.

I'm going to start, but hypothesizing that I'm going to see that each library is using the client id/secret and refresh token to generate their own access tokens which are flapping and locking one another out.

Would it be possible to allow this library to get and manage the access_token, at least, via the official Google Auth library for nodejs? That way it can (hopefully) be managed by a singleton instance dealing with the OAuth-level authentication (or any Google-supported authentication) and play nicer with the rest of the ecosystem?

Members aren't being populated for Custom Interests

Hey there ๐Ÿ‘‹,

When I try to create a Custom Interest I get back an error which indicates members aren't being populated but I am passing through params which include a collection of CustomInterestMember objects.

I pass through the following params

{
  "type":3,
  "name":"I like dogs",
  "description":"We love dogs, dogs are the best",
  "members":[
    {"member_type":2,"parameter":"dog"}
  ]
}

I get back the following error:

code: { fieldError: 2 },
  request:
   { customerId: '4674531019',
     operationsList: [ [Object] ],
     validateOnly: false },
  request_id: 'BQbtQVg1e9ouMxsKPaea9g',
  location: 'operations[0].create.members[0]',
  failure:
   { Error: 3 INVALID_ARGUMENT: The required field was not present.

It appears that the member list (element 7) is an empty array - we'd expect it to have one element.

'7':
              [ { wrappers_: null,
                  messageId_: undefined,
                  arrayIndexOffset_: -1,
                  array: [],
                  pivot_: 1.7976931348623157e+308,
                  convertedFloatingPointFields_: {} } ] },

Is there anything that I am missing here?

Can't create an Ad - Error: 3 INVALID_ARGUMENT

Hey there

I'm having difficulty creating an Ad for my Ad Group, I did notice there was an issue where you would pass 2 enums into a create mutation - maybe somewhat related.

I'm using the latest package v1.2.2,

Below is the stack

// stack
ad params { ad_group: 'customers/4674531019/adGroups/74761353488',
  status: 3,
  ad:
   { final_urls: [ 'http://www.example.com' ],
     type: 3,
     name: 'best ad ever',
     expanded_text_ad:
      { headline_part_1: 'Cruise to Mars #%d',
        headline_part_2: 'Best Space Cruise Line',
        description: 'Buy your tickets now!',
        path_1: 'all-inclusive',
        path_2: 'deals' } } }
{ Error: The required field was not present in the resource.
    at AdGroupAdService.<anonymous> (/Users/matt/rea/rea-google-campaigns/node_modules/google-ads-api/build/services/service.js:142:23)
    at Generator.throw (<anonymous>)
    at rejected (/Users/matt/rea/rea-google-campaigns/node_modules/google-ads-api/build/services/service.js:5:65)
    at <anonymous>
  code:
   { requestError: 0,
     biddingStrategyError: 0,
     urlFieldError: 0,
     listOperationError: 0,
     queryError: 0,
     mutateError: 0,
     fieldMaskError: 0,
     authorizationError: 0,
     internalError: 0,
     quotaError: 0,
     adError: 0,
     adGroupError: 0,
     campaignBudgetError: 0,
     campaignError: 0,
     authenticationError: 0,
     adGroupCriterionError: 0,
     adCustomizerError: 0,
     adGroupAdError: 0,
     adSharingError: 0,
     adxError: 0,
     assetError: 0,
     biddingError: 0,
     campaignCriterionError: 0,
     collectionSizeError: 0,
     countryCodeError: 0,
     criterionError: 0,
     customerError: 0,
     dateError: 0,
     dateRangeError: 0,
     distinctError: 0,
     feedAttributeReferenceError: 0,
     functionError: 0,
     functionParsingError: 0,
     idError: 0,
     imageError: 0,
     languageCodeError: 0,
     mediaBundleError: 0,
     mediaFileError: 0,
     multiplierError: 0,
     newResourceCreationError: 0,
     notEmptyError: 0,
     nullError: 0,
     operatorError: 0,
     rangeError: 0,
     recommendationError: 0,
     regionCodeError: 0,
     settingError: 0,
     stringFormatError: 0,
     stringLengthError: 0,
     operationAccessDeniedError: 0,
     resourceAccessDeniedError: 0,
     resourceCountLimitExceededError: 0,
     adGroupBidModifierError: 0,
     contextError: 0,
     fieldError: 2,
     sharedSetError: 0,
     sharedCriterionError: 0,
     campaignSharedSetError: 0,
     conversionActionError: 0,
     conversionAdjustmentUploadError: 0,
     conversionUploadError: 0,
     headerError: 0,
     databaseError: 0,
     policyFindingError: 0,
     enumError: 0,
     keywordPlanError: 0,
     keywordPlanCampaignError: 0,
     keywordPlanNegativeKeywordError: 0,
     keywordPlanAdGroupError: 0,
     keywordPlanKeywordError: 0,
     keywordPlanIdeaError: 0,
     accountBudgetProposalError: 0,
     userListError: 0,
     changeStatusError: 0,
     feedError: 0,
     geoTargetConstantSuggestionError: 0,
     feedItemError: 0,
     labelError: 0,
     billingSetupError: 0,
     customerClientLinkError: 0,
     customerManagerLinkError: 0,
     feedMappingError: 0,
     customerFeedError: 0,
     adGroupFeedError: 0,
     campaignFeedError: 0,
     customInterestError: 0,
     extensionFeedItemError: 0,
     adParameterError: 0,
     feedItemValidationError: 0,
     extensionSettingError: 0,
     feedItemTargetError: 0,
     policyViolationError: 0,
     mutateJobError: 0,
     partialFailureError: 0,
     policyValidationParameterError: 0 },
  request:
   { customerId: '4674531019',
     operationsList: [ [Object] ],
     partialFailure: false,
     validateOnly: false },
  request_id: '_8Ph9A0hYxiAur6tzn6Qqw',
  failure:
   { Error: 3 INVALID_ARGUMENT: The required field was not present in the resource.
    at Object.exports.createStatusError (/Users/matt/rea/rea-google-campaigns/node_modules/grpc/src/common.js:91:15)
    at Object.onReceiveStatus (/Users/matt/rea/rea-google-campaigns/node_modules/grpc/src/client_interceptors.js:1204:28)
    at InterceptingListener._callNext (/Users/matt/rea/rea-google-campaigns/node_modules/grpc/src/client_interceptors.js:568:42)
    at InterceptingListener.onReceiveStatus (/Users/matt/rea/rea-google-campaigns/node_modules/grpc/src/client_interceptors.js:618:8)
    at Object.grpc_1.default.ListenerBuilder.withOnReceiveStatus [as onReceiveStatus] (/Users/matt/rea/rea-google-campaigns/node_modules/google-ads-node/build/lib/interceptor.js:68:17)
    at InterceptingListener._callNext (/Users/matt/rea/rea-google-campaigns/node_modules/grpc/src/client_interceptors.js:568:42)
    at InterceptingListener.onReceiveStatus (/Users/matt/rea/rea-google-campaigns/node_modules/grpc/src/client_interceptors.js:618:8)
    at Object.grpc_1.default.ListenerBuilder.withOnReceiveStatus [as onReceiveStatus] (/Users/matt/rea/rea-google-campaigns/node_modules/google-ads-node/build/lib/interceptor.js:151:13)
    at InterceptingListener._callNext (/Users/matt/rea/rea-google-campaigns/node_modules/grpc/src/client_interceptors.js:568:42)
    at InterceptingListener.onReceiveStatus (/Users/matt/rea/rea-google-campaigns/node_modules/grpc/src/client_interceptors.js:618:8)
     code: 3,
     metadata: Metadata { _internal_repr: [Object] },
     details: 'The required field was not present in the resource.' } }

Here is my code which creates an Ad,

// service

export const createAd = async (ad: types.AdGroupAd) => {
  const response = await customer.adGroupAds.create(ad);
  return response.results;
};

// builder
import { createAd } from "../services/googleService";
import { types } from "google-ads-api";
import { AdGroupAdStatus, AdType } from "google-ads-node/build/lib/enums";

export const buildParamsForAd = adGroup => {
  /*eslint-disable @typescript-eslint/camelcase*/
  const ad: types.AdGroupAd = {
    ad_group: adGroup,
    status: AdGroupAdStatus.PAUSED,
    ad: {
      final_urls: ['http://www.example.com'],
      type: AdType.EXPANDED_TEXT_AD,
      name: 'best ad ever',
      expanded_text_ad: {
        headline_part_1: 'Cruise to Mars #%d',
        headline_part_2: 'Best Space Cruise Line',
        description: 'Buy your tickets now!',
        path_1: 'all-inclusive',
        path_2: 'deals'
      }
    },
  };

  return ad;
};

export const createGoogleAd = async adParams => {
  const adId = await createAd(adParams);

  return adId;
};

// cli
import minimist from "minimist";
import { buildParamsForAd, createGoogleAd } from "../ad/createAd";

// Example
// $ node dist/cli/createAdCli.js --adGroup "customers/4674531019/adGroups/74761353488"

const params = minimist(process.argv);
const adGroup: string = params["adGroup"];

const adParams = buildParamsForAd(adGroup);
console.log("ad params", adParams);

createGoogleAd(adParams)
  .then(data => console.log("ad:", data))
  .catch(err => console.log(err));

Not sure what argument is Invalid? Am I missing something here?

Help would be much appreciated,
Thanks :)

Service key authentication

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

I'm trying to use this package without asking an user to authenticate using a Web interface. My question here is: does it work with service keys based authentication?

Thanks you!

unhandled rejection error ETIMEDOUT

unhandled rejection error:

unhandledRejection Error: connect ETIMEDOUT 216.58.200.237:443
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1056:14) {
errno: 'ETIMEDOUT',
code: 'ETIMEDOUT',
syscall: 'connect',
address: '216.58.200.237',
port: 443
}

image

try-catch clause can not handle this error.
the exception was thrown by google-ads-api.

here is my code:

    async query(gaqlString) {
        try {
            const customer = this._getCustomer();
            const result = await customer.query(gaqlString);

            return this._result(0, 'ok', result);
        } catch (err) {
            return this._result(-1, err.message || err);
        }
    }

How to use a service account?

I'm submitting a ...

  • question about how to use this project

Summary

Since I'm not going to be accessing other users' accounts from my server (only one predefined account), the google api console suggested I should use a service account to authenticate.

Is there a way to use the service account json file downloaded from the credential UI with this library?

Thanks

Unable to create adgroup video ad: Internal error encountered

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

I'm unable to create Video Ad in existing Skippable in-stream Ad Group in Video Campaign - it fails with an error "Internal error encountered".
But in a same time I don't have any issues with managing Display Ads.

Am I missed something? I'll appreciate any advice.

Thank you

Other information

[email protected]
[email protected]
[email protected]

AdGroupAd:

{
    "ad_group": "customers/1234567890/adGroups/0987654321",
    "status": 2,
    "ad": {
        "type": 12,
        "name": "Unspecified",
        "final_urls": [
            "https://example.com?__noscript=true"
        ],
        "tracking_url_template": "https://example.com?gg_campaign_id={campaignid}&gg_adgroup_id={adgroupid}&gg_network={network}&gg_device={device}&gg_gclid={gclid}&gg_creative={creative}",
        "video_ad": {
            "media_file": "customers/1234567890/mediaFiles/1123456789",
            "in_stream": {
                "companion_banner": "customers/1234567890/mediaFiles/1223456789"
            }
        }
    }
}

Response:

{
    "code": 13,
    "metadata": {
        "_internal_repr": {
            "google.ads.googleads.v2.errors.googleadsfailure-bin": [
                {
                    "type": "Buffer",
                    "data": [
                        10,
                        41,
                        10,
                        2,
                        80,
                        2,
                        18,
                        31,
                        65,
                        110,
                        32,
                        105,
                        110,
                        116,
                        101,
                        114,
                        110,
                        97,
                        108,
                        32,
                        101,
                        114,
                        114,
                        111,
                        114,
                        32,
                        104,
                        97,
                        115,
                        32,
                        111,
                        99,
                        99,
                        117,
                        114,
                        114,
                        101,
                        100,
                        46,
                        26,
                        2,
                        42,
                        0
                    ]
                }
            ],
            "grpc-status-details-bin": [
                {
                    "type": "Buffer",
                    "data": [
                        8,
                        13,
                        18,
                        27,
                        73,
                        110,
                        116,
                        101,
                        114,
                        110,
                        97,
                        108,
                        32,
                        101,
                        114,
                        114,
                        111,
                        114,
                        32,
                        101,
                        110,
                        99,
                        111,
                        117,
                        110,
                        116,
                        101,
                        114,
                        101,
                        100,
                        46,
                        26,
                        114,
                        10,
                        67,
                        116,
                        121,
                        112,
                        101,
                        46,
                        103,
                        111,
                        111,
                        103,
                        108,
                        101,
                        97,
                        112,
                        105,
                        115,
                        46,
                        99,
                        111,
                        109,
                        47,
                        103,
                        111,
                        111,
                        103,
                        108,
                        101,
                        46,
                        97,
                        100,
                        115,
                        46,
                        103,
                        111,
                        111,
                        103,
                        108,
                        101,
                        97,
                        100,
                        115,
                        46,
                        118,
                        50,
                        46,
                        101,
                        114,
                        114,
                        111,
                        114,
                        115,
                        46,
                        71,
                        111,
                        111,
                        103,
                        108,
                        101,
                        65,
                        100,
                        115,
                        70,
                        97,
                        105,
                        108,
                        117,
                        114,
                        101,
                        18,
                        43,
                        10,
                        41,
                        10,
                        2,
                        80,
                        2,
                        18,
                        31,
                        65,
                        110,
                        32,
                        105,
                        110,
                        116,
                        101,
                        114,
                        110,
                        97,
                        108,
                        32,
                        101,
                        114,
                        114,
                        111,
                        114,
                        32,
                        104,
                        97,
                        115,
                        32,
                        111,
                        99,
                        99,
                        117,
                        114,
                        114,
                        101,
                        100,
                        46,
                        26,
                        2,
                        42,
                        0
                    ]
                }
            ],
            "request-id": [
                "cVjZ2So7RYlQsEKjWhBFWQ"
            ]
        },
        "flags": 0
    },
    "details": "Internal error encountered."
}

Open Source?

Is this project currently/going to remain open source? If so, are you willing to accept pull requests? Thanks.

asset related information issue

I'm submitting a ...

  • bug report
  • feature request
  • question about the decisions made in the repository
  • question about how to use this project

Summary

Other information

Error: Cannot find module './entities/AdGroupCriterions'

When deployed to Heroku (Linux), a server won't start because of this error. It looks like the filename wasn't updated from AdgroupCriterions to AdGroupCriterions but the references in Customer were before 0.5.0 was released to npm. When I look in node_modules the file is still named AdgroupCriterions. It seems like if the current version on Github is pushed to npm, that will fix the issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.