Skip to content

Commit

Permalink
adding jsonschema2adoc minisite task
Browse files Browse the repository at this point in the history
  • Loading branch information
rmannibucau committed May 20, 2023
1 parent 469889a commit 7850eef
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 7 deletions.
9 changes: 9 additions & 0 deletions _documentation/src/main/minisite/content/mojos.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,15 @@ Using `type=jsonschema` (recommended) or `type=io.yupiik.maven.service.action.bu
NOTE: the model classes can use a custom `@Description(title,description)` annotation (note that `@Doc` is also supported and `value` method can be used instead of `description`).
See `JsonDocExtractor` for more details.

==== JSON-Schema to asciidoc

Using `type=jsonschema2adoc` (recommended) or `type=io.yupiik.maven.service.action.builtin.JsonSchema2AdocGenerator` will generate an asciidoctor document from a JSON-Schema file (intended to be used with fusion mainly):

. `schema`: location of the schema (JSON).
. `root`: key under `schemas` map of the root schema to generate a doc for.
. `levelPrefix`: prefix to set for title (default `=`).
. `output`: where to generate the `.adoc`.

==== OpenMetrics renderer

Using `type=openmetrics2adoc` (recommended) or `type=io.yupiik.tools.minisite.action.builtin.OpenMetricsToAsciidoc` will generate an asciidoctor form from an OpenMetrics export.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2020 - 2023 - Yupiik SAS - https://www.yupiik.com
* 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.yupiik.maven.service.action.builtin;

import io.yupiik.maven.service.action.builtin.json.JsonSchema2Adoc;
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
import jakarta.json.bind.JsonbConfig;
import org.apache.johnzon.jsonschema.generator.Schema;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.function.Predicate;

import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable;

public class JsonSchema2AdocGenerator implements Runnable {
private final Map<String, String> configuration;

public JsonSchema2AdocGenerator(final Map<String, String> configuration) {
this.configuration = configuration;
}

@Override
public void run() {
final String schema = requireNonNull(configuration.get("schema"), "schema not set");
final Schemas schemas = loadSchema(schema);
final Schema root = requireNonNull(
schemas.schemas.get(requireNonNull(configuration.get("root"), "no root (schema) set in configuration")),
"root schema not found");
final String levelPrefix = configuration.getOrDefault("levelPrefix", "=");
final Predicate<Schema> schemaPredicate = s -> s.getRef() != null;
final JsonSchema2Adoc adoc = newAdocRenderer(root, levelPrefix, schemaPredicate, schemas);
adoc.prepare(root);
final Path out = Path.of(requireNonNull(configuration.get("output"), "output not set"));
try {
if (out.getParent() != null) {
Files.createDirectories(out.getParent());
}
Files.writeString(out, adoc.get().toString());
} catch (final IOException e) {
throw new IllegalStateException(e);
}
}

protected ForceEmptyMetaJsonSchema2Adoc newAdocRenderer(
final Schema root, final String levelPrefix, final Predicate<Schema> schemaPredicate, final Schemas schemas) {
return new ForceEmptyMetaJsonSchema2Adoc(levelPrefix, root, schemaPredicate, schemas);
}

private Schemas loadSchema(final String location) {
final Path path = Path.of(location);
try (final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty("johnzon.skip-cdi", true))) {
return jsonb.fromJson(Files.readString(path), Schemas.class);
} catch (final Exception e) {
throw new IllegalArgumentException(e);
}
}

public static class Schemas {
public Map<String, Schema> schemas;
}

protected static class ForceEmptyMetaJsonSchema2Adoc extends JsonSchema2Adoc {
private final Schemas schemas;

protected ForceEmptyMetaJsonSchema2Adoc(final String levelPrefix, final Schema root, final Predicate<Schema> shouldIgnore,
final Schemas schemas) {
super(levelPrefix, root, shouldIgnore);
this.schemas = schemas;
}

@Override
public void prepare(final Schema in) {
if (in.getTitle() == null) {
in.setTitle(ofNullable(in.getId()).orElse("Model"));
}
if (in.getDescription() == null) {
in.setDescription("");
}
if (in.getItems() != null && in.getItems().getRef() != null && in.getItems().getRef().startsWith("#/schemas/")) {
final Schema old = in.getItems();
in.setItems(ofNullable(schemas.schemas.get(in.getItems().getRef().substring("#/schemas/".length()))).orElse(old));
}
if (in.getRef() != null && in.getRef().startsWith("#/schemas/")) {
final Schema ref = schemas.schemas.get(in.getRef().substring("#/schemas/".length()));
if (ref != null) {
super.prepare(ref);
return;
}
}
super.prepare(in);
}

@Override
protected JsonSchema2Adoc nestedJsonSchema2Adoc(final Schema value, final String nextLevelPrefix) {
return new ForceEmptyMetaJsonSchema2Adoc(nextLevelPrefix, value, shouldIgnore, schemas);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@
@Log
@RequiredArgsConstructor
public class JsonSchema2Adoc implements Supplier<StringBuilder> {
private final String levelPrefix;
private final Schema schema;
private final Predicate<Schema> shouldIgnore;
private final JsonDocExtractor docExtractor = new JsonDocExtractor();
protected final String levelPrefix;
protected final Schema schema;
protected final Predicate<Schema> shouldIgnore;
protected final JsonDocExtractor docExtractor = new JsonDocExtractor();

public void prepare(final Schema in) {
final Schema schema = ofNullable(in).orElse(this.schema);
Expand Down Expand Up @@ -170,8 +170,9 @@ public StringBuilder get() {
nestedObjects.entrySet().stream()
.filter(e -> validObjectKeys.contains(e.getKey()))
.forEach(e -> {
final String anchorBase = toAnchor(e.getValue());
final StringBuilder content = new JsonSchema2Adoc(getNextLevelPrefix(), e.getValue(), shouldIgnore).get();
final Schema value = e.getValue();
final String anchorBase = toAnchor(value);
final StringBuilder content = nestedJsonSchema2Adoc(value, getNextLevelPrefix()).get();
if (anchorBase != null && content.length() > 0) {
main.append("[#").append(anchorBase).append("]\n").append(content).append("\n\n");
}
Expand All @@ -184,7 +185,7 @@ public StringBuilder get() {
.forEach(e -> {
final Schema items = e.getValue().getItems();
final String anchorBase = toAnchor(items);
final StringBuilder content = new JsonSchema2Adoc(getNextLevelPrefix(), items, shouldIgnore).get();
final StringBuilder content = nestedJsonSchema2Adoc(items, getNextLevelPrefix()).get();
if (anchorBase != null && content.length() > 0) {
main.append("[#").append(anchorBase).append("]\n").append(content).append("\n\n");
}
Expand All @@ -201,6 +202,10 @@ public StringBuilder get() {
return main;
}

protected JsonSchema2Adoc nestedJsonSchema2Adoc(final Schema value, final String nextLevelPrefix) {
return new JsonSchema2Adoc(nextLevelPrefix, value, shouldIgnore);
}

private String extractDefaultValue(final Schema schema) {
return schema.getDefaultValue() != null ? " Default Value: `" +
String.valueOf(schema.getDefaultValue()).replace("|", "\\|") + "`." : "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.yupiik.tools.minisite;

