Skip to content

Commit dc2cde9

Browse files
manusaclaude
andauthored
fix(crd-generator): improve SchemaCustomizer with tests and @repeatable support
- Add test for MergePatchCustomizer to ensure merge patch functionality works - Make @SchemaCustomizer annotation @repeatable for multiple customizers on same class - Improve exception messages to include customizer class name for easier debugging - Add SchemaCustomizer to Features cheatsheet table in documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 38cf445 commit dc2cde9

File tree

5 files changed

+51
-5
lines changed

5 files changed

+51
-5
lines changed

crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractJsonSchema.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -518,11 +518,10 @@ private T handleSchemaCustomizer(T objectSchema, Class<?> rawClass) {
518518
try {
519519
props[0] = sc.value().getConstructor().newInstance().apply(props[0], sc.input(),
520520
this.resolvingContext.kubernetesSerialization);
521-
} catch (Exception e) {
522-
if (!(e instanceof RuntimeException)) {
523-
e = new RuntimeException(e);
524-
}
525-
throw (RuntimeException) e;
521+
} catch (ReflectiveOperationException e) {
522+
throw new RuntimeException("Failed to instantiate or apply SchemaCustomizer: " + sc.value().getName(), e);
523+
} catch (RuntimeException e) {
524+
throw new RuntimeException("Failed to apply SchemaCustomizer: " + sc.value().getName(), e);
526525
}
527526
});
528527
if (props[0] != objectSchema) {

crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/v1/SchemaCustomizer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
import io.fabric8.kubernetes.client.utils.KubernetesSerialization;
2020

2121
import java.lang.annotation.ElementType;
22+
import java.lang.annotation.Repeatable;
2223
import java.lang.annotation.Retention;
2324
import java.lang.annotation.RetentionPolicy;
2425
import java.lang.annotation.Target;
2526

2627
@Target({ ElementType.TYPE })
2728
@Retention(RetentionPolicy.RUNTIME)
29+
@Repeatable(SchemaCustomizer.List.class)
2830
public @interface SchemaCustomizer {
2931

3032
public interface Customizer {
@@ -70,4 +72,10 @@ public JSONSchemaProps apply(JSONSchemaProps props, String input, KubernetesSeri
7072

7173
String input() default "";
7274

75+
@Target({ ElementType.TYPE })
76+
@Retention(RetentionPolicy.RUNTIME)
77+
@interface List {
78+
SchemaCustomizer[] value();
79+
}
80+
7381
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (C) 2015 Red Hat, Inc.
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+
* http://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+
package io.fabric8.crdv2.example.customized;
17+
18+
import io.fabric8.crdv2.generator.v1.SchemaCustomizer;
19+
20+
@SchemaCustomizer(input = "{\"description\": \"patched description\", \"minProperties\": 1}", value = SchemaCustomizer.MergePatchCustomizer.class)
21+
public class MergePatchCustomized {
22+
23+
public String existingField;
24+
25+
}

crd-generator/api-v2/src/test/java/io/fabric8/crdv2/generator/v1/SchemaCustomizerTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616
package io.fabric8.crdv2.generator.v1;
1717

1818
import io.fabric8.crdv2.example.customized.Customized;
19+
import io.fabric8.crdv2.example.customized.MergePatchCustomized;
1920
import io.fabric8.crdv2.example.customized.RawCustomized;
2021
import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps;
2122
import org.junit.jupiter.api.Test;
2223

2324
import static org.junit.jupiter.api.Assertions.assertEquals;
25+
import static org.junit.jupiter.api.Assertions.assertNotNull;
2426

2527
class SchemaCustomizerTest {
2628

@@ -37,4 +39,15 @@ void applyRawCustomizer() {
3739
assertEquals("integer", schema.getProperties().get("prop").getType());
3840
}
3941

42+
@Test
43+
void applyMergePatchCustomizer() {
44+
JSONSchemaProps schema = JsonSchema.from(MergePatchCustomized.class);
45+
// Verify the patch was applied
46+
assertEquals("patched description", schema.getDescription());
47+
assertEquals(1L, schema.getMinProperties());
48+
// Verify existing properties are preserved
49+
assertNotNull(schema.getProperties().get("existingField"));
50+
assertEquals("string", schema.getProperties().get("existingField").getType());
51+
}
52+
4053
}

doc/CRD-generator.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,7 @@ for directly manipulating the JSONSchemaProps of the annotated resource. This an
907907
| `io.fabric8.kubernetes.model.annotation.SpecReplicas` | The field is used in scale subresource as `specReplicaPath` |
908908
| `io.fabric8.kubernetes.model.annotation.StatusReplicas` | The field is used in scale subresource as `statusReplicaPath` |
909909
| `io.fabric8.kubernetes.model.annotation.LabelSelector` | The field is used in scale subresource as `labelSelectorPath` |
910+
| `io.fabric8.crdv2.generator.v1.SchemaCustomizer` | Advanced: Allows direct manipulation of the `JSONSchemaProps` via a custom `Customizer` class |
910911

911912

912913
A field of type `com.fasterxml.jackson.databind.JsonNode` is encoded as an empty object with `x-kubernetes-preserve-unknown-fields: true` defined.

0 commit comments

Comments
 (0)