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) throws IOException {
+ final String urlPrefix, final boolean isForSingleModule, final boolean includeDataStore)
+ 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 PathsSteam(context, writer, generator, stream, deviceName, urlPrefix));
+ stack.add(new PathsStream(context, writer, generator, stream, deviceName, urlPrefix, isForSingleModule,
+ includeDataStore));
stack.add(new SchemasStream(context, writer, generator, stream));
stack.add(new SecurityStream(writer));
}
+++ /dev/null
-/*
- * Copyright (c) 2023 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.impl;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.Iterator;
-import org.opendaylight.restconf.openapi.jaxrs.OpenApiBodyWriter;
-import org.opendaylight.restconf.openapi.model.PathEntity;
-import org.opendaylight.restconf.openapi.model.PostEntity;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.model.api.Module;
-
-public final class PathsSteam extends InputStream {
- private final Iterator<? extends Module> iterator;
- private final JsonGenerator generator;
- private final OpenApiBodyWriter writer;
- private final ByteArrayOutputStream stream;
- private final String deviceName;
- private final String urlPrefix;
-
- private Reader reader;
- private boolean eof;
-
- public PathsSteam(final EffectiveModelContext context, final OpenApiBodyWriter writer,
- final JsonGenerator generator, final ByteArrayOutputStream stream, final String deviceName,
- final String urlPrefix) {
- iterator = context.getModules().iterator();
- this.generator = generator;
- this.writer = writer;
- this.stream = stream;
- this.deviceName = deviceName;
- this.urlPrefix = urlPrefix;
- }
-
- @Override
- public int read() throws IOException {
- if (eof) {
- return -1;
- }
- if (reader == null) {
- generator.writeObjectFieldStart("paths");
- generator.flush();
- reader = new InputStreamReader(new ByteArrayInputStream(stream.toByteArray()), StandardCharsets.UTF_8);
- stream.reset();
- }
-
- var read = reader.read();
- while (read == -1) {
- if (iterator.hasNext()) {
- reader = new InputStreamReader(new PathStream(toPaths(iterator.next()), writer),
- StandardCharsets.UTF_8);
- read = reader.read();
- continue;
- }
- generator.writeEndObject();
- generator.flush();
- reader = new InputStreamReader(new ByteArrayInputStream(stream.toByteArray()), StandardCharsets.UTF_8);
- stream.reset();
- eof = true;
- return reader.read();
- }
-
- return read;
- }
-
- @Override
- public int read(final byte[] array, final int off, final int len) throws IOException {
- return super.read(array, off, len);
- }
-
- private Deque<PathEntity> toPaths(final Module module) {
- final var result = new ArrayDeque<PathEntity>();
- // RPC operations (via post) - RPCs have their own path
- for (final var rpc : module.getRpcs()) {
- // TODO connect path with payload
- final var post = new PostEntity(rpc, deviceName, module.getName());
- final String resolvedPath = "/rests/operations" + urlPrefix + "/" + module.getName() + ":"
- + rpc.getQName().getLocalName();
- final var entity = new PathEntity(resolvedPath, post);
- result.add(entity);
- }
-
- /**
- * TODO
- * for (final var container : module.getChildNodes()) {
- * // get
- * // post
- * // put
- * // patch
- * // delete
- *
- * // add path into deque
- * }
- */
- return result;
- }
-}
--- /dev/null
+/*
+ * Copyright (c) 2023 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.impl;
+
+import static org.opendaylight.restconf.openapi.util.RestDocgenUtil.resolveFullNameFromNode;
+import static org.opendaylight.restconf.openapi.util.RestDocgenUtil.resolvePathArgumentsName;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.restconf.openapi.jaxrs.OpenApiBodyWriter;
+import org.opendaylight.restconf.openapi.model.DeleteEntity;
+import org.opendaylight.restconf.openapi.model.GetEntity;
+import org.opendaylight.restconf.openapi.model.ParameterEntity;
+import org.opendaylight.restconf.openapi.model.ParameterSchemaEntity;
+import org.opendaylight.restconf.openapi.model.PatchEntity;
+import org.opendaylight.restconf.openapi.model.PathEntity;
+import org.opendaylight.restconf.openapi.model.PostEntity;
+import org.opendaylight.restconf.openapi.model.PutEntity;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+
+public final class PathsStream extends InputStream {
+ private final Iterator<? extends Module> iterator;
+ private final JsonGenerator generator;
+ private final OpenApiBodyWriter writer;
+ private final ByteArrayOutputStream stream;
+ private final EffectiveModelContext schemaContext;
+ private final String deviceName;
+ private final String urlPrefix;
+ private final boolean isForSingleModule;
+ private final boolean includeDataStore;
+
+ private static final String OPERATIONS = "/rests/operations";
+ private static final String DATA = "/rests/data";
+
+ private boolean hasRootPostLink;
+ private boolean hasAddedDataStore;
+
+ private Reader reader;
+ private boolean eof;
+
+ 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();
+ this.generator = generator;
+ this.writer = writer;
+ this.stream = stream;
+ this.schemaContext = schemaContext;
+ this.isForSingleModule = isForSingleModule;
+ this.deviceName = deviceName;
+ this.urlPrefix = urlPrefix;
+ this.includeDataStore = includeDataStore;
+ hasRootPostLink = false;
+ hasAddedDataStore = false;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (eof) {
+ return -1;
+ }
+ if (reader == null) {
+ generator.writeObjectFieldStart("paths");
+ generator.flush();
+ reader = new InputStreamReader(new ByteArrayInputStream(stream.toByteArray()), StandardCharsets.UTF_8);
+ stream.reset();
+ }
+
+ var read = reader.read();
+ while (read == -1) {
+ if (iterator.hasNext()) {
+ reader = new InputStreamReader(new PathStream(toPaths(iterator.next()), writer),
+ StandardCharsets.UTF_8);
+ read = reader.read();
+ continue;
+ }
+ generator.writeEndObject();
+ generator.flush();
+ reader = new InputStreamReader(new ByteArrayInputStream(stream.toByteArray()), StandardCharsets.UTF_8);
+ stream.reset();
+ eof = true;
+ return reader.read();
+ }
+
+ return read;
+ }
+
+ @Override
+ public int read(final byte @NonNull [] array, final int off, final int len) throws IOException {
+ return super.read(array, off, len);
+ }
+
+ private Deque<PathEntity> toPaths(final Module module) {
+ final var result = new ArrayDeque<PathEntity>();
+ if (includeDataStore && !hasAddedDataStore) {
+ final var dataPath = DATA + urlPrefix;
+ result.add(new PathEntity(dataPath, null, null, null,
+ new GetEntity(null, deviceName, "data", null, null, false),
+ null));
+ final var operationsPath = OPERATIONS + urlPrefix;
+ result.add(new PathEntity(operationsPath, null, null, null,
+ new GetEntity(null, deviceName, "operations", null, null, false),
+ null));
+ hasAddedDataStore = true;
+ }
+ // RPC operations (via post) - RPCs have their own path
+ for (final var rpc : module.getRpcs()) {
+ // TODO connect path with payload
+ final var localName = rpc.getQName().getLocalName();
+ final var post = new PostEntity(rpc, deviceName, module.getName(), new ArrayList<>(), localName, null);
+ final var resolvedPath = OPERATIONS + urlPrefix + "/" + module.getName() + ":" + localName;
+ final var entity = new PathEntity(resolvedPath, post, null, null, null, null);
+ result.add(entity);
+ }
+ for (final var node : module.getChildNodes()) {
+ final var moduleName = module.getName();
+ final boolean isConfig = node.isConfiguration();
+ final var nodeLocalName = node.getQName().getLocalName();
+
+ if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
+ if (isConfig && !hasRootPostLink && isForSingleModule) {
+ final var resolvedPath = DATA + urlPrefix;
+ result.add(new PathEntity(resolvedPath, new PostEntity(node, deviceName, moduleName,
+ new ArrayList<>(), nodeLocalName, module), null, null, null, null));
+ hasRootPostLink = true;
+ }
+ //process first node
+ final var pathParams = new ArrayList<ParameterEntity>();
+ final var localName = moduleName + ":" + nodeLocalName;
+ final var path = urlPrefix + "/" + processPath(node, pathParams, localName);
+ processChildNode(node, pathParams, moduleName, result, path, nodeLocalName, isConfig, schemaContext,
+ deviceName);
+ }
+ }
+ return result;
+ }
+
+ private static void processChildNode(final DataSchemaNode node, final List<ParameterEntity> pathParams,
+ final String moduleName, final Deque<PathEntity> result, final String path, final String refPath,
+ final boolean isConfig, final EffectiveModelContext schemaContext, final String deviceName) {
+ final var resourcePath = DATA + path;
+ final var fullName = resolveFullNameFromNode(node.getQName(), schemaContext);
+ final var firstChild = getListOrContainerChildNode((DataNodeContainer) node);
+ if (firstChild != null && node instanceof ContainerSchemaNode) {
+ result.add(processTopPathEntity(node, resourcePath, pathParams, moduleName, refPath, isConfig,
+ fullName, firstChild, deviceName));
+ } else {
+ result.add(processDataPathEntity(node, resourcePath, pathParams, moduleName, refPath,
+ isConfig, fullName, deviceName));
+ }
+ final var childNodes = ((DataNodeContainer) node).getChildNodes();
+ if (node instanceof ActionNodeContainer actionContainer) {
+ final var actionParams = new ArrayList<>(pathParams);
+ actionContainer.getActions().forEach(actionDef -> {
+ final var resourceActionPath = path + "/" + resolvePathArgumentsName(actionDef.getQName(),
+ node.getQName(), schemaContext);
+ final var childPath = OPERATIONS + resourceActionPath;
+ result.add(processRootAndActionPathEntity(actionDef, childPath, actionParams, moduleName,
+ refPath, deviceName));
+ });
+ }
+ for (final var childNode : childNodes) {
+ final var childParams = new ArrayList<>(pathParams);
+ final var newRefPath = refPath + "_" + childNode.getQName().getLocalName();
+ if (childNode instanceof ListSchemaNode || childNode instanceof ContainerSchemaNode) {
+ final var localName = resolvePathArgumentsName(childNode.getQName(), node.getQName(), schemaContext);
+ final var resourceDataPath = path + "/" + processPath(childNode, childParams, localName);
+ final var newConfig = isConfig && childNode.isConfiguration();
+ processChildNode(childNode, childParams, moduleName, result, resourceDataPath, newRefPath, newConfig,
+ schemaContext, deviceName);
+ }
+ }
+ }
+
+ private static <T extends DataNodeContainer> DataSchemaNode getListOrContainerChildNode(final T node) {
+ return node.getChildNodes().stream()
+ .filter(n -> n instanceof ListSchemaNode || n instanceof ContainerSchemaNode)
+ .findFirst().orElse(null);
+ }
+
+ private static PathEntity processDataPathEntity(final SchemaNode node, final String resourcePath,
+ final List<ParameterEntity> pathParams, final String moduleName, final String refPath,
+ final boolean isConfig, final String fullName, final String deviceName) {
+ if (isConfig) {
+ return new PathEntity(resourcePath, null,
+ new PatchEntity(node, deviceName, moduleName, pathParams, refPath, fullName),
+ new PutEntity(node, deviceName, moduleName, pathParams, refPath, fullName),
+ new GetEntity(node, deviceName, moduleName, pathParams, refPath, true),
+ new DeleteEntity(node, deviceName, moduleName, pathParams, refPath));
+ } else {
+ return new PathEntity(resourcePath, null, null, null,
+ new GetEntity(node, deviceName, moduleName, pathParams, refPath, false), null);
+ }
+ }
+
+ private static PathEntity processTopPathEntity(final SchemaNode node, final String resourcePath,
+ final List<ParameterEntity> pathParams, final String moduleName, final String refPath,
+ final boolean isConfig, final String fullName, final SchemaNode childNode, final String deviceName) {
+ if (isConfig) {
+ final var childNodeRefPath = refPath + "_" + childNode.getQName().getLocalName();
+ var post = new PostEntity(childNode, deviceName, moduleName, pathParams, childNodeRefPath, node);
+ if (!((DataSchemaNode) childNode).isConfiguration()) {
+ post = new PostEntity(node, deviceName, moduleName, pathParams, refPath, null);
+ }
+ return new PathEntity(resourcePath, post,
+ new PatchEntity(node, deviceName, moduleName, pathParams, refPath, fullName),
+ new PutEntity(node, deviceName, moduleName, pathParams, refPath, fullName),
+ new GetEntity(node, deviceName, moduleName, pathParams, refPath, true),
+ new DeleteEntity(node, deviceName, moduleName, pathParams, refPath));
+ } else {
+ return new PathEntity(resourcePath, null, null, null,
+ new GetEntity(node, deviceName, moduleName, pathParams, refPath, false), null);
+ }
+ }
+
+ private static PathEntity processRootAndActionPathEntity(final SchemaNode node, final String resourcePath,
+ final List<ParameterEntity> pathParams, final String moduleName, final String refPath,
+ final String deviceName) {
+ return new PathEntity(resourcePath,
+ new PostEntity(node, deviceName, moduleName, pathParams, refPath, null),
+ null, null, null, null);
+ }
+
+ private static String processPath(final DataSchemaNode node, final List<ParameterEntity> pathParams,
+ final String localName) {
+ final var path = new StringBuilder();
+ path.append(localName);
+ final var parameters = pathParams.stream()
+ .map(ParameterEntity::name)
+ .collect(Collectors.toSet());
+
+ if (node instanceof ListSchemaNode listSchemaNode) {
+ var prefix = "=";
+ var discriminator = 1;
+ for (final var listKey : listSchemaNode.getKeyDefinition()) {
+ final var keyName = listKey.getLocalName();
+ var paramName = keyName;
+ while (!parameters.add(paramName)) {
+ paramName = keyName + discriminator;
+ discriminator++;
+ }
+
+ final var pathParamIdentifier = prefix + "{" + paramName + "}";
+ prefix = ",";
+ path.append(pathParamIdentifier);
+
+ final var description = listSchemaNode.findDataChildByName(listKey)
+ .flatMap(DataSchemaNode::getDescription).orElse(null);
+
+ pathParams.add(new ParameterEntity(paramName, "path", true,
+ new ParameterSchemaEntity(getAllowedType(listSchemaNode, listKey), null), description));
+ }
+ }
+ return path.toString();
+ }
+
+ private static String getAllowedType(final ListSchemaNode list, final QName key) {
+ final var keyType = ((LeafSchemaNode) list.getDataChildByName(key)).getType();
+
+ // see: https://datatracker.ietf.org/doc/html/rfc7950#section-4.2.4
+ // see: https://swagger.io/docs/specification/data-models/data-types/
+ // TODO: Java 21 use pattern matching for switch
+ if (keyType instanceof Int8TypeDefinition) {
+ return "integer";
+ }
+ if (keyType instanceof Int16TypeDefinition) {
+ return "integer";
+ }
+ if (keyType instanceof Int32TypeDefinition) {
+ return "integer";
+ }
+ if (keyType instanceof Int64TypeDefinition) {
+ return "integer";
+ }
+ if (keyType instanceof Uint8TypeDefinition) {
+ return "integer";
+ }
+ if (keyType instanceof Uint16TypeDefinition) {
+ return "integer";
+ }
+ if (keyType instanceof Uint32TypeDefinition) {
+ return "integer";
+ }
+ if (keyType instanceof Uint64TypeDefinition) {
+ return "integer";
+ }
+
+ if (keyType instanceof DecimalTypeDefinition) {
+ return "number";
+ }
+
+ if (keyType instanceof BooleanTypeDefinition) {
+ return "boolean";
+ }
+
+ return "string";
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 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 static javax.ws.rs.core.Response.Status.NO_CONTENT;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import java.io.IOException;
+import java.util.List;
+import javax.ws.rs.HttpMethod;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+public final class DeleteEntity extends OperationEntity {
+
+ public DeleteEntity(final SchemaNode schema, final String deviceName, final String moduleName,
+ final List<ParameterEntity> parameters, final String refPath) {
+ super(schema, deviceName, moduleName, parameters, refPath);
+ }
+
+ @Override
+ protected String operation() {
+ return "delete";
+ }
+
+ @Override
+ @Nullable String summary() {
+ return SUMMARY_TEMPLATE.formatted(HttpMethod.DELETE, deviceName(), moduleName(), nodeName());
+ }
+
+ @Override
+ void generateResponses(final @NonNull JsonGenerator generator) throws IOException {
+ generator.writeObjectFieldStart(RESPONSES);
+ generator.writeObjectFieldStart(String.valueOf(NO_CONTENT.getStatusCode()));
+ generator.writeStringField(DESCRIPTION, "Deleted");
+ generator.writeEndObject();
+ generator.writeEndObject();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 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 static javax.ws.rs.core.Response.Status.OK;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import java.io.IOException;
+import java.util.List;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.MediaType;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+public final class GetEntity extends OperationEntity {
+
+ private final boolean isConfig;
+
+ public GetEntity(final SchemaNode schema, final String deviceName, final String moduleName,
+ final List<ParameterEntity> parameters, final String refPath, final boolean isConfig) {
+ super(schema, deviceName, moduleName, parameters, refPath);
+ this.isConfig = isConfig;
+ }
+
+ @Override
+ protected String operation() {
+ return "get";
+ }
+
+ @Override
+ @Nullable String summary() {
+ return SUMMARY_TEMPLATE.formatted(HttpMethod.GET, deviceName(), moduleName(), nodeName());
+ }
+
+ @Override
+ void generateResponses(final @NonNull JsonGenerator generator) throws IOException {
+ final var ref = COMPONENTS_PREFIX + moduleName() + "_" + refPath();
+ generator.writeObjectFieldStart(RESPONSES);
+ generator.writeObjectFieldStart(String.valueOf(OK.getStatusCode()));
+ generator.writeStringField(DESCRIPTION, String.valueOf(OK.getStatusCode()));
+ generator.writeObjectFieldStart(CONTENT);
+ generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
+ generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
+ generator.writeObjectFieldStart(SCHEMA);
+ generator.writeObjectFieldStart(PROPERTIES);
+ generator.writeObjectFieldStart(nodeName());
+ if (schema() instanceof ListSchemaNode) {
+ generator.writeStringField(TYPE, ARRAY);
+ generator.writeObjectFieldStart(ITEMS);
+ generator.writeStringField(REF, ref);
+ generator.writeStringField(TYPE, OBJECT);
+ generator.writeEndObject(); //end of items
+ } else {
+ generator.writeStringField(REF, ref);
+ generator.writeStringField(TYPE, OBJECT);
+ }
+ generator.writeEndObject(); //end of nodeName
+ generator.writeEndObject(); //end of props
+ generator.writeEndObject(); //end of schema
+ generator.writeEndObject(); //end of json
+ generator.writeEndObject(); //end of content
+ generator.writeEndObject(); //end of 200
+ generator.writeEndObject(); //end of responses
+ }
+
+ @Override
+ void generateParams(@NonNull JsonGenerator generator) throws IOException {
+ final var contentParam = new ParameterEntity(CONTENT, "query", !isConfig,
+ new ParameterSchemaEntity("string", List.of("config", "nonconfig", "all")), null);
+ parameters().add(contentParam);
+ generator.writeArrayFieldStart(PARAMETERS);
+ for (final var parameter : parameters()) {
+ final var schemaEnum = parameter.schema().schemaEnum();
+ generator.writeStartObject();
+ generator.writeStringField(NAME, parameter.name());
+ generator.writeStringField(IN, parameter.in());
+ generator.writeBooleanField(REQUIRED, parameter.required());
+ generator.writeObjectFieldStart(SCHEMA);
+ if (schemaEnum != null) {
+ generator.writeArrayFieldStart("enum");
+ for (final var enumCase : schemaEnum) {
+ generator.writeString(enumCase);
+ }
+ generator.writeEndArray(); //end of enum
+ }
+ generator.writeStringField(TYPE, parameter.schema().type());
+ generator.writeEndObject(); //end of schema
+ if (parameter.description() != null) {
+ generator.writeStringField(DESCRIPTION, parameter.description());
+ }
+ generator.writeEndObject(); //end of parameter
+ }
+ generator.writeEndArray(); //end of params
+ parameters().remove(contentParam);
+ }
+}
*/
package org.opendaylight.restconf.openapi.model;
+import static javax.ws.rs.core.Response.Status.OK;
+
import com.fasterxml.jackson.core.JsonGenerator;
import java.io.IOException;
+import java.util.List;
+import javax.ws.rs.HttpMethod;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
/**
* Archetype for an Operation.
*/
-public abstract sealed class OperationEntity extends OpenApiEntity permits PostEntity {
- private final OperationDefinition schema;
+public abstract sealed class OperationEntity extends OpenApiEntity permits DeleteEntity, GetEntity, PatchEntity,
+ PostEntity, PutEntity {
+ protected static final String SCHEMA = "schema";
+ protected static final String SUMMARY_TEMPLATE = "%s - %s - %s - %s";
+ protected static final String RESPONSES = "responses";
+ protected static final String DESCRIPTION = "description";
+ protected static final String OBJECT = "object";
+ protected static final String CONTENT = "content";
+ protected static final String COMPONENTS_PREFIX = "#/components/schemas/";
+ protected static final String PROPERTIES = "properties";
+ protected static final String TYPE = "type";
+ protected static final String ARRAY = "array";
+ protected static final String ITEMS = "items";
+ protected static final String REF = "$ref";
+ protected static final String PARAMETERS = "parameters";
+ protected static final String SUMMARY = "summary";
+ protected static final String NAME = "name";
+ protected static final String IN = "in";
+ protected static final String REQUIRED = "required";
+ protected static final String REQUEST_BODY = "requestBody";
+
+ private final SchemaNode schema;
private final String deviceName;
private final String moduleName;
+ private final String refPath;
+ private final List<ParameterEntity> parameters;
- protected OperationDefinition schema() {
+ protected SchemaNode schema() {
return schema;
}
return moduleName;
}
- public OperationEntity(final OperationDefinition schema, final String deviceName, final String moduleName) {
+ protected List<ParameterEntity> parameters() {
+ return parameters;
+ }
+
+ protected String refPath() {
+ return refPath;
+ }
+
+ public OperationEntity(final SchemaNode schema, final String deviceName, final String moduleName,
+ final List<ParameterEntity> parameters, final String refPath) {
this.schema = schema;
this.deviceName = deviceName;
this.moduleName = moduleName;
+ this.parameters = parameters;
+ this.refPath = refPath;
}
@Override
public void generate(@NonNull JsonGenerator generator) throws IOException {
- generator.writeObjectFieldStart(operation());
- final var deprecated = deprecated();
- if (deprecated != null) {
- generator.writeBooleanField("deprecated", deprecated);
+ if (schema() == null) {
+ generateGetRoot(generator, moduleName());
+ } else {
+ generator.writeObjectFieldStart(operation());
+ generateBasics(generator);
+ generateRequestBody(generator);
+ generateResponses(generator);
+ generateTags(generator);
+ generateParams(generator);
+ generator.writeEndObject();
}
+ }
+
+ public void generateBasics(@NonNull JsonGenerator generator) throws IOException {
final var description = description();
if (description != null) {
- generator.writeStringField("description", description);
+ generator.writeStringField(DESCRIPTION, description);
}
final var summary = summary();
if (summary != null) {
- generator.writeStringField("summary", summary);
+ generator.writeStringField(SUMMARY, summary);
}
- generateRequestBody(generator);
- generateResponses(generator);
- generateTags(generator);
- generateParams(generator);
- generator.writeEndObject();
}
protected abstract String operation();
return schema.getDescription().orElse("");
}
+ @Nullable String nodeName() {
+ if (schema() != null) {
+ return schema().getQName().getLocalName();
+ }
+ return null;
+ }
+
@Nullable abstract String summary();
void generateRequestBody(final @NonNull JsonGenerator generator) throws IOException {
}
void generateParams(final @NonNull JsonGenerator generator) throws IOException {
- generator.writeArrayFieldStart("parameters");
- // need empty array here
- generator.writeEndArray();
+ generator.writeArrayFieldStart(PARAMETERS);
+ if (!parameters().isEmpty()) {
+ for (final var parameter : parameters()) {
+ generator.writeStartObject();
+ generator.writeStringField(NAME, parameter.name());
+ generator.writeStringField(IN, parameter.in());
+ generator.writeBooleanField(REQUIRED, parameter.required());
+ generator.writeObjectFieldStart(SCHEMA);
+ generator.writeStringField(TYPE, parameter.schema().type());
+ generator.writeEndObject(); //end of schema
+ if (parameter.description() != null) {
+ generator.writeStringField(DESCRIPTION, parameter.description());
+ }
+ generator.writeEndObject(); //end of parameter
+ }
+ }
+ generator.writeEndArray(); //end of params
+ }
+
+
+ protected static void generateMediaTypeSchemaRef(final JsonGenerator generator, final String mediaType,
+ final String ref) throws IOException {
+ generator.writeObjectFieldStart(mediaType);
+ generator.writeObjectFieldStart(SCHEMA);
+ generator.writeStringField(REF, ref);
+ generator.writeEndObject();
+ generator.writeEndObject();
+ }
+
+ void generateGetRoot(final JsonGenerator generator, final String resourceType)
+ throws IOException {
+ generator.writeObjectFieldStart("get");
+ if (resourceType.equals("data")) {
+ generator.writeStringField(DESCRIPTION, "Queries the config (startup) datastore on the mounted hosted.");
+ } else if (resourceType.equals("operations")) {
+ generator.writeStringField(DESCRIPTION,
+ "Queries the available operations (RPC calls) on the mounted hosted.");
+ }
+ generator.writeObjectFieldStart(RESPONSES);
+ generator.writeObjectFieldStart(String.valueOf(OK.getStatusCode()));
+ generator.writeStringField(DESCRIPTION, "OK");
+ generator.writeEndObject(); //end of 200
+ generator.writeEndObject(); // end of responses
+ final var summary = HttpMethod.GET + " - " + deviceName() + " - datastore - " + resourceType;
+ generator.writeStringField(SUMMARY, summary);
+ generator.writeArrayFieldStart("tags");
+ generator.writeString(deviceName + " GET root");
+ generator.writeEndArray(); //end of tags
+ generator.writeEndObject(); //end of get
}
}
--- /dev/null
+/*
+ * Copyright (c) 2023 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 org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+public record ParameterEntity(@NonNull String name, @NonNull String in, boolean required,
+ @NonNull ParameterSchemaEntity schema, @Nullable String description) {
+ public ParameterEntity(final String name, final String in, final boolean required,
+ final ParameterSchemaEntity schema, final String description) {
+ this.name = name;
+ this.in = in;
+ this.required = required;
+ this.schema = schema;
+ this.description = description;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 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 java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+public record ParameterSchemaEntity(@NonNull String type, @Nullable List<String> schemaEnum) {
+ public ParameterSchemaEntity(final String type, final List<String> schemaEnum) {
+ this.type = type;
+ this.schemaEnum = schemaEnum;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 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 static javax.ws.rs.core.Response.Status.NO_CONTENT;
+import static javax.ws.rs.core.Response.Status.OK;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import java.io.IOException;
+import java.util.List;
+import javax.ws.rs.HttpMethod;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+public final class PatchEntity extends OperationEntity {
+
+ private final String fullName;
+
+ public PatchEntity(final SchemaNode schema, final String deviceName, final String moduleName,
+ final List<ParameterEntity> parameters, final String refPath, final String fullName) {
+ super(schema, deviceName, moduleName, parameters, refPath);
+ this.fullName = fullName;
+ }
+
+ @Override
+ protected String operation() {
+ return "patch";
+ }
+
+ @Override
+ @Nullable String summary() {
+ return SUMMARY_TEMPLATE.formatted(HttpMethod.PATCH, moduleName(), deviceName(), nodeName());
+ }
+
+ @Override
+ void generateResponses(final @NonNull JsonGenerator generator) throws IOException {
+ generator.writeObjectFieldStart(RESPONSES);
+ generator.writeObjectFieldStart(String.valueOf(OK.getStatusCode()));
+ generator.writeStringField(DESCRIPTION, "OK");
+ generator.writeEndObject(); //end of 200
+ generator.writeObjectFieldStart(String.valueOf(NO_CONTENT.getStatusCode()));
+ generator.writeStringField(DESCRIPTION, "Updated");
+ generator.writeEndObject(); //end of 204
+ generator.writeEndObject();
+ }
+
+ @Override
+ void generateRequestBody(final @NonNull JsonGenerator generator) throws IOException {
+ final var ref = COMPONENTS_PREFIX + moduleName() + "_" + refPath();
+ generator.writeObjectFieldStart(REQUEST_BODY);
+ generator.writeStringField(DESCRIPTION, nodeName());
+ generator.writeObjectFieldStart(CONTENT);
+ generator.writeObjectFieldStart("application/yang-data+json");
+ generator.writeObjectFieldStart(SCHEMA);
+ generator.writeObjectFieldStart(PROPERTIES);
+ generator.writeObjectFieldStart(fullName);
+ if (schema() instanceof ListSchemaNode) {
+ generator.writeStringField(TYPE, ARRAY);
+ generator.writeObjectFieldStart(ITEMS);
+ generator.writeStringField(REF, ref);
+ generator.writeStringField(TYPE, OBJECT);
+ generator.writeEndObject(); //end of items
+ } else {
+ generator.writeStringField(REF, ref);
+ generator.writeStringField(TYPE, OBJECT);
+ }
+ generator.writeEndObject(); //end of nodeName
+ generator.writeEndObject(); //end of props
+ generator.writeEndObject(); //end of schema
+ generator.writeEndObject(); //end of json
+ generateMediaTypeSchemaRef(generator, "application/yang-data+xml", ref);
+ generator.writeEndObject(); //end of content
+ generator.writeEndObject(); //end of request body
+ }
+}
public final class PathEntity extends OpenApiEntity {
private final @NonNull String path;
- private final @Nullable OperationEntity post;
+ private final @Nullable PostEntity post;
+ private final @Nullable PatchEntity patch;
+ private final @Nullable GetEntity get;
+ private final @Nullable PutEntity put;
+ private final @Nullable DeleteEntity delete;
- public PathEntity(final String path, final OperationEntity post) {
+ public PathEntity(final String path, final PostEntity post, final PatchEntity patch,
+ final PutEntity put, final GetEntity get, final DeleteEntity delete) {
this.path = Objects.requireNonNull(path);
this.post = post;
+ this.patch = patch;
+ this.put = put;
+ this.delete = delete;
+ this.get = get;
}
@Override
if (patchOperation != null) {
patchOperation.generate(generator);
}
+ final var deleteOperation = delete();
+ if (deleteOperation != null) {
+ deleteOperation.generate(generator);
+ }
+ final var getOperation = get();
+ if (getOperation != null) {
+ getOperation.generate(generator);
+ }
generator.writeEndObject();
}
}
@Nullable OperationEntity put() {
- return null;
+ return put;
}
@Nullable OperationEntity patch() {
- return null;
+ return patch;
+ }
+
+ @Nullable OperationEntity get() {
+ return get;
+ }
+
+ @Nullable OperationEntity delete() {
+ return delete;
}
}
*/
package org.opendaylight.restconf.openapi.model;
+import static javax.ws.rs.core.Response.Status.CREATED;
import static javax.ws.rs.core.Response.Status.NO_CONTENT;
import static javax.ws.rs.core.Response.Status.OK;
import static org.opendaylight.restconf.openapi.impl.DefinitionGenerator.OUTPUT_SUFFIX;
import com.fasterxml.jackson.core.JsonGenerator;
import java.io.IOException;
+import java.util.List;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.MediaType;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
public final class PostEntity extends OperationEntity {
- private static final String SUMMARY_TEMPLATE = "%s - %s - %s - %s";
+
+ private final DocumentedNode parentNode;
private static final String INPUT_SUFFIX = "_input";
private static final String INPUT_KEY = "input";
- private static final String OBJECT = "object";
- private static final String SCHEMA = "schema";
- private static final String TYPE = "type";
- private static final String DESCRIPTION = "description";
- private static final String COMPONENTS_PREFIX = "#/components/schemas/";
-
- public PostEntity(final OperationDefinition schema, final String deviceName, final String moduleName) {
- super(schema, deviceName, moduleName);
+ private static final String POST_DESCRIPTION = """
+ \n
+ Note:
+ In example payload, you can see only the first data node child of the resource to be created, following the
+ guidelines of RFC 8040, which allows us to create only one resource in POST request.
+ """;
+
+ public PostEntity(final SchemaNode schema, final String deviceName, final String moduleName,
+ final List<ParameterEntity> parameters, final String refPath, final DocumentedNode parentNode) {
+ super(schema, deviceName, moduleName, parameters, refPath);
+ this.parentNode = parentNode;
}
protected String operation() {
}
@Nullable String summary() {
- final var operationName = schema().getQName().getLocalName() + INPUT_SUFFIX;
- return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), operationName);
+ if (parentNode instanceof Module) {
+ return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), moduleName());
+ }
+ if (parentNode != null) {
+ return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(),
+ ((DataSchemaNode) parentNode).getQName().getLocalName());
+ }
+ return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), nodeName());
}
@Override
void generateResponses(final @NonNull JsonGenerator generator) throws IOException {
- if (schema() instanceof RpcDefinition rpc) {
- generator.writeObjectFieldStart("responses");
+ generator.writeObjectFieldStart(RESPONSES);
+ if (schema() instanceof OperationDefinition rpc) {
final var output = rpc.getOutput();
final var operationName = rpc.getQName().getLocalName();
if (!output.getChildNodes().isEmpty()) {
generator.writeObjectFieldStart(String.valueOf(OK.getStatusCode()));
generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
- generator.writeObjectFieldStart("content");
+ generator.writeObjectFieldStart(CONTENT);
generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
generator.writeEndObject();
generator.writeEndObject();
}
+ } else {
+ generator.writeObjectFieldStart(String.valueOf(CREATED.getStatusCode()));
+ generator.writeStringField(DESCRIPTION, "Created");
generator.writeEndObject();
}
+ generator.writeEndObject();
}
@Override
void generateRequestBody(final @NonNull JsonGenerator generator) throws IOException {
- if (schema() instanceof RpcDefinition rpc) {
- generator.writeObjectFieldStart("requestBody");
+ generator.writeObjectFieldStart(REQUEST_BODY);
+ if (schema() instanceof OperationDefinition rpc) {
final var input = rpc.getInput();
final var operationName = rpc.getQName().getLocalName();
generator.writeStringField(DESCRIPTION, operationName + INPUT_SUFFIX);
- generator.writeObjectFieldStart("content");
+ generator.writeObjectFieldStart(CONTENT);
if (!input.getChildNodes().isEmpty()) {
// TODO: add proper discriminator from DefinitionNames when schemas re-implementation is done
final var ref = COMPONENTS_PREFIX + moduleName() + "_" + operationName + INPUT_SUFFIX;
generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
generator.writeObjectFieldStart(SCHEMA);
- generator.writeObjectFieldStart("properties");
+ generator.writeObjectFieldStart(PROPERTIES);
generator.writeObjectFieldStart(INPUT_KEY);
generator.writeStringField(TYPE, OBJECT);
generator.writeEndObject();
generator.writeObjectFieldStart(SCHEMA);
generator.writeObjectFieldStart("xml");
- generator.writeStringField("name", INPUT_KEY);
+ generator.writeStringField(NAME, INPUT_KEY);
generator.writeStringField("namespace", input.getQName().getNamespace().toString());
generator.writeEndObject();
generator.writeEndObject();
}
generator.writeEndObject();
+ } else {
+ generator.writeStringField(DESCRIPTION, nodeName());
+ generator.writeObjectFieldStart(CONTENT);
+ final var ref = COMPONENTS_PREFIX + moduleName() + "_" + refPath();
+ //TODO: Remove if and fix this weird logic of top level nodes
+ var childConfig = true;
+ if (schema() instanceof DataNodeContainer dataSchema) {
+ final var child = dataSchema.getChildNodes().stream()
+ .filter(n -> n instanceof ListSchemaNode || n instanceof ContainerSchemaNode)
+ .findFirst().orElse(null);
+ if (child != null) {
+ childConfig = child.isConfiguration();
+ }
+ }
+ if (parentNode == null && !childConfig) {
+ generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
+ } else {
+ generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
+ generator.writeObjectFieldStart(SCHEMA);
+
+ generator.writeObjectFieldStart(PROPERTIES);
+ generator.writeObjectFieldStart(nodeName());
+ if (schema() instanceof ListSchemaNode) {
+ generator.writeStringField(TYPE, ARRAY);
+ generator.writeObjectFieldStart(ITEMS);
+ generator.writeStringField(REF, ref);
+ generator.writeStringField(TYPE, OBJECT);
+ generator.writeEndObject();
+ } else {
+ generator.writeStringField(REF, ref);
+ generator.writeStringField(TYPE, OBJECT);
+ }
+ generator.writeEndObject();
+ generator.writeEndObject();
+ generator.writeEndObject();
+ generator.writeEndObject();
+ }
+ generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
generator.writeEndObject();
}
+ generator.writeEndObject();
}
- private static void generateMediaTypeSchemaRef(final JsonGenerator generator, final String mediaType,
- final String ref) throws IOException {
- generator.writeObjectFieldStart(mediaType);
- generator.writeObjectFieldStart(SCHEMA);
- generator.writeStringField("$ref", ref);
- generator.writeEndObject();
- generator.writeEndObject();
+
+ @Override
+ public void generateBasics(@NonNull JsonGenerator generator) throws IOException {
+ final var description = description();
+ if (schema() instanceof OperationDefinition) {
+ generator.writeStringField(DESCRIPTION, description);
+ } else {
+ generator.writeStringField(DESCRIPTION, description + POST_DESCRIPTION);
+ }
+ final var summary = summary();
+ if (summary != null) {
+ generator.writeStringField(SUMMARY, summary);
+ }
+ }
+
+ @Override
+ @Nullable String description() {
+ if (parentNode != null) {
+ return parentNode.getDescription().orElse("");
+ } else {
+ return super.description();
+ }
}
}
--- /dev/null
+/*
+ * Copyright (c) 2023 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 static javax.ws.rs.core.Response.Status.CREATED;
+import static javax.ws.rs.core.Response.Status.NO_CONTENT;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import java.io.IOException;
+import java.util.List;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.MediaType;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+public final class PutEntity extends OperationEntity {
+
+ private final String fullName;
+
+ public PutEntity(final SchemaNode schema, final String deviceName, final String moduleName,
+ final List<ParameterEntity> parameters, final String refPath, final String fullName) {
+ super(schema, deviceName, moduleName, parameters, refPath);
+ this.fullName = fullName;
+ }
+
+ @Override
+ protected String operation() {
+ return "put";
+ }
+
+ @Override
+ @Nullable String summary() {
+ return SUMMARY_TEMPLATE.formatted(HttpMethod.PUT, moduleName(), deviceName(), nodeName());
+ }
+
+ @Override
+ void generateResponses(final @NonNull JsonGenerator generator) throws IOException {
+ generator.writeObjectFieldStart(RESPONSES);
+ generator.writeObjectFieldStart(String.valueOf(CREATED.getStatusCode()));
+ generator.writeStringField(DESCRIPTION, "Created");
+ generator.writeEndObject(); //end of 201
+ generator.writeObjectFieldStart(String.valueOf(NO_CONTENT.getStatusCode()));
+ generator.writeStringField(DESCRIPTION, "Updated");
+ generator.writeEndObject(); //end of 204
+ generator.writeEndObject();
+ }
+
+ @Override
+ void generateRequestBody(final @NonNull JsonGenerator generator) throws IOException {
+ generator.writeObjectFieldStart(REQUEST_BODY);
+ generator.writeStringField(DESCRIPTION, nodeName());
+ generator.writeObjectFieldStart(CONTENT);
+ final var ref = COMPONENTS_PREFIX + moduleName() + "_" + refPath();
+ generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
+ generator.writeObjectFieldStart(SCHEMA);
+ generator.writeObjectFieldStart(PROPERTIES);
+ generator.writeObjectFieldStart(fullName);
+ if (schema() instanceof ListSchemaNode) {
+ generator.writeStringField(TYPE, ARRAY);
+ generator.writeObjectFieldStart(ITEMS);
+ generator.writeStringField(REF, ref);
+ generator.writeStringField(TYPE, OBJECT);
+ generator.writeEndObject(); //end of items
+ } else {
+ generator.writeStringField(REF, ref);
+ generator.writeStringField(TYPE, OBJECT);
+ }
+ generator.writeEndObject(); //end of moduleName
+ generator.writeEndObject(); //end of props
+ generator.writeEndObject(); //end of schema
+ generator.writeEndObject(); //end of json
+
+ generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
+ generator.writeEndObject(); //end of content
+ generator.writeEndObject(); //end of request body
+ }
+}