diff --git a/CHANGELOG.md b/CHANGELOG.md index 54f96f01d8f..7987e7783b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Fix #4256: crd-generator-apt pom.xml includes transitive dependencies * Fix #4294: crd-generator respects JsonIgnore annotations on enum properties * Fix #4320: corrected leader transitions field on leader election leases +* Fix #4350: SchemaSwap annotation is now repeatable and is applied multiple times if classes are used more than once in the class hierarchy #### Improvements diff --git a/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java b/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java index 59fbfd70d6b..76759ca3511 100644 --- a/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java +++ b/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java @@ -81,6 +81,7 @@ public abstract class AbstractJsonSchema { public static final String ANNOTATION_NOT_NULL = "javax.validation.constraints.NotNull"; public static final String ANNOTATION_SCHEMA_FROM = "io.fabric8.crd.generator.annotation.SchemaFrom"; public static final String ANNOTATION_SCHEMA_SWAP = "io.fabric8.crd.generator.annotation.SchemaSwap"; + public static final String ANNOTATION_SCHEMA_SWAPS = "io.fabric8.crd.generator.annotation.SchemaSwaps"; public static final String JSON_NODE_TYPE = "com.fasterxml.jackson.databind.JsonNode"; @@ -119,9 +120,9 @@ public static String getSchemaTypeFor(TypeRef typeRef) { * @return The schema. */ protected T internalFrom(TypeDef definition, String... ignore) { - List schemaSwaps = new ArrayList<>(); + InternalSchemaSwaps schemaSwaps = new InternalSchemaSwaps(); T ret = internalFromImpl(definition, new HashSet<>(), schemaSwaps, ignore); - validateRemainingSchemaSwaps("unmatched class", schemaSwaps); + validateRemainingSchemaSwaps(schemaSwaps); return ret; } @@ -187,25 +188,53 @@ private static ClassRef extractClassRef(Object type) { } } - private InternalSchemaSwap extractSchemaSwap(AnnotationRef annotation) { - Map params = annotation.getParameters(); - return new InternalSchemaSwap( - extractClassRef(params.get("originalType")), - (String) params.get("fieldName"), - extractClassRef(params.get("targetType"))); + private void extractSchemaSwaps(ClassRef definitionType, AnnotationRef annotation, InternalSchemaSwaps schemaSwaps) { + String fullyQualifiedName = annotation.getClassRef().getFullyQualifiedName(); + switch (fullyQualifiedName) { + case ANNOTATION_SCHEMA_SWAP: + extractSchemaSwap(definitionType, annotation, schemaSwaps); + break; + case ANNOTATION_SCHEMA_SWAPS: + Map params = annotation.getParameters(); + Object[] values = (Object[]) params.get("value"); + for (Object value : values) { + extractSchemaSwap(definitionType, value, schemaSwaps); + } + break; + } + } + + private void extractSchemaSwap(ClassRef definitionType, Object annotation, InternalSchemaSwaps schemaSwaps) { + if (annotation instanceof SchemaSwap) { + SchemaSwap schemaSwap = (SchemaSwap) annotation; + schemaSwaps.registerSwap(definitionType, + extractClassRef(schemaSwap.originalType()), + schemaSwap.fieldName(), + extractClassRef(schemaSwap.targetType())); + + } else if (annotation instanceof AnnotationRef + && ((AnnotationRef) annotation).getClassRef().getFullyQualifiedName().equals(ANNOTATION_SCHEMA_SWAP)) { + Map params = ((AnnotationRef) annotation).getParameters(); + schemaSwaps.registerSwap(definitionType, + extractClassRef(params.get("originalType")), + (String) params.get("fieldName"), + extractClassRef(params.get("targetType"))); + + } else { + throw new IllegalArgumentException("Unmanaged annotation type passed to the SchemaSwaps: " + annotation); + } } - private void validateRemainingSchemaSwaps(String error, List schemaSwaps) { - if (!schemaSwaps.isEmpty()) { - String umatchedSchemaSwaps = schemaSwaps - .stream() - .map(InternalSchemaSwap::toString) - .collect(Collectors.joining(",", "[", "]")); - throw new IllegalArgumentException("SchemaSwap annotation error " + error + ": " + umatchedSchemaSwaps); + private void validateRemainingSchemaSwaps(InternalSchemaSwaps schemaSwaps) { + String unmatchedSchemaSwaps = schemaSwaps.getUnusedSwaps() + .map(Object::toString) + .collect(Collectors.joining(",")); + if (!unmatchedSchemaSwaps.isEmpty()) { + throw new IllegalArgumentException("Unmatched SchemaSwaps: " + unmatchedSchemaSwaps); } } - private T internalFromImpl(TypeDef definition, Set visited, List schemaSwaps, String... ignore) { + private T internalFromImpl(TypeDef definition, Set visited, InternalSchemaSwaps schemaSwaps, String... ignore) { final B builder = newBuilder(); Set ignores = ignore.length > 0 ? new LinkedHashSet<>(Arrays.asList(ignore)) : Collections @@ -215,19 +244,7 @@ private T internalFromImpl(TypeDef definition, Set visited, List newSchemaSwaps = definition - .getAnnotations() - .stream() - .filter(a -> a.getClassRef().getFullyQualifiedName().equals(ANNOTATION_SCHEMA_SWAP)) - .map(this::extractSchemaSwap) - .collect(Collectors.toList()); - - schemaSwaps.addAll(newSchemaSwaps); - - final Set currentSchemaSwaps = schemaSwaps - .stream() - .filter(iss -> iss.getOriginalType().getFullyQualifiedName().equals(definition.getFullyQualifiedName())) - .collect(Collectors.toSet()); + definition.getAnnotations().forEach(annotation -> extractSchemaSwaps(definition.toReference(), annotation, schemaSwaps)); // index potential accessors by name for faster lookup final Map accessors = indexPotentialAccessors(definition); @@ -239,11 +256,9 @@ private T internalFromImpl(TypeDef definition, Set visited, List matchedSchemaSwaps = facade.getMatchedSchemaSwaps(); - currentSchemaSwaps.removeAll(matchedSchemaSwaps); - schemaSwaps.removeAll(matchedSchemaSwaps); name = possiblyRenamedProperty.getName(); if (facade.required) { @@ -267,7 +282,6 @@ private T internalFromImpl(TypeDef definition, Set visited, List propertyOrAccessors = new ArrayList<>(4); - private final Set schemaSwaps; - private final Set matchedSchemaSwaps; private String renamedTo; private String description; private boolean required; @@ -395,10 +407,8 @@ private static class PropertyFacade { private String descriptionContributedBy; private TypeRef schemaFrom; - public PropertyFacade(Property property, Map potentialAccessors, Set schemaSwaps) { + public PropertyFacade(Property property, Map potentialAccessors, ClassRef schemaSwap) { original = property; - this.schemaSwaps = schemaSwaps; - this.matchedSchemaSwaps = new HashSet<>(); final String capitalized = property.getNameCapitalized(); final String name = property.getName(); propertyOrAccessors.add(PropertyOrAccessor.fromProperty(property)); @@ -414,21 +424,12 @@ public PropertyFacade(Property property, Map potentialAccessors, if (method != null) { propertyOrAccessors.add(PropertyOrAccessor.fromMethod(method, name)); } + schemaFrom = schemaSwap; } public Property process() { final String name = original.getName(); - Optional currentSchemaSwap = schemaSwaps - .stream() - .filter(iss -> iss.getFieldName().equals(name)) - .findFirst(); - - currentSchemaSwap.ifPresent(iss -> { - schemaFrom = iss.targetType; - matchedSchemaSwaps.add(iss); - }); - propertyOrAccessors.forEach(p -> { p.process(); final String contributorName = p.toString(); @@ -471,10 +472,6 @@ public Property process() { return new Property(original.getAnnotations(), typeRef, finalName, original.getComments(), original.getModifiers(), original.getAttributes()); } - - public Set getMatchedSchemaSwaps() { - return this.matchedSchemaSwaps; - } } private boolean isPotentialAccessor(Method method) { @@ -547,10 +544,10 @@ private String extractUpdatedNameFromJacksonPropertyIfPresent(Property property) * @return the structural schema associated with the specified property */ public T internalFrom(String name, TypeRef typeRef) { - return internalFromImpl(name, typeRef, new HashSet<>(), new ArrayList<>()); + return internalFromImpl(name, typeRef, new HashSet<>(), new InternalSchemaSwaps()); } - private T internalFromImpl(String name, TypeRef typeRef, Set visited, List schemaSwaps) { + private T internalFromImpl(String name, TypeRef typeRef, Set visited, InternalSchemaSwaps schemaSwaps) { // Note that ordering of the checks here is meaningful: we need to check for complex types last // in case some "complex" types are handled specifically if (typeRef.getDimensions() > 0 || io.sundr.model.utils.Collections.isCollection(typeRef)) { // Handle Collections & Arrays @@ -611,7 +608,7 @@ private T internalFromImpl(String name, TypeRef typeRef, Set visited, Li // Flag to detect cycles private boolean resolving = false; - private T resolveNestedClass(String name, TypeDef def, Set visited, List schemaSwaps) { + private T resolveNestedClass(String name, TypeDef def, Set visited, InternalSchemaSwaps schemaSwaps) { if (!resolving) { visited.clear(); resolving = true; diff --git a/crd-generator/api/src/main/java/io/fabric8/crd/generator/InternalSchemaSwaps.java b/crd-generator/api/src/main/java/io/fabric8/crd/generator/InternalSchemaSwaps.java new file mode 100644 index 00000000000..de8abd3bce3 --- /dev/null +++ b/crd-generator/api/src/main/java/io/fabric8/crd/generator/InternalSchemaSwaps.java @@ -0,0 +1,133 @@ +/** + * 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; + +import io.sundr.model.ClassRef; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.StringJoiner; +import java.util.stream.Stream; + +public class InternalSchemaSwaps { + private final Map swaps = new HashMap<>(); + + public void registerSwap(ClassRef definitionType, ClassRef originalType, String fieldName, ClassRef targetType) { + Value value = new Value(definitionType, originalType, fieldName, targetType); + swaps.put(new Key(originalType, fieldName), value); + } + + public Stream getUnusedSwaps() { + return swaps.values().stream().filter(value -> !value.used); + } + + public Optional lookupAndMark(ClassRef originalType, String name) { + Value value = swaps.get(new Key(originalType, name)); + if (value != null) { + value.markUsed(); + return Optional.of(value.getTargetType()); + } else { + return Optional.empty(); + } + } + + private static class Key { + private final ClassRef originalType; + private final String fieldName; + + public Key(ClassRef originalType, String fieldName) { + this.originalType = originalType; + this.fieldName = fieldName; + } + + public ClassRef getOriginalType() { + return originalType; + } + + public String getFieldName() { + return fieldName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Key key = (Key) o; + return Objects.equals(originalType, key.originalType) && Objects.equals(fieldName, key.fieldName); + } + + @Override + public int hashCode() { + return Objects.hash(originalType, fieldName); + } + + @Override + public String toString() { + return new StringJoiner(", ", Key.class.getSimpleName() + "[", "]") + .add("originalType=" + originalType) + .add("fieldName='" + fieldName + "'") + .toString(); + } + } + + public static class Value { + private final ClassRef originalType; + private final String fieldName; + private final ClassRef targetType; + private boolean used; + private final ClassRef definitionType; + + public Value(ClassRef definitionType, ClassRef originalType, String fieldName, ClassRef targetType) { + this.definitionType = definitionType; + this.originalType = originalType; + this.fieldName = fieldName; + this.targetType = targetType; + this.used = false; + } + + private void markUsed() { + this.used = true; + } + + public ClassRef getOriginalType() { + return originalType; + } + + public String getFieldName() { + return fieldName; + } + + public ClassRef getTargetType() { + return targetType; + } + + public boolean isUsed() { + return used; + } + + @Override + public String toString() { + return "@SchemaSwap(originalType=" + originalType + ", fieldName=\"" + fieldName + "\", targetType=" + targetType + + ") on " + definitionType; + } + } +} diff --git a/crd-generator/api/src/test/java/io/fabric8/crd/example/extraction/MultipleSchemaSwaps.java b/crd-generator/api/src/test/java/io/fabric8/crd/example/extraction/MultipleSchemaSwaps.java new file mode 100644 index 00000000000..88a4e09e653 --- /dev/null +++ b/crd-generator/api/src/test/java/io/fabric8/crd/example/extraction/MultipleSchemaSwaps.java @@ -0,0 +1,25 @@ +/** + * 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.example.extraction; + +import io.fabric8.crd.generator.annotation.SchemaSwap; +import io.fabric8.kubernetes.client.CustomResource; + +@SchemaSwap(originalType = SchemaSwapSpec.SomeObject.class, fieldName = "shouldBeString", targetType = String.class) +@SchemaSwap(originalType = SchemaSwapSpec.AnotherObject.class, fieldName = "shouldBeInt", targetType = Integer.class) +public class MultipleSchemaSwaps extends CustomResource { + +} diff --git a/crd-generator/api/src/test/java/io/fabric8/crd/example/extraction/SchemaSwapSpec.java b/crd-generator/api/src/test/java/io/fabric8/crd/example/extraction/SchemaSwapSpec.java new file mode 100644 index 00000000000..2f98fa315bb --- /dev/null +++ b/crd-generator/api/src/test/java/io/fabric8/crd/example/extraction/SchemaSwapSpec.java @@ -0,0 +1,30 @@ +/** + * 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.example.extraction; + +public class SchemaSwapSpec { + private SomeObject first; + private SomeObject second; + private AnotherObject third; + + static class SomeObject { + private int shouldBeString; + } + + static class AnotherObject { + private String shouldBeInt; + } +} diff --git a/crd-generator/api/src/test/java/io/fabric8/crd/generator/v1/JsonSchemaTest.java b/crd-generator/api/src/test/java/io/fabric8/crd/generator/v1/JsonSchemaTest.java index a0715d3cd44..da32ce91ff5 100644 --- a/crd-generator/api/src/test/java/io/fabric8/crd/generator/v1/JsonSchemaTest.java +++ b/crd-generator/api/src/test/java/io/fabric8/crd/generator/v1/JsonSchemaTest.java @@ -18,10 +18,11 @@ import com.fasterxml.jackson.databind.JsonNode; import io.fabric8.crd.example.annotated.Annotated; import io.fabric8.crd.example.basic.Basic; +import io.fabric8.crd.example.extraction.Extraction; import io.fabric8.crd.example.extraction.IncorrectExtraction; import io.fabric8.crd.example.extraction.IncorrectExtraction2; +import io.fabric8.crd.example.extraction.MultipleSchemaSwaps; import io.fabric8.crd.example.json.ContainingJson; -import io.fabric8.crd.example.extraction.Extraction; import io.fabric8.crd.example.person.Person; import io.fabric8.crd.generator.utils.Types; import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps; @@ -44,16 +45,16 @@ void shouldCreateJsonSchemaFromClass() { Map properties = schema.getProperties(); assertEquals(7, properties.size()); final List personTypes = properties.get("type").getEnum().stream().map(JsonNode::asText) - .collect(Collectors.toList()); + .collect(Collectors.toList()); assertEquals(2, personTypes.size()); assertTrue(personTypes.contains("crazy")); assertTrue(personTypes.contains("crazier")); final Map addressProperties = properties.get("addresses").getItems() - .getSchema().getProperties(); + .getSchema().getProperties(); assertEquals(5, addressProperties.size()); final List addressTypes = addressProperties.get("type").getEnum().stream() - .map(JsonNode::asText) - .collect(Collectors.toList()); + .map(JsonNode::asText) + .collect(Collectors.toList()); assertEquals(2, addressTypes.size()); assertTrue(addressTypes.contains("home")); assertTrue(addressTypes.contains("work")); @@ -178,15 +179,58 @@ void shouldExtractPropertiesSchemaFromExtractValueAnnotation() { assertNull(barProps.get("baz")); } + @Test + void shouldExtractPropertiesSchemaFromSchemaSwapAnnotations() { + TypeDef extraction = Types.typeDefFrom(MultipleSchemaSwaps.class); + JSONSchemaProps schema = JsonSchema.from(extraction); + assertNotNull(schema); + Map properties = schema.getProperties(); + assertEquals(2, properties.size()); + final JSONSchemaProps specSchema = properties.get("spec"); + Map spec = specSchema.getProperties(); + assertEquals(3, spec.size()); + + // 'first' is replaced by SchemaSwap from int to string + JSONSchemaProps first = spec.get("first"); + Map firstProps = first.getProperties(); + assertNotNull(firstProps); + JSONSchemaProps firstProperty = firstProps.get("shouldBeString"); + assertEquals("string", firstProperty.getType()); + + // 'second' is replaced by the same SchemaSwap that is applied multiple times + JSONSchemaProps second = spec.get("second"); + Map secondProps = second.getProperties(); + assertNotNull(secondProps); + JSONSchemaProps secondProperty = secondProps.get("shouldBeString"); + assertEquals("string", secondProperty.getType()); + + // 'third' is replaced by the another SchemaSwap + JSONSchemaProps third = spec.get("third"); + Map thirdProps = third.getProperties(); + assertNotNull(thirdProps); + JSONSchemaProps thirdProperty = thirdProps.get("shouldBeInt"); + assertEquals("integer", thirdProperty.getType()); + } + @Test void shouldThrowIfSchemaSwapHasUnmatchedField() { TypeDef incorrectExtraction = Types.typeDefFrom(IncorrectExtraction.class); - assertThrows(IllegalArgumentException.class, () -> JsonSchema.from(incorrectExtraction)); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> JsonSchema.from(incorrectExtraction)); + assertEquals( + "Unmatched SchemaSwaps: @SchemaSwap(originalType=io.fabric8.crd.example.extraction.ExtractionSpec, fieldName=\"FOO\", targetType=io" + + ".fabric8.crd.example.extraction.FooExtractor) on io.fabric8.crd.example.extraction.IncorrectExtraction", + exception.getMessage()); } @Test void shouldThrowIfSchemaSwapHasUnmatchedClass() { TypeDef incorrectExtraction2 = Types.typeDefFrom(IncorrectExtraction2.class); - assertThrows(IllegalArgumentException.class, () -> JsonSchema.from(incorrectExtraction2)); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> JsonSchema.from(incorrectExtraction2)); + assertEquals( + "Unmatched SchemaSwaps: @SchemaSwap(originalType=io.fabric8.crd.example.basic.BasicSpec, fieldName=\"bar\", targetType=io.fabric8.crd" + + ".example.extraction.FooExtractor) on io.fabric8.crd.example.extraction.IncorrectExtraction2", + exception.getMessage()); } } diff --git a/crd-generator/test/src/test/java/io/fabric8/crd/generator/schemaswaps/MultipleSchemaSwaps.java b/crd-generator/test/src/test/java/io/fabric8/crd/generator/schemaswaps/MultipleSchemaSwaps.java new file mode 100644 index 00000000000..81bc6c21849 --- /dev/null +++ b/crd-generator/test/src/test/java/io/fabric8/crd/generator/schemaswaps/MultipleSchemaSwaps.java @@ -0,0 +1,29 @@ +/** + * 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.schemaswaps; + +import io.fabric8.crd.generator.annotation.SchemaSwap; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.Version; + +@Version("v1") +@Group("acme.com") +@SchemaSwap(originalType = SchemaSwapSpec.SomeObject.class, fieldName = "shouldBeString", targetType = String.class) +@SchemaSwap(originalType = SchemaSwapSpec.AnotherObject.class, fieldName = "shouldBeInt", targetType = Integer.class) +public class MultipleSchemaSwaps extends CustomResource { + +} diff --git a/crd-generator/test/src/test/java/io/fabric8/crd/generator/schemaswaps/SchemaSwapCRDTest.java b/crd-generator/test/src/test/java/io/fabric8/crd/generator/schemaswaps/SchemaSwapCRDTest.java new file mode 100644 index 00000000000..a17153d3938 --- /dev/null +++ b/crd-generator/test/src/test/java/io/fabric8/crd/generator/schemaswaps/SchemaSwapCRDTest.java @@ -0,0 +1,69 @@ +/** + * 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.schemaswaps; + +import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition; +import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinitionVersion; +import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class SchemaSwapCRDTest { + + @Test + void testCrd() { + CustomResourceDefinition d = Serialization.unmarshal(getClass().getClassLoader() + .getResourceAsStream("META-INF/fabric8/multipleschemaswaps.acme.com-v1.yml"), + CustomResourceDefinition.class); + assertNotNull(d); + + CustomResourceDefinitionVersion v1 = d.getSpec().getVersions().get(0); + assertNotNull(v1); + assertEquals("v1", v1.getName()); + Map spec = v1.getSchema().getOpenAPIV3Schema().getProperties().get("spec").getProperties(); + assertNotNull(spec); + + // 'first' is replaced by SchemaSwap from int to string + JSONSchemaProps first = spec.get("first"); + Map firstProps = first.getProperties(); + assertNotNull(firstProps); + JSONSchemaProps firstProperty = firstProps.get("shouldBeString"); + assertEquals("string", firstProperty.getType()); + + // 'second' is replaced by the same SchemaSwap that is applied multiple times + JSONSchemaProps second = spec.get("second"); + Map secondProps = second.getProperties(); + assertNotNull(secondProps); + JSONSchemaProps secondProperty = secondProps.get("shouldBeString"); + assertEquals("string", secondProperty.getType()); + + // 'third' is replaced by the another SchemaSwap + JSONSchemaProps third = spec.get("third"); + Map thirdProps = third.getProperties(); + assertNotNull(thirdProps); + JSONSchemaProps thirdProperty = thirdProps.get("shouldBeInt"); + assertEquals("integer", thirdProperty.getType()); + } + +} diff --git a/crd-generator/test/src/test/java/io/fabric8/crd/generator/schemaswaps/SchemaSwapSpec.java b/crd-generator/test/src/test/java/io/fabric8/crd/generator/schemaswaps/SchemaSwapSpec.java new file mode 100644 index 00000000000..80dfab67d59 --- /dev/null +++ b/crd-generator/test/src/test/java/io/fabric8/crd/generator/schemaswaps/SchemaSwapSpec.java @@ -0,0 +1,30 @@ +/** + * 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.schemaswaps; + +public class SchemaSwapSpec { + private SomeObject first; + private SomeObject second; + private AnotherObject third; + + static class SomeObject { + private int shouldBeString; + } + + static class AnotherObject { + private String shouldBeInt; + } +}