google-ads-api
google-ads-api copied to clipboard
"null" is a valid value for some update properties
According to the Google Documentation, to set a keyword's cpc bid to the default (ad group level or campaign level) you should make an update call with the cpc_bid_micros set to null
. See Removing Bids. This doesn't seem to be possible using this library.
When I run an update operation on adgroupcriterion
with the following structure:
customer.adGroupCriteria.update({
resource_name: 'customers/customerid/adGroupCriterion/resourceid,
cpc_bid_micros: null
}, {partial_failure:true})
The library generates the following FieldMask
which is missing cpc_bid_micros
update_mask: {
paths: ["resource_name"]
}
For anyone looking for a quick workaround, here's a callback using the undocumented onMutationStart hook which can be placed in the Customer
creation:
let customer = Customer({
customer_id: "",
login_customer_id: "",
refresh_token: ""
},
{
onMutationStart(args) {
//TODO this hook is a workaround for a bug in the library that leaves the cpc_bid_micros out of the
// update mask when the value is null.
if(args['mutation'] && args['mutation']['operations']) {
args['mutation']['operations'].forEach(op => {
if(Object.keys(op['update']).includes('cpc_bid_micros') && !op['update_mask']['paths'].includes('cpc_bid_micros')) {
op['update_mask']['paths'].push('cpc_bid_micros');
}
})
}
}});
Faced with the same problem with clearing campaign.target_spend.cpc_bid_ceiling_micros
and campaign.maximize_conversions.target_cpa
.
Problem is global for this lib. If you set null to updating value, lib just skip and not set it as value to update. I've updated code provided by @ellisonc above to make it work with all null values and send them to Google. Works good for me:
type DataObject = {[key: string]: any};
/**
* Function to make object flatten. Can be used https://www.npmjs.com/package/flat package instead
*/
export function flatObject(input: DataObject): DataObject {
function flat(res: DataObject, key: string, val: any, pre = ''): DataObject {
const prefix = [pre, key].filter(v => v).join('.');
return typeof val === 'object' && val !== null
? Object.keys(val).reduce((prev, curr) => flat(prev, curr, val[curr], prefix), res)
: Object.assign(res, { [prefix]: val});
}
return Object.keys(input).reduce((prev, curr) => flat(prev, curr, input[curr]), {});
}
const customer = Customer({
customer_id: "",
login_customer_id: "",
refresh_token: ""
}, {
onMutationStart: (args: any) => {
const mutation = args.mutation as {operations: ({update: DataObject, update_mask: IFieldMask})[]}
if (!mutation?.operations?.length) {
return;
}
mutation?.operations.forEach((op) => {
const flatUpdateObj = flatObject(op.update);
const nullablePaths = Object.keys(flatUpdateObj).filter((flatKey) => flatUpdateObj[flatKey] === null);
op.update_mask.paths = op.update_mask.paths.concat(nullablePaths);
});
}
});