Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

grpc extension changes exceptions being thrown. #224

Open
lezhnev74 opened this issue Oct 30, 2019 · 8 comments
Open

grpc extension changes exceptions being thrown. #224

lezhnev74 opened this issue Oct 30, 2019 · 8 comments
Labels
difficulty: medium Intermediate difficulty; some experience with codebase may be helpful enhancement New feature or request P3 Prospects for future development or feature requests that would be nice to have. Typos or minor bugs

Comments

@lezhnev74
Copy link

I was testing campaign creation and particular case: name duplication error.
I found out that different exceptions are thrown in the runtime:

  • when grpc is enabled I get \Google\Ads\GoogleAds\Lib\V2\GoogleAdsException
  • when grpc is disabled I get \Google\ApiCore\ApiException

Not to say, that first case is much more preferable, as the exception contains useful extra information about the problem.

@PierrickVoulet
Copy link
Collaborator

PierrickVoulet commented Oct 30, 2019

Good feedback. In case of an error, REST and gRPC transports do not return the metadata in the exact same format:

  • gRPC

image

  • REST

image

The current implementation of the GoogleAdsExceptionMiddleware which translates the response including the metadata content into GoogleAdsException is able to process the gRPC format only.

This is not a bug per se, flagging this issue as an enhancement request.

You are saying that there is more information about the error in the GoogleAdsException (gRPC) compared to the ApiException (REST): could you please provide concrete examples of this?

@PierrickVoulet PierrickVoulet added the enhancement New feature or request label Oct 30, 2019
@lezhnev74
Copy link
Author

lezhnev74 commented Oct 31, 2019

You are saying that there is more information about the error in the GoogleAdsException (gRPC) compared to the ApiException (REST): could you please provide concrete examples of this?

Yes, to get details about the error I do this:

/** @var \Google\Ads\GoogleAds\Lib\V2\GoogleAdsException $e */
$errorArray = json_decode($e->getGoogleAdsFailure()->serializeToJsonString(), true);

# ApiException won't let me do that

That gives me roughly this data which I can work with:

[{"message": "AdGroup with the same name already exists for the campaign.", "trigger": {"stringValue": "Optimized AdGroup #5db9e837828d3"}, "location": {"fieldPathElements": [{"index": "6", "fieldName": "operations"}, {"fieldName": "create"}, {"fieldName": "name"}]}, "errorCode": {"adGroupError": "DUPLICATE_ADGROUP_NAME"}}]

Why do I serialize errors to JSON and deserialize it back? Because this is the only way I figured out to get the actual error code and details about the error. Is there a better way?

@PierrickVoulet
Copy link
Collaborator

Thanks for sharing, ApiException still provides methods like getMetadata and gives access to raw response data. I believe it contains the same information about the error(s) even if not as well structured as you have with a GoogleAdsException.

To be sure, do you mind executing a var_dump of both exception types that return with the same error(s)? I would be curious to see if you can spot any difference of information (and not only structural).

@lezhnev74
Copy link
Author

Nice! Can you show an example I can use, say, for getting errorCode value of the error?

@PierrickVoulet
Copy link
Collaborator

Sure, you could print the error codes using something like this:

try {
    // Does some API  call(s)
} catch (ApiException $apiException) {
    foreach($apiException->getMetadata() as $metadata) {
        if(
            array_key_exists('@type', $metadata) &&
            $metadata['@type'] == 'type.googleapis.com/google.ads.googleads.v2.errors.GoogleAdsFailure' &&
            array_key_exists('errors', $metadata)
        ) {
            foreach($metadata['errors'] as $error) {
                if(array_key_exists('errorCode', $error)) {
                    foreach($error['errorCode'] as $errorCode) {
                        print("Error Code: " . $errorCode);
                    }
                }
            }
        }
    }
}

HIH

@lezhnev74
Copy link
Author

Thank you! Let me play with that example.
Is there plans to develop a more intuitive way to access error?

