Adapt API to OpenApiObject removal 05/108305/35
authorIvan Hrasko <ivan.hrasko@pantheon.tech>
Tue, 10 Oct 2023 13:53:55 +0000 (15:53 +0200)
committerIvan Hrasko <ivan.hrasko@pantheon.tech>
Fri, 8 Dec 2023 13:52:11 +0000 (13:52 +0000)
Adapted REST API to use new logic.

Adapted tests for OpenApiInputStream.

JIRA: NETCONF-938
Change-Id: Idc6a35bb29f77c37e7ac70e873fd6b720fc5584d
Signed-off-by: Ivan Hrasko <ivan.hrasko@pantheon.tech>
Signed-off-by: lubos-cicut <lubos.cicut@pantheon.tech>
Signed-off-by: Yaroslav Lastivka <yaroslav.lastivka@pantheon.tech>
13 files changed:
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/api/OpenApiService.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/BaseYangOpenApiGenerator.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/OpenApiInputStream.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/OpenApiServiceImpl.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/PathsStream.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/SchemasStream.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/jaxrs/JaxbContextResolver.java [new file with mode: 0644]
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/model/InfoEntity.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/model/OpenApiObject.java [deleted file]
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/model/security/Http.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/model/security/SecuritySchemeObject.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/mountpoints/MountPointOpenApi.java
restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/impl/AbstractDocumentTest.java

index e6d2237e7e6e9bd715f85fb31db0638bf83e8329..7a243e049dcc247a4e6c60a17ef871b37fcdbbb7 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.restconf.openapi.api;
 
