Skip to content

Commit 9462ba0

Browse files
authored
Json schema support (#10686)
Json schema support Signed-off-by: David Kral <[email protected]>
1 parent 726eebd commit 9462ba0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+4173
-3
lines changed

all/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,14 @@
13051305
<groupId>io.helidon.discovery.providers</groupId>
13061306
<artifactId>helidon-discovery-providers-eureka</artifactId>
13071307
</dependency>
1308+
<dependency>
1309+
<groupId>io.helidon.json.schema</groupId>
1310+
<artifactId>helidon-json-schema</artifactId>
1311+
</dependency>
1312+
<dependency>
1313+
<groupId>io.helidon.json.schema</groupId>
1314+
<artifactId>helidon-json-schema-codegen</artifactId>
1315+
</dependency>
13081316
</dependencies>
13091317

13101318
</project>

bom/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,6 +1754,18 @@
17541754
<version>${helidon.version}</version>
17551755
</dependency>
17561756

1757+
<!-- Json schema -->
1758+
<dependency>
1759+
<groupId>io.helidon.json.schema</groupId>
1760+
<artifactId>helidon-json-schema</artifactId>
1761+
<version>${helidon.version}</version>
1762+
</dependency>
1763+
<dependency>
1764+
<groupId>io.helidon.json.schema</groupId>
1765+
<artifactId>helidon-json-schema-codegen</artifactId>
1766+
<version>${helidon.version}</version>
1767+
</dependency>
1768+
17571769
</dependencies>
17581770
</dependencyManagement>
17591771
</project>

builder/codegen/src/main/java/io/helidon/builder/codegen/BuilderCodegen.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ private static void generateCustomMethods(ClassModel.Builder classModel,
256256
CustomMethods customMethods) {
257257
for (CustomMethods.CustomMethod customMethod : customMethods.factoryMethods()) {
258258
TypeName typeName = customMethod.declaredMethod().returnType();
259+
if (typeName.isOptional()) {
260+
//Content of the Optional
261+
typeName = typeName.typeArguments().getFirst();
262+
}
259263
// there is a chance the typeName does not have a package (if "forward referenced"),
260264
// in that case compare just by classname (leap of faith...)
261265
if (typeName.packageName().isBlank()) {

json/pom.xml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Copyright (c) 2025 Oracle and/or its affiliates.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
18+
<project xmlns="http://maven.apache.org/POM/4.0.0"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
21+
<modelVersion>4.0.0</modelVersion>
22+
<parent>
23+
<groupId>io.helidon</groupId>
24+
<artifactId>helidon-project</artifactId>
25+
<version>4.3.0-SNAPSHOT</version>
26+
</parent>
27+
28+
<groupId>io.helidon.json</groupId>
29+
<artifactId>helidon-json-project</artifactId>
30+
<name>Helidon JSON Project</name>
31+
<packaging>pom</packaging>
32+
33+
<properties>
34+
<javadoc.fail-on-warnings>true</javadoc.fail-on-warnings>
35+
</properties>
36+
37+
<modules>
38+
<module>schema</module>
39+
</modules>
40+
41+
<build>
42+
<plugins>
43+
<plugin>
44+
<groupId>org.apache.maven.plugins</groupId>
45+
<artifactId>maven-dependency-plugin</artifactId>
46+
<executions>
47+
<execution>
48+
<id>check-dependencies</id>
49+
<phase>verify</phase>
50+
</execution>
51+
</executions>
52+
</plugin>
53+
</plugins>
54+
</build>
55+
56+
</project>

json/schema/codegen/pom.xml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Copyright (c) 2025 Oracle and/or its affiliates.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
18+
<project xmlns="http://maven.apache.org/POM/4.0.0"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
21+
<modelVersion>4.0.0</modelVersion>
22+
<parent>
23+
<groupId>io.helidon.json.schema</groupId>
24+
<artifactId>helidon-json-schema-project</artifactId>
25+
<version>4.3.0-SNAPSHOT</version>
26+
</parent>
27+
28+
<artifactId>helidon-json-schema-codegen</artifactId>
29+
<name>Helidon Json Schema Codegen</name>
30+
31+
<dependencies>
32+
<dependency>
33+
<groupId>io.helidon.codegen</groupId>
34+
<artifactId>helidon-codegen</artifactId>
35+
</dependency>
36+
<dependency>
37+
<groupId>io.helidon.common</groupId>
38+
<artifactId>helidon-common-types</artifactId>
39+
</dependency>
40+
<dependency>
41+
<groupId>io.helidon.codegen</groupId>
42+
<artifactId>helidon-codegen-class-model</artifactId>
43+
</dependency>
44+
<dependency>
45+
<groupId>io.helidon.metadata</groupId>
46+
<artifactId>helidon-metadata-hson</artifactId>
47+
</dependency>
48+
</dependencies>
49+
50+
</project>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates.
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+
package io.helidon.json.schema.codegen;
18+
19+
import java.io.ByteArrayOutputStream;
20+
import java.io.PrintWriter;
21+
import java.nio.charset.StandardCharsets;
22+
import java.util.Collection;
23+
24+
import io.helidon.codegen.CodegenContext;
25+
import io.helidon.codegen.CodegenException;
26+
import io.helidon.codegen.RoundContext;
27+
import io.helidon.codegen.classmodel.ClassModel;
28+
import io.helidon.codegen.classmodel.TypeArgument;
29+
import io.helidon.codegen.spi.CodegenExtension;
30+
import io.helidon.common.types.AccessModifier;
31+
import io.helidon.common.types.Annotation;
32+
import io.helidon.common.types.Annotations;
33+
import io.helidon.common.types.TypeInfo;
34+
import io.helidon.common.types.TypeName;
35+
import io.helidon.common.types.TypeNames;
36+
import io.helidon.metadata.hson.Hson;
37+
38+
class SchemaCodegen implements CodegenExtension {
39+
40+
private final CodegenContext ctx;
41+
42+
SchemaCodegen(CodegenContext ctx) {
43+
this.ctx = ctx;
44+
}
45+
46+
@Override
47+
public void process(RoundContext roundContext) {
48+
Collection<TypeInfo> schemas = roundContext.annotatedTypes(Types.JSON_SCHEMA_SCHEMA);
49+
for (TypeInfo schema : schemas) {
50+
try {
51+
generateSchema(roundContext, schema);
52+
} catch (Throwable ex) {
53+
throw new CodegenException("Failed to generate JSON schema for the type: " + schema, ex, schema);
54+
}
55+
}
56+
57+
}
58+
59+
private void generateSchema(RoundContext roundContext, TypeInfo schema) {
60+
TypeName annotatedTypeName = schema.typeName();
61+
SchemaInfo schemaInfo = SchemaInfo.create(schema, ctx);
62+
TypeName typeName = schemaInfo.generatedSchema();
63+
Hson.Struct helidonSchema = schemaInfo.schema();
64+
TypeName returnType = TypeName.builder()
65+
.type(Class.class)
66+
.addTypeArgument(TypeArgument.create("?"))
67+
.build();
68+
ClassModel.Builder builder = ClassModel.builder()
69+
.type(typeName)
70+
.accessModifier(AccessModifier.PACKAGE_PRIVATE)
71+
.addInterface(Types.JSON_SCHEMA_PROVIDER)
72+
.addAnnotation(Annotation.create(Types.SERVICE_SINGLETON))
73+
.addAnnotation(Annotation.builder()
74+
.typeName(Types.SERVICE_NAMED_BY_TYPE)
75+
.putValue("value", annotatedTypeName)
76+
.build())
77+
.sortStaticFields(false)
78+
.addField(fieldBuilder -> fieldBuilder.isStatic(true)
79+
.isFinal(true)
80+
.accessModifier(AccessModifier.PRIVATE)
81+
.name("STRING_SCHEMA")
82+
.type(String.class)
83+
.defaultValueContent("\"\"\"\n" + generateSchemaString(helidonSchema) + "\"\"\""))
84+
.addField(fieldBuilder -> fieldBuilder.isStatic(true)
85+
.accessModifier(AccessModifier.PRIVATE)
86+
.isFinal(true)
87+
.type(Types.LAZY_VALUE_SCHEMA)
88+
.name("LAZY_SCHEMA")
89+
.defaultValueContent("@[email protected](() -> "
90+
+ "@[email protected](STRING_SCHEMA))"))
91+
.addMethod(it -> it.name("schemaClass")
92+
.returnType(returnType)
93+
.addAnnotation(Annotations.OVERRIDE)
94+
.addContent("return ")
95+
.addContent(annotatedTypeName)
96+
.addContentLine(".class;"))
97+
.addMethod(it -> it.name("jsonSchema")
98+
.returnType(TypeNames.STRING)
99+
.addAnnotation(Annotations.OVERRIDE)
100+
.addContentLine("return STRING_SCHEMA;"))
101+
.addMethod(it -> it.name("schema")
102+
.returnType(Types.SCHEMA)
103+
.addAnnotation(Annotations.OVERRIDE)
104+
.addContent("return LAZY_SCHEMA.get();"));
105+
106+
roundContext.addGeneratedType(typeName,
107+
builder,
108+
annotatedTypeName,
109+
schema.originatingElement().orElse(annotatedTypeName));
110+
}
111+
112+
private String generateSchemaString(Hson.Struct helidonSchema) {
113+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
114+
try (PrintWriter writer = new PrintWriter(baos, true, StandardCharsets.UTF_8)) {
115+
helidonSchema.write(writer, true);
116+
}
117+
return baos.toString(StandardCharsets.UTF_8);
118+
}
119+
120+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates.
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+
package io.helidon.json.schema.codegen;
18+
19+
import java.util.Set;
20+
21+
import io.helidon.codegen.CodegenContext;
22+
import io.helidon.codegen.spi.CodegenExtension;
23+
import io.helidon.codegen.spi.CodegenExtensionProvider;
24+
import io.helidon.common.types.TypeName;
25+
26+
/**
27+
* {@link java.util.ServiceLoader} provider implementation for {@link io.helidon.codegen.spi.CodegenExtensionProvider},
28+
* that code generates JSON Schema.
29+
*/
30+
public class SchemaCodegenProvider implements CodegenExtensionProvider {
31+
32+
/**
33+
* Public constructor is required for {@link java.util.ServiceLoader}.
34+
*
35+
* @deprecated please do not use directly
36+
*/
37+
@Deprecated
38+
public SchemaCodegenProvider() {
39+
}
40+
41+
@Override
42+
public CodegenExtension create(CodegenContext ctx, TypeName generatorType) {
43+
return new SchemaCodegen(ctx);
44+
}
45+
46+
@Override
47+
public Set<TypeName> supportedAnnotations() {
48+
return Set.of(Types.JSON_SCHEMA_SCHEMA);
49+
}
50+
51+
}

0 commit comments

Comments
 (0)