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

feat: Dynamic Routing Headers for HttpJson #1667

Merged
merged 50 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
bf396ca
feat: Add RequestParamsExtractor to HttpJsonCallSettings
lqiu96 May 3, 2023
340cf32
feat: Add RequestParamsCallable classes
lqiu96 May 3, 2023
a32e3ff
feat: Update HttpJsonCallableFactory to use new RequestParamCallables
lqiu96 May 3, 2023
38e8a43
feat: Add RequestParam to CallOptions
lqiu96 May 3, 2023
5fd3e61
feat: Add RequestParam header for HttpJson
lqiu96 May 4, 2023
0fb5793
feat: Add initial Dynamic Routing Header showcase test
lqiu96 May 4, 2023
eb68bd7
feat: Ignore empty string for Request Param
lqiu96 May 4, 2023
355e977
feat: Regenerate test client code
lqiu96 May 4, 2023
a9a12ec
feat: Do not generate HttpBinding logic for REST
lqiu96 May 4, 2023
f2fc289
feat: Add Dynamic Routing Header showcase test cases
lqiu96 May 4, 2023
9475bcc
feat: Move header key value to constant
lqiu96 May 4, 2023
bad5c10
feat: Add URLEncoding for header
lqiu96 May 4, 2023
103d380
feat: Update showcase interceptor name
lqiu96 May 5, 2023
bb730a9
feat: Generate HttpJson Dyanmic Routing Header Stub
lqiu96 May 5, 2023
bb4dd19
feat: Fix Request Params Test
lqiu96 May 5, 2023
9499e4b
feat: Update variable names
lqiu96 May 5, 2023
be3ed79
feat: Add gRPC Dynamic Routing Header showcase test
lqiu96 May 5, 2023
e6bf883
feat: Clean up IT showcase test
lqiu96 May 5, 2023
d727746
chore: Add comments for RequestBuilderParams
lqiu96 May 5, 2023
8a72b75
feat: Generate test client code with Implict Routing Headers
lqiu96 May 5, 2023
0dcf1a7
feat: Regenerate test client code
lqiu96 May 6, 2023
6fb8dcd
fix: Generate httpjson golden test case
lqiu96 May 6, 2023
88094f4
chore: Fix format issues
lqiu96 May 8, 2023
c67bd36
chore: Regenerate integration golden tests
lqiu96 May 8, 2023
ca1c8f6
chore: Add more tests for RequestParamsBuilder
lqiu96 May 8, 2023
25b8ad1
chore: Update docs for showcase test
lqiu96 May 8, 2023
44cfdda
Merge branch 'main' into main-dynamic_routing_headers
lqiu96 May 8, 2023
91beb43
chore: Refactor TestClientInitializer to include interceptors
lqiu96 May 8, 2023
bfa9dd6
chore: Add null assertions for showcase test
lqiu96 May 8, 2023
4c8eb61
chore: Remove String.valueOf check for routing params
lqiu96 May 10, 2023
94941fb
chore: Remove String.valueOf check for routing params
lqiu96 May 10, 2023
1107603
chore: Resolve merge conflicts
lqiu96 May 11, 2023
e7b2dbf
chore: Regenerate the showcase clients
lqiu96 May 11, 2023
7b6c952
chore: Address pr comments
lqiu96 May 19, 2023
24aae30
chore: Update Dynamic Routing Header test cases
lqiu96 May 22, 2023
3038819
chore: Use PercentEscaper for percent encoding
lqiu96 May 30, 2023
fdce4e1
chore: Empty commit
lqiu96 May 30, 2023
b6b0be6
chore: Migrate transport specific code to abstract class
lqiu96 May 31, 2023
14a3ee4
chore: Address pr comments
lqiu96 Jun 1, 2023
cc17b03
chore: Address pr comments
lqiu96 Jun 1, 2023
0133e82
chore: Use HttpJsonMetadata for headers
lqiu96 Jun 1, 2023
8f2c5f3
chore: Revert call options changes
lqiu96 Jun 1, 2023
c630cf4
chore: Set default request header map
lqiu96 Jun 1, 2023
14c5f90
chore: Move encoding logic to RequestUrlParamsEncoder
lqiu96 Jun 2, 2023
15dfc28
chore: Fix lint issues
lqiu96 Jun 2, 2023
1f01dc1
chore: Add clirr ignore for RequestUrlParamsEncoder
lqiu96 Jun 2, 2023
bbbe31d
Merge branch 'main' into main-dynamic_routing_headers
lqiu96 Jun 2, 2023
55518cb
chore: Address PR comments
lqiu96 Jun 6, 2023
6674fef
Merge branch 'main' into main-dynamic_routing_headers
lqiu96 Jun 6, 2023
90c8a57
chore: Address PR comments
lqiu96 Jun 6, 2023
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,26 @@

