Skip to content

Commit 78bb1b7

Browse files
committed
Fixed #16 Support simple response type mapping
1 parent 2791e89 commit 78bb1b7

File tree

7 files changed

+203
-17
lines changed

7 files changed

+203
-17
lines changed

operator-gen-maven-plugin/src/main/java/org/acme/Configuration.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,20 @@ public class Configuration {
1313

1414
private final Config config;
1515
private final Map<String, String> pathParamMappings = new HashMap<>();
16+
private final Map<String, String> pathResponseMappings = new HashMap<>();
1617
private final Properties crdCustomizations;
1718

18-
public Configuration(Config config, List<String> pathParamMappings, Properties crdCustomizations) {
19+
public Configuration(Config config, List<String> pathParamMappings, List<String> pathResponseMappings, Properties crdCustomizations) {
1920
this.config = config;
2021
this.crdCustomizations = crdCustomizations;
2122
pathParamMappings.stream()
2223
.map(s -> s.split("="))
2324
.filter(a -> a.length > 1)
2425
.forEach(a -> this.pathParamMappings.put(a[0].trim(), a[1].trim()));
26+
pathResponseMappings.stream()
27+
.map(s -> s.split("="))
28+
.filter(a -> a.length > 1)
29+
.forEach(a -> this.pathResponseMappings.put(a[0].trim(), a[1].trim()));
2530
}
2631

2732
public String getCrdVersion() {
@@ -39,6 +44,10 @@ public List<String> getResponses() {
3944
public Map<String, String> getPathParamMappings() {
4045
return Collections.unmodifiableMap(this.pathParamMappings);
4146
}
47+
48+
public Map<String, String> getPathResponseMappings() {
49+
return Collections.unmodifiableMap(this.pathResponseMappings);
50+
}
4251

4352
public Config getConfig() {
4453
return config;

operator-gen-maven-plugin/src/main/java/org/acme/OperatorGenMojo.java

+10-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.acme.client.ApiClientMethodCallFactory;
1515
import org.acme.client.KiotaMethodCallFactory;
1616
import org.acme.client.ParameterResolver;
17+
import org.acme.client.ResponseResolver;
1718
import org.acme.gen.CrdResourceGen;
1819
import org.acme.gen.DependentGen;
1920
import org.acme.gen.ReconcilerGen;
@@ -60,6 +61,9 @@ public class OperatorGenMojo
6061
@Parameter(property = "pathParamMappings", required = false)
6162
private List<String> pathParamMappings = null;
6263

64+
@Parameter(property = "pathResponseMappings", required = false)
65+
private List<String> pathResponseMappings = null;
66+
6367
@Parameter(property = "crdCustomizations", required = false)
6468
private Properties crdCustomizations = new Properties();
6569

@@ -82,7 +86,7 @@ public class OperatorGenMojo
8286
public void execute()
8387
throws MojoExecutionException
8488
{
85-
config = new Configuration(ConfigProvider.getConfig(), pathParamMappings, crdCustomizations);
89+
config = new Configuration(ConfigProvider.getConfig(), pathParamMappings, pathResponseMappings, crdCustomizations);
8690
File f = sourceDestinationFolder;
8791
if ( !f.exists() )
8892
{
@@ -112,16 +116,17 @@ private void processResponseType(OpenAPI openApiDoc, File jsonsFile, String resp
112116
String crdVersion = config.getCrdVersion();
113117
String basePackage = config.getCrdPackage();
114118
CrudMapper mapper = new ResponseTypeMapper(openApiDoc, responseType);
115-
ParameterResolver resolver = new ParameterResolver(config, openApiDoc);
116-
ApiClientMethodCallFactory methodCalls = new KiotaMethodCallFactory(mapper, resolver);
119+
ParameterResolver paramResolver = new ParameterResolver(config, openApiDoc);
120+
ResponseResolver respResolver = new ResponseResolver(config);
121+
ApiClientMethodCallFactory methodCalls = new KiotaMethodCallFactory(mapper, paramResolver);
117122
String className = responseType.substring(0, 1).toUpperCase() + responseType.substring(1);
118123
Name crdName = new Name(new Name(basePackage), className);
119124
try {
120125
Path crdResOutputDir = sourceDestinationFolder.toPath().resolve("kubernetes");
121126
if (!Files.exists(crdResOutputDir)) {
122127
Files.createDirectory(crdResOutputDir);
123128
}
124-
CrdResourceGen resourceGen = new CrdResourceGen(crdResOutputDir.resolve(responseType + ".yaml"), jsonsFile.toPath(), crdName, mapper, resolver, config);
129+
CrdResourceGen resourceGen = new CrdResourceGen(crdResOutputDir.resolve(responseType + ".yaml"), jsonsFile.toPath(), crdName, mapper, paramResolver, config);
125130
resourceGen.create();
126131
} catch (IOException e) {
127132
LOG.error(String.format("Error processing response type '%s'", responseType), e);
@@ -133,7 +138,7 @@ private void processResponseType(OpenAPI openApiDoc, File jsonsFile, String resp
133138
reconciler.create();
134139

135140

136-
DependentGen dependent = new DependentGen(sourceDestinationFolder.toPath(), new Name(qualifierWithVersion, className), resource, methodCalls, mapper, resolver);
141+
DependentGen dependent = new DependentGen(sourceDestinationFolder.toPath(), new Name(qualifierWithVersion, className), resource, methodCalls, mapper, paramResolver, respResolver);
137142
dependent.create();
138143
}
139144

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.acme.client;
2+
3+
import org.acme.Configuration;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
7+
import com.github.javaparser.ast.NodeList;
8+
import com.github.javaparser.ast.expr.Expression;
9+
import com.github.javaparser.ast.expr.MethodCallExpr;
10+
11+
public class ResponseResolver {
12+
private static final Logger LOG = LoggerFactory.getLogger(ResponseResolver.class);
13+
private final Configuration config;
14+
15+
public ResponseResolver(Configuration config) {
16+
this.config = config;
17+
}
18+
19+
public Expression resolveResponse(String path, Expression input) {
20+
LOG.debug("Resolving response mapping for path {}", path);
21+
if (config.getPathResponseMappings().containsKey(path)) {
22+
LOG.debug("Found response mapping for path {}", path);
23+
String fieldMapping = config.getPathResponseMappings().get(path);
24+
String getter = toGetter(fieldMapping);
25+
return new MethodCallExpr(input, getter, new NodeList<>());
26+
}
27+
return input;
28+
}
29+
30+
private String toGetter(String fieldName) {
31+
return String.format("get%s%s", String.valueOf(fieldName.charAt(0)).toUpperCase(), fieldName.substring(1, fieldName.length()));
32+
}
33+
}

operator-gen-maven-plugin/src/main/java/org/acme/gen/DependentGen.java

+15-9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import org.acme.client.ApiClientMethodCallFactory;
1717
import org.acme.client.ParameterResolver;
18+
import org.acme.client.ResponseResolver;
1819
import org.acme.read.crud.CrudMapper;
1920
import org.eclipse.microprofile.openapi.models.Operation;
2021
import org.eclipse.microprofile.openapi.models.media.Schema;
@@ -86,7 +87,8 @@ public class DependentGen {
8687
private static final String FIELD_NAME_VERTX = "vertx";
8788
private static final String NAME_POSTFIX = "Dependent";
8889
private final Path path;
89-
private final ParameterResolver resolver;
90+
private final ParameterResolver paramResolver;
91+
private final ResponseResolver respResolver;
9092
private final Name name;
9193
private final Name resource;
9294
private final ApiClientMethodCallFactory methodCalls;
@@ -96,13 +98,14 @@ public class DependentGen {
9698
private final ClassOrInterfaceType resourceType;
9799
private final ClassOrInterfaceType collectionsType;
98100

99-
public DependentGen(Path path, Name name, Name resource, ApiClientMethodCallFactory methodCalls, CrudMapper mapper, ParameterResolver resolver) {
101+
public DependentGen(Path path, Name name, Name resource, ApiClientMethodCallFactory methodCalls, CrudMapper mapper, ParameterResolver paramResolver, ResponseResolver respResolver) {
100102
this.path = path;
101103
this.name = name;
102104
this.resource = resource;
103105
this.methodCalls = methodCalls;
104106
this.mapper = mapper;
105-
this.resolver = resolver;
107+
this.paramResolver = paramResolver;
108+
this.respResolver = respResolver;
106109
crdType = new ClassOrInterfaceType(null, name.toString());
107110
contextType = new ClassOrInterfaceType(null,
108111
new SimpleName(Context.class.getSimpleName()),
@@ -146,9 +149,9 @@ public void create() {
146149
desiredMethod(clazz);
147150
fetchMethod(clazz);
148151
mapper.createPath()
149-
.map(e -> e.getValue().getPOST())
150-
.ifPresent(op ->
151-
createMethod(cu, clazz, mapper.getByIdPath().get().getKey(), op)
152+
//.map(e -> e.getValue().getPOST())
153+
.ifPresent(p ->
154+
createMethod(cu, clazz, p.getKey(), p.getValue().getPOST())
152155
);
153156
mapper.patchPath()
154157
.map(e -> e.getValue().getPATCH())
@@ -264,7 +267,9 @@ private void createMethod(CompilationUnit cu, ClassOrInterfaceDeclaration clazz,
264267
ReturnStmt createReturn;
265268
if (hasPostResponse(op)) {
266269
createReturn = createCall
267-
.map(ReturnStmt::new).orElse(new ReturnStmt(new NullLiteralExpr()));
270+
.map(c -> respResolver.resolveResponse(path, c))
271+
.map(ReturnStmt::new)
272+
.orElse(new ReturnStmt(new NullLiteralExpr()));
268273
} else {
269274
createReturn = extractParamStatements(cu, path, createCall, body);
270275
}
@@ -301,7 +306,8 @@ private ReturnStmt extractParamStatements(CompilationUnit cu, String path, Optio
301306
}
302307

303308
private boolean hasPostResponse(Operation op) {
304-
return op.getResponses().getAPIResponse("201") != null && op.getResponses().getAPIResponse("201").getContent() != null;
309+
return (op.getResponses().getAPIResponse("201") != null && op.getResponses().getAPIResponse("201").getContent() != null)
310+
|| (op.getResponses().getAPIResponse("200") != null && op.getResponses().getAPIResponse("200").getContent() != null);
305311
}
306312

307313
private boolean hasUpdateResponse(Operation op) {
@@ -456,7 +462,7 @@ private CatchClause catch404() {
456462
*/
457463
private boolean mappedNameMethod(CompilationUnit cu, ClassOrInterfaceDeclaration clazz) {
458464
Map<String, String> parameterMap = mapper.getByIdPath()
459-
.map(p-> resolver.getParameterNameMappings(p.getKey()))
465+
.map(p-> paramResolver.getParameterNameMappings(p.getKey()))
460466
.orElse(Collections.emptyMap());
461467
NodeList<Expression> methodArgs = new NodeList<>();
462468
parameterMap.entrySet().stream()

operator-gen-maven-plugin/src/test/java/org/acme/client/KiotaClientNamingTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public V setValue(V value) {
5656
@BeforeEach
5757
void setUp() {
5858
mapper = Mockito.mock(CrudMapper.class);
59-
client = new KiotaMethodCallFactory(mapper, new ParameterResolver(new Configuration(ConfigProvider.getConfig(), new ArrayList<>(), null), null));
59+
client = new KiotaMethodCallFactory(mapper, new ParameterResolver(new Configuration(ConfigProvider.getConfig(), new ArrayList<>(), new ArrayList<>(), null), null));
6060
}
6161

6262
@Test

operator-gen-maven-plugin/src/test/java/org/acme/client/ParameterResolverTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.io.IOException;
77
import java.io.InputStream;
8+
import java.util.ArrayList;
89
import java.util.Collections;
910
import java.util.List;
1011
import java.util.Map;
@@ -38,7 +39,7 @@ public void setUp() {
3839
new Configuration(ConfigProvider.getConfig(),
3940
List.of("/admin/realms/{realm}/clients=spec.realm",
4041
"/admin/realms/{realm}/clients/{client-uuid}=spec.realm|status.uuid"),
41-
null),
42+
new ArrayList<>(), null),
4243
model);
4344
}
4445
} catch (IOException ex) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package org.acme.read.crud;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNotNull;
5+
import static org.junit.Assert.assertTrue;
6+
7+
import java.io.IOException;
8+
import java.io.InputStream;
9+
import java.util.HashSet;
10+
import java.util.Optional;
11+
import java.util.Set;
12+
import java.util.Map.Entry;
13+
import java.util.stream.Stream;
14+
15+
import org.acme.read.ModelReader;
16+
import org.eclipse.microprofile.config.ConfigProvider;
17+
import org.eclipse.microprofile.openapi.models.OpenAPI;
18+
import org.eclipse.microprofile.openapi.models.PathItem;
19+
import org.eclipse.microprofile.openapi.models.media.Schema;
20+
import org.junit.jupiter.api.BeforeAll;
21+
import org.junit.jupiter.api.Disabled;
22+
import org.junit.jupiter.params.ParameterizedTest;
23+
import org.junit.jupiter.params.provider.MethodSource;
24+
25+
import io.quarkus.smallrye.openapi.runtime.OpenApiConstants;
26+
import io.smallrye.openapi.api.OpenApiConfig;
27+
import io.smallrye.openapi.api.OpenApiConfigImpl;
28+
import io.smallrye.openapi.runtime.OpenApiProcessor;
29+
import io.smallrye.openapi.runtime.OpenApiStaticFile;
30+
import io.smallrye.openapi.runtime.io.Format;
31+
32+
@Disabled
33+
public class ResponseTypeMapperApicurioTest {
34+
private static OpenAPI model;
35+
private static ModelReader reader;
36+
private static final Set<String> EXCLUDED_RESPONSES = Set.of("conflict", "empty", "error", "forbidden",
37+
"invalidTopicsError", "notFound", "parameterBodies", "redirect", "string", "validationError",
38+
"EmptyRepository" /* Only used for 409 response */,
39+
"FileDeleteResponse" /* Used for single operation to delete a file in a repository */,
40+
"FileResponse" /* Used for single operation to update a file in a repository */,
41+
"FilesResponse" /* Modify multiple files in a repository */
42+
);
43+
44+
45+
46+
@BeforeAll
47+
public static void setUp() {
48+
try (InputStream is = ResponseTypeMapperGiteaTest.class.getClassLoader().getResourceAsStream("apicurio.json")) {
49+
try (OpenApiStaticFile staticFile = new OpenApiStaticFile(is, Format.JSON)) {
50+
OpenApiConfig openApiConfig = new OpenApiConfigImpl(ConfigProvider.getConfig());
51+
model = OpenApiProcessor.modelFromStaticFile(openApiConfig, staticFile);
52+
reader = new ModelReader(model);
53+
}
54+
} catch (IOException ex) {
55+
throw new RuntimeException("Could not find [" + OpenApiConstants.BASE_NAME + Format.JSON + "]");
56+
}
57+
}
58+
59+
@ParameterizedTest
60+
@MethodSource("responseTypesWithGetById")
61+
void getByIdPath(String modelName) {
62+
ResponseTypeMapper analyzer = new ResponseTypeMapper(model, modelName);
63+
if (!analyzer.isArrayType()) {
64+
Optional<Entry<String, PathItem>> byIdPath = analyzer.getByIdPath();
65+
assertNotNull(byIdPath);
66+
assertTrue(byIdPath.isPresent());
67+
assertNotNull(byIdPath.get().getValue().getGET());
68+
System.out.println("GetById path for " + modelName + " is " + byIdPath.get().getKey());
69+
Schema schema = byIdPath.get().getValue().getGET().getResponses().getAPIResponse("200").getContent().getMediaType(analyzer.getResponseMediaType()).getSchema();
70+
assertEquals(analyzer.getByIdSchemaName(), ResponseTypeMapper.resolveSchemaName(schema));
71+
}
72+
}
73+
74+
@ParameterizedTest
75+
@MethodSource("responseTypesWithDelete")
76+
void deletePath(String modelName) {
77+
ResponseTypeMapper analyzer = new ResponseTypeMapper(model, modelName);
78+
if (!analyzer.isArrayType()) {
79+
Optional<Entry<String, PathItem>> deletePath = analyzer.deletePath();
80+
assertNotNull(deletePath);
81+
assertTrue(deletePath.isPresent());
82+
assertNotNull(deletePath.get().getValue().getDELETE());
83+
System.out.println("Delete path for " + modelName + " is " + deletePath.get().getKey());
84+
}
85+
}
86+
87+
@ParameterizedTest
88+
@MethodSource("responseTypesWithPatch")
89+
void putPath(String modelName) {
90+
ResponseTypeMapper analyzer = new ResponseTypeMapper(model, modelName);
91+
if (!analyzer.isArrayType()) {
92+
Optional<Entry<String, PathItem>> putPath = analyzer.putPath();
93+
assertNotNull(putPath);
94+
assertTrue(putPath.isPresent());
95+
assertNotNull(putPath.get().getValue().getPUT());
96+
System.out.println("Patch path for " + modelName + " is " + putPath.get().getKey());
97+
}
98+
}
99+
100+
@ParameterizedTest
101+
@MethodSource("responseTypesWithCreate")
102+
void createPath(String modelName) {
103+
ResponseTypeMapper analyzer = new ResponseTypeMapper(model, modelName);
104+
if (!analyzer.isArrayType()) {
105+
Optional<Entry<String, PathItem>> createPath = analyzer.createPath();
106+
assertNotNull(createPath);
107+
assertTrue(createPath.isPresent());
108+
assertNotNull(createPath.get().getValue().getPOST());
109+
System.out.println("Create path for " + modelName + " is " + createPath.get().getKey());
110+
}
111+
}
112+
113+
private static Stream<String> responseTypesWithGetById() {
114+
Set<String> noFindById = new HashSet<String>(EXCLUDED_RESPONSES);
115+
return reader.getResponseTypeOrSchemaNames(e -> !noFindById.contains(e.getKey()));
116+
}
117+
118+
private static Stream<String> responseTypesWithPatch() {
119+
Set<String> noPatch = new HashSet<String>(EXCLUDED_RESPONSES);
120+
return reader.getResponseTypeOrSchemaNames(e -> !noPatch.contains(e.getKey()));
121+
}
122+
123+
private static Stream<String> responseTypesWithCreate() {
124+
Set<String> noCreate = new HashSet<String>(EXCLUDED_RESPONSES);
125+
return reader.getResponseTypeOrSchemaNames(e -> !noCreate.contains(e.getKey()));
126+
}
127+
128+
private static Stream<String> responseTypesWithDelete() {
129+
Set<String> noDelete = new HashSet<String>(EXCLUDED_RESPONSES);
130+
return reader.getResponseTypeOrSchemaNames(e -> !noDelete.contains(e.getKey()));
131+
}
132+
}

0 commit comments

Comments
 (0)