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 00811da80b2..cb3429160cc 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 @@ -15,31 +15,47 @@ */ package io.fabric8.crd.generator; +import static io.sundr.model.utils.Types.BOOLEAN_REF; +import static io.sundr.model.utils.Types.DOUBLE_REF; +import static io.sundr.model.utils.Types.INT_REF; +import static io.sundr.model.utils.Types.LONG_REF; +import static io.sundr.model.utils.Types.STRING_REF; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import io.fabric8.crd.generator.annotation.SchemaSwap; + +import io.fabric8.crd.generator.utils.Properties; import io.fabric8.crd.generator.utils.Types; import io.fabric8.kubernetes.api.model.Duration; import io.fabric8.kubernetes.api.model.IntOrString; import io.fabric8.kubernetes.api.model.Quantity; import io.sundr.builder.internal.functions.TypeAs; -import io.sundr.model.*; +import io.sundr.model.AnnotationRef; +import io.sundr.model.ClassRef; +import io.sundr.model.Method; +import io.sundr.model.PrimitiveRefBuilder; +import io.sundr.model.Property; +import io.sundr.model.TypeDef; +import io.sundr.model.TypeRef; import io.sundr.utils.Strings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; -import java.util.stream.Collectors; - -import static io.sundr.model.utils.Types.BOOLEAN_REF; - -import static io.sundr.model.utils.Types.STRING_REF; - -import static io.sundr.model.utils.Types.INT_REF; - -import static io.sundr.model.utils.Types.LONG_REF; - -import static io.sundr.model.utils.Types.DOUBLE_REF; /** * Encapsulates the common logic supporting OpenAPI schema generation for CRD generation. @@ -237,7 +253,8 @@ private T internalFromImpl(TypeDef definition, Set visited, List accessors = indexPotentialAccessors(definition); - for (Property property : definition.getProperties()) { + List visibleProperties = Properties.getVisibleProperties(definition); + for (Property property : visibleProperties) { String name = property.getName(); if (property.isStatic() || ignores.contains(name)) { LOGGER.debug("Ignoring property {}", name); @@ -485,6 +502,9 @@ public Set getMatchedSchemaSwaps() { private boolean isPotentialAccessor(Method method) { final String name = method.getName(); + if (!method.isPublic() || method.isStatic() || method.isTransient()) { + return false; + } return name.startsWith("is") || name.startsWith("get") || name.startsWith("set"); } @@ -589,7 +609,7 @@ private T internalFromImpl(String name, TypeRef typeRef, Set visited, Li // check if we're dealing with an enum if (def.isEnum()) { - final JsonNode[] enumValues = def.getProperties().stream() + final JsonNode[] enumValues = Properties.getVisibleProperties(def).stream() .map(this::extractUpdatedNameFromJacksonPropertyIfPresent) .filter(n -> !n.startsWith("$")) .map(JsonNodeFactory.instance::textNode) diff --git a/crd-generator/api/src/main/java/io/fabric8/crd/generator/utils/Properties.java b/crd-generator/api/src/main/java/io/fabric8/crd/generator/utils/Properties.java new file mode 100644 index 00000000000..cc7ef348b9d --- /dev/null +++ b/crd-generator/api/src/main/java/io/fabric8/crd/generator/utils/Properties.java @@ -0,0 +1,110 @@ +package io.fabric8.crd.generator.utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import io.sundr.model.AnnotationRef; +import io.sundr.model.Method; +import io.sundr.model.Property; +import io.sundr.model.PropertyBuilder; +import io.sundr.model.TypeDef; + +// TODO: We should use the visibility settings from the object mapper that is being used. +public class Properties { + // Get a list of visible properties. + // Returns public fields as well as private fields that are accessible + // through get or is methods + public static List getVisibleProperties(TypeDef def) { + List props = new ArrayList<>(); + if (def.getFullyQualifiedName().startsWith("java.")) { + return props; + } + + // Get public properties + for (Property p : def.getProperties()) { + if (!p.isPublic() || p.isTransient()) { + continue; + } + props.add(p); + } + + // Get getters + for (Method m : def.getMethods()) { + if ( + !m.isPublic() || m.isTransient() || m.isStatic() || + m.getName().equals("getClass") || + !(m.getName().startsWith("get") || m.getName().startsWith("is")) + ) { + continue; + } + + String propName = propertyNameForMethod(m); + // Find the property for the getter. + boolean found = false; + for (Property p : def.getProperties()) { + if (p.getName().equals(propName)) { + + List mergedAnnotations = Stream.concat( + p.getAnnotations().stream(), + m.getAnnotations().stream() + ) + .collect(Collectors.toList()); + + Property newProp = new PropertyBuilder(p).withAnnotations(mergedAnnotations).build(); + props.add(newProp); + + found = true; + break; + } + } + + if (!found) { + props.add(propertyFromMethod(m)); + } + } + + return props; + } + + private static Property propertyFromMethod(Method m) { + return new PropertyBuilder() + .withName(propertyNameForMethod(m)) + .withTypeRef(m.getReturnType()) + .withAnnotations(m.getAnnotations()) + .withModifiers(m.getModifiers()) + .withAttributes(m.getAttributes()) + .withComments(m.getComments()) + .build(); + } + + private static String propertyNameForMethod(Method method) { + String name = method.getName(); + if (name.equals("get") || name.equals("is")) { + return name; + } + + if (name.startsWith("get")) { + return lowerFirstLetter(removePrefix(name, "get")); + } else if (name.startsWith("is")) { + return lowerFirstLetter(removePrefix(name, "is")); + } + return name; + } + + private static String removePrefix(String text, String prefix) { + if (text.startsWith(prefix)) { + return text.substring(prefix.length()); + } + return text; + } + + private static String lowerFirstLetter(String text) { + if (text.isEmpty()) { + return text; + } + + return Character.toLowerCase(text.charAt(0)) + text.substring(1); + } +} diff --git a/crd-generator/api/src/main/java/io/fabric8/crd/generator/utils/Types.java b/crd-generator/api/src/main/java/io/fabric8/crd/generator/utils/Types.java index a4f9498f323..61538f9828a 100644 --- a/crd-generator/api/src/main/java/io/fabric8/crd/generator/utils/Types.java +++ b/crd-generator/api/src/main/java/io/fabric8/crd/generator/utils/Types.java @@ -15,23 +15,40 @@ */ package io.fabric8.crd.generator.utils; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.fabric8.kubernetes.api.model.Namespaced; import io.fabric8.kubernetes.client.CustomResource; import io.sundr.adapter.api.AdapterContext; import io.sundr.adapter.api.Adapters; -import io.sundr.adapter.apt.AptAdapter; -import io.sundr.adapter.apt.AptContext; import io.sundr.builder.TypedVisitor; -import io.sundr.model.*; +import io.sundr.model.ClassRef; +import io.sundr.model.ClassRefBuilder; +import io.sundr.model.Method; +import io.sundr.model.PrimitiveRef; +import io.sundr.model.Property; +import io.sundr.model.PropertyBuilder; +import io.sundr.model.TypeDef; +import io.sundr.model.TypeDefBuilder; +import io.sundr.model.TypeParamDef; +import io.sundr.model.TypeParamRef; +import io.sundr.model.TypeRef; +import io.sundr.model.VoidRef; +import io.sundr.model.WildcardRef; import io.sundr.model.functions.GetDefinition; import io.sundr.model.repo.DefinitionRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; public class Types { @@ -60,11 +77,12 @@ public static TypeDef unshallow(TypeDef definition) { final List classRefs = new ArrayList<>(Types.projectSuperClasses(definition)); // resolve properties final List properties = Types.projectProperties(definition); + final List methods = Types.projectMethods(definition); // re-create TypeDef with all the needed information return new TypeDef(definition.getKind(), definition.getPackageName(), definition.getName(), definition.getComments(), definition.getAnnotations(), classRefs, definition.getImplementsList(), definition.getParameters(), properties, - definition.getConstructors(), definition.getMethods(), definition.getOuterTypeName(), + definition.getConstructors(), methods, definition.getOuterTypeName(), definition.getInnerTypes(), definition.getModifiers(), definition.getAttributes()); } @@ -159,23 +177,30 @@ private static Set projectSuperClasses(TypeDef definition) { private static List projectProperties(TypeDef typeDef) { final String fqn = typeDef.getFullyQualifiedName(); return Stream.concat( - typeDef.getProperties().stream().filter(p -> { - // enums have self-referential static properties for each enum case so we cannot ignore them - if (typeDef.isEnum()) { - final TypeRef typeRef = p.getTypeRef(); - if (typeRef instanceof ClassRef && fqn.equals(((ClassRef) typeRef).getFullyQualifiedName())) { - // we're dealing with an enum case so keep it - return true; + typeDef.getProperties() + .stream() + .filter(p -> { + if (p.isTransient()) { + return false; } - } - // otherwise exclude all static properties - return !p.isStatic(); - }), + + // enums have self-referential static properties for each enum case so we cannot ignore them + if (typeDef.isEnum()) { + final TypeRef typeRef = p.getTypeRef(); + if (typeRef instanceof ClassRef && fqn.equals(((ClassRef) typeRef).getFullyQualifiedName())) { + // we're dealing with an enum case so keep it + return true; + } + } + // otherwise exclude all static and transient properties + return !p.isStatic(); + }), typeDef.getExtendsList().stream() .filter(e -> !e.getFullyQualifiedName().startsWith("java.")) .flatMap(e -> projectProperties(projectDefinition(e)) .stream() - .filter(p -> filterCustomResourceProperties(e).test(p))) + .filter(p -> filterCustomResourceProperties(e).test(p)) + ) ) .collect(Collectors.toList()); @@ -188,6 +213,49 @@ private static Predicate filterCustomResourceProperties(ClassRef ref) (p.getName().equals("spec") || p.getName().equals("status"))); } + /** + * All non-static methods (including inherited). + * + * @param typeDef The type. + * @return A list with all methods. + */ + private static List projectMethods(TypeDef typeDef) { + final String fqn = typeDef.getFullyQualifiedName(); + return Stream.concat( + typeDef.getMethods() + .stream() + .filter(m -> { + if (m.isTransient()) { + return false; + } + // enums have self-referential static properties for each enum case so we cannot ignore them + if (typeDef.isEnum()) { + TypeRef typeRef = m.getReturnType(); + if (typeRef instanceof ClassRef && fqn.equals(((ClassRef) typeRef).getFullyQualifiedName())) { + // we're dealing with an enum case so keep it + return true; + } + } + // otherwise exclude all static properties + return !m.isStatic(); + }), + typeDef.getExtendsList() + .stream() + .filter(e -> !e.getFullyQualifiedName().startsWith("java.")) + .flatMap(e -> projectMethods(projectDefinition(e)) + .stream() + .filter(m -> filterCustomResourceMethods(e).test(m)) + ) + ) + .collect(Collectors.toList()); + } + + private static Predicate filterCustomResourceMethods(ClassRef ref) { + return m -> !m.isStatic() && + (!ref.getFullyQualifiedName().equals(CUSTOM_RESOURCE_NAME) || + (m.getName().equals("getSpec") || m.getName().equals("getStatus"))); + } + public static void output(TypeDef def) { final StringBuilder builder = new StringBuilder(300); @@ -201,7 +269,7 @@ public static void describeType(TypeDef def, String indent, Set visited, } visited.add(def.getFullyQualifiedName()); - for (Property property : def.getProperties()) { + for (Property property : Properties.getVisibleProperties(def)) { TypeRef typeRef = property.getTypeRef(); if (typeRef instanceof ClassRef) { final TypeDef typeDef = typeDefFrom((ClassRef) typeRef); @@ -308,7 +376,7 @@ public static boolean isNamespaced(TypeDef definition, Set visited) { * @return the an optional property. */ public static Optional findStatusProperty(TypeDef typeDef) { - return typeDef.getProperties().stream() + return Properties.getVisibleProperties(typeDef).stream() .filter(Types::isStatusProperty) .findFirst(); } diff --git a/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/AnnotatedMultiPropertyPathDetector.java b/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/AnnotatedMultiPropertyPathDetector.java index b821017f703..b3a22fefb70 100644 --- a/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/AnnotatedMultiPropertyPathDetector.java +++ b/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/AnnotatedMultiPropertyPathDetector.java @@ -15,6 +15,7 @@ */ package io.fabric8.crd.generator.visitor; +import io.fabric8.crd.generator.utils.Properties; import io.fabric8.crd.generator.utils.Types; import io.sundr.builder.TypedVisitor; import io.sundr.model.ClassRef; @@ -56,7 +57,7 @@ public AnnotatedMultiPropertyPathDetector(String prefix, String annotationName, @Override public void visit(TypeDefBuilder builder) { TypeDef type = builder.build(); - final List props = type.getProperties(); + final List props = Properties.getVisibleProperties(type); for (Property p : props) { if (parents.contains(p)) { continue; @@ -75,7 +76,7 @@ public void visit(TypeDefBuilder builder) { if (!parents.contains(p)) { ClassRef classRef = (ClassRef) p.getTypeRef(); TypeDef propertyType = Types.typeDefFrom(classRef); - if (!propertyType.isEnum()) { + if (!(propertyType.isEnum() || propertyType.getFullyQualifiedName().startsWith("java."))) { List newParents = new ArrayList<>(parents); newParents.add(p); new TypeDefBuilder(propertyType) diff --git a/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/AnnotatedPropertyPathDetector.java b/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/AnnotatedPropertyPathDetector.java index 4ddfb7b3e5b..84d51090b6e 100644 --- a/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/AnnotatedPropertyPathDetector.java +++ b/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/AnnotatedPropertyPathDetector.java @@ -15,17 +15,22 @@ */ package io.fabric8.crd.generator.visitor; +import static io.fabric8.crd.generator.AbstractJsonSchema.ANNOTATION_JSON_IGNORE; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import io.fabric8.crd.generator.utils.Properties; import io.fabric8.crd.generator.utils.Types; import io.sundr.builder.TypedVisitor; +import io.sundr.model.AnnotationRef; import io.sundr.model.ClassRef; import io.sundr.model.Property; import io.sundr.model.TypeDef; import io.sundr.model.TypeDefBuilder; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; public class AnnotatedPropertyPathDetector extends TypedVisitor { @@ -45,46 +50,69 @@ public AnnotatedPropertyPathDetector(String prefix, String annotationName, List< this(prefix, annotationName, parents, new AtomicReference<>(Optional.empty())); } - public AnnotatedPropertyPathDetector(String prefix, String annotationName, List parents, AtomicReference> reference) { + public AnnotatedPropertyPathDetector(String prefix, String annotationName, List parents, + AtomicReference> reference) { this.prefix = prefix; this.annotationName = annotationName; this.parents = parents; this.reference = reference; } + private static boolean excludePropertyProcessing(Property p) { + for (AnnotationRef annotation : p.getAnnotations()) { + if (annotation.getClassRef().getFullyQualifiedName().equals(ANNOTATION_JSON_IGNORE)) { + return true; + } + } + return false; + } + @Override public void visit(TypeDefBuilder builder) { TypeDef type = builder.build(); - final List properties = type.getProperties(); + final List properties = Properties.getVisibleProperties(type); + if (visitProperties(properties)) { + return; + } + visitPropertiesClasses(properties); + } + + private void visitPropertiesClasses(List properties) { for (Property p : properties) { - if (parents.contains(p)) { - continue; + if (!(p.getTypeRef() instanceof ClassRef)) { + continue; + } + if (!parents.contains(p) && !excludePropertyProcessing(p)) { + ClassRef classRef = (ClassRef) p.getTypeRef(); + TypeDef propertyType = Types.typeDefFrom(classRef); + if (!(propertyType.isEnum() || propertyType.getFullyQualifiedName().startsWith("java."))) { + List newParents = new ArrayList<>(parents); + newParents.add(p); + new TypeDefBuilder(propertyType) + .accept(new AnnotatedPropertyPathDetector(prefix, annotationName, newParents, reference)) + .build(); } + } + } + } + + private boolean visitProperties(List properties) { + for (Property p : properties) { + if (parents.contains(p)) { + continue; + } - List newParents = new ArrayList<>(parents); - boolean match = p.getAnnotations().stream().anyMatch(a -> a.getClassRef().getName().equals(annotationName)); + List newParents = new ArrayList<>(parents); + for (AnnotationRef annotation : p.getAnnotations()) { + boolean match = annotation.getClassRef().getName().equals(annotationName); if (match) { newParents.add(p); reference.set(Optional.of(newParents.stream().map(Property::getName).collect(Collectors.joining(DOT, prefix, "")))); - return; + return true; } + } } - - properties.stream() - .filter(p -> p.getTypeRef() instanceof ClassRef) - .forEach(p -> { - if (!parents.contains(p)) { - ClassRef classRef = (ClassRef) p.getTypeRef(); - TypeDef propertyType = Types.typeDefFrom(classRef); - if (!propertyType.isEnum()) { - List newParents = new ArrayList<>(parents); - newParents.add(p); - new TypeDefBuilder(propertyType) - .accept(new AnnotatedPropertyPathDetector(prefix, annotationName, newParents, reference)) - .build(); - } - } - }); + return false; } public Optional getPath() { diff --git a/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/ClassDependenciesVisitor.java b/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/ClassDependenciesVisitor.java index 93b733f885e..54931483961 100644 --- a/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/ClassDependenciesVisitor.java +++ b/crd-generator/api/src/main/java/io/fabric8/crd/generator/visitor/ClassDependenciesVisitor.java @@ -15,16 +15,25 @@ */ package io.fabric8.crd.generator.visitor; +import static io.sundr.model.Attributeable.ALSO_IMPORT; + import io.fabric8.crd.generator.utils.Types; import io.sundr.builder.TypedVisitor; +import io.sundr.model.AnnotationRef; import io.sundr.model.ClassRef; +import io.sundr.model.Method; +import io.sundr.model.Property; import io.sundr.model.TypeDef; import io.sundr.model.TypeDefBuilder; +import io.sundr.model.TypeParamDef; import io.sundr.model.TypeRef; import io.sundr.model.utils.Collections; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -54,7 +63,7 @@ public void visit(TypeDefBuilder builder) { } // process all references to see if they need to be added or not - type.getReferences().forEach(c -> { + getAccessibleReferences(type).forEach(c -> { final String fqn = c.getFullyQualifiedName(); if (ignore(fqn)) { return; @@ -104,4 +113,58 @@ public static Set getDependentClassesFromCRDName(String crdName) { .flatMap(crClassName -> traversedClasses.get(crClassName).stream()) .collect(Collectors.toSet()); } + + private static List getAccessibleReferences(TypeDef def) { + final List refs = new ArrayList<>(); + + for (AnnotationRef a : def.getAnnotations()) { + refs.addAll(a.getReferences()); + } + + for (ClassRef i : def.getImplementsList()) { + refs.addAll(i.getReferences()); + } + + for (ClassRef e : def.getExtendsList()) { + refs.addAll(e.getReferences()); + } + + for (Property property : def.getProperties()) { + refs.addAll(property.getReferences()); + } + + for (Method method : def.getConstructors()) { + if (method.isPublic()) { + refs.addAll(method.getReferences()); + } + } + + for (Method method : def.getMethods()) { + if (method.isPublic()) { + refs.addAll(method.getReferences()); + } + } + + for (TypeParamDef typeParamDef : def.getParameters()) { + for (ClassRef bound : typeParamDef.getBounds()) { + refs.addAll(bound.getReferences()); + } + } + + for (TypeDef innerType : def.getInnerTypes()) { + if (innerType.isPublic()) { + refs.addAll(innerType.getReferences()); + } + } + + if (def.getAttributes().containsKey(ALSO_IMPORT)) { + Object obj = def.getAttributes().get(ALSO_IMPORT); + if (obj instanceof ClassRef) { + refs.add((ClassRef) obj); + } else if (obj instanceof Collection) { + refs.addAll((Collection) obj); + } + } + return refs; + } } diff --git a/crd-generator/api/src/test/java/io/fabric8/crd/example/accessible/AccessibleClass.java b/crd-generator/api/src/test/java/io/fabric8/crd/example/accessible/AccessibleClass.java new file mode 100644 index 00000000000..8f8259fbd27 --- /dev/null +++ b/crd-generator/api/src/test/java/io/fabric8/crd/example/accessible/AccessibleClass.java @@ -0,0 +1,23 @@ +package io.fabric8.crd.example.accessible; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class AccessibleClass { + private int gettable; + private String invisible; + public String visible; + public transient volatile Foo foo = new Foo(); + + @JsonProperty("gettable") + public int getGettable() { + return gettable; + } + + public boolean isTruthy() { + return true; + } + + public static class Foo { + public String bar; + } +} diff --git a/crd-generator/api/src/test/java/io/fabric8/crd/example/annotated/AnnotatedSpec.java b/crd-generator/api/src/test/java/io/fabric8/crd/example/annotated/AnnotatedSpec.java index cfac63b53e8..d7a6b22f947 100644 --- a/crd-generator/api/src/test/java/io/fabric8/crd/example/annotated/AnnotatedSpec.java +++ b/crd-generator/api/src/test/java/io/fabric8/crd/example/annotated/AnnotatedSpec.java @@ -25,15 +25,15 @@ public class AnnotatedSpec { @JsonProperty("from-field") @JsonPropertyDescription("from-field-description") - private String field; + public String field; private int foo; @JsonProperty - private String unnamed; + public String unnamed; @NotNull private boolean emptySetter; - private AnnotatedEnum anEnum; + public AnnotatedEnum anEnum; @Min(0) // a non-string value attribute - private int sizedField; + public int sizedField; @JsonIgnore private int ignoredFoo; @@ -61,6 +61,10 @@ public void setEmptySetter(boolean emptySetter) { this.emptySetter = emptySetter; } + public boolean isEmptySetter() { + return emptySetter; + } + public enum AnnotatedEnum { non, @JsonProperty("oui") Yes } diff --git a/crd-generator/api/src/test/java/io/fabric8/crd/example/person/Address.java b/crd-generator/api/src/test/java/io/fabric8/crd/example/person/Address.java index 3f66e0027b0..50a2f3c27a3 100644 --- a/crd-generator/api/src/test/java/io/fabric8/crd/example/person/Address.java +++ b/crd-generator/api/src/test/java/io/fabric8/crd/example/person/Address.java @@ -16,11 +16,11 @@ package io.fabric8.crd.example.person; public class Address { - private String street; - private int number; - private String zip; - private String country; - private Type type; + public String street; + public int number; + public String zip; + public String country; + public Type type; public enum Type { home, work diff --git a/crd-generator/api/src/test/java/io/fabric8/crd/example/person/Person.java b/crd-generator/api/src/test/java/io/fabric8/crd/example/person/Person.java index 4b8f4510806..28b696eab8f 100644 --- a/crd-generator/api/src/test/java/io/fabric8/crd/example/person/Person.java +++ b/crd-generator/api/src/test/java/io/fabric8/crd/example/person/Person.java @@ -20,15 +20,24 @@ public class Person { - private String firstName; - private Optional middleName; - private String lastName; - private int birthYear; - private List hobbies; - private AddressList addresses; - private Type type; + public String firstName; + public Optional middleName; + public String lastName; + public int birthYear; + public List hobbies; + public AddressList addresses; + public Type type; + public transient Foo transientFoo; + + protected static Foo builder() { + return new Foo(); + } public enum Type { crazy, crazier } + + public static class Foo { + public static String bar; + } } diff --git a/crd-generator/api/src/test/java/io/fabric8/crd/generator/utils/PropertiesTest.java b/crd-generator/api/src/test/java/io/fabric8/crd/generator/utils/PropertiesTest.java new file mode 100644 index 00000000000..1882b66a74d --- /dev/null +++ b/crd-generator/api/src/test/java/io/fabric8/crd/generator/utils/PropertiesTest.java @@ -0,0 +1,50 @@ +package io.fabric8.crd.generator.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import io.fabric8.crd.example.accessible.AccessibleClass; +import io.fabric8.crd.example.webserver.WebServerWithStatusProperty; +import io.sundr.model.Property; +import io.sundr.model.TypeDef; + +public class PropertiesTest { + @Test + public void testClassWithNoPublicProperties() { + TypeDef def = Types.typeDefFrom(WebServerWithStatusProperty.class); + List props = Properties.getVisibleProperties(def); + assertEquals(0, props.size()); + } + + private Property findByName(List props, String name) { + return props.stream() + .filter(p -> p.getName().equals(name)) + .findAny() + .get(); + } + + @Test + public void testClassWithMixedVisibility() { + TypeDef def = Types.typeDefFrom(AccessibleClass.class); + List props = Properties.getVisibleProperties(def); + assertEquals(3, props.size()); + + Property publicProp = findByName(props, "visible"); + assertTrue(publicProp.isPublic()); + + Property getterProp = findByName(props, "gettable"); + assertTrue(getterProp.isPrivate()); + // Got annotations from getter method + assertEquals(1, getterProp.getAnnotations().size()); + + Property boolProp = findByName(props, "truthy"); + assertFalse(boolProp.isPrivate()); + // Got annotations from getter method + assertEquals(0, boolProp.getAnnotations().size()); + } +} diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/base/OperationSupport.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/base/OperationSupport.java index 9d34fedb95d..fcb5a68602c 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/base/OperationSupport.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/base/OperationSupport.java @@ -50,6 +50,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import static io.fabric8.kubernetes.client.internal.PatchUtils.patchMapper; @@ -281,7 +282,10 @@ protected void handleDelete(URL requestUrl, long gracePeriodSeconds, DeletionPro deleteOptions.setDryRun(Collections.singletonList("All")); } - HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().delete(JSON, JSON_MAPPER.writeValueAsString(deleteOptions)).url(requestUrl); + String deleteOptionsBody = JSON_MAPPER.writeValueAsString(deleteOptions); + LOG.debug("Executing delete for {} with body {}", requestUrl, deleteOptionsBody); + + HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().delete(JSON, deleteOptionsBody).url(requestUrl); handleResponse(requestBuilder, null, Collections.emptyMap()); }