import com.google.api.gax.grpc.GrpcCallSettings;
import com.google.api.gax.grpc.GrpcStubCallableFactory;
import com.google.api.gax.rpc.RequestParamsBuilder;
import com.google.api.generator.engine.ast.AssignmentExpr;
import com.google.api.generator.engine.ast.ConcreteReference;
import com.google.api.generator.engine.ast.EnumRefExpr;
import com.google.api.generator.engine.ast.Expr;
import com.google.api.generator.engine.ast.ExprStatement;
import com.google.api.generator.engine.ast.IfStatement;
import com.google.api.generator.engine.ast.LambdaExpr;
import com.google.api.generator.engine.ast.LogicalOperationExpr;
import com.google.api.generator.engine.ast.MethodInvocationExpr;
import com.google.api.generator.engine.ast.RelationalOperationExpr;
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.Variable;
import com.google.api.generator.engine.ast.VariableExpr;
import com.google.api.generator.gapic.composer.common.AbstractTransportServiceStubClassComposer;
import com.google.api.generator.gapic.composer.store.TypeStore;
import com.google.api.generator.gapic.model.HttpBindings.HttpBinding;
import com.google.api.generator.gapic.model.Message;
import com.google.api.generator.gapic.model.Method;
import com.google.api.generator.gapic.model.RoutingHeaderRule.RoutingHeaderParam;
import com.google.api.generator.gapic.model.Service;
import com.google.api.generator.gapic.utils.JavaStyle;
import com.google.api.pathtemplate.PathTemplate;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.longrunning.stub.GrpcOperationsStub;
import io.grpc.MethodDescriptor;
import io.grpc.protobuf.ProtoUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -200,46 +186,6 @@ protected EnumRefExpr getMethodDescriptorMethodTypeExpr(Method protoMethod) {
.build();
}

@Override
protected Expr createTransportSettingsInitExpr(
Method method,
VariableExpr transportSettingsVarExpr,
VariableExpr methodDescriptorVarExpr,
List<Statement> classStatements) {
MethodInvocationExpr callSettingsBuilderExpr =
MethodInvocationExpr.builder()
.setStaticReferenceType(getTransportContext().transportCallSettingsType())
.setGenerics(transportSettingsVarExpr.type().reference().generics())
.setMethodName("newBuilder")
.build();
callSettingsBuilderExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(callSettingsBuilderExpr)
.setMethodName("setMethodDescriptor")
.setArguments(Arrays.asList(methodDescriptorVarExpr))
.build();

if (method.shouldSetParamsExtractor()) {
callSettingsBuilderExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(callSettingsBuilderExpr)
.setMethodName("setParamsExtractor")
.setArguments(createRequestParamsExtractorClassInstance(method, classStatements))
.build();
}

callSettingsBuilderExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(callSettingsBuilderExpr)
.setMethodName("build")
.setReturnType(transportSettingsVarExpr.type())
.build();
return AssignmentExpr.builder()
.setVariableExpr(transportSettingsVarExpr.toBuilder().setIsDecl(true).build())
.setValueExpr(callSettingsBuilderExpr)
.build();
}