@lezhnev74
Copy link
Author

Tried this test, but it gave me no error codes.

$budget = new CampaignBudget([
    'name' => new StringValue([
        'value' => sprintf('Budget for campaign [%s]', uniqid())
    ]),
    'delivery_method' => BudgetDeliveryMethod::STANDARD,
    'amount_micros' => new Int64Value(['value' => -100]) // wrong value cuases failure
]);
$op = new CampaignBudgetOperation();
$op->setCreate($budget);

$client = $this->app->make(GoogleAdsClientBuilder::class)->build($userId, $googleClientId);
$campaignBudgetServiceClient = $client->getCampaignBudgetServiceClient();
try {
    $campaignBudgetServiceClient->mutateCampaignBudgets($googleClientId, [$op]);
} catch (ApiException $e) {
    foreach($e->getMetadata() as $metadata) {
        if(
            array_key_exists('@type', $metadata) &&
            $metadata['@type'] == 'type.googleapis.com/google.ads.googleads.v2.errors.GoogleAdsFailure' &&
            array_key_exists('errors', $metadata)
        ) {
            foreach($metadata['errors'] as $error) {
                if(array_key_exists('errorCode', $error)) {
                    foreach($error['errorCode'] as $errorCode) {
                        print("Error Code: " . $errorCode);
                    }
                }
            }
        }
    }
}

The API call logged:

Request
-------
Method Name: /google.ads.googleads.v2.services.CampaignBudgetService/MutateCampaignBudgets
Host: googleads.googleapis.com
Headers: {
    "x-goog-api-client": "gl-php\/7.2.23 gapic\/ gax\/1.2.0 grpc\/1.23.1",
    "x-goog-request-params": "customer_id=8530332597",
    "developer-token": "REDACTED"
}
Request: {"customerId":"8530332597","operations":[{"create":{"name":"Budget for campaign [5dc173920a363]","amountMicros":"-100","deliveryMethod":"STANDARD"}}]}

Response
-------
Headers: {
    "request-id": "6OL1RE-W0C1FZgRWStM1eQ",
    "date": "Tue, 05 Nov 2019 13:05:24 GMT",
    "alt-svc": "quic=\":443\"; ma=2592000; v=\"46,43\",h3-Q049=\":443\"; ma=2592000,h3-Q048=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000"
}

Fault
-------
Status code: 3
Details: Request contains an invalid argument.
Failure: {"errors":[{"errorCode":{"rangeError":"TOO_LOW"},"message":"Too low.","location":{"fieldPathElements":[{"fieldName":"operations","index":"0"},{"fieldName":"create"},{"fieldName":"amount_micros"}]}}]}

PHP extensions:

php -m
Starting instaon_mysql ... done
[PHP Modules]
bcmath
Core
ctype
curl
date
dom
exif
fileinfo
filter
ftp
gd
grpc
hash
iconv
imap
json
libxml
mbstring
mysqlnd
openssl
pcntl
pcre
PDO
pdo_mysql
pdo_sqlite
Phar
posix
protobuf
readline
Reflection
session
SimpleXML
soap
sockets
sodium
SPL
sqlite3
standard
tokenizer
xdebug
xml
xmlreader
xmlwriter
zip
zlib

[Zend Modules]
Xdebug

@PierrickVoulet
Copy link
Collaborator

The main condition I drafted might be overkill, did you try to simplify keeping only:

array_key_exists('errors', $metadata)

@PierrickVoulet PierrickVoulet removed their assignment Feb 28, 2020
@fiboknacky fiboknacky added difficulty: medium Intermediate difficulty; some experience with codebase may be helpful P3 Prospects for future development or feature requests that would be nice to have. Typos or minor bugs labels Apr 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty: medium Intermediate difficulty; some experience with codebase may be helpful enhancement New feature or request P3 Prospects for future development or feature requests that would be nice to have. Typos or minor bugs
Projects
None yet
Development

No branches or pull requests

3 participants