Skip to content

Commit

Permalink
#869. A basic end-to-end solution.
Browse files Browse the repository at this point in the history
- Add unit tests for RoutingRuleParser
  • Loading branch information
blakeli0 committed Jan 4, 2022
1 parent 0bc0aad commit 83d1abd
Show file tree
Hide file tree
Showing 11 changed files with 600 additions and 168 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@

import com.google.api.gax.grpc.GrpcCallSettings;
import com.google.api.gax.grpc.GrpcStubCallableFactory;
import com.google.api.gax.rpc.RequestParamsExtractor;
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.MethodDefinition;
import com.google.api.generator.engine.ast.MethodInvocationExpr;
import com.google.api.generator.engine.ast.NewObjectExpr;
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;
Expand All @@ -34,13 +35,16 @@
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.GapicContext;
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.RoutingHeaders.RoutingHeader;
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.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.longrunning.stub.GrpcOperationsStub;
import io.grpc.MethodDescriptor;
Expand All @@ -56,6 +60,7 @@
import java.util.stream.Collectors;

public class GrpcServiceStubClassComposer extends AbstractTransportServiceStubClassComposer {

private static final GrpcServiceStubClassComposer INSTANCE = new GrpcServiceStubClassComposer();

// Legacy support for the original reroute_to_grpc_interface option in gapic.yaml. These two APIs
Expand Down Expand Up @@ -212,19 +217,10 @@ protected Expr createTransportSettingsInitExpr(
.build();

if (method.hasHttpBindings()) {
NewObjectExpr newExtractorExpr =
NewObjectExpr.builder()
.setType(
TypeNode.withReference(
ConcreteReference.withClazz(ExplicitRoutingHeaderExtractor.class)))
.setArguments()
.build();
callSettingsBuilderExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(callSettingsBuilderExpr)
.setMethodName("setParamsExtractor")
// set custom extractor
// .setArguments(newExtractorExpr)
.setArguments(createRequestParamsExtractorClassInstance(method))
.build();
}
Expand All @@ -241,6 +237,161 @@ protected Expr createTransportSettingsInitExpr(
.build();
}

@Override
protected List<MethodDefinition> createClassMethods(
GapicContext context,
Service service,
TypeStore typeStore,
Map<String, VariableExpr> classMemberVarExprs,
Map<String, VariableExpr> callableClassMemberVarExprs,
Map<String, VariableExpr> protoMethodNameToDescriptorVarExprs) {
List<MethodDefinition> classMethods =
super.createClassMethods(
context,
service,
typeStore,
classMemberVarExprs,
callableClassMemberVarExprs,
protoMethodNameToDescriptorVarExprs);
// TODO: need a way to check do we need to create this method or not, or make it an inner method
classMethods.add(createAddParamsMethod());
return classMethods;
}

private MethodDefinition createAddParamsMethod() {
TypeNode paramsVarType =
TypeNode.withReference(
ConcreteReference.builder()
.setClazz(ImmutableMap.Builder.class)
.setGenerics(TypeNode.STRING.reference(), TypeNode.STRING.reference())
.build());
VariableExpr paramsVarExpr =
VariableExpr.builder()
.setVariable(Variable.builder().setName("params").setType(paramsVarType).build())
.setIsDecl(true)
.build();
VariableExpr fieldValueExpr =
VariableExpr.builder()
.setVariable(Variable.builder().setName("fieldValue").setType(TypeNode.STRING).build())
.setIsDecl(true)
.build();
VariableExpr keyExpr =
VariableExpr.builder()
.setVariable(Variable.builder().setName("headerKey").setType(TypeNode.STRING).build())
.setIsDecl(true)
.build();
VariableExpr patternExpr =
VariableExpr.builder()
.setVariable(Variable.builder().setName("pattern").setType(TypeNode.STRING).build())
.setIsDecl(true)
.build();
List<Statement> methodBody = new ArrayList<>();
TypeNode pathTemplateType =
TypeNode.withReference(ConcreteReference.builder().setClazz(PathTemplate.class).build());
VariableExpr pathTemplateVar =
VariableExpr.builder()
.setVariable(
Variable.builder().setName("pathTemplate").setType(pathTemplateType).build())
.setIsDecl(true)
.build();
VariableExpr patternExprNonDecl =
VariableExpr.builder()
.setVariable(Variable.builder().setName("pattern").setType(TypeNode.STRING).build())
.build();
Expr pathTemplateExpr =
AssignmentExpr.builder()
.setVariableExpr(pathTemplateVar)
.setValueExpr(
MethodInvocationExpr.builder()
.setStaticReferenceType(pathTemplateType)
.setMethodName("create")
.setArguments(patternExprNonDecl)
.setReturnType(pathTemplateType)
.build())
.build();
ExprStatement exprStatement1 = ExprStatement.withExpr(pathTemplateExpr);
methodBody.add(exprStatement1);

VariableExpr pathTemplateVarNonDecl =
VariableExpr.builder()
.setVariable(
Variable.builder().setName("pathTemplate").setType(pathTemplateType).build())
.build();
TypeNode matchedValuesType =
TypeNode.withReference(
ConcreteReference.builder()
.setClazz(Map.class)
.setGenerics(TypeNode.STRING.reference(), TypeNode.STRING.reference())
.build());
VariableExpr matchedValuesVar =
VariableExpr.builder()
.setVariable(
Variable.builder().setName("matchedValues").setType(matchedValuesType).build())
.setIsDecl(true)
.build();
VariableExpr fieldValueExprNonDecl =
VariableExpr.builder()
.setVariable(Variable.builder().setName("fieldValue").setType(pathTemplateType).build())
.build();
Expr matchedValuesExpr =
AssignmentExpr.builder()
.setVariableExpr(matchedValuesVar)
.setValueExpr(
MethodInvocationExpr.builder()
.setExprReferenceExpr(pathTemplateVarNonDecl)
.setMethodName("match")
.setArguments(fieldValueExprNonDecl)
.setReturnType(matchedValuesType)
.build())
.build();
ExprStatement exprStatement2 = ExprStatement.withExpr(matchedValuesExpr);
methodBody.add(exprStatement2);

VariableExpr matchedValuesVarNonDecl =
VariableExpr.builder()
.setVariable(
Variable.builder().setName("matchedValues").setType(matchedValuesType).build())
.build();
Expr checkMatchedValuesNull =
RelationalOperationExpr.notEqualToWithExprs(
matchedValuesVarNonDecl, ValueExpr.createNullExpr());
VariableExpr paramsVarExprNonDecl =
VariableExpr.builder()
.setVariable(Variable.builder().setName("params").setType(paramsVarType).build())
.build();
VariableExpr keyExprNonDecl =
VariableExpr.builder()
.setVariable(Variable.builder().setName("headerKey").setType(TypeNode.STRING).build())
.build();
Expr getMatchedValueExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(matchedValuesVarNonDecl)
.setMethodName("get")
.setArguments(keyExprNonDecl)
.build();
Expr putParamsExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(paramsVarExprNonDecl)
.setMethodName("put")
.setArguments(keyExprNonDecl, getMatchedValueExpr)
.build();
ExprStatement putParamsStatement = ExprStatement.withExpr(putParamsExpr);
IfStatement ifStatement =
IfStatement.builder()
.setConditionExpr(checkMatchedValuesNull)
.setBody(ImmutableList.of(putParamsStatement))
.build();
methodBody.add(ifStatement);

return MethodDefinition.builder()
.setScope(ScopeNode.PROTECTED)
.setReturnType(TypeNode.VOID)
.setName("addParams")
.setBody(methodBody)
.setArguments(paramsVarExpr, fieldValueExpr, keyExpr, patternExpr)
.build();
}

@Override
protected String getProtoRpcFullMethodName(Service protoService, Method protoMethod) {
if (protoMethod.isMixin()) {
Expand Down Expand Up @@ -290,70 +441,49 @@ private LambdaExpr createRequestParamsExtractorClassInstance(Method method) {

for (HttpBinding httpBindingFieldBinding : method.httpBindings().pathParameters()) {
// Handle foo.bar cases by descending into the subfields.
MethodInvocationExpr.Builder requestFieldGetterExprBuilder =
MethodInvocationExpr.builder().setExprReferenceExpr(requestVarExpr);
String[] descendantFields = httpBindingFieldBinding.name().split("\\.");
for (int i = 0; i < descendantFields.length; i++) {
String currFieldName = descendantFields[i];
String bindingFieldMethodName =
String.format("get%s", JavaStyle.toUpperCamelCase(currFieldName));
requestFieldGetterExprBuilder =
requestFieldGetterExprBuilder.setMethodName(bindingFieldMethodName);
if (i < descendantFields.length - 1) {
requestFieldGetterExprBuilder =
MethodInvocationExpr.builder()
.setExprReferenceExpr(requestFieldGetterExprBuilder.build());
}
}
MethodInvocationExpr requestBuilderExpr =
createRequestFieldGetterExpr(requestVarExpr, httpBindingFieldBinding.name());

MethodInvocationExpr requestBuilderExpr = requestFieldGetterExprBuilder.build();
Expr valueOfExpr =
MethodInvocationExpr.builder()
.setStaticReferenceType(TypeNode.STRING)
.setMethodName("valueOf")
.setArguments(requestBuilderExpr)
.build();

// TODO: completely remove this part if routing headers is not null?
// Comment out for now. TODO: completely remove this part if routing headers is not null?
// Are these params used for anything else other than implicit dynamic routing?
Expr paramsPutExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(paramsVarExpr)
.setMethodName("put")
.setArguments(
ValueExpr.withValue(StringObjectValue.withValue(httpBindingFieldBinding.name())),
valueOfExpr)
.build();
bodyExprs.add(paramsPutExpr);
// Expr paramsPutExpr =
// MethodInvocationExpr.builder()
// .setExprReferenceExpr(paramsVarExpr)
// .setMethodName("put")
// .setArguments(
//
// ValueExpr.withValue(StringObjectValue.withValue(httpBindingFieldBinding.name())),
// valueOfExpr)
// .build();
// bodyExprs.add(paramsPutExpr);
}

for (RoutingHeader routingHeader : method.routingHeaders().routingHeadersSet()) {
MethodInvocationExpr.Builder requestFieldGetterExprBuilder =
MethodInvocationExpr.builder().setExprReferenceExpr(requestVarExpr);
// TODO: support nested field
String currFieldName = routingHeader.field();
String bindingFieldMethodName =
String.format("get%s", JavaStyle.toUpperCamelCase(currFieldName));
requestFieldGetterExprBuilder =
requestFieldGetterExprBuilder.setMethodName(bindingFieldMethodName);

MethodInvocationExpr requestBuilderExpr = requestFieldGetterExprBuilder.build();
Expr valueOfExpr =
MethodInvocationExpr.builder()
.setStaticReferenceType(TypeNode.STRING)
.setMethodName("valueOf")
.setArguments(requestBuilderExpr)
.build();
for (RoutingHeader routingHeader : method.routingHeaders().routingHeadersList()) {
MethodInvocationExpr requestFieldGetterExpr =
createRequestFieldGetterExpr(requestVarExpr, routingHeader.field());

Expr paramsPutExpr =
Expr routingHeaderKeyExpr =
ValueExpr.withValue(StringObjectValue.withValue(routingHeader.name()));
Expr routingHeaderPatternExpr =
ValueExpr.withValue(StringObjectValue.withValue(routingHeader.pattern()));
MethodInvocationExpr addParamsMethodExpr =
MethodInvocationExpr.builder()
.setExprReferenceExpr(paramsVarExpr)
.setMethodName("put")
.setMethodName("addParams")
.setArguments(
ValueExpr.withValue(StringObjectValue.withValue(routingHeader.name())),
valueOfExpr)
paramsVarExpr,
requestFieldGetterExpr,
routingHeaderKeyExpr,
routingHeaderPatternExpr)
.build();
bodyExprs.add(paramsPutExpr);

bodyExprs.add(addParamsMethodExpr);
}

TypeNode returnType =
Expand All @@ -379,12 +509,23 @@ private LambdaExpr createRequestParamsExtractorClassInstance(Method method) {
.build();
}

class ExplicitRoutingHeaderExtractor implements RequestParamsExtractor {

@Override
public Map<String, String> extract(Object request) {
// no way to extract the field value since request type is dynamic
return null;
private MethodInvocationExpr createRequestFieldGetterExpr(
VariableExpr requestVarExpr, String fieldName) {
MethodInvocationExpr.Builder requestFieldGetterExprBuilder =
MethodInvocationExpr.builder().setExprReferenceExpr(requestVarExpr);
String[] descendantFields = fieldName.split("\\.");
for (int i = 0; i < descendantFields.length; i++) {
String currFieldName = descendantFields[i];
String bindingFieldMethodName =
String.format("get%s", JavaStyle.toUpperCamelCase(currFieldName));
requestFieldGetterExprBuilder =
requestFieldGetterExprBuilder.setMethodName(bindingFieldMethodName);
if (i < descendantFields.length - 1) {
requestFieldGetterExprBuilder =
MethodInvocationExpr.builder()
.setExprReferenceExpr(requestFieldGetterExprBuilder.build());
}
}
return requestFieldGetterExprBuilder.build();
}
}

0 comments on commit 83d1abd

Please sign in to comment.