@Override
protected String getProtoRpcFullMethodName(Service protoService, Method protoMethod) {
if (protoMethod.isMixin()) {
Expand All @@ -255,244 +201,4 @@ protected String getProtoRpcFullMethodName(Service protoService, Method protoMet
// long-term solution.
return String.format("google.iam.v1.IAMPolicy/%s", protoMethod.name());
}

private LambdaExpr createRequestParamsExtractorClassInstance(
Method method, List<Statement> classStatements) {
List<Statement> bodyStatements = new ArrayList<>();
VariableExpr requestVarExpr =
VariableExpr.withVariable(
Variable.builder().setType(method.inputType()).setName("request").build());
TypeNode returnType =
TypeNode.withReference(
ConcreteReference.builder()
.setClazz(Map.class)
.setGenerics(TypeNode.STRING.reference(), TypeNode.STRING.reference())
.build());
MethodInvocationExpr.Builder returnExpr =
MethodInvocationExpr.builder().setReturnType(returnType);
// If the google.api.routing annotation is present(even with empty routing parameters),
// the implicit routing headers specified in the google.api.http annotation should not be sent
if (method.routingHeaderRule() == null) {
createRequestParamsExtractorBodyForHttpBindings(
method, requestVarExpr, bodyStatements, returnExpr);
} else {
createRequestParamsExtractorBodyForRoutingHeaders(
method, requestVarExpr, classStatements, bodyStatements, returnExpr);
}

// Overrides extract().
// https://github.com/googleapis/gax-java/blob/8d45d186e36ae97b789a6f89d80ae5213a773b65/gax/src/main/java/com/google/api/gax/rpc/RequestParamsExtractor.java#L55
return LambdaExpr.builder()
.setArguments(requestVarExpr.toBuilder().setIsDecl(true).build())
.setBody(bodyStatements)
.setReturnExpr(returnExpr.build())
.build();
}

private void createRequestParamsExtractorBodyForHttpBindings(
Method method,
VariableExpr requestVarExpr,
List<Statement> bodyStatements,
MethodInvocationExpr.Builder returnExprBuilder) {
TypeNode paramsVarType =
TypeNode.withReference(
ConcreteReference.builder()
.setClazz(ImmutableMap.Builder.class)
.setGenerics(TypeNode.STRING.reference(), TypeNode.STRING.reference())
.build());
VariableExpr paramsVarExpr =
VariableExpr.withVariable(
Variable.builder().setName("params").setType(paramsVarType).build());

Expr paramsAssignExpr =
AssignmentExpr.builder()
.setVariableExpr(paramsVarExpr.toBuilder().setIsDecl(true).build())
.setValueExpr(
MethodInvocationExpr.builder()
.setStaticReferenceType(FIXED_TYPESTORE.get("ImmutableMap"))
.setMethodName("builder")
.setReturnType(paramsVarType)
.build())
.build();
bodyStatements.add(ExprStatement.withExpr(paramsAssignExpr));

for (HttpBinding httpBindingFieldBinding : method.httpBindings().pathParameters()) {
MethodInvocationExpr requestBuilderExpr =
createRequestFieldGetterExpr(requestVarExpr, httpBindingFieldBinding.name());
Expr valueOfExpr =
MethodInvocationExpr.builder()
.setStaticReferenceType(TypeNode.STRING)
.setMethodName("valueOf")
.setArguments(requestBuilderExpr)
.build();

Expr paramsPutExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(paramsVarExpr)
.setMethodName("put")
.setArguments(
ValueExpr.withValue(StringObjectValue.withValue(httpBindingFieldBinding.name())),
valueOfExpr)
.build();
bodyStatements.add(ExprStatement.withExpr(paramsPutExpr));
}

returnExprBuilder.setExprReferenceExpr(paramsVarExpr).setMethodName("build");
}

private void createRequestParamsExtractorBodyForRoutingHeaders(
Method method,
VariableExpr requestVarExpr,
List<Statement> classStatements,
List<Statement> bodyStatements,
MethodInvocationExpr.Builder returnExprBuilder) {
TypeNode routingHeadersBuilderType =
TypeNode.withReference(
ConcreteReference.builder().setClazz(RequestParamsBuilder.class).build());
VariableExpr routingHeadersBuilderVarExpr =
VariableExpr.builder()
.setVariable(
Variable.builder().setName("builder").setType(routingHeadersBuilderType).build())
.setIsDecl(true)
.build();
MethodInvocationExpr routingHeaderBuilderInvokeExpr =
MethodInvocationExpr.builder()
.setStaticReferenceType(routingHeadersBuilderType)
.setMethodName("create")
.setReturnType(routingHeadersBuilderType)
.build();
Expr routingHeadersBuilderInitExpr =
AssignmentExpr.builder()
.setVariableExpr(routingHeadersBuilderVarExpr)
.setValueExpr(routingHeaderBuilderInvokeExpr)
.build();
bodyStatements.add(ExprStatement.withExpr(routingHeadersBuilderInitExpr));
List<RoutingHeaderParam> routingHeaderParams = method.routingHeaderRule().routingHeaderParams();
VariableExpr routingHeadersBuilderVarNonDeclExpr =
VariableExpr.builder()
.setVariable(
Variable.builder().setName("builder").setType(routingHeadersBuilderType).build())
.build();
for (int i = 0; i < routingHeaderParams.size(); i++) {
RoutingHeaderParam routingHeaderParam = routingHeaderParams.get(i);
MethodInvocationExpr requestFieldGetterExpr =
createRequestFieldGetterExpr(requestVarExpr, routingHeaderParam.fieldName());
Expr routingHeaderKeyExpr =
ValueExpr.withValue(StringObjectValue.withValue(routingHeaderParam.key()));
String pathTemplateName =
String.format("%s_%s_PATH_TEMPLATE", JavaStyle.toUpperSnakeCase(method.name()), i);
TypeNode pathTemplateType =
TypeNode.withReference(ConcreteReference.withClazz(PathTemplate.class));
Variable pathTemplateVar =
Variable.builder().setType(pathTemplateType).setName(pathTemplateName).build();
Expr routingHeaderPatternExpr = VariableExpr.withVariable(pathTemplateVar);
Statement pathTemplateClassVar =
createPathTemplateClassStatement(routingHeaderParam, pathTemplateType, pathTemplateVar);
classStatements.add(pathTemplateClassVar);
MethodInvocationExpr addParamMethodExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(routingHeadersBuilderVarNonDeclExpr)
.setMethodName("add")
.setArguments(requestFieldGetterExpr, routingHeaderKeyExpr, routingHeaderPatternExpr)
.build();

ExprStatement addParamStatement = ExprStatement.withExpr(addParamMethodExpr);
// No need to add null check if there is no nested fields
if (routingHeaderParam.getDescendantFieldNames().size() == 1) {
bodyStatements.add(addParamStatement);
} else {
IfStatement ifStatement =
IfStatement.builder()
.setConditionExpr(
fieldValuesNotNullConditionExpr(
requestVarExpr, routingHeaderParam.getDescendantFieldNames()))
.setBody(ImmutableList.of(addParamStatement))
.build();
bodyStatements.add(ifStatement);
}
}
returnExprBuilder
.setExprReferenceExpr(routingHeadersBuilderVarNonDeclExpr)
.setMethodName("build");
}

private Statement createPathTemplateClassStatement(
RoutingHeaderParam routingHeaderParam, TypeNode pathTemplateType, Variable pathTemplateVar) {
VariableExpr pathTemplateVarExpr =
VariableExpr.builder()
.setVariable(pathTemplateVar)
.setIsDecl(true)
.setIsStatic(true)
.setIsFinal(true)
.setScope(ScopeNode.PRIVATE)
.build();
ValueExpr valueExpr =
ValueExpr.withValue(StringObjectValue.withValue(routingHeaderParam.pattern()));
Expr pathTemplateExpr =
AssignmentExpr.builder()
.setVariableExpr(pathTemplateVarExpr)
.setValueExpr(
MethodInvocationExpr.builder()
.setStaticReferenceType(pathTemplateType)
.setMethodName("create")
.setArguments(valueExpr)
.setReturnType(pathTemplateType)
.build())
.build();
return ExprStatement.withExpr(pathTemplateExpr);
}

private Expr fieldValuesNotNullConditionExpr(
VariableExpr requestVarExpr, List<String> fieldNames) {
MethodInvocationExpr.Builder requestFieldGetterExprBuilder =
MethodInvocationExpr.builder().setExprReferenceExpr(requestVarExpr);
Expr fieldValuesNotNullExpr = null;
for (int i = 0; i < fieldNames.size() - 1; i++) {
String currFieldName = fieldNames.get(i);
String bindingFieldMethodName =
String.format("get%s", JavaStyle.toUpperCamelCase(currFieldName));
requestFieldGetterExprBuilder =
requestFieldGetterExprBuilder.setMethodName(bindingFieldMethodName);
// set return type of each method invocation to String just to pass the validation for
// RelationalOperationExpr that both side of relational operation needs to be a valid equality
// type
MethodInvocationExpr requestGetterExpr =
requestFieldGetterExprBuilder.setReturnType(TypeNode.STRING).build();
Expr currentValueNotNullExpr =
RelationalOperationExpr.notEqualToWithExprs(
requestGetterExpr, ValueExpr.createNullExpr());
if (fieldValuesNotNullExpr == null) {
fieldValuesNotNullExpr = currentValueNotNullExpr;
} else {
fieldValuesNotNullExpr =
LogicalOperationExpr.logicalAndWithExprs(
fieldValuesNotNullExpr, currentValueNotNullExpr);
}
requestFieldGetterExprBuilder =
MethodInvocationExpr.builder().setExprReferenceExpr(requestGetterExpr);
}
return fieldValuesNotNullExpr;
}

private MethodInvocationExpr createRequestFieldGetterExpr(
VariableExpr requestVarExpr, String fieldName) {
MethodInvocationExpr.Builder requestFieldGetterExprBuilder =
MethodInvocationExpr.builder().setExprReferenceExpr(requestVarExpr);
List<String> descendantFields = Splitter.on(".").splitToList(fieldName);
// Handle foo.bar cases by descending into the subfields.
// e.g. foo.bar -> request.getFoo().getBar()
for (int i = 0; i < descendantFields.size(); i++) {
String currFieldName = descendantFields.get(i);
String bindingFieldMethodName =
String.format("get%s", JavaStyle.toUpperCamelCase(currFieldName));
requestFieldGetterExprBuilder =
requestFieldGetterExprBuilder.setMethodName(bindingFieldMethodName);
if (i < descendantFields.size() - 1) {
requestFieldGetterExprBuilder =
MethodInvocationExpr.builder()
.setExprReferenceExpr(requestFieldGetterExprBuilder.build());
}
}
return requestFieldGetterExprBuilder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -210,45 +210,6 @@ protected List<MethodDefinition> createOperationsStubGetterMethod(
return super.createOperationsStubGetterMethod(service, operationsStubVarExpr);
}

@Override
protected Expr createTransportSettingsInitExpr(
Method method,
VariableExpr transportSettingsVarExpr,
VariableExpr methodDescriptorVarExpr,
List<Statement> classStatements) {
MethodInvocationExpr callSettingsBuilderExpr =
MethodInvocationExpr.builder()
.setStaticReferenceType(
FIXED_REST_TYPESTORE.get(HttpJsonCallSettings.class.getSimpleName()))
.setGenerics(transportSettingsVarExpr.type().reference().generics())
.setMethodName("newBuilder")
.build();
callSettingsBuilderExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(callSettingsBuilderExpr)
.setMethodName("setMethodDescriptor")
.setArguments(Arrays.asList(methodDescriptorVarExpr))
.build();

callSettingsBuilderExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(callSettingsBuilderExpr)
.setMethodName("setTypeRegistry")
.setArguments(Arrays.asList(TYPE_REGISTRY_VAR_EXPR))
.build();

callSettingsBuilderExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(callSettingsBuilderExpr)
.setMethodName("build")
.setReturnType(transportSettingsVarExpr.type())
.build();
return AssignmentExpr.builder()
.setVariableExpr(transportSettingsVarExpr.toBuilder().setIsDecl(true).build())
.setValueExpr(callSettingsBuilderExpr)
.build();
}

@Override
protected List<AnnotationNode> createClassAnnotations(Service service) {
List<AnnotationNode> annotations = super.createClassAnnotations(service);
Expand Down
Loading