Move REST API Doc build in docs/ (#1492)

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2023-11-29 09:52:40 +02:00
committed by GitHub
parent ad876720e8
commit b362698af8
9 changed files with 192 additions and 127 deletions

4
docs/.gitignore vendored
View File

@@ -6,8 +6,8 @@ content/rest-api/*.yaml
node_modules node_modules
package.json package.json
package-lock.json package-lock.json
# themse # themes
themes themes/hugo-material-docs
# hugo # hugo
public public
.hugo_build.lock .hugo_build.lock

View File

@@ -1,6 +1,6 @@
<!-- <!--
Copyright (c) 2018 Bosch Software Innovations GmbH and others Copyright (c) 2023 Bosch.IO GmbH and others
This program and the accompanying materials are made This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0 available under the terms of the Eclipse Public License 2.0
@@ -16,8 +16,9 @@
<artifactId>hawkbit-parent</artifactId> <artifactId>hawkbit-parent</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<artifactId>docs</artifactId> <artifactId>docs</artifactId>
<packaging>pom</packaging> <!-- <packaging>pom</packaging>-->
<name>hawkBit :: Documentation</name> <name>hawkBit :: Documentation</name>
<description>Documentation for hawkBit</description> <description>Documentation for hawkBit</description>
@@ -56,43 +57,6 @@
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-openapi</id>
<phase>prepare-package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-update-server</artifactId>
<version>${project.version}</version>
<classifier>mgmt-openapi</classifier>
<type>yaml</type>
<overWrite>true</overWrite>
<outputDirectory>${basedir}/content/rest-api</outputDirectory>
<destFileName>mgmt.yaml</destFileName>
</artifactItem>
<artifactItem>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-update-server</artifactId>
<version>${project.version}</version>
<classifier>ddi-openapi</classifier>
<type>yaml</type>
<overWrite>true</overWrite>
<outputDirectory>${basedir}/content/rest-api</outputDirectory>
<destFileName>ddi.yaml</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId> <artifactId>exec-maven-plugin</artifactId>
@@ -162,4 +126,29 @@
</plugins> </plugins>
</build> </build>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-boot-starter</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-test</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project> </project>

View File

@@ -0,0 +1,38 @@
/**
* 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.doc;
import org.eclipse.hawkbit.autoconfigure.security.EnableHawkbitManagedSecurityConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* A {@link SpringBootApplication} annotated class with a main method to start.
* The minimal configuration for the stand alone hawkBit server.
*
*/
@SpringBootApplication
@EnableHawkbitManagedSecurityConfiguration
// Exception squid:S1118 - Spring boot standard behavior
@SuppressWarnings({ "squid:S1118" })
public class Start {
/**
* Main method to start the spring-boot application.
*
* @param args
* the VM arguments.
*/
// Exception squid:S2095 - Spring boot standard behavior
@SuppressWarnings({ "squid:S2095" })
public static void main(final String[] args) {
SpringApplication.run(Start.class, args);
}
}

View File

@@ -0,0 +1,41 @@
#
# 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
#
# User Security
spring.security.user.name=admin
spring.security.user.password={noop}admin
spring.main.allow-bean-definition-overriding=true
# Http Encoding
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
# DDI authentication configuration
hawkbit.server.ddi.security.authentication.anonymous.enabled=false
hawkbit.server.ddi.security.authentication.targettoken.enabled=false
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false
# Optional events
hawkbit.server.repository.publish-target-poll-event=false
## Configuration for DMF/RabbitMQ integration
hawkbit.dmf.rabbitmq.enabled=false
# Swagger Configuration
#springdoc.swagger-ui.path=/update-server-documentation
#springdoc.api-docs.path=/update-server-api-docs
springdoc.show-oauth2-endpoints=true
springdoc.api-docs.version=openapi_3_0
springdoc.show-login-endpoint=true
springdoc.packages-to-scan=org.eclipse.hawkbit.mgmt,org.eclipse.hawkbit.ddi
springdoc.swagger-ui.oauth2RedirectUrl=/login/oauth2/code/suite
springdoc.paths-to-exclude=/system/**

View File

@@ -0,0 +1,14 @@
______ _ _ _ _ ____ _ _
| ____| | (_) | | | | | _ \(_) |
| |__ ___| |_ _ __ ___ ___ | |__ __ ___ _| | _| |_) |_| |_
| __| / __| | | '_ \/ __|/ _ \ | '_ \ / _` \ \ /\ / / |/ / _ <| | __|
| |___| (__| | | |_) \__ \ __/ | | | | (_| |\ V V /| <| |_) | | |_
|______\___|_|_| .__/|___/\___| |_| |_|\__,_| \_/\_/ |_|\_\____/|_|\__|
| |
|_|
Eclipse hawkBit Update Server ${application.formatted-version}
using Spring Boot ${spring-boot.formatted-version}
Go to https://www.eclipse.org/hawkbit for more information.

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015 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
-->
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.eclipse.hawkbit.eventbus.DeadEventListener" level="WARN" />
<Logger name="org.springframework.boot.actuate.audit.listener.AuditListener" level="WARN" />
<Logger name="org.hibernate.validator.internal.util.Version" level="WARN" />
<!-- Security Log with hints on potential attacks -->
<logger name="server-security" level="INFO" />
<!-- Suppressing "More than one Servlet Mapping defined. WebSocket may not work"
error due to the way VaadinServletConfiguration configures the endpoints mapping ("/UI" and "/UI/*").
At the end only the first "/UI" is taken for websocket communication. -->
<logger name="org.atmosphere.util.IOUtils" level="OFF" />
<Root level="INFO">
<appender-ref ref="CONSOLE" />
</Root>
</configuration>

View File

@@ -8,7 +8,7 @@
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipse.hawkbit.app; package org.eclipse.hawkbit.doc;
import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
@@ -36,9 +36,11 @@ import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith({SharedSqlTestDatabaseExtension.class}) @ExtendWith({SharedSqlTestDatabaseExtension.class})
class RestApiDocTest { class RestApiDocTest {
private static final String MANAGEMENT_PREFIX = "mgmt-openapi"; private static final String MANAGEMENT_PREFIX = "mgmt";
private static final String DDI_PREFIX = "ddi-openapi"; private static final String DDI_PREFIX = "ddi";
private static final String TARGET_DIRECTORY = "target/rest-api/"; private static final String TARGET_DIRECTORY = "content/rest-api/";
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@LocalServerPort @LocalServerPort
private int port; private int port;
@@ -46,39 +48,37 @@ class RestApiDocTest {
@Autowired @Autowired
private TestRestTemplate restTemplate; private TestRestTemplate restTemplate;
private final ObjectMapper objectMapper = new ObjectMapper();
@Test @Test
void openapiJson() throws IOException { void openapiJson() throws IOException {
ResponseEntity<String> response = final ResponseEntity<String> response =
restTemplate.getForEntity("http://localhost:" + port + "/v3/api-docs", String.class); restTemplate.getForEntity("http://localhost:" + port + "/v3/api-docs", String.class);
String openapiDoc = response.getBody(); final String openapiDoc = response.getBody();
assertThat(openapiDoc).isNotNull(); assertThat(openapiDoc).isNotNull();
splitDocumentation(openapiDoc); splitDocumentation(openapiDoc);
} }
private void splitDocumentation(String json) throws IOException { private static void splitDocumentation(final String json) throws IOException {
processDocumentation(json, true); processDocumentation(json, true);
processDocumentation(json, false); processDocumentation(json, false);
} }
private void processDocumentation(String json, boolean isMgmt) throws IOException { private static void processDocumentation(final String json, final boolean isMgmt) throws IOException {
JsonNode rootNode = objectMapper.readTree(json); final JsonNode rootNode = OBJECT_MAPPER.readTree(json);
updateJsonNodeForApi(rootNode, isMgmt); updateJsonNodeForApi(rootNode, isMgmt);
saveDocumentation(rootNode, isMgmt); saveDocumentation(rootNode, isMgmt);
} }
private void updateJsonNodeForApi(JsonNode rootNode, boolean isMgmt) { private static void updateJsonNodeForApi(final JsonNode rootNode, final boolean isMgmt) {
removeTags(rootNode, isMgmt); removeTags(rootNode, isMgmt);
removePaths(rootNode, isMgmt); removePaths(rootNode, isMgmt);
removeComponents(rootNode, isMgmt); removeComponents(rootNode, isMgmt);
} }
private void removeTags(JsonNode rootNode, boolean isMgmt) { private static void removeTags(final JsonNode rootNode, final boolean isMgmt) {
ArrayNode tagsNode = (ArrayNode) rootNode.get("tags"); final ArrayNode tagsNode = (ArrayNode) rootNode.get("tags");
ArrayNode modifiedTagsNode = objectMapper.createArrayNode(); final ArrayNode modifiedTagsNode = OBJECT_MAPPER.createArrayNode();
for (JsonNode tagNode : tagsNode) { for (final JsonNode tagNode : tagsNode) {
String tagName = tagNode.get("name").asText(); String tagName = tagNode.get("name").asText();
if (isMgmt != tagName.startsWith("DDI")) { if (isMgmt != tagName.startsWith("DDI")) {
modifiedTagsNode.add(tagNode); modifiedTagsNode.add(tagNode);
@@ -87,17 +87,17 @@ class RestApiDocTest {
((ObjectNode) rootNode).set("tags", modifiedTagsNode); ((ObjectNode) rootNode).set("tags", modifiedTagsNode);
} }
private void removePaths(JsonNode rootNode, boolean isMgmt) { private static void removePaths(final JsonNode rootNode, final boolean isMgmt) {
ObjectNode pathsNode = (ObjectNode) rootNode.get("paths"); final ObjectNode pathsNode = (ObjectNode) rootNode.get("paths");
List<String> fieldsToRemove = new ArrayList<>(); final List<String> fieldsToRemove = new ArrayList<>();
pathsNode.fieldNames().forEachRemaining(fieldName -> { pathsNode.fieldNames().forEachRemaining(fieldName -> {
JsonNode pathNode = pathsNode.get(fieldName); final JsonNode pathNode = pathsNode.get(fieldName);
pathNode.fieldNames().forEachRemaining(path -> { pathNode.fieldNames().forEachRemaining(path -> {
JsonNode methodNode = pathNode.get(path); final JsonNode methodNode = pathNode.get(path);
JsonNode tagsNode = methodNode.get("tags"); final JsonNode tagsNode = methodNode.get("tags");
if (tagsNode != null) { if (tagsNode != null) {
for (JsonNode tagNode : tagsNode) { for (JsonNode tagNode : tagsNode) {
String tag = tagNode.asText(); final String tag = tagNode.asText();
if (isMgmt == tag.startsWith("DDI")) { if (isMgmt == tag.startsWith("DDI")) {
fieldsToRemove.add(fieldName); fieldsToRemove.add(fieldName);
break; break;
@@ -109,8 +109,8 @@ class RestApiDocTest {
fieldsToRemove.forEach(pathsNode::remove); fieldsToRemove.forEach(pathsNode::remove);
} }
private void removeComponents(JsonNode rootNode, boolean isMgmt) { private static void removeComponents(final JsonNode rootNode, final boolean isMgmt) {
ObjectNode schemasNode = (ObjectNode) rootNode.get("components").get("schemas"); final ObjectNode schemasNode = (ObjectNode) rootNode.get("components").get("schemas");
List<String> fieldsToRemove = new ArrayList<>(); List<String> fieldsToRemove = new ArrayList<>();
schemasNode.fieldNames().forEachRemaining(fieldName -> { schemasNode.fieldNames().forEachRemaining(fieldName -> {
@@ -121,7 +121,7 @@ class RestApiDocTest {
fieldsToRemove.forEach(schemasNode::remove); fieldsToRemove.forEach(schemasNode::remove);
} }
private boolean shouldDeleteComponent(String fieldName, boolean isMgmt) { private static boolean shouldDeleteComponent(final String fieldName, final boolean isMgmt) {
if (isMgmt) { if (isMgmt) {
return fieldName.startsWith("Ddi"); return fieldName.startsWith("Ddi");
} }
@@ -129,25 +129,25 @@ class RestApiDocTest {
} }
private void saveDocumentation(JsonNode rootNode, boolean isMgmt) throws IOException { private static void saveDocumentation(final JsonNode rootNode, final boolean isMgmt) throws IOException {
String prefix = isMgmt ? MANAGEMENT_PREFIX : DDI_PREFIX; final String prefix = isMgmt ? MANAGEMENT_PREFIX : DDI_PREFIX;
saveAsJson(rootNode, prefix); saveAsJson(rootNode, prefix);
saveAsYaml(rootNode, prefix); saveAsYaml(rootNode, prefix);
} }
private void saveAsJson(JsonNode rootNode, String prefix) throws IOException { private static void saveAsJson(final JsonNode rootNode, final String prefix) throws IOException {
Path targetPath = getTargetPath(prefix, ".json"); final Path targetPath = getTargetPath(prefix, ".json");
Files.writeString(targetPath, objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode)); Files.writeString(targetPath, OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode));
} }
private void saveAsYaml(JsonNode rootNode, String prefix) throws IOException { private static void saveAsYaml(final JsonNode rootNode, final String prefix) throws IOException {
YAMLMapper yamlMapper = new YAMLMapper(); final YAMLMapper yamlMapper = new YAMLMapper();
Path targetPath = getTargetPath(prefix, ".yaml"); final Path targetPath = getTargetPath(prefix, ".yaml");
Files.writeString(targetPath, yamlMapper.writeValueAsString(rootNode)); Files.writeString(targetPath, yamlMapper.writeValueAsString(rootNode));
} }
private Path getTargetPath(String prefix, String extension) throws IOException { private static Path getTargetPath(final String prefix, final String extension) throws IOException {
Path targetPath = Paths.get(TARGET_DIRECTORY + prefix + extension); final Path targetPath = Paths.get(TARGET_DIRECTORY + prefix + extension);
Files.createDirectories(targetPath.getParent()); Files.createDirectories(targetPath.getParent());
return targetPath; return targetPath;
} }

View File

@@ -32,56 +32,6 @@
</plugins> </plugins>
</build> </build>
</profile> </profile>
<profile>
<id>attach-artifacts-profile</id>
<!-- This profile activates when -DskipTests is not provided -->
<activation>
<property>
<name>!skipTests</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>attach-artifacts</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>${project.build.directory}/rest-api/mgmt-openapi.json</file>
<type>json</type>
<classifier>mgmt-openapi</classifier>
</artifact>
<artifact>
<file>${project.build.directory}/rest-api/mgmt-openapi.yaml</file>
<type>yaml</type>
<classifier>mgmt-openapi</classifier>
</artifact>
<artifact>
<file>${project.build.directory}/rest-api/ddi-openapi.json</file>
<type>json</type>
<classifier>ddi-openapi</classifier>
</artifact>
<artifact>
<file>${project.build.directory}/rest-api/ddi-openapi.yaml</file>
<type>yaml</type>
<classifier>ddi-openapi</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles> </profiles>
<build> <build>
<plugins> <plugins>