googleads / google-ads-dotnet

This project hosts the .NET client library for the Google Ads API.

Home Page:https://developers.google.com/google-ads/api

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

LoginCustomerId interpreted as an optional instead of a string

tassosl opened this issue · comments

Describe the bug:
Related to question 479 and after some discussion with the Google Ads API support, it seems that v14.1.0 and v14.2.0 interpret LoginCustomerId as an optional instead of a string value. It seems it causes requests that were succeeding with earlier version of this nuget package to fail when upgraded. The behaviour change seems to align with the GRPC library change in v14.0.0.

Steps to Reproduce:
Code snippet that demonstrates the error. Running this with nuget package v11.0.0 or v12.1.0 (Ads API v10 or v11) works, but v14.1.0 and v14.2.0 (with Ads API v12) fail.

var developerToken = "";
var mccAccountNumber = "";
var oauthClientId = "";
var oauthClientSecret = "";
var oauthRefreshToken = "";

var config = new GoogleAdsConfig
{
    DeveloperToken = developerToken,
    LoginCustomerId = mccAccountNumber,
    OAuth2Mode = OAuth2Flow.APPLICATION,
    OAuth2ClientId = oauthClientId,
    OAuth2ClientSecret = oauthClientSecret,
    OAuth2RefreshToken = oauthRefreshToken
};
var client = new GoogleAdsClient(config);
var service = client.GetService(GoogleServices.V12.GoogleAdsService);

var budget = new CampaignBudgetOperation();
// Set the properties on budget
var mutateOperations = new List<MutateOperation> { new MutateOperation { CampaignBudgetOperation = operation } };
var request = new MutateGoogleAdsRequest
{
    CustomerId = account.ImmutableId.ToString(),
    PartialFailure = true
};
request.MutateOperations.AddRange(mutateOperations);

var callSettings = client.ServiceContext.CallSettings;
client.MutateAsync(request, callSettings);
...

Expected behavior:

Campaign budget updated and location(s) of new resource(s) returned, in this particular example. As mentioned in question 479, running the exact same code with just import changes works in v11.0.0 (Ads API v10) and v12.1.0 (Ads API v11).

Client library version and API version:
Client library version: v14.1.0 or v14.2.0
Google Ads API version: V12
.NET version: net5.0
Operating system (Linux, Windows, ...) and version (if the bug is platform-specific): Windows

Request/Response Logs:

GoogleAds.DetailedRequestLogs Information: 1: [2023 - 01 - 04 23: 34: 39Z] -
-------------- - BEGIN API CALL---------------

Request
------ -

Method Name: /google.ads.googleads.v12.services.GoogleAdsService/Mutate
Host:
Headers: {
    "x-goog-api-client": "gl-dotnet/5.0.0 gapic/14.2.0 gax/4.0.0+7489c236e0a5801d0f6e89bf1c23433f48521956 grpc/2.46.3 gccl/2.1.0 pb/3.21.5+638779f353731a0a04496bde20d14164684c3d93",
    "developer-token": "REDACTED",
    "login-customer-id": "REDACTED",
    "x-goog-request-params": "customer_id=REDACTED"
}
{
    "customerId": "REDACTED",
    "mutateOperations": [{
            "campaignBudgetOperation": {
                "create": {
                    "deliveryMethod": "STANDARD",
                    "period": "DAILY",
                    "name": "REDACTED",
                    "amountMicros": "1000000000",
                    "explicitlyShared": false
                }
            }
        }
    ],
    "partialFailure": true
}

