diff --git a/docs/README.md b/docs/README.md
index 198b86113..21dda6a65 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -4,10 +4,9 @@ theme. Compiling the documentation is not included within the regular Maven buil
## Prerequisites
1. **Install Hugo**: see [installing Hugo](https://gohugo.io/getting-started/installing/) documentation on how to install Hugo.
-2. **Install JQ**: see [installing jq](https://jqlang.github.io/jq/download/) documentation on how to install jq.
-3. **Install NODE.js and npm** see [installing Node.js and npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) documentation on how to install Node.js and npm
-4. **Install Redocly CLI** see [installing Redocly CLI](https://redocly.com/docs/cli/installation/) documentation on how to install Redocly CLI
-5. **Install hawkBit**: run `mvn install` in the parent directory to generate the latest REST docs for hawkBit.
+2. **Install NODE.js and npm** see [installing Node.js and npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) documentation on how to install Node.js and npm
+3. **Install Redocly CLI** see [installing Redocly CLI](https://redocly.com/docs/cli/installation/) documentation on how to install Redocly CLI
+4. **Install hawkBit**: run `mvn install` in the parent directory to generate the latest REST docs for hawkBit.
## Build and Serve documentation
diff --git a/docs/build-htmls.sh b/docs/build-htmls.sh
index 0f0a61693..a1506c59e 100644
--- a/docs/build-htmls.sh
+++ b/docs/build-htmls.sh
@@ -24,7 +24,7 @@ else
fi
# Execute the npx command
-npx @redocly/cli build-docs ${CURRENT_DIR}/content/rest-api/mgmt.json -o ${CURRENT_DIR}/content/rest-api/mgmt.html
+npx @redocly/cli build-docs ${CURRENT_DIR}/content/rest-api/mgmt.yaml -o ${CURRENT_DIR}/content/rest-api/mgmt.html
if [ $? != 0 ]; then
echo "[ERROR] Failed to execute the Redoc CLI command form MGMT API."
@@ -34,7 +34,7 @@ else
fi
# Execute the npx command
-npx @redocly/cli build-docs ${CURRENT_DIR}/content/rest-api/ddi.json -o ${CURRENT_DIR}/content/rest-api/ddi.html
+npx @redocly/cli build-docs ${CURRENT_DIR}/content/rest-api/ddi.yaml -o ${CURRENT_DIR}/content/rest-api/ddi.html
if [ $? != 0 ]; then
echo "[ERROR] Failed to execute the Redoc CLI command form DDI API."
diff --git a/docs/pom.xml b/docs/pom.xml
index ad9af91a1..a4f17816d 100644
--- a/docs/pom.xml
+++ b/docs/pom.xml
@@ -72,11 +72,21 @@
org.eclipse.hawkbit
hawkbit-update-server
${project.version}
- openapi
- json
+ mgmt-openapi
+ yaml
true
${basedir}/content/rest-api
- openapi.json
+ mgmt.yaml
+
+
+ org.eclipse.hawkbit
+ hawkbit-update-server
+ ${project.version}
+ ddi-openapi
+ yaml
+ true
+ ${basedir}/content/rest-api
+ ddi.yaml
@@ -88,21 +98,6 @@
exec-maven-plugin
${exec-maven-plugin.version}
-
- split-docs
-
- exec
-
- install
-
- ${shell}
- ${project.basedir}
-
- ${shell.option}
- split-doc.${batch.ext}
-
-
-
build-htmls
diff --git a/docs/split-doc.sh b/docs/split-doc.sh
deleted file mode 100644
index 9125e90f2..000000000
--- a/docs/split-doc.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# Copyright (c) 2018 Bosch Software Innovations GmbH and others
-#
-# This program and the accompanying materials are made
-# available under the terms of the Eclipse Public License 2.0
-# which is available at https://www.eclipse.org/legal/epl-2.0/
-#
-# SPDX-License-Identifier: EPL-2.0
-#
-
-#!/bin/bash
-
-CURRENT_DIR=$(pwd)
-input_file=${CURRENT_DIR}/content/rest-api/openapi.json
-mgmt_file=${CURRENT_DIR}/content/rest-api/mgmt.json
-ddi_file=${CURRENT_DIR}/content/rest-api/ddi.json
-
-jq '
- .paths |= with_entries(
- select(
- reduce .value[] as $item (
- false;
- . or ($item.tags? | index("DDI Root Controller")) == null
- )
- )
- )
- | .tags |= map(select(.name | contains("DDI") | not))
- | .components.schemas = (.components.schemas | with_entries(select(.key | startswith("Ddi") | not)))
-' "$input_file" > "$mgmt_file"
-
-jq '
- .paths |= with_entries(
- select(
- reduce .value[] as $item (
- false;
- . or ($item.tags? | index("DDI Root Controller")) != null
- )
- )
- )
- | .tags |= map(select(.name | contains("DDI")))
- | .components.schemas = (
- .components.schemas
- | with_entries(
- select(
- (.key | startswith("Ddi"))
- or (.key | . == "Link")
- or (.key | . == "ExceptionInfo")
- )
- )
- )
-' "$input_file" > "$ddi_file"
-
diff --git a/hawkbit-runtime/hawkbit-update-server/pom.xml b/hawkbit-runtime/hawkbit-update-server/pom.xml
index 6b27beb3d..d62be5878 100644
--- a/hawkbit-runtime/hawkbit-update-server/pom.xml
+++ b/hawkbit-runtime/hawkbit-update-server/pom.xml
@@ -32,65 +32,63 @@
+
+ attach-artifacts-profile
+
+
+
+ !skipTests
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ attach-artifacts
+ package
+
+ attach-artifact
+
+
+
+
+ ${project.build.directory}/rest-api/mgmt-openapi.json
+ json
+ mgmt-openapi
+
+
+ ${project.build.directory}/rest-api/mgmt-openapi.yaml
+ yaml
+ mgmt-openapi
+
+
+ ${project.build.directory}/rest-api/ddi-openapi.json
+ json
+ ddi-openapi
+
+
+ ${project.build.directory}/rest-api/ddi-openapi.yaml
+ yaml
+ ddi-openapi
+
+
+
+
+
+
+
+
+
-
-
- org.springdoc
- springdoc-openapi-maven-plugin
- 1.4
-
-
-
- generate-json
- integration-test
-
- generate
-
-
- openapi.json
- http://localhost:8080/v3/api-docs
- true
-
-
-
-
- generate-yaml
- integration-test
-
- generate
-
-
- openapi.yaml
- http://localhost:8080/v3/api-docs.yaml
- true
-
-
-
-
org.springframework.boot
spring-boot-maven-plugin
-
- -Dspring.application.admin.enabled=true
- org.eclipse.hawkbit.app.Start
-
-
- pre-integration-test
- pre-api-docs-generation
-
- start
-
-
-
- post-api-docs-generation
- post-integration-test
-
- stop
-
-
repackage
diff --git a/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/RestApiDocTest.java b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/RestApiDocTest.java
new file mode 100644
index 000000000..79f1c6ffa
--- /dev/null
+++ b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/RestApiDocTest.java
@@ -0,0 +1,154 @@
+/**
+ * Copyright (c) 2023 Bosch.IO GmbH and others
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.eclipse.hawkbit.app;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.ActiveProfiles;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles({"maven"})
+class RestApiDocTest {
+ private static final String MANAGEMENT_PREFIX = "mgmt-openapi";
+ private static final String DDI_PREFIX = "ddi-openapi";
+ private static final String TARGET_DIRECTORY = "target/rest-api/";
+
+ @LocalServerPort
+ private int port;
+
+ @Autowired
+ private TestRestTemplate restTemplate;
+
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ @Test
+ void openapiJson() throws IOException {
+ ResponseEntity response =
+ restTemplate.getForEntity("http://localhost:" + port + "/v3/api-docs", String.class);
+ String openapiDoc = response.getBody();
+ assertThat(openapiDoc).isNotNull();
+ splitDocumentation(openapiDoc);
+ }
+
+ private void splitDocumentation(String json) throws IOException {
+ processDocumentation(json, true);
+ processDocumentation(json, false);
+ }
+
+ private void processDocumentation(String json, boolean isMgmt) throws IOException {
+ JsonNode rootNode = objectMapper.readTree(json);
+ updateJsonNodeForApi(rootNode, isMgmt);
+ saveDocumentation(rootNode, isMgmt);
+ }
+
+ private void updateJsonNodeForApi(JsonNode rootNode, boolean isMgmt) {
+ removeTags(rootNode, isMgmt);
+ removePaths(rootNode, isMgmt);
+ removeComponents(rootNode, isMgmt);
+ }
+
+ private void removeTags(JsonNode rootNode, boolean isMgmt) {
+ ArrayNode tagsNode = (ArrayNode) rootNode.get("tags");
+ ArrayNode modifiedTagsNode = objectMapper.createArrayNode();
+
+ for (JsonNode tagNode : tagsNode) {
+ String tagName = tagNode.get("name").asText();
+ if (isMgmt != tagName.startsWith("DDI")) {
+ modifiedTagsNode.add(tagNode);
+ }
+ }
+
+ ((ObjectNode) rootNode).set("tags", modifiedTagsNode);
+ }
+ private void removePaths(JsonNode rootNode, boolean isMgmt) {
+ ObjectNode pathsNode = (ObjectNode) rootNode.get("paths");
+ List fieldsToRemove = new ArrayList<>();
+ pathsNode.fieldNames().forEachRemaining(fieldName -> {
+ JsonNode pathNode = pathsNode.get(fieldName);
+ pathNode.fieldNames().forEachRemaining(path -> {
+ JsonNode methodNode = pathNode.get(path);
+ JsonNode tagsNode = methodNode.get("tags");
+ if (tagsNode != null) {
+ for (JsonNode tagNode : tagsNode) {
+ String tag = tagNode.asText();
+ if (isMgmt == tag.startsWith("DDI")) {
+ fieldsToRemove.add(fieldName);
+ break;
+ }
+ }
+ }
+ });
+ });
+ fieldsToRemove.forEach(pathsNode::remove);
+ }
+
+ private void removeComponents(JsonNode rootNode, boolean isMgmt) {
+ ObjectNode schemasNode = (ObjectNode) rootNode.get("components").get("schemas");
+
+ List fieldsToRemove = new ArrayList<>();
+ schemasNode.fieldNames().forEachRemaining(fieldName -> {
+ if (shouldDeleteComponent(fieldName, isMgmt)) {
+ fieldsToRemove.add(fieldName);
+ }
+ });
+ fieldsToRemove.forEach(schemasNode::remove);
+ }
+
+ private boolean shouldDeleteComponent(String fieldName, boolean isMgmt) {
+ if (isMgmt) {
+ return fieldName.startsWith("Ddi");
+ }
+ return !(fieldName.startsWith("Ddi") || fieldName.equals("Link") || fieldName.equals("ExceptionInfo"));
+
+ }
+
+ private void saveDocumentation(JsonNode rootNode, boolean isMgmt) throws IOException {
+ String prefix = isMgmt ? MANAGEMENT_PREFIX : DDI_PREFIX;
+ saveAsJson(rootNode, prefix);
+ saveAsYaml(rootNode, prefix);
+ }
+
+ private void saveAsJson(JsonNode rootNode, String prefix) throws IOException {
+ Path targetPath = getTargetPath(prefix, ".json");
+ Files.writeString(targetPath, objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode));
+ }
+
+ private void saveAsYaml(JsonNode rootNode, String prefix) throws IOException {
+ YAMLMapper yamlMapper = new YAMLMapper();
+ Path targetPath = getTargetPath(prefix, ".yaml");
+ Files.writeString(targetPath, yamlMapper.writeValueAsString(rootNode));
+ }
+
+ private Path getTargetPath(String prefix, String extension) throws IOException {
+ Path targetPath = Paths.get(TARGET_DIRECTORY + prefix + extension);
+ Files.createDirectories(targetPath.getParent());
+ return targetPath;
+ }
+}
+