Repository: googleapis/google-api-java-client
Pull request #2462 (2024-05-06 18:44:27)

feat: Add ApiVersion Support
Changes:
- Pass `X-Google-Api-Version` as a header as part of requests if apiVersion exists in the Discovery Doc

Corresponding generator change https://github.com/googleapis/google-api-java-client-services/pull/20930

This PR will go in first then merge in the generator change once this is released and in maven central.

Logs:
```
2024-05-09 14:37:57,833 CONFIG   com.google.api.client.http.HttpTransport - -------------- REQUEST  --------------
GET {URL}
Accept-Encoding: gzip
Authorization: Bearer {BEARER_TOKEN}
User-Agent: DeltaV Example Java App Google-API-Java-Client/2.4.1-SNAPSHOT Google-HTTP-Java-Client/1.44.1 (gzip)
x-goog-user-project: {PROJECT_ID}
x-goog-api-client: gl-java/17.0.10 gdcl/2.4.1 linux/6.6.15
x-google-api-version: v1_20230601
```

lqiu96: > Can you add that (the generated code uses this field to set the header) in the source code comment? Also if there's any public document available for API versions in https://google.aip.dev/, reference it from here.

Will do, thanks!


Patch:
diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequest.java b/google-api-client/src/main/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequest.java
index cd6d12b6..f729375f 100644
--- a/google-api-client/src/main/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequest.java
+++ b/google-api-client/src/main/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequest.java
@@ -37,6 +37,7 @@ import com.google.api.client.http.HttpResponseInterceptor;
 import com.google.api.client.http.UriTemplate;
 import com.google.api.client.util.GenericData;
 import com.google.api.client.util.Preconditions;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
 import java.io.IOException;
 import java.io.InputStream;
@@ -62,7 +63,13 @@ public abstract class AbstractGoogleClientRequest<T> extends GenericData {
    */
   public static final String USER_AGENT_SUFFIX = "Google-API-Java-Client";
 
-  private static final String API_VERSION_HEADER = "X-Goog-Api-Client";
+  private static final String API_CLIENT_HEADER = "X-Goog-Api-Client";
+
+  /**
+   * The generated request class will pass this constant as part of the header if the RPC supports
+   * ApiVersion.
+   */
+  protected static final String API_VERSION_HEADER = "X-Google-Api-Version";
 
   /** Google client. */
   private final AbstractGoogleClient abstractGoogleClient;
@@ -133,7 +140,7 @@ public abstract class AbstractGoogleClientRequest<T> extends GenericData {
       requestHeaders.setUserAgent(USER_AGENT_SUFFIX + "/" + GoogleUtils.VERSION);
     }
     // Set the header for the Api Client version (Java and OS version)
-    requestHeaders.set(API_VERSION_HEADER, ApiClientVersion.DEFAULT_VERSION);
+    requestHeaders.set(API_CLIENT_HEADER, ApiClientVersion.DEFAULT_VERSION);
   }
 
   /**
@@ -312,6 +319,12 @@ public abstract class AbstractGoogleClientRequest<T> extends GenericData {
     return this;
   }
 
+  /** Returns the ApiVersion set in the headers. If ApiVersion is not set, null is returned. */
+  @VisibleForTesting
+  String getApiVersionHeader() {
+    return (String) requestHeaders.get(API_VERSION_HEADER);
+  }
+
   /**
    * Returns the HTTP headers of the last response or {@code null} before request has been executed.
    */


Test patch:
diff --git a/google-api-client/src/main/java/com/google/api/client/googleapis/testing/services/MockGoogleClientRequest.java b/google-api-client/src/main/java/com/google/api/client/googleapis/testing/services/MockGoogleClientRequest.java
index c1cf32cc..b3b0ad4d 100644
--- a/google-api-client/src/main/java/com/google/api/client/googleapis/testing/services/MockGoogleClientRequest.java
+++ b/google-api-client/src/main/java/com/google/api/client/googleapis/testing/services/MockGoogleClientRequest.java
@@ -18,6 +18,7 @@ import com.google.api.client.http.HttpContent;
 import com.google.api.client.http.HttpHeaders;
 import com.google.api.client.http.UriTemplate;
 import com.google.api.client.util.Beta;
