Skip to content
This repository has been archived by the owner on Sep 26, 2023. It is now read-only.

Commit

Permalink
feat: [REGAPIC] Add support for additional bindings (#1680)
Browse files Browse the repository at this point in the history
Additional bindings are currently relevant only for generated unit tests

**[edit]** Also update IAM dependency in dependencies.properties (it is used in generated build gralde files for self-service)
  • Loading branch information
vam-google authored May 18, 2022
1 parent faafafb commit 59b3699
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 35 deletions.
4 changes: 2 additions & 2 deletions dependencies.properties
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ maven.com_google_auto_value_auto_value=com.google.auto.value:auto-value:1.9
maven.com_google_auto_value_auto_value_annotations=com.google.auto.value:auto-value-annotations:1.9
maven.com_google_api_api_common=com.google.api:api-common:2.1.5
maven.org_threeten_threetenbp=org.threeten:threetenbp:1.5.0
maven.com_google_api_grpc_grpc_google_iam_v1=com.google.api.grpc:grpc-google-iam-v1:1.0.9
maven.com_google_api_grpc_proto_google_iam_v1=com.google.api.grpc:proto-google-iam-v1:1.0.9
maven.com_google_api_grpc_grpc_google_iam_v1=com.google.api.grpc:grpc-google-iam-v1:1.3.4
maven.com_google_api_grpc_proto_google_iam_v1=com.google.api.grpc:proto-google-iam-v1:1.3.4
maven.com_google_http_client_google_http_client=com.google.http-client:google-http-client:1.41.5
maven.com_google_http_client_google_http_client_gson=com.google.http-client:google-http-client-gson:1.41.5
maven.org_codehaus_mojo_animal_sniffer_annotations=org.codehaus.mojo:animal-sniffer-annotations:1.18
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
*/
package com.google.api.gax.httpjson;

import com.google.api.core.BetaApi;
import com.google.api.pathtemplate.PathTemplate;
import java.util.Collections;
import java.util.List;
import java.util.Map;

Expand All @@ -48,4 +50,10 @@ public interface HttpRequestFormatter<MessageFormatT> {

/** Path template for endpoint URL path. */
PathTemplate getPathTemplate();

/** Additional (alternative) path templates for endpoint URL path. */
@BetaApi
default List<PathTemplate> getAdditionalPathTemplates() {
return Collections.emptyList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@
*/
package com.google.api.gax.httpjson;

import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
import com.google.api.pathtemplate.PathTemplate;
import com.google.protobuf.Message;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/** Creates parts of a HTTP request from a protobuf message. */
public class ProtoMessageRequestFormatter<RequestT extends Message>
Expand All @@ -47,28 +50,35 @@ public class ProtoMessageRequestFormatter<RequestT extends Message>
private final String rawPath;
private final PathTemplate pathTemplate;
private final FieldsExtractor<RequestT, Map<String, String>> pathVarsExtractor;
private final List<String> additionalRawPaths;
private final List<PathTemplate> additionalPathTemplates;

private ProtoMessageRequestFormatter(
FieldsExtractor<RequestT, String> requestBodyExtractor,
FieldsExtractor<RequestT, Map<String, List<String>>> queryParamsExtractor,
String rawPath,
PathTemplate pathTemplate,
FieldsExtractor<RequestT, Map<String, String>> pathVarsExtractor) {
FieldsExtractor<RequestT, Map<String, String>> pathVarsExtractor,
List<String> additionalRawPaths,
List<PathTemplate> additionalPathTemplates) {
this.requestBodyExtractor = requestBodyExtractor;
this.queryParamsExtractor = queryParamsExtractor;
this.rawPath = rawPath;
this.pathTemplate = pathTemplate;
this.pathVarsExtractor = pathVarsExtractor;
this.additionalRawPaths = additionalRawPaths;
this.additionalPathTemplates = additionalPathTemplates;
}

public static <RequestT extends Message>
ProtoMessageRequestFormatter.Builder<RequestT> newBuilder() {
return new Builder<>();
return new Builder<RequestT>().setAdditionalPaths();
}

public Builder<RequestT> toBuilder() {
return new Builder<RequestT>()
.setPath(rawPath, pathVarsExtractor)
.setAdditionalPaths(additionalRawPaths.toArray(new String[] {}))
.setQueryParamsExtractor(queryParamsExtractor)
.setRequestBodyExtractor(requestBodyExtractor);
}
Expand All @@ -91,6 +101,12 @@ public String getPath(RequestT apiMessage) {
return pathTemplate.instantiate(pathVarsExtractor.extract(apiMessage));
}

@BetaApi
@Override
public List<PathTemplate> getAdditionalPathTemplates() {
return additionalPathTemplates;
}

/* {@inheritDoc} */
@Override
public PathTemplate getPathTemplate() {
Expand All @@ -104,6 +120,7 @@ public static class Builder<RequestT extends Message> {
private FieldsExtractor<RequestT, Map<String, List<String>>> queryParamsExtractor;
private String rawPath;
private FieldsExtractor<RequestT, Map<String, String>> pathVarsExtractor;
private List<String> rawAdditionalPaths;

public Builder<RequestT> setRequestBodyExtractor(
FieldsExtractor<RequestT, String> requestBodyExtractor) {
Expand All @@ -124,6 +141,12 @@ public Builder<RequestT> setPath(
return this;
}

@BetaApi
public Builder<RequestT> setAdditionalPaths(String... rawAdditionalPaths) {
this.rawAdditionalPaths = Arrays.asList(rawAdditionalPaths);
return this;
}

@InternalApi
public Builder<RequestT> updateRawPath(String target, String replacement) {
this.rawPath = this.rawPath.replace(target, replacement);
Expand All @@ -136,7 +159,9 @@ public ProtoMessageRequestFormatter<RequestT> build() {
queryParamsExtractor,
rawPath,
PathTemplate.create(rawPath),
pathVarsExtractor);
pathVarsExtractor,
rawAdditionalPaths,
rawAdditionalPaths.stream().map(PathTemplate::create).collect(Collectors.toList()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,14 @@ public class HttpJsonDirectCallableTest {
.setRequestFormatter(
ProtoMessageRequestFormatter.<Field>newBuilder()
.setPath(
"/fake/v1/name/{name}",
"/fake/v1/name/{name=bob/*}",
request -> {
Map<String, String> fields = new HashMap<>();
ProtoRestSerializer<Field> serializer = ProtoRestSerializer.create();
serializer.putPathParam(fields, "name", request.getName());
return fields;
})
.setAdditionalPaths("/fake/v1/name/{name=john/*}")
.setQueryParamsExtractor(
request -> {
Map<String, List<String>> fields = new HashMap<>();
Expand Down Expand Up @@ -212,7 +213,7 @@ public void testErrorNullContentFailedResponse() throws InterruptedException {

private Field createTestMessage() {
return Field.newBuilder() // "echo" service
.setName("imTheBestField")
.setName("john/imTheBestField")
.setNumber(2)
.setCardinality(Cardinality.CARDINALITY_OPTIONAL)
.setDefaultValue("blah")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

public class ProtoMessageRequestFormatterTest {
private Field field;
private HttpRequestFormatter<Field> formatter;
private ProtoMessageRequestFormatter<Field> formatter;

@Before
public void setUp() {
Expand All @@ -60,36 +60,28 @@ public void setUp() {
formatter =
ProtoMessageRequestFormatter.<Field>newBuilder()
.setPath(
"/api/v1/names/{name}/aggregated",
new FieldsExtractor<Field, Map<String, String>>() {
@Override
public Map<String, String> extract(Field request) {
Map<String, String> fields = new HashMap<>();
ProtoRestSerializer<Field> serializer = ProtoRestSerializer.create();
serializer.putPathParam(fields, "name", request.getName());
serializer.putPathParam(fields, "kindValue", request.getKindValue());
return fields;
}
"/api/v1/names/{name=field_name1/**}/aggregated",
request -> {
Map<String, String> fields = new HashMap<>();
ProtoRestSerializer<Field> serializer = ProtoRestSerializer.create();
serializer.putPathParam(fields, "name", request.getName());
serializer.putPathParam(fields, "kindValue", request.getKindValue());
return fields;
})
.setQueryParamsExtractor(
new FieldsExtractor<Field, Map<String, List<String>>>() {
@Override
public Map<String, List<String>> extract(Field request) {
Map<String, List<String>> fields = new HashMap<>();
ProtoRestSerializer<Field> serializer = ProtoRestSerializer.create();
serializer.putQueryParam(fields, "number", request.getNumber());
serializer.putQueryParam(fields, "typeUrl", request.getTypeUrl());
return fields;
}
request -> {
Map<String, List<String>> fields = new HashMap<>();
ProtoRestSerializer<Field> serializer = ProtoRestSerializer.create();
serializer.putQueryParam(fields, "number", request.getNumber());
serializer.putQueryParam(fields, "typeUrl", request.getTypeUrl());
return fields;
})
.setRequestBodyExtractor(
new FieldsExtractor<Field, String>() {
@Override
public String extract(Field request) {
ProtoRestSerializer<Field> serializer = ProtoRestSerializer.create();
return serializer.toBody("field", request);
}
request -> {
ProtoRestSerializer<Field> serializer = ProtoRestSerializer.create();
return serializer.toBody("field", request);
})
.setAdditionalPaths("/api/v1/names/{name=field_name1/**}/hello")
.build();
}

Expand All @@ -100,6 +92,12 @@ public void getQueryParamNames() {
expected.put("number", Arrays.asList("2"));
expected.put("typeUrl", Arrays.asList(""));
Truth.assertThat(queryParamNames).isEqualTo(expected);

// Test toBuilder() case
queryParamNames = formatter.toBuilder().build().getQueryParamNames(field);
expected.put("number", Arrays.asList("2"));
expected.put("typeUrl", Arrays.asList(""));
Truth.assertThat(queryParamNames).isEqualTo(expected);
}

@Test
Expand All @@ -117,18 +115,61 @@ public void getRequestBody() {
+ " }]\n"
+ "}";
Truth.assertThat(bodyJson).isEqualTo(expectedBodyJson);

// Test toBuilder() case
formatter.toBuilder().build().getRequestBody(field);
Truth.assertThat(bodyJson).isEqualTo(expectedBodyJson);
}

@Test
public void getPath() {
String path = formatter.getPath(field);
Truth.assertThat(path).isEqualTo("api/v1/names/field_name1/aggregated");

// Test toBuilder() case
path = formatter.toBuilder().build().getPath(field);
Truth.assertThat(path).isEqualTo("api/v1/names/field_name1/aggregated");
}

@Test
public void getPathTemplate() {
String path =
formatter.getPathTemplate().instantiate(Collections.singletonMap("name", "field_name1"));
Truth.assertThat(path).isEqualTo("api/v1/names/field_name1/aggregated");

// Test toBuilder() case
path =
formatter
.toBuilder()
.build()
.getPathTemplate()
.instantiate(Collections.singletonMap("name", "field_name1"));
Truth.assertThat(path).isEqualTo("api/v1/names/field_name1/aggregated");
}

@Test
public void getPathTemplates() {
String path =
formatter
.getAdditionalPathTemplates()
.get(0)
.instantiate(Collections.singletonMap("name", "field_name1"));
Truth.assertThat(path).isEqualTo("api/v1/names/field_name1/hello");

// Test toBuilder() case
path =
formatter
.toBuilder()
.build()
.getAdditionalPathTemplates()
.get(0)
.instantiate(Collections.singletonMap("name", "field_name1"));
Truth.assertThat(path).isEqualTo("api/v1/names/field_name1/hello");
}

@Test
public void updateRawPath() {
String path = formatter.toBuilder().updateRawPath("/v1/", "/v1beta1/").build().getPath(field);
Truth.assertThat(path).isEqualTo("api/v1beta1/names/field_name1/aggregated");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,11 @@ public MockLowLevelHttpResponse getHttpResponse(String httpMethod, String fullTa
}

PathTemplate pathTemplate = methodDescriptor.getRequestFormatter().getPathTemplate();
// Server figures out which RPC method is called based on the endpoint path pattern.
if (!pathTemplate.matches(relativePath)) {
List<PathTemplate> additionalPathTemplates =
methodDescriptor.getRequestFormatter().getAdditionalPathTemplates();
// Server figures out which RPC method is called based on the endpoint path pattern(s).
if (!pathTemplate.matches(relativePath)
&& additionalPathTemplates.stream().noneMatch(pt -> pt.matches(relativePath))) {
continue;
}

Expand Down

0 comments on commit 59b3699

Please sign in to comment.