Skip to content

Commit 4a99a8c

Browse files
feat: Add GcRuleBuilder for safe GC rule construction (#2758)
* feat: Add GcRuleBuilder for safe GC rule construction * chore: generate libraries at Tue Jan 20 22:09:26 UTC 2026 * address feedback * chore: generate libraries at Wed Jan 21 18:00:03 UTC 2026 --------- Co-authored-by: cloud-java-bot <cloud-java-bot@google.com>
1 parent 439c413 commit 4a99a8c

File tree

4 files changed

+307
-0
lines changed

4 files changed

+307
-0
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.bigtable.admin.v2.models;
18+
19+
import com.google.bigtable.admin.v2.GcRule;
20+
import com.google.protobuf.util.Durations;
21+
import java.time.Duration;
22+
23+
/**
24+
* Factory for creating safe GcRule protos.
25+
*
26+
* <p>Use this class to construct {@link GcRule} instances instead of the raw proto builder ({@link
27+
* GcRule#newBuilder()}) to avoid common pitfalls with "oneof" fields (e.g. accidentally overwriting
28+
* max age with max versions).
29+
*/
30+
public final class GcRuleBuilder {
31+
private GcRuleBuilder() {} // Static utility
32+
33+
// Entry points for composite rules
34+
35+
/**
36+
* Starts building an Intersection (AND) rule.
37+
*
38+
* @return A new builder for an intersection rule.
39+
*/
40+
public static IntersectionRuleBuilder intersection() {
41+
return new IntersectionRuleBuilder();
42+
}
43+
44+
/**
45+
* Starts building a Union (OR) rule.
46+
*
47+
* @return A new builder for a union rule.
48+
*/
49+
public static UnionRuleBuilder union() {
50+
return new UnionRuleBuilder();
51+
}
52+
53+
// Entry points for simple rules (return the Proto directly)
54+
55+
/**
56+
* Creates a Max Age rule.
57+
*
58+
* @param age The maximum age of the cell.
59+
* @return The constructed GcRule proto.
60+
*/
61+
public static GcRule maxAge(Duration age) {
62+
long seconds = age.getSeconds();
63+
int nanos = age.getNano();
64+
return GcRule.newBuilder()
65+
.setMaxAge(Durations.fromNanos(seconds * 1_000_000_000L + nanos))
66+
.build();
67+
}
68+
69+
/**
70+
* Creates a Max Versions rule.
71+
*
72+
* @param versions The maximum number of versions.
73+
* @return The constructed GcRule proto.
74+
*/
75+
public static GcRule maxVersions(int versions) {
76+
return GcRule.newBuilder().setMaxNumVersions(versions).build();
77+
}
78+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.bigtable.admin.v2.models;
18+
19+
import com.google.bigtable.admin.v2.GcRule;
20+
import com.google.bigtable.admin.v2.GcRule.Intersection;
21+
22+
/**
23+
* Builder for creating an Intersection (AND) GC Rule.
24+
*
25+
* <p>This class ensures type safety when constructing composite rules, preventing the misuse of the
26+
* standard builder's ambiguous setters.
27+
*/
28+
public final class IntersectionRuleBuilder {
29+
private final Intersection.Builder intersectionBuilder = Intersection.newBuilder();
30+
31+
/**
32+
* Adds a rule to the intersection.
33+
*
34+
* @param rule The rule to add to the intersection.
35+
* @return The builder instance for chaining.
36+
*/
37+
public IntersectionRuleBuilder add(GcRule rule) {
38+
intersectionBuilder.addRules(rule);
39+
return this;
40+
}
41+
42+
/**
43+
* Builds the final GcRule proto.
44+
*
45+
* @return The constructed GcRule proto.
46+
*/
47+
public GcRule build() {
48+
return GcRule.newBuilder().setIntersection(intersectionBuilder).build();
49+
}
50+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.bigtable.admin.v2.models;
18+
19+
import com.google.bigtable.admin.v2.GcRule;
20+
import com.google.bigtable.admin.v2.GcRule.Union;
21+
22+
/**
23+
* Builder for creating a Union (OR) GC Rule.
24+
*
25+
* <p>This class ensures type safety when constructing composite rules, preventing the misuse of the
26+
* standard builder's ambiguous setters.
27+
*/
28+
public final class UnionRuleBuilder {
29+
private final Union.Builder unionBuilder = Union.newBuilder();
30+
31+
/**
32+
* Adds a rule to the union.
33+
*
34+
* @param rule The rule to add to the union.
35+
* @return The builder instance for chaining.
36+
*/
37+
public UnionRuleBuilder add(GcRule rule) {
38+
unionBuilder.addRules(rule);
39+
return this;
40+
}
41+
42+
/**
43+
* Builds the final GcRule proto.
44+
*
45+
* @return The constructed GcRule proto.
46+
*/
47+
public GcRule build() {
48+
return GcRule.newBuilder().setUnion(unionBuilder).build();
49+
}
50+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.bigtable.admin.v2.models;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
21+
import com.google.bigtable.admin.v2.GcRule;
22+
import com.google.protobuf.util.Durations;
23+
import java.time.Duration;
24+
import org.junit.Test;
25+
import org.junit.runner.RunWith;
26+
import org.junit.runners.JUnit4;
27+
28+
@RunWith(JUnit4.class)
29+
public class GcRuleBuilderTest {
30+
31+
@Test
32+
public void maxAge_createsCorrectProto() {
33+
GcRule rule = GcRuleBuilder.maxAge(Duration.ofHours(1));
34+
35+
assertThat(rule.hasMaxAge()).isTrue();
36+
assertThat(rule.getMaxAge()).isEqualTo(Durations.fromHours(1));
37+
}
38+
39+
@Test
40+
public void maxVersions_createsCorrectProto() {
41+
GcRule rule = GcRuleBuilder.maxVersions(5);
42+
43+
assertThat(rule.hasMaxNumVersions()).isTrue();
44+
assertThat(rule.getMaxNumVersions()).isEqualTo(5);
45+
}
46+
47+
@Test
48+
public void intersection_buildsNestedRules() {
49+
// Expected Proto structure
50+
GcRule expected =
51+
GcRule.newBuilder()
52+
.setIntersection(
53+
GcRule.Intersection.newBuilder()
54+
.addRules(GcRule.newBuilder().setMaxNumVersions(1).build())
55+
.addRules(GcRule.newBuilder().setMaxAge(Durations.fromHours(2)).build()))
56+
.build();
57+
58+
// Using the new Builder
59+
GcRule actual =
60+
GcRuleBuilder.intersection()
61+
.add(GcRuleBuilder.maxVersions(1))
62+
.add(GcRuleBuilder.maxAge(Duration.ofHours(2)))
63+
.build();
64+
65+
assertThat(actual).isEqualTo(expected);
66+
}
67+
68+
@Test
69+
public void union_buildsNestedRules() {
70+
// Expected Proto structure
71+
GcRule expected =
72+
GcRule.newBuilder()
73+
.setUnion(
74+
GcRule.Union.newBuilder()
75+
.addRules(GcRule.newBuilder().setMaxNumVersions(10).build())
76+
.addRules(GcRule.newBuilder().setMaxAge(Durations.fromDays(5)).build()))
77+
.build();
78+
79+
// Using the new Builder
80+
GcRule actual =
81+
GcRuleBuilder.union()
82+
.add(GcRuleBuilder.maxVersions(10))
83+
.add(GcRuleBuilder.maxAge(Duration.ofDays(5)))
84+
.build();
85+
86+
assertThat(actual).isEqualTo(expected);
87+
}
88+
89+
@Test
90+
public void nestedComplexRules_workCorrectly() {
91+
// Expected Proto structure: Union of (Version(1) OR Intersection(Age(1h) AND Version(5)))
92+
GcRule expected =
93+
GcRule.newBuilder()
94+
.setUnion(
95+
GcRule.Union.newBuilder()
96+
.addRules(GcRule.newBuilder().setMaxNumVersions(1).build())
97+
.addRules(
98+
GcRule.newBuilder()
99+
.setIntersection(
100+
GcRule.Intersection.newBuilder()
101+
.addRules(
102+
GcRule.newBuilder()
103+
.setMaxAge(Durations.fromHours(1))
104+
.build())
105+
.addRules(GcRule.newBuilder().setMaxNumVersions(5).build())
106+
.build())
107+
.build())
108+
.build())
109+
.build();
110+
111+
// Using the new Builder
112+
GcRule actual =
113+
GcRuleBuilder.union()
114+
.add(GcRuleBuilder.maxVersions(1))
115+
.add(
116+
GcRuleBuilder.intersection()
117+
.add(GcRuleBuilder.maxAge(Duration.ofHours(1)))
118+
.add(GcRuleBuilder.maxVersions(5))
119+
.build())
120+
.build();
121+
122+
// Verify the structure matches the raw proto construction
123+
assertThat(actual).isEqualTo(expected);
124+
125+
// Verify specific properties
126+
assertThat(actual.getUnion().getRulesCount()).isEqualTo(2);
127+
assertThat(actual.getUnion().getRules(1).getIntersection().getRulesCount()).isEqualTo(2);
128+
}
129+
}

0 commit comments

Comments
 (0)