Response
--------
Headers: {
    "request-id": "LYpklyZ85eUYCQG9OyuvlA",
    "date": "Wed, 04 Jan 2023 23:34:39 GMT",
    "alt-svc": "quic=\":443\"; ma=2592000",
    "google.ads.googleads.v12.errors.googleadsfailure-bin": "Cl4KA5AEAxJXVGhlIGxvZ2luIGN1c3RvbWVyIGlkIGhlYWRlciAnT3B0aW9uYWxbNDAzNzAwODc2OCwgNDAzNzAwODc2OF0nIGNvdWxkIG5vdCBiZSB2YWxpZGF0ZWQuEhZMWXBrbHlaODVlVVlDUUc5T3l1dmxB",
    "grpc-status-details-bin": "CAMSJVJlcXVlc3QgY29udGFpbnMgYW4gaW52YWxpZCBhcmd1bWVudC4awAEKRHR5cGUuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLmFkcy5nb29nbGVhZHMudjEyLmVycm9ycy5Hb29nbGVBZHNGYWlsdXJlEngKXgoDkAQDEldUaGUgbG9naW4gY3VzdG9tZXIgaWQgaGVhZGVyICdPcHRpb25hbFs0MDM3MDA4NzY4LCA0MDM3MDA4NzY4XScgY291bGQgbm90IGJlIHZhbGlkYXRlZC4SFkxZcGtseVo4NWVVWUNRRzlPeXV2bEE="
}

Fault: {
    "StatusCode": 3,
    "Details": "Request contains an invalid argument.",
    "RequestId": "LYpklyZ85eUYCQG9OyuvlA",
    "Failure": {
        "errors": [{
                "errorCode": {
                    "headerError": "INVALID_LOGIN_CUSTOMER_ID"
                },
                "message": "The login customer id header 'Optional[REDACTED, REDACTED]' could not be validated."
            }
        ],
        "requestId": "LYpklyZ85eUYCQG9OyuvlA"
    }
}
----------------END API CALL----------------

Other mutate and search operations also depict the same behaviour and also fail in the new versions vs the old.

Could you post a log from the old library? My guess is that Microsoft and Google implementations of grpc protocol differs in how empty headers are handled. I don't think anything has changed on the library itself, but I'll investigate more.

Could you post a log from the old library? My guess is that Microsoft and Google implementations of grpc protocol differs in how empty headers are handled. I don't think anything has changed on the library itself, but I'll investigate more.

Hey @AnashOommen here it is (posted originally in question 479 which might have more details). Not sure if the logs are 100% accurate for the headers since it is GRPC, but hope it helps.

Let me know if you need anything else or if you find something.

GoogleAds.DetailedRequestLogs Verbose: 1: [2023 - 01 - 04 23: 43: 00Z] -
-------------- - BEGIN API CALL-------------- -

Request
------ -

Method Name: /google.ads.googleads.v10.services.GoogleAdsService/Mutate
Host: https: //googleads.googleapis.com
Headers: {
    "x-goog-api-client": "gl-dotnet/5.0.0 gapic/11.0.0 gax/3.5.0+6cfcac416e08abe8a5d580f8d87095c9fdba577e grpc/2.41.0 gccl/11.0.0",
    "developer-token": "REDACTED",
    "login-customer-id": "REDACTED",
    "x-goog-request-params": "customer_id=REDACTED"
}
{
    "customerId": "REDACTED",
    "mutateOperations": [{
            "campaignBudgetOperation": {
                "create": {
                    "deliveryMethod": "STANDARD",
                    "period": "DAILY",
                    "name": "REDACTED",
                    "amountMicros": "1000000000",
                    "explicitlyShared": false
                }
            }
        }
    ],
    "partialFailure": true
}

Response
--------
Headers: {
    "content-disposition": "attachment",
    "request-id": "oK70D1SKOlLgLaUFyxoQXA",
    "date": "Wed, 04 Jan 2023 23:43:00 GMT",
    "alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\""
}
{
    "mutateOperationResponses": [{
            "campaignBudgetResult": {
                "resourceName": "customers/REDACTED/campaignBudgets/REDACTED"
            }
        }
    ]
}
----------------END API CALL----------------

This is a duplicate of #479 and happens due to the difference in behaviour of Grpc.Core v/s Grpc.Net.Client. See googleapis/gax-dotnet#672 for an explanation.

To get the old behaviour as a temporary workaround, you can do

config.UseGrpcCore = true;

A more permanent fix would be to construct a CallSetting from scratch instead of modifying client.ServiceContext.CallSettings.

E.g.

CancellationTokenSource source = new CancellationTokenSource(60 * 1000);
CallSettings callSettings = CallSettings.FromCancellationToken(source.Token);
client.MutateAsync(request, callSettings);

I don't think it is trivial to consolidate the behaviour between Microsoft's and Google's implementation of grpc, but I'll continue exploring that option.