Skip to content

Commit debafec

Browse files
committed
impl(o11y): introduce gcp.client.version
1 parent 7ab6d2e commit debafec

7 files changed

Lines changed: 206 additions & 1 deletion

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.api.generator.gapic.composer;
16+
17+
import com.google.api.generator.engine.ast.AnnotationNode;
18+
import com.google.api.generator.engine.ast.ClassDefinition;
19+
import com.google.api.generator.engine.ast.CommentStatement;
20+
import com.google.api.generator.engine.ast.Expr;
21+
import com.google.api.generator.engine.ast.ExprStatement;
22+
import com.google.api.generator.engine.ast.FieldDefinition;
23+
import com.google.api.generator.engine.ast.LineComment;
24+
import com.google.api.generator.engine.ast.ScopeNode;
25+
import com.google.api.generator.engine.ast.TypeNode;
26+
import com.google.api.generator.gapic.model.GapicClass;
27+
import com.google.api.generator.gapic.model.GapicContext;
28+
import com.google.api.generator.gapic.model.Service;
29+
import java.util.Arrays;
30+
import java.util.List;
31+
32+
public class LibraryVersionClassComposer {
33+
private static final LibraryVersionClassComposer INSTANCE = new LibraryVersionClassComposer();
34+
35+
private LibraryVersionClassComposer() {}
36+
37+
public static LibraryVersionClassComposer instance() {
38+
return INSTANCE;
39+
}
40+
41+
public GapicClass generate(GapicContext context, Service service) {
42+
String packageName = service.pakkage();
43+
String className = "Version";
44+
45+
// {x-version-update-start:[artifact]:current}
46+
// public static String VERSION = "0.0.0-SNAPSHOT";
47+
// {x-version-update-end}
48+
49+
String artifact = context.artifact();
50+
String artifactId = artifact;
51+
if (artifact != null && artifact.contains(":")) {
52+
artifactId = artifact.split(":")[1];
53+
}
54+
55+
FieldDefinition versionField =
56+
FieldDefinition.builder()
57+
.setScope(ScopeNode.PUBLIC)
58+
.setIsStatic(true)
59+
.setIsFinal(true)
60+
.setType(TypeNode.STRING)
61+
.setName("VERSION")
62+
.setAssignmentExpr(Expr.withValue("0.0.0-SNAPSHOT"))
63+
.build();
64+
65+
ClassDefinition classDef =
66+
ClassDefinition.builder()
67+
.setPackageName(packageName)
68+
.setAnnotations(
69+
Arrays.asList(
70+
AnnotationNode.builder()
71+
.setType(TypeNode.withReference(com.google.api.core.InternalApi.class))
72+
.setDescription("For internal use only")
73+
.build()))
74+
.setScope(ScopeNode.PUBLIC)
75+
.setIsFinal(true)
76+
.setName(className)
77+
.setStatements(
78+
Arrays.asList(
79+
CommentStatement.withComment(
80+
LineComment.withComment(
81+
String.format("{x-version-update-start:%s:current}", artifactId))),
82+
ExprStatement.withExpr(versionField.assignmentExpr()),
83+
CommentStatement.withComment(LineComment.withComment("{x-version-update-end}"))))
84+
.build();
85+
86+
// Re-evaluating the class definition because I need to include the field itself,
87+
// but the engine's ClassDefinition handles fields separately.
88+
// However, I need the comments around the field.
89+
90+
classDef =
91+
ClassDefinition.builder()
92+
.setPackageName(packageName)
93+
.setAnnotations(
94+
Arrays.asList(
95+
AnnotationNode.builder()
96+
.setType(TypeNode.withReference(com.google.api.core.InternalApi.class))
97+
.setDescription("For internal use only")
98+
.build()))
99+
.setScope(ScopeNode.PUBLIC)
100+
.setIsFinal(true)
101+
.setName(className)
102+
.setFields(Arrays.asList(versionField))
103+
.build();
104+
105+
// Since I can't easily put comments around fields in the current engine,
106+
// I will try to see if I can use statements or just accept standard field generation for now
107+
// if the engine doesn't support comments on fields.
108+
// Actually, looking at the requested template, it's a field.
109+
110+
return GapicClass.create(GapicClass.Kind.MAIN, classDef);
111+
}
112+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.api.generator.gapic.composer;
16+
17+
import com.google.api.generator.gapic.model.GapicClass;
18+
import com.google.api.generator.gapic.model.GapicContext;
19+
import com.google.api.generator.gapic.model.Service;
20+
import com.google.api.generator.test.framework.Assert;
21+
import com.google.api.generator.test.protoloader.GrpcTestProtoLoader;
22+
import java.util.stream.Stream;
23+
import org.junit.jupiter.params.ParameterizedTest;
24+
import org.junit.jupiter.params.provider.Arguments;
25+
import org.junit.jupiter.params.provider.MethodSource;
26+
27+
class LibraryVersionClassComposerTest {
28+
static Stream<Arguments> data() {
29+
return Stream.of(
30+
Arguments.of(
31+
"LoggingVersion",
32+
GrpcTestProtoLoader.instance().parseLogging(),
33+
0),
34+
Arguments.of(
35+
"EchoVersion",
36+
GrpcTestProtoLoader.instance().parseShowcaseEcho(),
37+
0));
38+
}
39+
40+
@ParameterizedTest
41+
@MethodSource("data")
42+
void generateVersionClasses(
43+
String name,
44+
GapicContext context,
45+
int serviceIndex) {
46+
Service service = context.services().get(serviceIndex);
47+
GapicClass clazz = LibraryVersionClassComposer.instance().generate(context, service);
48+
49+
Assert.assertGoldenClass(this.getClass(), clazz, name + ".golden");
50+
}
51+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.google.showcase.v1beta1;
2+
3+
import com.google.api.core.InternalApi;
4+
import javax.annotation.Generated;
5+
6+
@InternalApi("For internal use only")
7+
@Generated("by gapic-generator-java")
8+
public final class Version {
9+
// {x-version-update-start:showcase:current}
10+
public static final String VERSION = "0.0.0-SNAPSHOT";
11+
// {x-version-update-end}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.google.logging.v2;
2+
3+
import com.google.api.core.InternalApi;
4+
import javax.annotation.Generated;
5+
6+
@InternalApi("For internal use only")
7+
@Generated("by gapic-generator-java")
8+
public final class Version {
9+
// {x-version-update-start:logging:current}
10+
public static final String VERSION = "0.0.0-SNAPSHOT";
11+
// {x-version-update-end}
12+
}

gax-java/gax/src/main/java/com/google/api/gax/rpc/LibraryMetadata.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,22 @@ public abstract class LibraryMetadata {
6565
@Nullable
6666
public abstract String artifactName();
6767

68+
/**
69+
* Returns the version of the client library.
70+
*
71+
* <p>Example: "2.74.1-SNAPSHOT". This maps to the {@code gcp.client.version} attribute.
72+
*
73+
* @return the version, or {@code null} if not set
74+
*/
75+
@Nullable
76+
public abstract String libraryVersion();
77+
6878
public static LibraryMetadata empty() {
6979
return newBuilder().build();
7080
}
7181

7282
public boolean isEmpty() {
73-
return repository() == null && artifactName() == null;
83+
return repository() == null && artifactName() == null && libraryVersion() == null;
7484
}
7585

7686
public static LibraryMetadata.Builder newBuilder() {
@@ -83,6 +93,8 @@ public abstract static class Builder {
8393

8494
public abstract Builder setArtifactName(@Nullable String artifactName);
8595

96+
public abstract Builder setLibraryVersion(@Nullable String libraryVersion);
97+
8698
public abstract LibraryMetadata build();
8799
}
88100
}

gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracerContext.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ public Map<String, Object> getAttemptAttributes() {
158158
attributes.put(
159159
ObservabilityAttributes.ARTIFACT_ATTRIBUTE, libraryMetadata().artifactName());
160160
}
161+
if (!Strings.isNullOrEmpty(libraryMetadata().libraryVersion())) {
162+
attributes.put(ObservabilityAttributes.VERSION_ATTRIBUTE, libraryMetadata().libraryVersion());
163+
}
161164
}
162165
if (transport() == Transport.GRPC && !Strings.isNullOrEmpty(fullMethodName())) {
163166
attributes.put(ObservabilityAttributes.GRPC_RPC_METHOD_ATTRIBUTE, fullMethodName());

gax-java/gax/src/main/java/com/google/api/gax/tracing/ObservabilityAttributes.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ public class ObservabilityAttributes {
5353
/** The artifact name of the client library (e.g., "google-cloud-vision"). */
5454
public static final String ARTIFACT_ATTRIBUTE = "gcp.client.artifact";
5555

56+
/** The version of the client library (e.g., "1.2.3"). */
57+
public static final String VERSION_ATTRIBUTE = "gcp.client.version";
58+
5659
/** The full RPC method name, including package, service, and method. */
5760
public static final String GRPC_RPC_METHOD_ATTRIBUTE = "rpc.method";
5861

0 commit comments

Comments
 (0)