From ba039e98a3bf5c521d7b726e63a8982bf30f01f3 Mon Sep 17 00:00:00 2001 From: Nicole Lee Date: Wed, 12 Aug 2026 05:13:28 +0000 Subject: [PATCH 1/2] feat: added poc for option c for compute lro error details --- .../engine/writer/JavaWriterVisitor.java | 16 ++ .../generator/gapic/composer/Composer.java | 5 + .../common/RetrySettingsComposer.java | 19 +- .../ComputeLroErrorParserClassComposer.java | 215 ++++++++++++++++++ .../gax/httpjson/HttpJsonLroErrorParser.java | 18 ++ .../httpjson/ProtoOperationTransformers.java | 32 ++- 6 files changed, 302 insertions(+), 3 deletions(-) create mode 100644 sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/rest/ComputeLroErrorParserClassComposer.java create mode 100644 sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLroErrorParser.java diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java index dc243eec8898..ccfb51681cf0 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java @@ -536,11 +536,27 @@ public void visit(ArithmeticOperationExpr arithmeticOperationExpr) { @Override public void visit(UnaryOperationExpr unaryOperationExpr) { + boolean needsParen = unaryOperationExpr.expr() instanceof InstanceofExpr + || unaryOperationExpr.expr() instanceof RelationalOperationExpr + || unaryOperationExpr.expr() instanceof LogicalOperationExpr; + if (unaryOperationExpr.operatorKind().isPrefixOperator()) { operator(unaryOperationExpr.operatorKind()); + if (needsParen) { + leftParen(); + } unaryOperationExpr.expr().accept(this); + if (needsParen) { + rightParen(); + } } else { + if (needsParen) { + leftParen(); + } unaryOperationExpr.expr().accept(this); + if (needsParen) { + rightParen(); + } operator(unaryOperationExpr.operatorKind()); } } diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/Composer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/Composer.java index edb180676c82..3b64b4c6cf57 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/Composer.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/Composer.java @@ -28,6 +28,7 @@ import com.google.api.generator.gapic.composer.grpc.ServiceStubSettingsClassComposer; import com.google.api.generator.gapic.composer.grpcrest.HttpJsonServiceClientTestClassComposer; import com.google.api.generator.gapic.composer.resourcename.ResourceNameHelperClassComposer; +import com.google.api.generator.gapic.composer.rest.ComputeLroErrorParserClassComposer; import com.google.api.generator.gapic.composer.rest.HttpJsonServiceCallableFactoryClassComposer; import com.google.api.generator.gapic.composer.rest.HttpJsonServiceStubClassComposer; import com.google.api.generator.gapic.model.GapicClass; @@ -102,6 +103,10 @@ public static List generateStubClasses(GapicContext context) { clazzes.add( HttpJsonServiceCallableFactoryClassComposer.instance().generate(context, s)); clazzes.add(HttpJsonServiceStubClassComposer.instance().generate(context, s)); + if (s.pakkage().startsWith("com.google.cloud.compute.v1")) { + clazzes.add( + ComputeLroErrorParserClassComposer.instance().generate(context, s)); + } } else if (context.transport() == Transport.GRPC) { clazzes.add(ServiceStubClassComposer.instance().generate(context, s)); clazzes.add(ServiceStubSettingsClassComposer.instance().generate(context, s)); diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/RetrySettingsComposer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/RetrySettingsComposer.java index 98784fc8818f..eb7f5a4c2ff9 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/RetrySettingsComposer.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/RetrySettingsComposer.java @@ -30,10 +30,12 @@ import com.google.api.generator.engine.ast.Expr; import com.google.api.generator.engine.ast.ExprStatement; import com.google.api.generator.engine.ast.MethodInvocationExpr; +import com.google.api.generator.engine.ast.NewObjectExpr; import com.google.api.generator.engine.ast.PrimitiveValue; import com.google.api.generator.engine.ast.StringObjectValue; import com.google.api.generator.engine.ast.TypeNode; import com.google.api.generator.engine.ast.ValueExpr; +import com.google.api.generator.engine.ast.VaporReference; import com.google.api.generator.engine.ast.Variable; import com.google.api.generator.engine.ast.VariableExpr; import com.google.api.generator.gapic.composer.store.TypeStore; @@ -323,6 +325,21 @@ public static Expr createLroSettingsBuilderExpr( Variable.builder().setType(TypeNode.CLASS_OBJECT).setName("class").build()) .setStaticReferenceType(t) .build(); + + List createArgs = new ArrayList<>(); + createArgs.add(classFieldRefFn.apply(method.lro().responseType())); + if (service.pakkage().startsWith("com.google.cloud.compute.v1") && operationResponseTransformer.reference().pakkage().equals("com.google.api.gax.httpjson")) { + createArgs.add( + NewObjectExpr.builder() + .setType( + TypeNode.withReference( + VaporReference.builder() + .setName("ComputeLroErrorParser") + .setPakkage(service.pakkage() + ".stub") + .build())) + .build()); + } + builderSettingsExpr = MethodInvocationExpr.builder() .setExprReferenceExpr(builderSettingsExpr) @@ -331,7 +348,7 @@ public static Expr createLroSettingsBuilderExpr( MethodInvocationExpr.builder() .setStaticReferenceType(operationResponseTransformer) .setMethodName("create") - .setArguments(classFieldRefFn.apply(method.lro().responseType())) + .setArguments(createArgs) .build()) .build(); builderSettingsExpr = diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/rest/ComputeLroErrorParserClassComposer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/rest/ComputeLroErrorParserClassComposer.java new file mode 100644 index 000000000000..0cb543b8a4bd --- /dev/null +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/rest/ComputeLroErrorParserClassComposer.java @@ -0,0 +1,215 @@ +package com.google.api.generator.gapic.composer.rest; + +import com.google.api.generator.engine.ast.AssignmentExpr; +import com.google.api.generator.engine.ast.BlockStatement; +import com.google.api.generator.engine.ast.CastExpr; +import com.google.api.generator.engine.ast.ClassDefinition; +import com.google.api.generator.engine.ast.ConcreteReference; +import com.google.api.generator.engine.ast.Expr; +import com.google.api.generator.engine.ast.ExprStatement; +import com.google.api.generator.engine.ast.ForStatement; +import com.google.api.generator.engine.ast.IfStatement; +import com.google.api.generator.engine.ast.InstanceofExpr; +import com.google.api.generator.engine.ast.LogicalOperationExpr; +import com.google.api.generator.engine.ast.UnaryOperationExpr; +import com.google.api.generator.engine.ast.MethodDefinition; +import com.google.api.generator.engine.ast.MethodInvocationExpr; +import com.google.api.generator.engine.ast.NewObjectExpr; +import com.google.api.generator.engine.ast.PrimitiveValue; +import com.google.api.generator.engine.ast.RelationalOperationExpr; +import com.google.api.generator.engine.ast.ReturnExpr; +import com.google.api.generator.engine.ast.ScopeNode; +import com.google.api.generator.engine.ast.Statement; +import com.google.api.generator.engine.ast.StringObjectValue; +import com.google.api.generator.engine.ast.TypeNode; +import com.google.api.generator.engine.ast.ValueExpr; +import com.google.api.generator.engine.ast.VaporReference; +import com.google.api.generator.engine.ast.Variable; +import com.google.api.generator.engine.ast.VariableExpr; +import com.google.api.generator.gapic.composer.comment.CommentComposer; +import com.google.api.generator.gapic.model.GapicClass; +import com.google.api.generator.gapic.model.GapicContext; +import com.google.api.generator.gapic.model.Service; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ComputeLroErrorParserClassComposer { + private static final ComputeLroErrorParserClassComposer INSTANCE = new ComputeLroErrorParserClassComposer(); + + public static ComputeLroErrorParserClassComposer instance() { + return INSTANCE; + } + + public GapicClass generate(GapicContext context, Service service) { + String pakkage = service.pakkage() + ".stub"; + TypeNode parserType = TypeNode.withReference( + VaporReference.builder().setName("ComputeLroErrorParser").setPakkage(pakkage).build()); + + TypeNode httpJsonLroErrorParserType = TypeNode.withReference( + ConcreteReference.withClazz(com.google.api.gax.httpjson.HttpJsonLroErrorParser.class)); + TypeNode errorDetailsType = TypeNode.withReference( + ConcreteReference.withClazz(com.google.api.gax.rpc.ErrorDetails.class)); + TypeNode operationType = TypeNode.withReference( + VaporReference.builder().setName("Operation").setPakkage(service.pakkage()).build()); + TypeNode errorsType = TypeNode.withReference( + VaporReference.builder().setName("Errors").setPakkage(service.pakkage()).build()); + TypeNode errorInfoType = TypeNode.withReference( + ConcreteReference.withClazz(com.google.rpc.ErrorInfo.class)); + TypeNode anyType = TypeNode.withReference( + ConcreteReference.withClazz(com.google.protobuf.Any.class)); + TypeNode listAnyType = TypeNode.withReference( + ConcreteReference.builder().setClazz(java.util.List.class).setGenerics(anyType.reference()).build()); + TypeNode arrayListAnyType = TypeNode.withReference( + ConcreteReference.builder().setClazz(java.util.ArrayList.class).setGenerics(anyType.reference()).build()); + + VariableExpr responseVar = VariableExpr.withVariable( + Variable.builder().setType(TypeNode.OBJECT).setName("response").build()); + VariableExpr operationVar = VariableExpr.withVariable( + Variable.builder().setType(operationType).setName("operation").build()); + VariableExpr rawErrorMessagesVar = VariableExpr.withVariable( + Variable.builder().setType(listAnyType).setName("rawErrorMessages").build()); + VariableExpr errorVar = VariableExpr.withVariable( + Variable.builder().setType(errorsType).setName("error").build()); + VariableExpr errorInfoVar = VariableExpr.withVariable( + Variable.builder().setType(errorInfoType).setName("errorInfo").build()); + VariableExpr sbVar = VariableExpr.withVariable( + Variable.builder().setType(TypeNode.withReference(ConcreteReference.withClazz(StringBuilder.class))).setName("sb").build()); + + // parse() method + List parseBody = new ArrayList<>(); + parseBody.add(IfStatement.builder() + .setConditionExpr(UnaryOperationExpr.logicalNotWithExpr( + InstanceofExpr.builder().setExpr(responseVar).setCheckType(operationType).build())) + .setBody(Arrays.asList(ExprStatement.withExpr(ReturnExpr.withExpr(ValueExpr.createNullExpr())))) + .build()); + parseBody.add(ExprStatement.withExpr(AssignmentExpr.builder() + .setVariableExpr(operationVar.toBuilder().setIsDecl(true).build()) + .setValueExpr(CastExpr.builder().setType(operationType).setExpr(responseVar).build()) + .build())); + parseBody.add(IfStatement.builder() + .setConditionExpr(UnaryOperationExpr.logicalNotWithExpr( + MethodInvocationExpr.builder().setExprReferenceExpr(operationVar).setMethodName("hasError").setReturnType(TypeNode.BOOLEAN).build())) + .setBody(Arrays.asList(ExprStatement.withExpr(ReturnExpr.withExpr(ValueExpr.createNullExpr())))) + .build()); + parseBody.add(ExprStatement.withExpr(AssignmentExpr.builder() + .setVariableExpr(rawErrorMessagesVar.toBuilder().setIsDecl(true).build()) + .setValueExpr(NewObjectExpr.builder().setType(arrayListAnyType).build()) + .build())); + + Expr getErrorsListExpr = MethodInvocationExpr.builder() + .setExprReferenceExpr(MethodInvocationExpr.builder().setExprReferenceExpr(operationVar).setMethodName("getError").build()) + .setMethodName("getErrorsList").build(); + + List forBody = new ArrayList<>(); + Expr errorInfoBuilderExpr = MethodInvocationExpr.builder().setStaticReferenceType(errorInfoType).setMethodName("newBuilder").build(); + errorInfoBuilderExpr = MethodInvocationExpr.builder().setExprReferenceExpr(errorInfoBuilderExpr).setMethodName("setReason") + .setArguments(MethodInvocationExpr.builder().setExprReferenceExpr(errorVar).setMethodName("getCode").setReturnType(TypeNode.STRING).build()).build(); + errorInfoBuilderExpr = MethodInvocationExpr.builder().setExprReferenceExpr(errorInfoBuilderExpr).setMethodName("setDomain") + .setArguments(ValueExpr.withValue(StringObjectValue.withValue("googleapis.com"))).build(); + errorInfoBuilderExpr = MethodInvocationExpr.builder().setExprReferenceExpr(errorInfoBuilderExpr).setMethodName("putMetadata") + .setArguments(ValueExpr.withValue(StringObjectValue.withValue("message")), MethodInvocationExpr.builder().setExprReferenceExpr(errorVar).setMethodName("getMessage").setReturnType(TypeNode.STRING).build()).build(); + errorInfoBuilderExpr = MethodInvocationExpr.builder().setExprReferenceExpr(errorInfoBuilderExpr).setMethodName("putMetadata") + .setArguments(ValueExpr.withValue(StringObjectValue.withValue("location")), MethodInvocationExpr.builder().setExprReferenceExpr(errorVar).setMethodName("getLocation").setReturnType(TypeNode.STRING).build()).build(); + errorInfoBuilderExpr = MethodInvocationExpr.builder().setExprReferenceExpr(errorInfoBuilderExpr).setMethodName("build").setReturnType(errorInfoType).build(); + + forBody.add(ExprStatement.withExpr(AssignmentExpr.builder() + .setVariableExpr(errorInfoVar.toBuilder().setIsDecl(true).build()) + .setValueExpr(errorInfoBuilderExpr) + .build())); + forBody.add(ExprStatement.withExpr(MethodInvocationExpr.builder() + .setExprReferenceExpr(rawErrorMessagesVar).setMethodName("add") + .setArguments(MethodInvocationExpr.builder().setStaticReferenceType(anyType).setMethodName("pack").setArguments(errorInfoVar).setReturnType(anyType).build()) + .build())); + + parseBody.add(ForStatement.builder() + .setLocalVariableExpr(errorVar.toBuilder().setIsDecl(true).build()) + .setCollectionExpr(getErrorsListExpr) + .setBody(forBody) + .build()); + + Expr parseReturnExpr = MethodInvocationExpr.builder() + .setExprReferenceExpr(MethodInvocationExpr.builder() + .setExprReferenceExpr(MethodInvocationExpr.builder().setStaticReferenceType(errorDetailsType).setMethodName("builder").build()) + .setMethodName("setRawErrorMessages").setArguments(rawErrorMessagesVar).build()) + .setMethodName("build").setReturnType(errorDetailsType).build(); + + MethodDefinition parseMethod = MethodDefinition.builder() + .setAnnotations(Arrays.asList(com.google.api.generator.engine.ast.AnnotationNode.OVERRIDE)) + .setScope(ScopeNode.PUBLIC) + .setReturnType(errorDetailsType) + .setName("parse") + .setArguments(responseVar.toBuilder().setIsDecl(true).build()) + .setBody(parseBody) + .setReturnExpr(parseReturnExpr) + .build(); + + // parseErrorMessage() method + List parseErrorBody = new ArrayList<>(); + parseErrorBody.add(IfStatement.builder() + .setConditionExpr(UnaryOperationExpr.logicalNotWithExpr( + InstanceofExpr.builder().setExpr(responseVar).setCheckType(operationType).build())) + .setBody(Arrays.asList(ExprStatement.withExpr(ReturnExpr.withExpr(ValueExpr.createNullExpr())))) + .build()); + parseErrorBody.add(ExprStatement.withExpr(AssignmentExpr.builder() + .setVariableExpr(operationVar.toBuilder().setIsDecl(true).build()) + .setValueExpr(CastExpr.builder().setType(operationType).setExpr(responseVar).build()) + .build())); + + Expr condition1 = UnaryOperationExpr.logicalNotWithExpr(MethodInvocationExpr.builder().setExprReferenceExpr(operationVar).setMethodName("hasError").setReturnType(TypeNode.BOOLEAN).build()); + Expr condition2 = RelationalOperationExpr.equalToWithExprs( + MethodInvocationExpr.builder().setExprReferenceExpr(MethodInvocationExpr.builder().setExprReferenceExpr(operationVar).setMethodName("getError").build()).setMethodName("getErrorsCount").setReturnType(TypeNode.INT).build(), + ValueExpr.withValue(PrimitiveValue.builder().setType(TypeNode.INT).setValue("0").build())); + parseErrorBody.add(IfStatement.builder() + .setConditionExpr(LogicalOperationExpr.logicalOrWithExprs(condition1, condition2)) + .setBody(Arrays.asList(ExprStatement.withExpr(ReturnExpr.withExpr(ValueExpr.createNullExpr())))) + .build()); + + parseErrorBody.add(ExprStatement.withExpr(AssignmentExpr.builder() + .setVariableExpr(sbVar.toBuilder().setIsDecl(true).build()) + .setValueExpr(NewObjectExpr.builder().setType(sbVar.type()).build()) + .build())); + + List forBody2 = new ArrayList<>(); + forBody2.add(IfStatement.builder() + .setConditionExpr(RelationalOperationExpr.lessThanWithExprs( + ValueExpr.withValue(PrimitiveValue.builder().setType(TypeNode.INT).setValue("0").build()), + MethodInvocationExpr.builder().setExprReferenceExpr(sbVar).setMethodName("length").setReturnType(TypeNode.INT).build())) + .setBody(Arrays.asList(ExprStatement.withExpr(MethodInvocationExpr.builder().setExprReferenceExpr(sbVar).setMethodName("append").setArguments(ValueExpr.withValue(StringObjectValue.withValue("; "))).build()))) + .build()); + + Expr appendExpr = MethodInvocationExpr.builder().setExprReferenceExpr(sbVar).setMethodName("append").setArguments(MethodInvocationExpr.builder().setExprReferenceExpr(errorVar).setMethodName("getCode").build()).build(); + appendExpr = MethodInvocationExpr.builder().setExprReferenceExpr(appendExpr).setMethodName("append").setArguments(ValueExpr.withValue(StringObjectValue.withValue(": "))).build(); + appendExpr = MethodInvocationExpr.builder().setExprReferenceExpr(appendExpr).setMethodName("append").setArguments(MethodInvocationExpr.builder().setExprReferenceExpr(errorVar).setMethodName("getMessage").build()).build(); + forBody2.add(ExprStatement.withExpr(appendExpr)); + + parseErrorBody.add(ForStatement.builder() + .setLocalVariableExpr(errorVar.toBuilder().setIsDecl(true).build()) + .setCollectionExpr(getErrorsListExpr) + .setBody(forBody2) + .build()); + + Expr parseErrorReturnExpr = MethodInvocationExpr.builder().setExprReferenceExpr(sbVar).setMethodName("toString").setReturnType(TypeNode.STRING).build(); + + MethodDefinition parseErrorMethod = MethodDefinition.builder() + .setAnnotations(Arrays.asList(com.google.api.generator.engine.ast.AnnotationNode.OVERRIDE)) + .setScope(ScopeNode.PUBLIC) + .setReturnType(TypeNode.STRING) + .setName("parseErrorMessage") + .setArguments(responseVar.toBuilder().setIsDecl(true).build()) + .setBody(parseErrorBody) + .setReturnExpr(parseErrorReturnExpr) + .build(); + + ClassDefinition classDef = ClassDefinition.builder() + .setFileHeader(CommentComposer.APACHE_LICENSE_COMMENT) + .setPackageString(pakkage) + .setName("ComputeLroErrorParser") + .setScope(ScopeNode.PUBLIC) + .setImplementsTypes(Arrays.asList(httpJsonLroErrorParserType)) + .setMethods(Arrays.asList(parseMethod, parseErrorMethod)) + .build(); + + return GapicClass.create(GapicClass.Kind.STUB, classDef); + } +} diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLroErrorParser.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLroErrorParser.java new file mode 100644 index 000000000000..361a7c03bb84 --- /dev/null +++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLroErrorParser.java @@ -0,0 +1,18 @@ +package com.google.api.gax.httpjson; + +import com.google.api.core.BetaApi; +import com.google.api.gax.rpc.ErrorDetails; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +@BetaApi("The surface for custom LRO error parsing is not stable yet and may change.") +public interface HttpJsonLroErrorParser { + /** Parses custom LRO response object into standard ErrorDetails. */ + @Nullable + ErrorDetails parse(Object response); + + /** Concatenates custom LRO response errors into a single descriptive message. */ + @Nullable + String parseErrorMessage(Object response); +} diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoOperationTransformers.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoOperationTransformers.java index cc9200b87763..747e642b01a3 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoOperationTransformers.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoOperationTransformers.java @@ -32,11 +32,13 @@ import com.google.api.core.ApiFunction; import com.google.api.gax.longrunning.OperationSnapshot; import com.google.api.gax.rpc.ApiExceptionFactory; +import com.google.api.gax.rpc.ErrorDetails; import com.google.api.gax.rpc.StatusCode.Code; import com.google.protobuf.Any; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** Public for technical reasons; intended for use by generated code. */ @NullMarked @@ -46,25 +48,46 @@ private ProtoOperationTransformers() {} public static class ResponseTransformer implements ApiFunction { private final AnyTransformer transformer; + @Nullable private final HttpJsonLroErrorParser errorParser; private ResponseTransformer(Class packedClass) { this.transformer = new AnyTransformer<>(packedClass); + this.errorParser = null; + } + + private ResponseTransformer( + Class packedClass, @Nullable HttpJsonLroErrorParser errorParser) { + this.transformer = new AnyTransformer<>(packedClass); + this.errorParser = errorParser; } @Override public ResponseT apply(OperationSnapshot operationSnapshot) { if (!operationSnapshot.getErrorCode().getCode().equals(Code.OK)) { + @Nullable ErrorDetails details = null; + String errorMessage = operationSnapshot.getErrorMessage(); + + if (errorParser != null && operationSnapshot.getResponse() != null) { + details = errorParser.parse(operationSnapshot.getResponse()); + String parsedMsg = errorParser.parseErrorMessage(operationSnapshot.getResponse()); + if (parsedMsg != null && !parsedMsg.isEmpty()) { + errorMessage = parsedMsg; + } + } else { + details = operationSnapshot.getErrorDetails(); + } + throw ApiExceptionFactory.createException( "Operation with name \"" + operationSnapshot.getName() + "\" failed with status = " + operationSnapshot.getErrorCode() + " and message = " - + operationSnapshot.getErrorMessage(), + + errorMessage, null, operationSnapshot.getErrorCode(), false, - operationSnapshot.getErrorDetails()); + details); } if (!(operationSnapshot.getResponse() instanceof Any)) { @@ -88,6 +111,11 @@ public static ResponseTransformer create( Class packedClass) { return new ResponseTransformer<>(packedClass); } + + public static ResponseTransformer create( + Class packedClass, @Nullable HttpJsonLroErrorParser errorParser) { + return new ResponseTransformer<>(packedClass, errorParser); + } } public static class MetadataTransformer From f7641c55ebfe9fc61f16daddd1ae17381e4407f8 Mon Sep 17 00:00:00 2001 From: Nicole Lee Date: Wed, 12 Aug 2026 05:15:55 +0000 Subject: [PATCH 2/2] added license headers for new files --- .../ComputeLroErrorParserClassComposer.java | 14 +++++++++ .../gax/httpjson/HttpJsonLroErrorParser.java | 30 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/rest/ComputeLroErrorParserClassComposer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/rest/ComputeLroErrorParserClassComposer.java index 0cb543b8a4bd..b0e09e6ebe07 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/rest/ComputeLroErrorParserClassComposer.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/rest/ComputeLroErrorParserClassComposer.java @@ -1,3 +1,17 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package com.google.api.generator.gapic.composer.rest; import com.google.api.generator.engine.ast.AssignmentExpr; diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLroErrorParser.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLroErrorParser.java index 361a7c03bb84..611892d0a6d8 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLroErrorParser.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonLroErrorParser.java @@ -1,3 +1,33 @@ +/* + * Copyright 2026 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package com.google.api.gax.httpjson; import com.google.api.core.BetaApi;