Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.google.ads.googleads.lib.GoogleAdsClient;
import com.google.ads.googleads.v8.errors.GoogleAdsError;
import com.google.ads.googleads.v8.errors.GoogleAdsException;
import com.google.ads.googleads.v8.errors.GoogleAdsFailure;
import com.google.ads.googleads.v8.resources.AdGroup;
import com.google.ads.googleads.v8.services.AdGroupOperation;
import com.google.ads.googleads.v8.services.AdGroupServiceClient;
Expand All @@ -30,7 +31,6 @@
import com.google.ads.googleads.v8.services.MutateAdGroupsResponse;
import com.google.ads.googleads.v8.utils.ErrorUtils;
import com.google.ads.googleads.v8.utils.ResourceNames;
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
Expand All @@ -57,7 +57,7 @@ private static class HandlePartialFailureParams extends CodeSampleParams {
private Long campaignId;
}

public static void main(String[] args) throws InvalidProtocolBufferException {
public static void main(String[] args) {
HandlePartialFailureParams params = new HandlePartialFailureParams();
if (!params.parseArguments(args)) {
// Either pass the required parameters for this example on the command line, or insert them
Expand Down Expand Up @@ -96,8 +96,7 @@ public static void main(String[] args) throws InvalidProtocolBufferException {
}

/** Runs the example. */
public void runExample(GoogleAdsClient googleAdsClient, long customerId, long campaignId)
throws InvalidProtocolBufferException {
public void runExample(GoogleAdsClient googleAdsClient, long customerId, long campaignId) {
MutateAdGroupsResponse response = createAdGroups(googleAdsClient, customerId, campaignId);

// Checks for existence of any partial failures in the response.
Expand Down Expand Up @@ -165,15 +164,18 @@ private boolean checkIfPartialFailureErrorExists(MutateAdGroupsResponse response

/** Displays the result from the mutate operation. */
// [START handle_partial_failure_2]
private void printResults(MutateAdGroupsResponse response) throws InvalidProtocolBufferException {
private void printResults(MutateAdGroupsResponse response) {
int operationIndex = 0;
for (MutateAdGroupResult result : response.getResultsList()) {
if (ErrorUtils.getInstance().isPartialFailureResult(result)) {
// May throw on this line. Most likely this means the wrong version of the ErrorUtils
// class has been used.
GoogleAdsFailure googleAdsFailure = ErrorUtils.getInstance()
.getGoogleAdsFailure(response.getPartialFailureError());

for (GoogleAdsError error :
ErrorUtils.getInstance()
.getGoogleAdsErrors(operationIndex, response.getPartialFailureError())) {
.getGoogleAdsErrors(operationIndex, googleAdsFailure)) {
System.out.printf("Operation %d failed with error: %s%n", operationIndex, error);
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.google.ads.googleads.v8.enums.OfflineUserDataJobTypeEnum.OfflineUserDataJobType;
import com.google.ads.googleads.v8.errors.GoogleAdsError;
import com.google.ads.googleads.v8.errors.GoogleAdsException;
import com.google.ads.googleads.v8.errors.GoogleAdsFailure;
import com.google.ads.googleads.v8.resources.OfflineUserDataJob;
import com.google.ads.googleads.v8.resources.UserList;
import com.google.ads.googleads.v8.services.AddOfflineUserDataJobOperationsRequest;
Expand All @@ -44,6 +45,7 @@
import com.google.ads.googleads.v8.services.SearchGoogleAdsStreamResponse;
import com.google.ads.googleads.v8.services.UserListOperation;
import com.google.ads.googleads.v8.services.UserListServiceClient;
import com.google.ads.googleads.v8.utils.ErrorUtils;
import com.google.api.gax.rpc.ServerStream;
import com.google.common.collect.ImmutableList;
import java.io.FileNotFoundException;
Expand Down Expand Up @@ -219,11 +221,13 @@ private void addUsersToCustomerMatchUserList(
// NOTE: The details of each partial failure error are not printed here, you can refer to
// the example HandlePartialFailure.java to learn more.
if (response.hasPartialFailureError()) {
GoogleAdsFailure googleAdsFailure =
ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError());
System.out.printf(
"Encountered %d partial failure errors while adding %d operations to the offline user "
+ "data job: '%s'. Only the successfully added operations will be executed when "
+ "the job runs.%n",
response.getPartialFailureError().getDetailsCount(),
googleAdsFailure.getErrorsCount(),
Comment thread
nwbirnie marked this conversation as resolved.
userDataJobOperations.size(),
response.getPartialFailureError().getMessage());
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
import com.google.ads.googleads.lib.GoogleAdsClient;
import com.google.ads.googleads.v8.errors.GoogleAdsError;
import com.google.ads.googleads.v8.errors.GoogleAdsException;
import com.google.ads.googleads.v8.errors.GoogleAdsFailure;
import com.google.ads.googleads.v8.services.CallConversion;
import com.google.ads.googleads.v8.services.CallConversionResult;
import com.google.ads.googleads.v8.services.ConversionUploadServiceClient;
import com.google.ads.googleads.v8.services.CustomVariable;
import com.google.ads.googleads.v8.services.UploadCallConversionsRequest;
import com.google.ads.googleads.v8.services.UploadCallConversionsResponse;
import com.google.ads.googleads.v8.utils.ErrorUtils;
import com.google.ads.googleads.v8.utils.ResourceNames;
import java.io.FileNotFoundException;
import java.io.IOException;
Expand Down Expand Up @@ -185,6 +187,11 @@ private void runExample(

// Prints any partial failure errors returned.
if (response.hasPartialFailureError()) {
GoogleAdsFailure googleAdsFailure =
ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError());
googleAdsFailure
.getErrorsList()
.forEach(e -> System.out.println("Partial failure occurred: " + e.getMessage()));
throw new RuntimeException(
"Partial failure occurred " + response.getPartialFailureError().getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
import com.google.ads.googleads.v8.enums.ConversionAdjustmentTypeEnum.ConversionAdjustmentType;
import com.google.ads.googleads.v8.errors.GoogleAdsError;
import com.google.ads.googleads.v8.errors.GoogleAdsException;
import com.google.ads.googleads.v8.errors.GoogleAdsFailure;
import com.google.ads.googleads.v8.services.ConversionAdjustment;
import com.google.ads.googleads.v8.services.ConversionAdjustmentResult;
import com.google.ads.googleads.v8.services.ConversionAdjustmentUploadServiceClient;
import com.google.ads.googleads.v8.services.GclidDateTimePair;
import com.google.ads.googleads.v8.services.RestatementValue;
import com.google.ads.googleads.v8.services.UploadConversionAdjustmentsRequest;
import com.google.ads.googleads.v8.services.UploadConversionAdjustmentsResponse;
import com.google.ads.googleads.v8.utils.ErrorUtils;
import com.google.ads.googleads.v8.utils.ResourceNames;
import java.io.FileNotFoundException;
import java.io.IOException;
Expand Down Expand Up @@ -203,8 +205,11 @@ private void runExample(

// Prints any partial errors returned.
if (response.hasPartialFailureError()) {
System.out.printf(
"Partial error encountered: '%s'.%n", response.getPartialFailureError().getMessage());
GoogleAdsFailure googleAdsFailure =
ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError());
googleAdsFailure
.getErrorsList()
.forEach(e -> System.out.println("Partial failure occurred: " + e.getMessage()));
} else {
// Prints the result.
ConversionAdjustmentResult result = response.getResults(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
import com.google.ads.googleads.lib.GoogleAdsClient;
import com.google.ads.googleads.v8.errors.GoogleAdsError;
import com.google.ads.googleads.v8.errors.GoogleAdsException;
import com.google.ads.googleads.v8.errors.GoogleAdsFailure;
import com.google.ads.googleads.v8.services.ClickConversion;
import com.google.ads.googleads.v8.services.ClickConversionResult;
import com.google.ads.googleads.v8.services.ConversionUploadServiceClient;
import com.google.ads.googleads.v8.services.CustomVariable;
import com.google.ads.googleads.v8.services.UploadClickConversionsRequest;
import com.google.ads.googleads.v8.services.UploadClickConversionsResponse;
import com.google.ads.googleads.v8.utils.ErrorUtils;
import com.google.ads.googleads.v8.utils.ResourceNames;
import java.io.FileNotFoundException;
import java.io.IOException;
Expand Down Expand Up @@ -182,8 +184,11 @@ private void runExample(

// Prints any partial errors returned.
if (response.hasPartialFailureError()) {
System.out.printf(
"Partial error encountered: '%s'.%n", response.getPartialFailureError().getMessage());
GoogleAdsFailure googleAdsFailure =
ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError());
googleAdsFailure
.getErrorsList()
.forEach(e -> System.out.println("Partial failure occurred: " + e.getMessage()));
}

// Prints the result.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.google.ads.googleads.v8.enums.OfflineUserDataJobTypeEnum.OfflineUserDataJobType;
import com.google.ads.googleads.v8.errors.GoogleAdsError;
import com.google.ads.googleads.v8.errors.GoogleAdsException;
import com.google.ads.googleads.v8.errors.GoogleAdsFailure;
import com.google.ads.googleads.v8.resources.OfflineUserDataJob;
import com.google.ads.googleads.v8.services.AddOfflineUserDataJobOperationsRequest;
import com.google.ads.googleads.v8.services.AddOfflineUserDataJobOperationsResponse;
Expand All @@ -36,6 +37,7 @@
import com.google.ads.googleads.v8.services.GoogleAdsServiceClient;
import com.google.ads.googleads.v8.services.OfflineUserDataJobOperation;
import com.google.ads.googleads.v8.services.OfflineUserDataJobServiceClient;
import com.google.ads.googleads.v8.utils.ErrorUtils;
import com.google.ads.googleads.v8.utils.ResourceNames;
import com.google.common.collect.ImmutableList;
import java.io.FileNotFoundException;
Expand Down Expand Up @@ -469,11 +471,16 @@ private void addTransactionsToOfflineUserDataJob(
// NOTE: The details of each partial failure error are not printed here, you can refer to
// the example HandlePartialFailure.java to learn more.
if (response.hasPartialFailureError()) {
GoogleAdsFailure googleAdsFailure =
ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError());
googleAdsFailure
.getErrorsList()
.forEach(e -> System.out.println("Partial failure occurred: " + e.getMessage()));
System.out.printf(
"Encountered %d partial failure errors while adding %d operations to the offline user "
+ "data job: '%s'. Only the successfully added operations will be executed when "
+ "the job runs.%n",
response.getPartialFailureError().getDetailsCount(),
ErrorUtils.getInstance().getFailedOperationIndices(googleAdsFailure).size(),
userDataJobOperations.size(),
response.getPartialFailureError().getMessage());
} else {
Expand Down
1 change: 1 addition & 0 deletions google-ads/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ sourceSets {
runtimeClasspath += sourceSets.main.output
}
}
sourceSets.test.java.srcDir new File(buildDir, 'generated/source/proto/test')

protobuf {
protoc {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/** Contains utility methods for handling partial failure of operations. */
public abstract class AbstractErrorUtils<
Expand Down Expand Up @@ -77,9 +79,9 @@ public abstract class AbstractErrorUtils<
* empty list otherwise.
*
* <p>This method supports <code>XXXService.mutate(request)</code> where the request contains a
* list of operations named "operations". It also supports
* <code>GoogleAdsService.mutateGoogleAds(request)</code>, where the request contains a list of
* <code>MutateOperation</code>s named "mutate_operations".
* list of operations named "operations". It also supports <code>
* GoogleAdsService.mutateGoogleAds(request)</code>, where the request contains a list of <code>
* MutateOperation</code>s named "mutate_operations".
*
* @param operationIndex the index of the operation, starting from 0.
* @param partialFailureStatus a partialFailure status, with the detail list containing {@link
Expand Down Expand Up @@ -108,8 +110,7 @@ public List<GoogleAdsErrorT> getGoogleAdsErrors(
List<GoogleAdsErrorT> result = new ArrayList();
Comment thread
jradcliff marked this conversation as resolved.
// Searches all the errors for one relating to the specified operation.
for (ErrorPath<GoogleAdsErrorT> path : getErrorPaths(googleAdsFailure)) {
if (("operations".equals(path.getFieldName())
|| "mutate_operations".equals(path.getFieldName()))
if (path.isOperationIndex()
&& path.getIndex().isPresent()
&& path.getIndex().get() == operationIndex) {
GoogleAdsErrorT error = path.getError();
Expand All @@ -121,15 +122,49 @@ public List<GoogleAdsErrorT> getGoogleAdsErrors(
return result;
}

/** Provides a convenience method to get all failed operation indices. */
public List<Long> getFailedOperationIndices(GoogleAdsFailureT googleAdsFailureT) {
return StreamSupport.stream(getErrorPaths(googleAdsFailureT).spliterator(), false)
.filter(ErrorPath::isOperationIndex)
.filter(p -> p.getIndex().isPresent())
.map(p -> (Long) p.getIndex().get())
.distinct()
.collect(Collectors.toList());
}

/**
* Unpacks a single {@link GoogleAdsFailureT} from an {@link Any} instance.
*
* @throws InvalidProtocolBufferException if {@link GoogleAdsFailureT} is not able to unpack the
* protocol buffer. This is most likely due to using the wrong version of <code>ErrorUtils
* </code> being used.
* @throws DeserializeException if an {@link InvalidProtocolBufferException} is encountered. This
* would indicate that the detail object was not-null, but the contents couldn't be
* deserialized to the target type. This may indicate that the target type is incorrect, or
* that the content of the Any message is incorrect.
* @throws NullPointerException if detail is null.
*/
public GoogleAdsFailureT getGoogleAdsFailure(Any detail) throws InvalidProtocolBufferException {
return detail.unpack(getGoogleAdsFailureClass());
public GoogleAdsFailureT getGoogleAdsFailure(Any detail) {
try {
return detail.unpack(getGoogleAdsFailureClass());
Comment thread
nwbirnie marked this conversation as resolved.
} catch (InvalidProtocolBufferException e) {
Comment thread
nwbirnie marked this conversation as resolved.
throw new DeserializeException(e);
}
}

/**
* Unpacks the GoogleAdsFailureT instance form a partial failure status object.
*
* <p>The status object contains a details repeated field. This contains at most 1 Any protos
* which encode a GoogleAdsFailure instance.
*
* @param partialFailureStatus the partial failure Status object returned in the repsponse.
* @return the GoogleAdsFailure instance describing the partial failures, or null if none is
* found.
* @throws DeserializeException if an {@link InvalidProtocolBufferException} is encountered.
* @throws NullPointerException if partialFailureStatus is null.
*/
public GoogleAdsFailureT getGoogleAdsFailure(Status partialFailureStatus) {
return partialFailureStatus.getDetailsCount() == 0
Comment thread
nwbirnie marked this conversation as resolved.
? null
: getGoogleAdsFailure(partialFailureStatus.getDetails(0));
}

/** Checks if a result in a mutate response is a partial failure. */
Expand Down Expand Up @@ -192,8 +227,7 @@ protected static class ErrorPath<GoogleAdsErrorType extends Message> {
private final String fieldName;
private final Optional<Long> index;

public ErrorPath(
GoogleAdsErrorType error, String fieldName, Optional<Long> index) {
public ErrorPath(GoogleAdsErrorType error, String fieldName, Optional<Long> index) {
this.error = error;
this.fieldName = fieldName;
this.index = index;
Expand All @@ -210,5 +244,17 @@ public String getFieldName() {
public Optional<Long> getIndex() {
return index;
}

public boolean isOperationIndex() {
return "operations".equals(getFieldName()) || "mutate_operations".equals(getFieldName());
}
}

/** Indicates an error occurred deserializing an API error object. */
public static class DeserializeException extends RuntimeException {

public DeserializeException(Throwable cause) {
super(cause);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,53 @@ public void getGoogleAdsErrors_duplicates_whenErrorsDiffer()
assertEquals(error1, result.get(1));
}

@Test
public void getFailedOperationIndices_returnsOperation() {
MockPath path0 =
MockPath.newBuilder()
.setIndex(Int64Value.newBuilder().setValue(123))
.setFieldName(operationsFieldName)
.build();
MockError error0 = MockError.newBuilder().addLocation(path0).build();
MockFailure failure = MockFailure.newBuilder().addErrors(error0).build();
List<Long> result = impl.getFailedOperationIndices(failure);
assertEquals(1, result.size());
assertEquals(123L, (long) result.get(0));
}

@Test
public void getFailedOperationIndices_removesDuplicates() {
MockPath path0 =
MockPath.newBuilder()
.setIndex(Int64Value.newBuilder().setValue(123))
.setFieldName(operationsFieldName)
.build();
MockPath path1 =
MockPath.newBuilder()
.setIndex(Int64Value.newBuilder().setValue(123))
.setFieldName(operationsFieldName)
.build();
MockError error0 = MockError.newBuilder().addLocation(path0).build();
MockError error1 = MockError.newBuilder().addLocation(path1).build();
MockFailure failure = MockFailure.newBuilder().addErrors(error0).addErrors(error1).build();
List<Long> result = impl.getFailedOperationIndices(failure);
assertEquals(1, result.size());
assertEquals(123L, (long) result.get(0));
}

@Test
public void getFailedOperationIndices_ignoresNonOperationErrors() {
MockPath path0 =
MockPath.newBuilder()
.setIndex(Int64Value.newBuilder().setValue(123))
.setFieldName("someotherfield")
.build();
MockError error0 = MockError.newBuilder().addLocation(path0).build();
MockFailure failure = MockFailure.newBuilder().addErrors(error0).build();
List<Long> result = impl.getFailedOperationIndices(failure);
assertEquals(0, result.size());
}

// We do want a dummy here for the version specific code, rather than a mock, so we can test the
// base class methods.
private static class TestImpl extends AbstractErrorUtils<MockFailure, MockError, MockPath> {
Expand Down