From 2836baafa1114761cffbfdd4aee2322a4a931f8f Mon Sep 17 00:00:00 2001 From: Blake Li Date: Thu, 27 Jan 2022 14:02:26 -0500 Subject: [PATCH] feat: Add a builder to handle the common logic of extracting routing header values from request (#1598) In order to support explicit dynamic routing headers, we have to match-and-extract field values from requests during runtime for each configured routing rule parameter, this PR is to add a request params builder to handle the common logics. --- .../api/gax/rpc/RequestParamsBuilder.java | 78 ++++++++++++++++ .../api/gax/rpc/RequestParamsBuilderTest.java | 92 +++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 gax/src/main/java/com/google/api/gax/rpc/RequestParamsBuilder.java create mode 100644 gax/src/test/java/com/google/api/gax/rpc/RequestParamsBuilderTest.java diff --git a/gax/src/main/java/com/google/api/gax/rpc/RequestParamsBuilder.java b/gax/src/main/java/com/google/api/gax/rpc/RequestParamsBuilder.java new file mode 100644 index 000000000..a7e4a696b --- /dev/null +++ b/gax/src/main/java/com/google/api/gax/rpc/RequestParamsBuilder.java @@ -0,0 +1,78 @@ +/* + * Copyright 2022 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.rpc; + +import com.google.api.core.BetaApi; +import com.google.api.pathtemplate.PathTemplate; +import com.google.common.collect.ImmutableMap; +import java.util.Map; + +/** + * This builder class builds a request params map that will be used by autogenerated implementation + * of {@link RequestParamsExtractor}. + */ +@BetaApi +public class RequestParamsBuilder { + + private final ImmutableMap.Builder paramsBuilder; + + private RequestParamsBuilder() { + this.paramsBuilder = ImmutableMap.builder(); + } + + public static RequestParamsBuilder create() { + return new RequestParamsBuilder(); + } + + /** + * Add an entry to paramsBuilder by match-and-extract field values from requests based on + * pre-configured path templates. This method is called repeatedly for each configured routing + * rule parameter, it's possible that the incoming field value from request is null or there is no + * matches found, we'll continue the match-and-extract process for the next routing rule parameter + * in such case. + * + * @param fieldValue the field value from a request + * @param headerKey the header key for the routing header param + * @param pathTemplate {@link PathTemplate} the path template used for match-and-extract + */ + public void add(String fieldValue, String headerKey, PathTemplate pathTemplate) { + if (fieldValue == null) { + return; + } + Map matchedValues = pathTemplate.match(fieldValue); + if (matchedValues != null && matchedValues.containsKey(headerKey)) { + paramsBuilder.put(headerKey, matchedValues.get(headerKey)); + } + } + + public Map build() { + return paramsBuilder.build(); + } +} diff --git a/gax/src/test/java/com/google/api/gax/rpc/RequestParamsBuilderTest.java b/gax/src/test/java/com/google/api/gax/rpc/RequestParamsBuilderTest.java new file mode 100644 index 000000000..887479d4e --- /dev/null +++ b/gax/src/test/java/com/google/api/gax/rpc/RequestParamsBuilderTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2022 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.rpc; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.api.pathtemplate.PathTemplate; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class RequestParamsBuilderTest { + + private RequestParamsBuilder requestParamsBuilder; + + @Before + public void setUp() { + requestParamsBuilder = RequestParamsBuilder.create(); + } + + @Test + public void add_happyPath() { + String headerKey = "table_location"; + Map actual = + getRoutingHeaders( + headerKey, + "projects/**/{table_location=instances/*}", + "projects/my_cozy_home/instances/living_room"); + assertThat(actual).containsExactly(headerKey, "instances/living_room"); + } + + @Test + public void add_matchedValuesWithNoRoutingHeaderKey() { + Map actual = + getRoutingHeaders("table_location", "projects/**", "projects/my_cozy_home/"); + assertThat(actual).isEmpty(); + } + + @Test + public void add_emptyMatchedValues() { + Map actual = + getRoutingHeaders( + "table_location", + "projects/**/{table_location=instances/*}", + "projects/does_not_matter"); + assertThat(actual).isEmpty(); + } + + @Test + public void add_nullFieldValue() { + Map actual = getRoutingHeaders("table_location", "projects/**", null); + assertThat(actual).isEmpty(); + } + + private Map getRoutingHeaders( + String headerKey, String patternString, String fieldValue) { + PathTemplate pathTemplate = PathTemplate.create(patternString); + requestParamsBuilder.add(fieldValue, headerKey, pathTemplate); + return requestParamsBuilder.build(); + } +}