diff --git a/CHANGELOG.md b/CHANGELOG.md index 526b78f490c..bde3019fd43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ * Fix #7048: Support for Kubernetes v1.34 (Of Wind & Will) * Fix #7371: Update Fabric8 OpenShift Model as per OpenShift 4.20 * Fix #7355: (crd-generator) Add SchemaCustomizer annotation for advanced schema modification +* Fix #7375: (crd-generator) Support @JsonClassDescription for adding descriptions to classes in the generated CRD schema. #### _**Note**_: Breaking changes * Fix #7048: **admissionregistration.v1beta1**: Removed `ValidatingAdmissionPolicy` and related classes (`ValidatingAdmissionPolicyBinding`, `AuditAnnotation`, `Validation`, `ValidatingAdmissionPolicyBindingSpec`) from `v1beta1` as they have graduated to GA in `admissionregistration.v1`. `MutatingAdmissionPolicy` has been promoted from `v1alpha1` to `v1beta1`. The `V1beta1AdmissionRegistrationAPIGroupDSL` and `V1beta1AdmissionRegistrationAPIGroupClient` have been updated accordingly. diff --git a/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractJsonSchema.java b/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractJsonSchema.java index f8211f10a38..03d911c69dc 100644 --- a/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractJsonSchema.java +++ b/crd-generator/api-v2/src/main/java/io/fabric8/crdv2/generator/AbstractJsonSchema.java @@ -15,6 +15,7 @@ */ package io.fabric8.crdv2.generator; +import com.fasterxml.jackson.annotation.JsonClassDescription; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.BeanProperty; @@ -370,7 +371,9 @@ private void setMinMax(BeanProperty beanProperty, } public void updateSchema(T schema) { - schema.setDescription(description); + if (Utils.isNotNullOrEmpty(description)) { + schema.setDescription(description); + } schema.setDefault(defaultValue); if (nullable) { schema.setNullable(true); @@ -430,6 +433,11 @@ private T resolveObject(LinkedHashMap visited, InternalSchemaSwa Class rawClass = gos.javaType.getRawClass(); collectDependentClasses(rawClass); + JsonClassDescription classDescription = rawClass.getAnnotation(JsonClassDescription.class); + if (classDescription != null) { + objectSchema.setDescription(classDescription.value()); + } + // while it should not be repeating, we reuse this method to look for preserve unknown on the class hierarchy consumeRepeatingAnnotation(rawClass, PreserveUnknownFields.class, ignored -> objectSchema.setXKubernetesPreserveUnknownFields(true)); diff --git a/crd-generator/test/src/test/java/io/fabric8/crd/generator/approvaltests/CRDGeneratorApprovalTest.java b/crd-generator/test/src/test/java/io/fabric8/crd/generator/approvaltests/CRDGeneratorApprovalTest.java index 144042e26cf..d5e0ffa0be2 100644 --- a/crd-generator/test/src/test/java/io/fabric8/crd/generator/approvaltests/CRDGeneratorApprovalTest.java +++ b/crd-generator/test/src/test/java/io/fabric8/crd/generator/approvaltests/CRDGeneratorApprovalTest.java @@ -22,6 +22,7 @@ import io.fabric8.crd.generator.CRDInfo; import io.fabric8.crd.generator.approvaltests.annotated.Annotated; import io.fabric8.crd.generator.approvaltests.complex.Complex; +import io.fabric8.crd.generator.approvaltests.described.Described; import io.fabric8.crd.generator.approvaltests.inherited.Child; import io.fabric8.crd.generator.approvaltests.json.ContainingJson; import io.fabric8.crd.generator.approvaltests.k8svalidation.K8sValidation; @@ -183,6 +184,7 @@ static Stream crdApprovalCasesBase(String crdVersion) { static Stream crdApprovalCasesApiV2(String crdVersion) { final List cases = new ArrayList<>(); for (boolean parallel : new boolean[] { false, true }) { + cases.add(new TestCase("describeds.samples.fabric8.io", crdVersion, parallel, Described.class)); cases.add(new TestCase("printercolumns.sample.fabric8.io", crdVersion, parallel, PrinterColumn.class)); cases.add(new TestCase("requireds.samples.fabric8.io", crdVersion, parallel, Required.class)); cases.add(new TestCase("selectablefields.sample.fabric8.io", crdVersion, parallel, SelectableField.class)); diff --git a/crd-generator/test/src/test/java/io/fabric8/crd/generator/approvaltests/described/Described.java b/crd-generator/test/src/test/java/io/fabric8/crd/generator/approvaltests/described/Described.java new file mode 100644 index 00000000000..cb038067661 --- /dev/null +++ b/crd-generator/test/src/test/java/io/fabric8/crd/generator/approvaltests/described/Described.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fabric8.crd.generator.approvaltests.described; + +import com.fasterxml.jackson.annotation.JsonClassDescription; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("samples.fabric8.io") +@Version("v1") +@JsonClassDescription("This is a top-level description.") +public class Described extends CustomResource { +} \ No newline at end of file diff --git a/crd-generator/test/src/test/java/io/fabric8/crd/generator/approvaltests/described/DescribedSpec.java b/crd-generator/test/src/test/java/io/fabric8/crd/generator/approvaltests/described/DescribedSpec.java new file mode 100644 index 00000000000..b7cdfccee0f --- /dev/null +++ b/crd-generator/test/src/test/java/io/fabric8/crd/generator/approvaltests/described/DescribedSpec.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fabric8.crd.generator.approvaltests.described; + +import com.fasterxml.jackson.annotation.JsonClassDescription; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import lombok.Data; + +/** + * Test spec for @JsonClassDescription support. + */ +@Data +@JsonClassDescription("This is a spec-class-level description.") +public class DescribedSpec { + + @JsonPropertyDescription("This is a field-level description.") + private String field; + + private Nested nested; + + @Data + @JsonClassDescription("This is a nested-class-level description.") + public static class Nested { + + @JsonPropertyDescription("This is a nested-field-level description.") + private String nestedField; + + private DeeplyNested deeplyNested; + } + + @Data + @JsonClassDescription("This is a deeply-nested-class-level description.") + public static class DeeplyNested { + + @JsonPropertyDescription("This is a leaf-field-level description.") + private String leafField; + } +} \ No newline at end of file diff --git a/crd-generator/test/src/test/resources/io/fabric8/crd/generator/approvaltests/CRDGeneratorApprovalTest.approvalTest.describeds.samples.fabric8.io.v1.approved.yml b/crd-generator/test/src/test/resources/io/fabric8/crd/generator/approvaltests/CRDGeneratorApprovalTest.approvalTest.describeds.samples.fabric8.io.v1.approved.yml new file mode 100644 index 00000000000..ac0bbc62fcc --- /dev/null +++ b/crd-generator/test/src/test/resources/io/fabric8/crd/generator/approvaltests/CRDGeneratorApprovalTest.approvalTest.describeds.samples.fabric8.io.v1.approved.yml @@ -0,0 +1,44 @@ +# Generated by Fabric8 CRDGenerator, manual edits might get overwritten! +apiVersion: "apiextensions.k8s.io/v1" +kind: "CustomResourceDefinition" +metadata: + name: "describeds.samples.fabric8.io" +spec: + group: "samples.fabric8.io" + names: + kind: "Described" + plural: "describeds" + singular: "described" + scope: "Cluster" + versions: + - name: "v1" + schema: + openAPIV3Schema: + description: "This is a top-level description." + properties: + spec: + description: "This is a spec-class-level description." + properties: + field: + description: "This is a field-level description." + type: "string" + nested: + description: "This is a nested-class-level description." + properties: + deeplyNested: + description: "This is a deeply-nested-class-level description." + properties: + leafField: + description: "This is a leaf-field-level description." + type: "string" + type: "object" + nestedField: + description: "This is a nested-field-level description." + type: "string" + type: "object" + type: "object" + status: + type: "object" + type: "object" + served: true + storage: true diff --git a/doc/CRD-generator.md b/doc/CRD-generator.md index 16975a7fe0f..972b4fc7af7 100644 --- a/doc/CRD-generator.md +++ b/doc/CRD-generator.md @@ -137,6 +137,37 @@ The generated field in the CRD will be named after the value provided in the ann type: object ``` +### com.fasterxml.jackson.annotation.JsonClassDescription + +If a class is annotated with `com.fasterxml.jackson.annotation.JsonClassDescription`, the provided text will be used as the `description` for that object type in the generated CRD schema. + +This ensures the description is associated with the class itself in the resulting schema: + +```java +@JsonClassDescription("Description for the Example resource") +public class Example extends CustomResource implements Namespaced {} + +@JsonClassDescription("Description for the spec object") +public class ExampleSpec { + private String field; +} + +``` + +The generated CRD will include these descriptions for each object type: + +```yaml + schema: + openAPIV3Schema: + description: "Description for the Example resource" + type: object + properties: + spec: + description: "Description for the spec object" + type: object + +``` + ### com.fasterxml.jackson.annotation.JsonPropertyDescription If a field or one of its accessors is annotated with `com.fasterxml.jackson.annotation.JsonPropertyDescription` @@ -884,7 +915,8 @@ for directly manipulating the JSONSchemaProps of the annotated resource. This an | Annotation | Description | |-----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| | `com.fasterxml.jackson.annotation.JsonProperty` | The field is named after the provided value instead of looking up the java field name | -| `com.fasterxml.jackson.annotation.JsonPropertyDescription` | The provided text is be embedded in the `description` of the field | +| `com.fasterxml.jackson.annotation.JsonClassDescription` | The provided text is embedded in the `description` of the class | +| `com.fasterxml.jackson.annotation.JsonPropertyDescription` | The provided text is embedded in the `description` of the field | | `com.fasterxml.jackson.annotation.JsonIgnore` | The field is ignored | | `io.fabric8.crd.generator.annotation.PreserveUnknownFields` | The field have `x-kubernetes-preserve-unknown-fields: true` defined | | `com.fasterxml.jackson.annotation.JsonAnyGetter` | The corresponding object have `x-kubernetes-preserve-unknown-fields: true` defined |