+import com.google.common.base.Strings;
 
 /**
  * {@link Beta} <br>
@@ -46,7 +47,32 @@ public class MockGoogleClientRequest<T> extends AbstractGoogleClientRequest<T> {
       String uriTemplate,
       HttpContent content,
       Class<T> responseClass) {
+    this(client, method, uriTemplate, content, responseClass, null);
+  }
+
+  /**
+   * @param client Google client
+   * @param method HTTP Method
+   * @param uriTemplate URI template for the path relative to the base URL. If it starts with a "/"
+   *     the base path from the base URL will be stripped out. The URI template can also be a full
+   *     URL. URI template expansion is done using {@link UriTemplate#expand(String, String, Object,
+   *     boolean)}
+   * @param content HTTP content or {@code null} for none
+   * @param responseClass response class to parse into
+   * @param apiVersion ApiVersion to be passed to the header
+   */
+  public MockGoogleClientRequest(
+      AbstractGoogleClient client,
+      String method,
+      String uriTemplate,
+      HttpContent content,
+      Class<T> responseClass,
+      String apiVersion) {
     super(client, method, uriTemplate, content, responseClass);
+    // Matches generator code: Null or Empty String is not set to the header
+    if (!Strings.isNullOrEmpty(apiVersion)) {
+      getRequestHeaders().set(API_VERSION_HEADER, apiVersion);
+    }
   }
 
   @Override
diff --git a/google-api-client/src/test/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequestTest.java b/google-api-client/src/test/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequestTest.java
index 1eefdb13..79e156b3 100644
--- a/google-api-client/src/test/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequestTest.java
+++ b/google-api-client/src/test/java/com/google/api/client/googleapis/services/AbstractGoogleClientRequestTest.java
@@ -242,6 +242,49 @@ public class AbstractGoogleClientRequestTest extends TestCase {
     request.executeUnparsed();
   }
 
+  public void testSetsApiVersionHeader_apiVersionSet() {
+    String apiVersion = "testVersion";
+    HttpTransport transport = new MockHttpTransport();
+    MockGoogleClient client =
+        new MockGoogleClient.Builder(transport, ROOT_URL, SERVICE_PATH, JSON_OBJECT_PARSER, null)
+            .build();
+    AbstractGoogleClientRequest<Void> request =
+        new MockGoogleClientRequest<>(
+            client, HttpMethods.GET, URI_TEMPLATE, null, Void.class, apiVersion);
+    assertEquals(apiVersion, request.getApiVersionHeader());
+  }
+
+  public void testDoesNotSetsApiVersionHeader_noApiVersionSet() {
+    HttpTransport transport = new MockHttpTransport();
+    MockGoogleClient client =
+        new MockGoogleClient.Builder(transport, ROOT_URL, SERVICE_PATH, JSON_OBJECT_PARSER, null)
+            .build();
+    AbstractGoogleClientRequest<Void> request =
+        new MockGoogleClientRequest<>(client, HttpMethods.GET, URI_TEMPLATE, null, Void.class);
+    assertNull(request.getApiVersionHeader());
+  }
+
+  public void testNullApiVersionHeader_noApiVersionSet() {
+    HttpTransport transport = new MockHttpTransport();
+    MockGoogleClient client =
+        new MockGoogleClient.Builder(transport, ROOT_URL, SERVICE_PATH, JSON_OBJECT_PARSER, null)
+            .build();
+    final AbstractGoogleClientRequest<Void> request =
+        new MockGoogleClientRequest<>(
+            client, HttpMethods.GET, URI_TEMPLATE, null, Void.class, null);
+    assertNull(request.getApiVersionHeader());
+  }
+
+  public void testEmptyStringApiVersionHeader_noApiVersionSet() {
+    HttpTransport transport = new MockHttpTransport();
+    MockGoogleClient client =
+        new MockGoogleClient.Builder(transport, ROOT_URL, SERVICE_PATH, JSON_OBJECT_PARSER, null)
+            .build();
+    final AbstractGoogleClientRequest<Void> request =
+        new MockGoogleClientRequest<>(client, HttpMethods.GET, URI_TEMPLATE, null, Void.class, "");
+    assertNull(request.getApiVersionHeader());
+  }
+
   public void testSetsApiClientHeader() throws IOException {
     HttpTransport transport =
         new AssertHeaderTransport("X-Goog-Api-Client", "gl-java/\\d+\\.\\d+\\.\\d+.*");