2 * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.restconf.openapi.model;
10 import static javax.ws.rs.core.Response.Status.CREATED;
11 import static javax.ws.rs.core.Response.Status.NO_CONTENT;
12 import static javax.ws.rs.core.Response.Status.OK;
14 import com.fasterxml.jackson.core.JsonGenerator;
15 import java.io.IOException;
16 import java.util.List;
17 import javax.ws.rs.HttpMethod;
18 import javax.ws.rs.core.MediaType;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
23 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
25 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.Module;
27 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
28 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
30 public final class PostEntity extends OperationEntity {
32 private final DocumentedNode parentNode;
33 private static final String INPUT_SUFFIX = "_input";
34 private static final String INPUT_KEY = "input";
35 private static final String POST_DESCRIPTION = """
38 In example payload, you can see only the first data node child of the resource to be created, following the
39 guidelines of RFC 8040, which allows us to create only one resource in POST request.
42 public PostEntity(final SchemaNode schema, final String deviceName, final String moduleName,
43 final List<ParameterEntity> parameters, final String refPath, final DocumentedNode parentNode) {
44 super(schema, deviceName, moduleName, parameters, refPath);
45 this.parentNode = parentNode;
48 protected String operation() {
52 @Nullable String summary() {
53 if (parentNode instanceof Module) {
54 return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), moduleName());
56 if (parentNode != null) {
57 return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(),
58 ((DataSchemaNode) parentNode).getQName().getLocalName());
60 return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), nodeName());
64 void generateResponses(final @NonNull JsonGenerator generator) throws IOException {
65 generator.writeObjectFieldStart(RESPONSES);
66 if (schema() instanceof OperationDefinition rpc) {
67 final var output = rpc.getOutput();
68 final var operationName = rpc.getQName().getLocalName();
69 if (!output.getChildNodes().isEmpty()) {
70 // TODO: add proper discriminator from DefinitionNames when schemas re-implementation is done
71 final var ref = COMPONENTS_PREFIX + moduleName() + "_" + operationName + "_output";
72 generator.writeObjectFieldStart(String.valueOf(OK.getStatusCode()));
73 generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
75 generator.writeObjectFieldStart(CONTENT);
76 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
77 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
78 generator.writeEndObject();
80 generator.writeEndObject();
83 generator.writeObjectFieldStart(String.valueOf(NO_CONTENT.getStatusCode()));
84 generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
85 generator.writeEndObject();
89 generator.writeObjectFieldStart(String.valueOf(CREATED.getStatusCode()));
90 generator.writeStringField(DESCRIPTION, "Created");
91 generator.writeEndObject();
93 generator.writeEndObject();
97 void generateRequestBody(final @NonNull JsonGenerator generator) throws IOException {
98 generator.writeObjectFieldStart(REQUEST_BODY);
99 if (schema() instanceof OperationDefinition rpc) {
100 final var input = rpc.getInput();
101 final var operationName = rpc.getQName().getLocalName();
102 generator.writeStringField(DESCRIPTION, operationName + INPUT_SUFFIX);
103 generator.writeObjectFieldStart(CONTENT);
104 if (!input.getChildNodes().isEmpty()) {
105 // TODO: add proper discriminator from DefinitionNames when schemas re-implementation is done
106 final var ref = COMPONENTS_PREFIX + moduleName() + "_" + operationName + INPUT_SUFFIX;
107 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
108 generator.writeObjectFieldStart(SCHEMA);
109 generator.writeObjectFieldStart("properties");
110 generator.writeObjectFieldStart(INPUT_KEY);
111 generator.writeStringField("$ref", ref);
112 generator.writeStringField(TYPE, OBJECT);
113 generator.writeEndObject();
114 generator.writeEndObject();
115 generator.writeEndObject();
116 generator.writeEndObject();
117 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
119 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
120 generator.writeObjectFieldStart(SCHEMA);
122 generator.writeObjectFieldStart(PROPERTIES);
123 generator.writeObjectFieldStart(INPUT_KEY);
124 generator.writeStringField(TYPE, OBJECT);
125 generator.writeEndObject();
126 generator.writeEndObject();
128 generator.writeStringField(TYPE, OBJECT);
129 generator.writeEndObject();
130 generator.writeEndObject();
132 generator.writeObjectFieldStart(MediaType.APPLICATION_XML);
133 generator.writeObjectFieldStart(SCHEMA);
135 generator.writeObjectFieldStart("xml");
136 generator.writeStringField(NAME, INPUT_KEY);
137 generator.writeStringField("namespace", input.getQName().getNamespace().toString());
138 generator.writeEndObject();
140 generator.writeStringField(TYPE, OBJECT);
141 generator.writeEndObject();
142 generator.writeEndObject();
144 generator.writeEndObject();
146 generator.writeStringField(DESCRIPTION, nodeName());
147 generator.writeObjectFieldStart(CONTENT);
148 final var ref = COMPONENTS_PREFIX + moduleName() + "_" + refPath();
149 //TODO: Remove if and fix this weird logic of top level nodes
150 var childConfig = true;
151 if (schema() instanceof DataNodeContainer dataSchema) {
152 final var child = dataSchema.getChildNodes().stream()
153 .filter(n -> n instanceof ListSchemaNode || n instanceof ContainerSchemaNode)
154 .findFirst().orElse(null);
156 childConfig = child.isConfiguration();
159 if (parentNode == null && !childConfig) {
160 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
162 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
163 generator.writeObjectFieldStart(SCHEMA);
165 generator.writeObjectFieldStart(PROPERTIES);
166 generator.writeObjectFieldStart(nodeName());
167 if (schema() instanceof ListSchemaNode) {
168 generator.writeStringField(TYPE, ARRAY);
169 generator.writeObjectFieldStart(ITEMS);
170 generator.writeStringField(REF, ref);
171 generator.writeStringField(TYPE, OBJECT);
172 generator.writeEndObject();
174 generator.writeStringField(REF, ref);
175 generator.writeStringField(TYPE, OBJECT);
177 generator.writeEndObject();
178 generator.writeEndObject();
179 generator.writeEndObject();
180 generator.writeEndObject();
182 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
183 generator.writeEndObject();
185 generator.writeEndObject();
190 public void generateBasics(@NonNull JsonGenerator generator) throws IOException {
191 final var description = description();
192 if (schema() instanceof OperationDefinition) {
193 generator.writeStringField(DESCRIPTION, description);
195 generator.writeStringField(DESCRIPTION, description + POST_DESCRIPTION);
197 final var summary = summary();
198 if (summary != null) {
199 generator.writeStringField(SUMMARY, summary);
204 @Nullable String description() {
205 if (parentNode != null) {
206 return parentNode.getDescription().orElse("");
208 return super.description();