+import java.io.IOException;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -28,7 +29,7 @@ public interface OpenApiService {
     @GET
     @Path("/single")
     @Produces(MediaType.APPLICATION_JSON)
-    Response getAllModulesDoc(@Context UriInfo uriInfo);
+    Response getAllModulesDoc(@Context UriInfo uriInfo) throws IOException;
 
     /**
      * Generates Swagger compliant document listing APIs for module.
@@ -37,7 +38,7 @@ public interface OpenApiService {
     @Path("/{module}({revision})")
     @Produces(MediaType.APPLICATION_JSON)
     Response getDocByModule(@PathParam("module") String module, @PathParam("revision") String revision,
-                            @Context UriInfo uriInfo);
+                            @Context UriInfo uriInfo) throws IOException;
 
     /**
      * Redirects to embedded swagger ui.
@@ -65,7 +66,7 @@ public interface OpenApiService {
     @Produces(MediaType.APPLICATION_JSON)
     Response getMountDocByModule(@PathParam("instance") String instanceNum,
                                  @PathParam("module") String module, @PathParam("revision") String revision,
-                                 @Context UriInfo uriInfo);
+                                 @Context UriInfo uriInfo) throws IOException;
 
     /**
      * Generates Swagger compliant document listing APIs for all modules of mount point.
@@ -73,5 +74,5 @@ public interface OpenApiService {
     @GET
     @Path("/mounts/{instance}")
     @Produces(MediaType.APPLICATION_JSON)
-    Response getMountDoc(@PathParam("instance") String instanceNum, @Context UriInfo uriInfo);
+    Response getMountDoc(@PathParam("instance") String instanceNum, @Context UriInfo uriInfo) throws IOException;
 }
index 2f6648b4741aab614c8631f381dc3df91e525a70..9661103fa17ce2e845b3753b89f298d5fb8423d3 100644 (file)
@@ -18,14 +18,12 @@ import static org.opendaylight.restconf.openapi.util.RestDocgenUtil.resolveFullN
 import static org.opendaylight.restconf.openapi.util.RestDocgenUtil.resolvePathArgumentsName;
 
 import com.google.common.base.Preconditions;
-import com.google.common.collect.Range;
 import java.io.IOException;
 import java.time.format.DateTimeParseException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -36,15 +34,10 @@ import java.util.stream.Collectors;
 import javax.ws.rs.core.UriInfo;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
-import org.opendaylight.restconf.openapi.model.Components;
-import org.opendaylight.restconf.openapi.model.Info;
-import org.opendaylight.restconf.openapi.model.OpenApiObject;
 import org.opendaylight.restconf.openapi.model.Operation;
 import org.opendaylight.restconf.openapi.model.Parameter;
 import org.opendaylight.restconf.openapi.model.Path;
 import org.opendaylight.restconf.openapi.model.Schema;
-import org.opendaylight.restconf.openapi.model.Server;
-import org.opendaylight.restconf.openapi.model.security.Http;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
@@ -74,20 +67,9 @@ public abstract class BaseYangOpenApiGenerator {
 
     private static final Logger LOG = LoggerFactory.getLogger(BaseYangOpenApiGenerator.class);
     private static final String CONTROLLER_RESOURCE_NAME = "Controller";
-
-    public static final String API_VERSION = "1.0.0";
-    public static final String OPEN_API_VERSION = "3.0.3";
     public static final String BASE_PATH = "/";
     public static final String MODULE_NAME_SUFFIX = "_module";
-    public static final String BASIC_AUTH_NAME = "basicAuth";
-    public static final Http OPEN_API_BASIC_AUTH = new Http("basic", null, null);
-    public static final List<Map<String, List<String>>> SECURITY = List.of(Map.of(BASIC_AUTH_NAME, List.of()));
-    public static final String DESCRIPTION = """
-        We are providing full API for configurational data which can be edited (by POST, PUT, PATCH and DELETE).
-        For operational data we only provide GET API.\n
-        For majority of request you can see only config data in examples. That is because we can show only one example
-        per request. The exception when you can see operational data in example is when data are representing
-        operational (config false) container with no config data in it.""";
+    public static final List<Map<String, List<String>>> SECURITY = List.of(Map.of("basicAuth", List.of()));
 
     private final DOMSchemaService schemaService;
 
@@ -95,62 +77,27 @@ public abstract class BaseYangOpenApiGenerator {
         this.schemaService = requireNonNull(schemaService);
     }
 
-    public OpenApiObject getControllerModulesDoc(final UriInfo uriInfo, final DefinitionNames definitionNames) {
+    public OpenApiInputStream getControllerModulesDoc(final UriInfo uriInfo) throws IOException {
         final var context = requireNonNull(schemaService.getGlobalContext());
         final var schema = createSchemaFromUriInfo(uriInfo);
         final var host = createHostFromUriInfo(uriInfo);
         final var title = "Controller modules of RESTCONF";
-        final var info = new Info(API_VERSION, title, DESCRIPTION);
-        final var servers = List.of(new Server(schema + "://" + host + BASE_PATH));
-
-        final var paths = new HashMap<String, Path>();
-        final var schemas = new HashMap<String, Schema>();
-        for (final var module : getSortedModules(context)) {
-            LOG.debug("Working on [{},{}]...", module.getName(), module.getQNameModule().getRevision().orElse(null));
-            schemas.putAll(getSchemas(module, context, definitionNames, false));
-            paths.putAll(getPaths(module, "", CONTROLLER_RESOURCE_NAME, context, definitionNames, false));
-        }
-
-        final var components = new Components(schemas, Map.of(BASIC_AUTH_NAME, OPEN_API_BASIC_AUTH));
-        return new OpenApiObject(OPEN_API_VERSION, info, servers, paths, components, SECURITY);
+        final var url = schema + "://" + host + BASE_PATH;
+        final var modules = context.getModules();
+        return new OpenApiInputStream(context, title, url, SECURITY, CONTROLLER_RESOURCE_NAME, "",false, false,
+            modules);
     }
 
-    public static Set<Module> filterByRange(final SortedSet<Module> modules, final Range<Integer> range) {
-        if (range.equals(Range.all())) {
-            return modules;
-        }
-        final int begin = range.lowerEndpoint();
-        final int end = range.upperEndpoint();
-
-        Module firstModule = null;
-
-        final Iterator<Module> iterator = modules.iterator();
-        int counter = 0;
-        while (iterator.hasNext() && counter < end) {
-            final Module module = iterator.next();
-            if (containsListOrContainer(module.getChildNodes()) || !module.getRpcs().isEmpty()) {
-                if (counter == begin) {
-                    firstModule = module;
-                }
-                counter++;
-            }
-        }
-
-        if (iterator.hasNext()) {
-            return modules.subSet(firstModule, iterator.next());
-        } else {
-            return modules.tailSet(firstModule);
-        }
-    }
-
-    public OpenApiObject getApiDeclaration(final String module, final String revision, final UriInfo uriInfo) {
+    public OpenApiInputStream getApiDeclaration(final String module, final String revision, final UriInfo uriInfo)
+            throws IOException {
         final EffectiveModelContext schemaContext = schemaService.getGlobalContext();
         Preconditions.checkState(schemaContext != null);
         return getApiDeclaration(module, revision, uriInfo, schemaContext, "", CONTROLLER_RESOURCE_NAME);
     }
 
-    public OpenApiObject getApiDeclaration(final String moduleName, final String revision, final UriInfo uriInfo,
-            final EffectiveModelContext schemaContext, final String context, final @NonNull String deviceName) {
+    public OpenApiInputStream getApiDeclaration(final String moduleName, final String revision, final UriInfo uriInfo,
+            final EffectiveModelContext schemaContext, final String urlPrefix, final @NonNull String deviceName)
+            throws IOException {
         final Optional<Revision> rev;
 
         try {
@@ -165,13 +112,11 @@ public abstract class BaseYangOpenApiGenerator {
 
         final var schema = createSchemaFromUriInfo(uriInfo);
         final var host = createHostFromUriInfo(uriInfo);
-        final var info = new Info(API_VERSION, module.getName(), DESCRIPTION);
-        final var servers = List.of(new Server(schema + "://" + host + BASE_PATH));
-        final var definitionNames = new DefinitionNames();
-        final var schemas = getSchemas(module, schemaContext, definitionNames, true);
-        final var components = new Components(schemas, Map.of(BASIC_AUTH_NAME, OPEN_API_BASIC_AUTH));
-        final var paths = getPaths(module, context, deviceName, schemaContext, definitionNames, true);
-        return new OpenApiObject(OPEN_API_VERSION, info, servers, paths, components, SECURITY);
+        final var title = module.getName();
+        final var url = schema + "://" + host + BASE_PATH;
+        final var modules = List.of(module);
+        return new OpenApiInputStream(schemaContext, title, url, SECURITY,  deviceName, urlPrefix, true, false,
+            modules);
     }
 
     public String createHostFromUriInfo(final UriInfo uriInfo) {
@@ -300,15 +245,6 @@ public abstract class BaseYangOpenApiGenerator {
         }
     }
 
-    private static boolean containsListOrContainer(final Iterable<? extends DataSchemaNode> nodes) {
-        for (final DataSchemaNode child : nodes) {
-            if (child instanceof ListSchemaNode || child instanceof ContainerSchemaNode) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private static Path operations(final DataSchemaNode node, final String moduleName,
             final String deviceName, final List<Parameter> pathParams, final boolean isConfig, final String parentName,
             final DefinitionNames definitionNames, final String fullName) {
index 209f259ac6c0bd6983817c862e9e6660522d40b7..7a7f391980a38677d15b603453c20bbfc777e972 100644 (file)
@@ -17,18 +17,18 @@ import java.io.InputStreamReader;
 import java.io.Reader;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayDeque;
+import java.util.Collection;
 import java.util.Deque;
 import java.util.List;
 import java.util.Map;
 import org.opendaylight.restconf.openapi.jaxrs.OpenApiBodyWriter;
-import org.opendaylight.restconf.openapi.model.Info;
 import org.opendaylight.restconf.openapi.model.InfoEntity;
 import org.opendaylight.restconf.openapi.model.OpenApiVersionEntity;
 import org.opendaylight.restconf.openapi.model.SecurityEntity;
-import org.opendaylight.restconf.openapi.model.Server;
 import org.opendaylight.restconf.openapi.model.ServerEntity;
 import org.opendaylight.restconf.openapi.model.ServersEntity;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
 
 public final class OpenApiInputStream extends InputStream {
     private final ByteArrayOutputStream stream = new ByteArrayOutputStream();
@@ -39,18 +39,17 @@ public final class OpenApiInputStream extends InputStream {
 
     private boolean eof;
 
-    public OpenApiInputStream(final EffectiveModelContext context, final String openApiVersion, final Info info,
-            final List<Server> servers, final List<Map<String, List<String>>> security, final String deviceName,
-            final String urlPrefix, final boolean isForSingleModule, final boolean includeDataStore)
+    public OpenApiInputStream(final EffectiveModelContext context, final String title, final String url,
+            final List<Map<String, List<String>>> security, final String deviceName, final String urlPrefix,
+            final boolean isForSingleModule, final boolean includeDataStore, final Collection<? extends Module> modules)
             throws IOException {
         final OpenApiBodyWriter writer = new OpenApiBodyWriter(generator, stream);
         stack.add(new OpenApiVersionStream(new OpenApiVersionEntity(), writer));
-        stack.add(new InfoStream(new InfoEntity(info.version(), info.title(), info.description()), writer));
-        stack.add(new ServersStream(new ServersEntity(
-            List.of(new ServerEntity(servers.iterator().next().url()))), writer));
+        stack.add(new InfoStream(new InfoEntity(title), writer));
+        stack.add(new ServersStream(new ServersEntity(List.of(new ServerEntity(url))), writer));
         stack.add(new PathsStream(context, writer, generator, stream, deviceName, urlPrefix, isForSingleModule,
-            includeDataStore));
-        stack.add(new SchemasStream(context, writer, generator, stream));
+            includeDataStore, modules.iterator()));
+        stack.add(new SchemasStream(context, writer, generator, stream, modules.iterator()));
         stack.add(new SecurityStream(writer, new SecurityEntity(security)));
     }
 
index 4b8b31c3d4f0fd08a08073e79f1f847bb3ead27f..89eedd623826cfa32fcc258f836c1649c43a3186 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.restconf.openapi.impl;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
 import java.util.List;
 import java.util.stream.Collectors;
 import javax.inject.Inject;
@@ -20,7 +21,6 @@ import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.restconf.openapi.api.OpenApiService;
 import org.opendaylight.restconf.openapi.model.MountPointInstance;
-import org.opendaylight.restconf.openapi.model.OpenApiObject;
 import org.opendaylight.restconf.openapi.mountpoints.MountPointOpenApi;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -68,20 +68,19 @@ public final class OpenApiServiceImpl implements OpenApiService {
     }
 
     @Override
-    public Response getAllModulesDoc(final UriInfo uriInfo) {
-        final DefinitionNames definitionNames = new DefinitionNames();
-        final OpenApiObject doc = openApiGeneratorRFC8040.getControllerModulesDoc(uriInfo, definitionNames);
-        return Response.ok(doc).build();
+    public Response getAllModulesDoc(final UriInfo uriInfo) throws IOException {
+        final OpenApiInputStream stream = openApiGeneratorRFC8040.getControllerModulesDoc(uriInfo);
+        return Response.ok(stream).build();
     }
 
     /**
      * Generates Swagger compliant document listing APIs for module.
      */
     @Override
-    public Response getDocByModule(final String module, final String revision, final UriInfo uriInfo) {
-        return Response.ok(
-            openApiGeneratorRFC8040.getApiDeclaration(module, revision, uriInfo))
-            .build();
+    public Response getDocByModule(final String module, final String revision, final UriInfo uriInfo)
+            throws IOException {
+        final OpenApiInputStream stream = openApiGeneratorRFC8040.getApiDeclaration(module, revision, uriInfo);
+        return Response.ok(stream).build();
     }
 
     /**
@@ -103,17 +102,17 @@ public final class OpenApiServiceImpl implements OpenApiService {
 
     @Override
     public Response getMountDocByModule(final String instanceNum, final String module,
-                                                     final String revision, final UriInfo uriInfo) {
-        final OpenApiObject api = mountPointOpenApiRFC8040.getMountPointApi(uriInfo, Long.parseLong(instanceNum),
-            module, revision);
-        return Response.ok(api).build();
+            final String revision, final UriInfo uriInfo) throws IOException {
+        final OpenApiInputStream stream =
+            mountPointOpenApiRFC8040.getMountPointApi(uriInfo, Long.parseLong(instanceNum), module, revision);
+        return Response.ok(stream).build();
     }
 
     @Override
-    public Response getMountDoc(final String instanceNum, final UriInfo uriInfo) {
+    public Response getMountDoc(final String instanceNum, final UriInfo uriInfo) throws IOException {
         final String stringPageNum = uriInfo.getQueryParameters().getFirst(PAGE_NUM);
-        final OpenApiObject api = mountPointOpenApiRFC8040.getMountPointApi(uriInfo,
-                Long.parseLong(instanceNum), stringPageNum);
-        return Response.ok(api).build();
+        final OpenApiInputStream stream =
+            mountPointOpenApiRFC8040.getMountPointApi(uriInfo, Long.parseLong(instanceNum), stringPageNum);
+        return Response.ok(stream).build();
     }
 }
index adca13602ebffed9f140ea11a6e484900c61e4e2..f6cb5e950ec268010646d0b870486bddbe78ab40 100644 (file)
@@ -77,8 +77,9 @@ public final class PathsStream extends InputStream {
 
     public PathsStream(final EffectiveModelContext schemaContext, final OpenApiBodyWriter writer,
             final JsonGenerator generator, final ByteArrayOutputStream stream, final String deviceName,
-            final String urlPrefix, final boolean isForSingleModule, final boolean includeDataStore) {
-        iterator = schemaContext.getModules().iterator();
+            final String urlPrefix, final boolean isForSingleModule, final boolean includeDataStore,
+            final Iterator<? extends Module> iterator) {
+        this.iterator = iterator;
         this.generator = generator;
         this.writer = writer;
         this.stream = stream;
index 24cc2f173e3d98fdd4c537b09a467cf93d0d1582..3ef816f57b371ef7d8b4572b3cd38247772d687d 100644 (file)
@@ -54,8 +54,9 @@ public final class SchemasStream extends InputStream {
     private boolean eos;
 
     public SchemasStream(final EffectiveModelContext context, final OpenApiBodyWriter writer,
-            final JsonGenerator generator, final ByteArrayOutputStream stream) {
-        iterator = context.getModules().iterator();
+            final JsonGenerator generator, final ByteArrayOutputStream stream,
+            final Iterator<? extends Module> iterator) {
+        this.iterator = iterator;
         this.context = context;
         this.writer = writer;
         this.generator = generator;
diff --git a/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/jaxrs/JaxbContextResolver.java b/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/jaxrs/JaxbContextResolver.java
new file mode 100644 (file)
index 0000000..3102331
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.openapi.jaxrs;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+@Provider
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public class JaxbContextResolver implements ContextResolver<ObjectMapper> {
+
+    @Override
+    public ObjectMapper getContext(final Class<?> klass) {
+        return null; // must return null so that JAX-RS can continue context search
+    }
+}
index f1ad49ac07a34e1d704c84484916428af0b9c738..ab67d2acc90876cb585946ca3275275a8bd648cc 100644 (file)
@@ -12,28 +12,26 @@ import java.io.IOException;
 import org.eclipse.jdt.annotation.NonNull;
 
 public final class InfoEntity extends OpenApiEntity {
-    private final String version;
     private final String title;
-    private final String description;
+    private static final String DESCRIPTION = """
+        We are providing full API for configurational data which can be edited (by POST, PUT, PATCH and DELETE).
+        For operational data we only provide GET API.\n
+        For majority of request you can see only config data in examples. That is because we can show only one example
+        per request. The exception when you can see operational data in example is when data are representing
+        operational (config false) container with no config data in it.""";
 
-    public InfoEntity(final String version, final String title, final String description) {
-        this.version = version;
+    public InfoEntity(final String title) {
         this.title = title;
-        this.description = description;
     }
 
     @Override
     public void generate(@NonNull JsonGenerator generator) throws IOException {
         generator.writeObjectFieldStart("info");
-        if (version != null) {
-            generator.writeStringField("version", version);
-        }
+        generator.writeStringField("version", "1.0.0");
         if (title != null) {
             generator.writeStringField("title", title);
         }
-        if (description != null) {
-            generator.writeStringField("description", description);
-        }
+        generator.writeStringField("description", DESCRIPTION);
         generator.writeEndObject();
     }
 }
diff --git a/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/model/OpenApiObject.java b/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/model/OpenApiObject.java
deleted file mode 100644 (file)
index c7de610..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.openapi.model;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import java.util.List;
-import java.util.Map;
-
-@JsonInclude(Include.NON_NULL)
-public record OpenApiObject(
-        String openapi,
-        Info info,
-        List<Server> servers,
-        Map<String, Path> paths,
-        Components components,
-        List<Map<String, List<String>>> security) {
-}
index de5c14ec3af956b74ace45f9ad29cf2631450361..37e837ea07c606c254ac5ba446849836d4437803 100644 (file)
@@ -9,11 +9,9 @@ package org.opendaylight.restconf.openapi.model.security;
 
 import static java.util.Objects.requireNonNull;
 
-import com.fasterxml.jackson.annotation.JsonInclude;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 
-@JsonInclude(JsonInclude.Include.NON_NULL)
 public record Http(
         @NonNull String scheme,
         @Nullable String description,
index a831a8db3f9f5f364b53873034c1173d77005838..27cb33050f1660d38ff7e8e1ddc34f1d1673dd93 100644 (file)
@@ -7,12 +7,7 @@
  */
 package org.opendaylight.restconf.openapi.model.security;
 
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-@JsonInclude(JsonInclude.Include.NON_NULL)
 public interface SecuritySchemeObject {
-    @JsonProperty("type")
     Type type();
 
     /**
index 2da154bae6cc06a0533062918bdfe6580f9ed8c9..dd6d5649f6d5e2531414808e80bb00ec14e6a8d3 100644 (file)
@@ -9,42 +9,22 @@ package org.opendaylight.restconf.openapi.mountpoints;
 
 import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
-import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.API_VERSION;
 import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.BASE_PATH;
-import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.BASIC_AUTH_NAME;
-import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.DESCRIPTION;
-import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.OPEN_API_BASIC_AUTH;
-import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.OPEN_API_VERSION;
 import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.SECURITY;
-import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.filterByRange;
-import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.getSortedModules;
-import static org.opendaylight.restconf.openapi.impl.OpenApiServiceImpl.DEFAULT_PAGESIZE;
-import static org.opendaylight.restconf.openapi.model.builder.OperationBuilder.SUMMARY_TEMPLATE;
 
-import com.google.common.collect.Range;
+import java.io.IOException;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicLong;
-import javax.ws.rs.HttpMethod;
-import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.dom.api.DOMMountPointListener;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator;
-import org.opendaylight.restconf.openapi.impl.DefinitionNames;
-import org.opendaylight.restconf.openapi.model.Components;
-import org.opendaylight.restconf.openapi.model.Info;
-import org.opendaylight.restconf.openapi.model.OpenApiObject;
-import org.opendaylight.restconf.openapi.model.Operation;
-import org.opendaylight.restconf.openapi.model.Path;
-import org.opendaylight.restconf.openapi.model.ResponseObject;
-import org.opendaylight.restconf.openapi.model.Schema;
-import org.opendaylight.restconf.openapi.model.Server;
+import org.opendaylight.restconf.openapi.impl.OpenApiInputStream;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -159,24 +139,25 @@ public class MountPointOpenApi implements DOMMountPointListener, AutoCloseable {
             .orElse(null);
     }
 
-    public OpenApiObject getMountPointApi(final UriInfo uriInfo, final Long id, final String module,
-            final String revision) {
+    public OpenApiInputStream getMountPointApi(final UriInfo uriInfo, final Long id, final String module,
+            final String revision) throws IOException  {
         final YangInstanceIdentifier iid = getInstanceId(id);
         final EffectiveModelContext context = getSchemaContext(iid);
         final String urlPrefix = getYangMountUrl(iid);
-        final String deviceName  = extractDeviceName(iid);
+        final String deviceName = extractDeviceName(iid);
 
         if (context == null) {
             return null;
         }
 
         if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) {
-            return generateDataStoreOpenApi(uriInfo, urlPrefix, deviceName);
+            return generateDataStoreOpenApi(context, uriInfo, urlPrefix, deviceName);
         }
         return openApiGenerator.getApiDeclaration(module, revision, uriInfo, context, urlPrefix, deviceName);
     }
 
-    public OpenApiObject getMountPointApi(final UriInfo uriInfo, final Long id, final @Nullable String strPageNum) {
+    public OpenApiInputStream getMountPointApi(final UriInfo uriInfo, final Long id, final @Nullable String strPageNum)
+            throws IOException {
         final var iid = getInstanceId(id);
         final var context = getSchemaContext(iid);
         final var urlPrefix = getYangMountUrl(iid);
@@ -185,42 +166,22 @@ public class MountPointOpenApi implements DOMMountPointListener, AutoCloseable {
         if (context == null) {
             return null;
         }
-        final var definitionNames = new DefinitionNames();
 
         boolean includeDataStore = true;
-        Range<Integer> range = Range.all();
         if (strPageNum != null) {
             final var pageNum = Integer.parseInt(strPageNum);
-            final var end = DEFAULT_PAGESIZE * pageNum - 1;
-            int start = end - DEFAULT_PAGESIZE;
-            if (pageNum == 1) {
-                start++;
-            } else {
+            if (pageNum != 1) {
                 includeDataStore = false;
             }
-            range = Range.closed(start, end);
         }
 
         final var schema = openApiGenerator.createSchemaFromUriInfo(uriInfo);
         final var host = openApiGenerator.createHostFromUriInfo(uriInfo);
         final var title = deviceName + " modules of RESTCONF";
-        final var info = new Info(API_VERSION, title, DESCRIPTION);
-        final var servers = List.of(new Server(schema + "://" + host + BASE_PATH));
-
-        final var modules = getSortedModules(context);
-        final var filteredModules = filterByRange(modules, range);
-        final var paths = new HashMap<String, Path>();
-        final var schemas = new HashMap<String, Schema>();
-        for (final var module : filteredModules) {
-            LOG.debug("Working on [{},{}]...", module.getName(), module.getQNameModule().getRevision().orElse(null));
-            schemas.putAll(openApiGenerator.getSchemas(module, context, definitionNames, false));
-            paths.putAll(openApiGenerator.getPaths(module, urlPrefix, deviceName, context, definitionNames, false));
-        }
-        final var components = new Components(schemas, Map.of(BASIC_AUTH_NAME, OPEN_API_BASIC_AUTH));
-        if (includeDataStore) {
-            paths.putAll(getDataStoreApiPaths(urlPrefix, deviceName));
-        }
-        return new OpenApiObject(OPEN_API_VERSION, info, servers, paths, components, SECURITY);
+        final var url = schema + "://" + host + BASE_PATH;
+        final var modules = context.getModules();
+        return new OpenApiInputStream(context, title, url, SECURITY, deviceName, urlPrefix, false, includeDataStore,
+            modules);
     }
 
     private static String extractDeviceName(final YangInstanceIdentifier iid) {
@@ -228,43 +189,14 @@ public class MountPointOpenApi implements DOMMountPointListener, AutoCloseable {
                 .values().getElement().toString();
     }
 
-    private OpenApiObject generateDataStoreOpenApi(final UriInfo uriInfo, final String context,
-            final String deviceName) {
-        final var info = new Info(API_VERSION, context, DESCRIPTION);
+    private OpenApiInputStream generateDataStoreOpenApi(EffectiveModelContext modelContext,
+            final UriInfo uriInfo, final String urlPrefix, final String deviceName) throws IOException {
         final var schema = openApiGenerator.createSchemaFromUriInfo(uriInfo);
         final var host = openApiGenerator.createHostFromUriInfo(uriInfo);
-        final var servers = List.of(new Server(schema + "://" + host + BASE_PATH));
-        final var components = new Components(new HashMap<>(), Map.of(BASIC_AUTH_NAME, OPEN_API_BASIC_AUTH));
-        final var paths = getDataStoreApiPaths(context, deviceName);
-        return new OpenApiObject(OPEN_API_VERSION, info, servers, paths, components, SECURITY);
-    }
-
-    private Map<String, Path> getDataStoreApiPaths(final String context, final String deviceName) {
-        final var dataBuilder = new Path.Builder();
-        dataBuilder.get(createGetPathItem("data",
-                "Queries the config (startup) datastore on the mounted hosted.", deviceName));
-
-        final var operationsBuilder = new Path.Builder();
-        operationsBuilder.get(createGetPathItem("operations",
-                "Queries the available operations (RPC calls) on the mounted hosted.", deviceName));
-
-        return Map.of(openApiGenerator.getResourcePath("data", context), dataBuilder.build(),
-            openApiGenerator.getResourcePath("operations", context), operationsBuilder.build());
-    }
-
-    private static Operation createGetPathItem(final String resourceType, final String description,
-            final String deviceName) {
-        final String summary = SUMMARY_TEMPLATE.formatted(HttpMethod.GET, deviceName, "datastore", resourceType);
-        final List<String> tags = List.of(deviceName + " GET root");
-        final ResponseObject okResponse = new ResponseObject.Builder()
-            .description(Response.Status.OK.getReasonPhrase())
-            .build();
-        return new Operation.Builder()
-            .tags(tags)
-            .responses(Map.of(String.valueOf(Response.Status.OK.getStatusCode()), okResponse))
-            .description(description)
-            .summary(summary)
-            .build();
+        final var url = schema + "://" + host + BASE_PATH;
+        final var modules = modelContext.getModules();
+        return new OpenApiInputStream(modelContext, urlPrefix, url, SECURITY, deviceName, urlPrefix, true, false,
+            modules);
     }
 
     @Override
index 3e64359eb4b015682dd7b0d75576b1fd55f6e950..002897c4348283f97b284c757255fbe803f68f58 100644 (file)
@@ -13,6 +13,7 @@ import static org.mockito.Mockito.when;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import java.net.URI;
+import java.nio.charset.StandardCharsets;
 import java.util.Optional;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
@@ -68,7 +69,8 @@ public abstract class AbstractDocumentTest {
         final var getAllController = createMockUriInfo(URI + "single");
         final var controllerDocAll = openApiService.getAllModulesDoc(getAllController).getEntity();
 
-        return MAPPER.writeValueAsString(controllerDocAll);
+        return new String(((OpenApiInputStream) controllerDocAll).readAllBytes(),
+            StandardCharsets.UTF_8);
     }
 
     protected static String getDocByModule(final String moduleName, final String revision) throws Exception {
@@ -79,7 +81,8 @@ public abstract class AbstractDocumentTest {
         final var getModuleController = createMockUriInfo(uri);
         final var controllerDocModule = openApiService.getDocByModule(moduleName, revision, getModuleController);
 
-        return MAPPER.writeValueAsString(controllerDocModule.getEntity());
+        return new String(((OpenApiInputStream) controllerDocModule.getEntity()).readAllBytes(),
+            StandardCharsets.UTF_8);
     }
 
 
@@ -88,7 +91,8 @@ public abstract class AbstractDocumentTest {
         when(getAllDevice.getQueryParameters()).thenReturn(ImmutableMultivaluedMap.empty());
         final var deviceDocAll = openApiService.getMountDoc("1", getAllDevice);
 
-        return MAPPER.writeValueAsString(deviceDocAll.getEntity());
+        return new String(((OpenApiInputStream) deviceDocAll.getEntity()).readAllBytes(),
+            StandardCharsets.UTF_8);
     }
 
 
@@ -96,7 +100,8 @@ public abstract class AbstractDocumentTest {
         final var getDevice = createMockUriInfo(URI + "mounts/1/" + moduleName);
         final var deviceDoc = openApiService.getMountDocByModule("1", moduleName, revision, getDevice);
 
-        return MAPPER.writeValueAsString(deviceDoc.getEntity());
+        return new String(((OpenApiInputStream) deviceDoc.getEntity()).readAllBytes(),
+            StandardCharsets.UTF_8);
     }
 
     public static UriInfo createMockUriInfo(final String urlPrefix) throws Exception {