import io.yupiik.maven.service.action.builtin.CopyFile;
import io.yupiik.maven.service.action.builtin.JsonSchema2AdocGenerator;
import io.yupiik.maven.service.action.builtin.JsonSchemaGenerator;
import io.yupiik.maven.service.action.builtin.MojoDocumentationGeneration;
import io.yupiik.tools.minisite.action.builtin.DownloadAndUnzip;
Expand Down Expand Up @@ -52,6 +53,8 @@ private Class<? extends Runnable> findActionClass(final String name) throws Clas
return MojoDocumentationGeneration.class;
case "copy":
return CopyFile.class;
case "jsonschema2adoc":
return JsonSchema2AdocGenerator.class;
case "jsonschema":
return JsonSchemaGenerator.class;
case "openmetrics2adoc":
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2020 - 2023 - Yupiik SAS - https://www.yupiik.com
* 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.yupiik.maven.service.action.builtin;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;

class JsonSchema2AdocGeneratorTest {
@Test
void run(@TempDir final Path work) throws IOException {
final var schema = Files.writeString(Files.createDirectories(work).resolve("schema.json"), "{\n" +
" \"schemas\": {\n" +
" \"io.yupiik.test.MyRootObject\": {\n" +
" \"type\": \"object\",\n" +
" \"properties\": {\n" +
" \"aliases\": {\n" +
" \"type\": \"array\",\n" +
" \"items\": {\n" +
" \"nullable\": true,\n" +
" \"$ref\": \"#/schemas/io.yupiik.test.MyObject\"\n" +
" }\n" +
" }\n" +
" },\n" +
" \"$id\": \"io.yupiik.test.MyRootObject\"\n" +
" },\n" +
" \"io.yupiik.test.MyObject\": {\n" +
" \"type\": \"object\",\n" +
" \"properties\": {\n" +
" \"name\": {\n" +
" \"nullable\": true,\n" +
" \"type\": \"string\"\n" +
" },\n" +
" \"value\": {\n" +
" \"nullable\": true,\n" +
" \"type\": \"string\"\n" +
" }\n" +
" },\n" +
" \"$id\": \"io.yupiik.test.MyObject\"\n" +
" }\n" +
" }\n" +
"}");
final var output = work.resolve("output");
final var conf = Map.of(
"schema", schema.toString(),
"output", output.toString(),
"root", "io.yupiik.test.MyRootObject");
new JsonSchema2AdocGenerator(conf).run();
assertEquals("" +
"= io.yupiik.test.MyRootObject\n" +
"\n" +
"[cols=\"2,2m,1,5\", options=\"header\"]\n" +
".io.yupiik.test.MyRootObject\n" +
"|===\n" +
"|Name|JSON Name|Type|Description\n" +
"|<<io.yupiik.test.MyObject>>|aliases|array of object|-\n" +
"|===\n" +
"\n" +
"[#io.yupiik.test.MyObject]\n" +
"== Model\n" +
"\n" +
"[cols=\"2,2m,1,5\", options=\"header\"]\n" +
".Model\n" +
"|===\n" +
"|Name|JSON Name|Type|Description\n" +
"|name|name|string|-\n" +
"|value|value|string|-\n" +
"|===\n" +
"\n" +
"\n" +
"",
Files.readString(output));
}
}

0 comments on commit 7850eef

Please sign in to comment.