Skip to content

Commit 32b3473

Browse files
Javatar81andreaTPmanusa
committed
fix(java-generator): java generator does not recognize fields in CRDs other than metadata, spec, and status (#6216)
* Integration tests for top level fields * Added generation of classes for top level properties * Added generation for getter/setters for top level properties * Small refactorings, clean up. * Extended integration test with alwaysPreserveUnknown=true * Removed read_only access from kind and apiVersion to keep them out of additionalProperties * Merged changelog * Fixed format violations * Added tests w/wo additionalProps + negative test, simplified test crd * Fixed formatting issues in ITs. * review: java-generator additional fields Signed-off-by: Marc Nuri <[email protected]> --------- Signed-off-by: Marc Nuri <[email protected]> Co-authored-by: Andrea Peruffo <[email protected]> Co-authored-by: Marc Nuri <[email protected]>
1 parent 725ee5c commit 32b3473

File tree

21 files changed

+1261
-39
lines changed

21 files changed

+1261
-39
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#### Bugs
55
* Fix #6038: Support for Gradle configuration cache
6+
* Fix #6214: Java generator does not recognize fields in CRDs other than metadata, spec, and status
67

78
#### Improvements
89
* Fix #5264: Remove deprecated `Config.errorMessages` field

java-generator/core/src/main/java/io/fabric8/java/generator/CRGeneratorRunner.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,19 @@
2828
import java.util.Arrays;
2929
import java.util.Collections;
3030
import java.util.List;
31+
import java.util.Map;
32+
import java.util.Map.Entry;
3133
import java.util.Objects;
3234
import java.util.Optional;
35+
import java.util.Set;
3336
import java.util.stream.Collectors;
37+
import java.util.stream.Stream;
3438

3539
public class CRGeneratorRunner {
3640

3741
private final Config config;
42+
private static final Set<String> STD_PROPS = Stream.of("metadata", "spec", "status", "apiVersion", "kind")
43+
.collect(Collectors.toSet());
3844

3945
public CRGeneratorRunner(Config config) {
4046
this.config = config;
@@ -50,13 +56,11 @@ public List<WritableCRCompilationUnit> generate(CustomResourceDefinition crd, St
5056
for (CustomResourceDefinitionVersion crdv : crSpec.getVersions()) {
5157
String version = crdv.getName();
5258

53-
String pkg = Optional.ofNullable(basePackageName)
59+
String pkgNotOverridden = Optional.ofNullable(basePackageName)
5460
.map(p -> p + "." + version)
5561
.orElse(version);
5662

57-
if (config.getPackageOverrides().containsKey(pkg)) {
58-
pkg = config.getPackageOverrides().get(pkg);
59-
}
63+
final String pkg = config.getPackageOverrides().getOrDefault(pkgNotOverridden, pkgNotOverridden);
6064

6165
AbstractJSONSchema2Pojo specGenerator = null;
6266

@@ -73,6 +77,17 @@ public List<WritableCRCompilationUnit> generate(CustomResourceDefinition crd, St
7377
crName + "Status", status, pkg, config);
7478
}
7579

80+
boolean preserveUnknownFields = Boolean.TRUE
81+
.equals(crdv.getSchema().getOpenAPIV3Schema().getXKubernetesPreserveUnknownFields());
82+
83+
Map<String, JSONSchemaProps> topLevelProps = crdv.getSchema().getOpenAPIV3Schema().getProperties().entrySet().stream()
84+
.filter(e -> !STD_PROPS.contains(e.getKey()))
85+
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
86+
87+
List<String> requiredTopLevelProps = crdv.getSchema().getOpenAPIV3Schema().getRequired().stream()
88+
.filter(prop -> !STD_PROPS.contains(prop))
89+
.collect(Collectors.toList());
90+
7691
AbstractJSONSchema2Pojo crGenerator = new JCRObject(
7792
pkg,
7893
crName,
@@ -81,6 +96,10 @@ public List<WritableCRCompilationUnit> generate(CustomResourceDefinition crd, St
8196
scope,
8297
crName + "Spec",
8398
crName + "Status",
99+
topLevelProps,
100+
requiredTopLevelProps,
101+
preserveUnknownFields,
102+
crdv.getSchema().getOpenAPIV3Schema().getDescription(),
84103
specGenerator != null,
85104
statusGenerator != null,
86105
crdv.getStorage(),

java-generator/core/src/main/java/io/fabric8/java/generator/nodes/JCRObject.java

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@
2424
import com.github.javaparser.ast.expr.StringLiteralExpr;
2525
import com.github.javaparser.ast.type.ClassOrInterfaceType;
2626
import io.fabric8.java.generator.Config;
27+
import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps;
2728

29+
import java.util.ArrayList;
2830
import java.util.Collections;
31+
import java.util.List;
32+
import java.util.Map;
2933

30-
public class JCRObject extends AbstractJSONSchema2Pojo implements JObjectExtraAnnotations {
34+
public class JCRObject extends JObject implements JObjectExtraAnnotations {
3135

32-
private final String pkg;
33-
private final String type;
34-
private final String className;
3536
private final String group;
3637
private final String version;
3738
private final String scope;
@@ -53,18 +54,20 @@ public JCRObject(
5354
String scope,
5455
String specClassName,
5556
String statusClassName,
57+
Map<String, JSONSchemaProps> toplevelProps,
58+
List<String> required,
59+
boolean preserveUnknownFields,
60+
String description,
5661
boolean withSpec,
5762
boolean withStatus,
5863
boolean storage,
5964
boolean served,
6065
String singular,
6166
String plural,
6267
Config config) {
63-
super(config, null, false, null, null);
6468

65-
this.pkg = (pkg == null) ? "" : pkg.trim();
66-
this.type = (this.pkg.isEmpty()) ? type : pkg + "." + type;
67-
this.className = type;
69+
super(pkg, type, toplevelProps, required, preserveUnknownFields, config, description, false, null);
70+
6871
this.group = group;
6972
this.version = version;
7073
this.scope = scope;
@@ -146,7 +149,13 @@ public GeneratorResult generateJava() {
146149
if (config.isObjectExtraAnnotations()) {
147150
addExtraAnnotations(clz);
148151
}
152+
153+
List<GeneratorResult.ClassResult> buffer = generateJavaFields(clz);
154+
155+
List<GeneratorResult.ClassResult> results = new ArrayList<>();
156+
results.add(new GeneratorResult.ClassResult(className, cu));
157+
results.addAll(buffer);
149158
return new GeneratorResult(
150-
Collections.singletonList(new GeneratorResult.ClassResult(className, cu)));
159+
Collections.unmodifiableList(results));
151160
}
152161
}

java-generator/core/src/main/java/io/fabric8/java/generator/nodes/JObject.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import com.github.javaparser.ast.NodeList;
2323
import com.github.javaparser.ast.PackageDeclaration;
2424
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
25-
import com.github.javaparser.ast.body.EnumDeclaration;
2625
import com.github.javaparser.ast.body.FieldDeclaration;
2726
import com.github.javaparser.ast.body.MethodDeclaration;
2827
import com.github.javaparser.ast.body.VariableDeclarator;
@@ -45,9 +44,9 @@
4544
public class JObject extends AbstractJSONSchema2Pojo implements JObjectExtraAnnotations {
4645

4746
public static final String DEPRECATED_FIELD_MARKER = "deprecated";
48-
private final String type;
49-
private final String className;
50-
private final String pkg;
47+
protected final String type;
48+
protected final String className;
49+
protected final String pkg;
5150
private final Map<String, AbstractJSONSchema2Pojo> fields;
5251
private final Set<String> required;
5352
private final Set<String> deprecated = new HashSet<>();
@@ -142,7 +141,7 @@ private String getSortedFieldsAsParam(Set<String> list) {
142141
StringBuilder sb = new StringBuilder();
143142
sb.append("{");
144143
while (!sortedFields.isEmpty()) {
145-
sb.append("\"" + sortedFields.remove(0) + "\"");
144+
sb.append("\"").append(sortedFields.remove(0)).append("\"");
146145
if (!sortedFields.isEmpty()) {
147146
sb.append(",");
148147
}
@@ -188,6 +187,14 @@ public GeneratorResult generateJava() {
188187

189188
clz.addImplementedType(new ClassOrInterfaceType(null, "io.fabric8.kubernetes.api.model.KubernetesResource"));
190189

190+
List<GeneratorResult.ClassResult> buffer = generateJavaFields(clz);
191+
192+
buffer.add(new GeneratorResult.ClassResult(this.className, cu));
193+
194+
return new GeneratorResult(buffer);
195+
}
196+
197+
protected List<GeneratorResult.ClassResult> generateJavaFields(ClassOrInterfaceDeclaration clz) {
191198
List<GeneratorResult.ClassResult> buffer = new ArrayList<>(this.fields.size() + 1);
192199

193200
List<String> sortedKeys = this.fields.keySet().stream().sorted().collect(Collectors.toList());
@@ -201,12 +208,8 @@ public GeneratorResult generateJava() {
201208
// For now the inner types are only for enums
202209
boolean isEnum = !gr.getInnerClasses().isEmpty();
203210
if (isEnum) {
204-
for (GeneratorResult.ClassResult enumCR : gr.getInnerClasses()) {
205-
Optional<EnumDeclaration> ed = enumCR.getEnumByName(enumCR.getName());
206-
if (ed.isPresent()) {
207-
clz.addMember(ed.get());
208-
}
209-
}
211+
gr.getInnerClasses()
212+
.forEach(enumCR -> enumCR.getEnumByName(enumCR.getName()).ifPresent(clz::addMember));
210213
}
211214
buffer.addAll(gr.getTopLevelClasses());
212215

@@ -342,10 +345,7 @@ public GeneratorResult generateJava() {
342345
additionalSetter
343346
.setBody(new BlockStmt().addStatement(new NameExpr("this." + Keywords.ADDITIONAL_PROPERTIES + ".put(key, value)")));
344347
}
345-
346-
buffer.add(new GeneratorResult.ClassResult(this.className, cu));
347-
348-
return new GeneratorResult(buffer);
348+
return buffer;
349349
}
350350

351351
/**

java-generator/core/src/test/java/io/fabric8/java/generator/GeneratorTest.java

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,16 @@ void testCR() {
5656
// Arrange
5757
JCRObject cro = new JCRObject(
5858
"v1alpha1",
59-
"t",
59+
"Type",
6060
"g",
6161
"v",
6262
"Namespaced",
6363
"Spec",
6464
"Status",
65+
Collections.emptyMap(),
66+
Collections.emptyList(),
67+
false,
68+
"",
6569
true,
6670
true,
6771
true,
@@ -75,7 +79,7 @@ void testCR() {
7579

7680
// Assert
7781
assertEquals(1, res.getTopLevelClasses().size());
78-
assertEquals("t", res.getTopLevelClasses().get(0).getName());
82+
assertEquals("Type", res.getTopLevelClasses().get(0).getName());
7983
assertEquals("v1alpha1",
8084
res.getTopLevelClasses().get(0).getPackageDeclaration().get().getNameAsString());
8185
}
@@ -85,12 +89,16 @@ void testNamespacedCR() {
8589
// Arrange
8690
JCRObject cro = new JCRObject(
8791
null,
88-
"t",
92+
"Type",
8993
"g",
9094
"v",
9195
"Namespaced",
9296
"Spec",
9397
"Status",
98+
Collections.emptyMap(),
99+
Collections.emptyList(),
100+
false,
101+
"",
94102
true,
95103
true,
96104
true,
@@ -104,20 +112,24 @@ void testNamespacedCR() {
104112

105113
// Assert
106114
assertEquals("io.fabric8.kubernetes.api.model.Namespaced", res.getTopLevelClasses().get(0)
107-
.getClassByName("t").get().getImplementedTypes().get(0).getNameWithScope());
115+
.getClassByName("Type").get().getImplementedTypes().get(0).getNameWithScope());
108116
}
109117

110118
@Test
111119
void testClusterScopeCR() {
112120
// Arrange
113121
JCRObject cro = new JCRObject(
114122
null,
115-
"t",
123+
"Type",
116124
"g",
117125
"v",
118126
"Cluster",
119127
"Spec",
120128
"Status",
129+
Collections.emptyMap(),
130+
Collections.emptyList(),
131+
false,
132+
"",
121133
true,
122134
true,
123135
true,
@@ -130,20 +142,24 @@ void testClusterScopeCR() {
130142
GeneratorResult res = cro.generateJava();
131143

132144
// Assert
133-
assertTrue(res.getTopLevelClasses().get(0).getClassByName("t").get().getImplementedTypes().isEmpty());
145+
assertTrue(res.getTopLevelClasses().get(0).getClassByName("Type").get().getImplementedTypes().isEmpty());
134146
}
135147

136148
@Test
137149
void testCRWithoutNamespace() {
138150
// Arrange
139151
JCRObject cro = new JCRObject(
140152
null,
141-
"t",
153+
"Type",
142154
"g",
143155
"v",
144156
"Namespaced",
145157
"Spec",
146158
"Status",
159+
Collections.emptyMap(),
160+
Collections.emptyList(),
161+
false,
162+
"",
147163
true,
148164
true,
149165
true,
@@ -157,7 +173,7 @@ void testCRWithoutNamespace() {
157173

158174
// Assert
159175
assertEquals(1, res.getTopLevelClasses().size());
160-
assertEquals("t", res.getTopLevelClasses().get(0).getName());
176+
assertEquals("Type", res.getTopLevelClasses().get(0).getName());
161177
}
162178

163179
@Test
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
17+
invoker.goals=test

0 commit comments

Comments
 (0)