2 * Copyright (c) 2020 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.builder;
10 import static org.opendaylight.restconf.openapi.impl.DefinitionGenerator.INPUT;
11 import static org.opendaylight.restconf.openapi.impl.DefinitionGenerator.INPUT_SUFFIX;
12 import static org.opendaylight.restconf.openapi.impl.DefinitionGenerator.OUTPUT_SUFFIX;
14 import com.fasterxml.jackson.databind.node.ArrayNode;
15 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
16 import com.fasterxml.jackson.databind.node.ObjectNode;
17 import java.util.ArrayList;
18 import java.util.List;
19 import javax.ws.rs.HttpMethod;
20 import javax.ws.rs.core.MediaType;
21 import javax.ws.rs.core.Response;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.opendaylight.restconf.openapi.impl.DefinitionNames;
24 import org.opendaylight.restconf.openapi.model.Operation;
25 import org.opendaylight.restconf.openapi.model.Parameter;
26 import org.opendaylight.restconf.openapi.model.Schema;
27 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
31 import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
33 public final class OperationBuilder {
34 public static final String CONTENT_KEY = "content";
35 public static final String COMPONENTS_PREFIX = "#/components/schemas/";
36 public static final String DESCRIPTION_KEY = "description";
37 public static final String INPUT_KEY = "input";
38 public static final String NAME_KEY = "name";
39 public static final String PROPERTIES_KEY = "properties";
40 public static final String REF_KEY = "$ref";
41 public static final String SCHEMA_KEY = "schema";
42 public static final String SUMMARY_TEMPLATE = "%s - %s - %s - %s";
43 public static final String TOP = "_TOP";
44 public static final String XML_KEY = "xml";
45 private static final List<String> MIME_TYPES = List.of(MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON);
46 private static final String OBJECT = "object";
47 private static final String TYPE_KEY = "type";
49 private OperationBuilder() {
53 public static Operation buildPost(final String parentName, final String nodeName, final String discriminator,
54 final String moduleName, final @NonNull String deviceName, final String description,
55 final List<Parameter> pathParams) {
56 final var summary = SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName, moduleName, nodeName);
57 final List<String> tags = List.of(deviceName + " " + moduleName);
58 final List<Parameter> parameters = new ArrayList<>(pathParams);
59 final ObjectNode ref = JsonNodeFactory.instance.objectNode();
60 final String cleanDefName = parentName + "_" + nodeName;
61 final String defName = cleanDefName + discriminator;
62 final String xmlDefName = cleanDefName + discriminator;
63 ref.put(REF_KEY, COMPONENTS_PREFIX + defName);
64 final ObjectNode requestBody = createRequestBodyParameter(defName, xmlDefName, nodeName, summary);
65 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
66 responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
67 buildResponse(Response.Status.CREATED.getReasonPhrase()));
69 return new Operation.Builder()
71 .parameters(parameters)
72 .requestBody(requestBody)
74 .description(description)
79 public static Operation buildGet(final DataSchemaNode node, final String moduleName,
80 final @NonNull String deviceName, final List<Parameter> pathParams, final String defName,
81 final String defNameTop, final boolean isConfig) {
82 final String description = node.getDescription().orElse("");
83 final String summary = SUMMARY_TEMPLATE.formatted(HttpMethod.GET, deviceName, moduleName,
84 node.getQName().getLocalName());
85 final List<String> tags = List.of(deviceName + " " + moduleName);
86 final List<Parameter> parameters = new ArrayList<>(pathParams);
87 parameters.add(buildQueryParameters(isConfig));
88 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
89 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
90 final ObjectNode xmlSchema = JsonNodeFactory.instance.objectNode();
91 if (node.isConfiguration()) {
92 schema.put(REF_KEY, COMPONENTS_PREFIX + defNameTop);
93 xmlSchema.put(REF_KEY, COMPONENTS_PREFIX + defName);
95 responses.set(String.valueOf(Response.Status.OK.getStatusCode()),
96 buildResponse(Response.Status.OK.getReasonPhrase(), schema, xmlSchema));
98 return new Operation.Builder()
100 .parameters(parameters)
101 .responses(responses)
102 .description(description)
107 private static Parameter buildQueryParameters(final boolean isConfig) {
108 final ArrayNode cases = JsonNodeFactory.instance.arrayNode()
113 return new Parameter.Builder()
117 .schema(new Schema.Builder().type("string").schemaEnum(cases).build())
121 public static Operation buildPut(final DataSchemaNode node, final String parentName, final String moduleName,
122 final @NonNull String deviceName, final List<Parameter> pathParams, final String fullName) {
123 final String nodeName = node.getQName().getLocalName();
124 final String summary = SUMMARY_TEMPLATE.formatted(HttpMethod.PUT, moduleName, deviceName, nodeName);
125 final List<String> tags = List.of(deviceName + " " + moduleName);
126 final List<Parameter> parameters = new ArrayList<>(pathParams);
127 final String defName = parentName + "_" + nodeName;
128 final boolean isList = node instanceof ListSchemaNode;
129 final ObjectNode requestBody = createPutRequestBodyParameter(defName, fullName, isList);
131 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
132 responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
133 buildResponse(Response.Status.CREATED.getReasonPhrase()));
134 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse("Updated"));
136 return new Operation.Builder()
138 .parameters(parameters)
139 .requestBody(requestBody)
140 .responses(responses)
141 .description(node.getDescription().orElse(""))
146 public static Operation buildPatch(final String parentName, final String nodeName, final String moduleName,
147 final @NonNull String deviceName, final String description, final List<Parameter> pathParams) {
148 final String summary = SUMMARY_TEMPLATE.formatted(HttpMethod.PATCH, deviceName, moduleName, nodeName);
149 final List<String> tags = List.of(deviceName + " " + moduleName);
150 final List<Parameter> parameters = new ArrayList<>(pathParams);
151 final String defName = parentName + "_" + nodeName + TOP;
152 final String xmlDefName = parentName + "_" + nodeName;
153 final ObjectNode requestBody = createRequestBodyParameter(defName, xmlDefName, nodeName, summary);
155 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
156 responses.set(String.valueOf(Response.Status.OK.getStatusCode()),
157 buildResponse(Response.Status.OK.getReasonPhrase()));
158 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse("Updated"));
160 return new Operation.Builder()
162 .parameters(parameters)
163 .requestBody(requestBody)
164 .responses(responses)
165 .description(description)
170 public static Operation buildDelete(final DataSchemaNode node, final String moduleName,
171 final @NonNull String deviceName, final List<Parameter> pathParams) {
172 final String summary = SUMMARY_TEMPLATE.formatted(HttpMethod.DELETE, deviceName, moduleName,
173 node.getQName().getLocalName());
174 final List<String> tags = List.of(deviceName + " " + moduleName);
175 final String description = node.getDescription().orElse("");
176 final List<Parameter> parameters = new ArrayList<>(pathParams);
178 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
179 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse("Deleted"));
181 return new Operation.Builder()
183 .parameters(parameters)
184 .responses(responses)
185 .description(description)
190 public static Operation buildPostOperation(final OperationDefinition operDef, final String moduleName,
191 final @NonNull String deviceName, final String parentName, final DefinitionNames definitionNames,
192 final List<Parameter> parentPathParameters) {
193 final List<Parameter> parameters = new ArrayList<>(parentPathParameters);
194 final String operationName = operDef.getQName().getLocalName();
195 final String inputName = operationName + INPUT_SUFFIX;
196 final String summary = SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName, moduleName, operationName);
198 final InputSchemaNode input = operDef.getInput();
199 final OutputSchemaNode output = operDef.getOutput();
200 ObjectNode requestBody;
201 if (!input.getChildNodes().isEmpty()) {
202 final String discriminator = definitionNames.getDiscriminator(input);
203 final String clearDefName = parentName + "_" + operationName + INPUT_SUFFIX;
204 final String defName = clearDefName + discriminator;
205 final String defTopName = clearDefName + TOP + discriminator;
206 requestBody = createRequestBodyParameter(defTopName, defName, inputName, summary);
208 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
209 final ObjectNode jsonSchema = JsonNodeFactory.instance.objectNode();
210 final ObjectNode properties = JsonNodeFactory.instance.objectNode();
211 final ObjectNode inputSchema = JsonNodeFactory.instance.objectNode();
212 inputSchema.put(TYPE_KEY, OBJECT);
213 properties.set(INPUT_KEY, inputSchema);
214 jsonSchema.put(TYPE_KEY, OBJECT);
215 jsonSchema.set(PROPERTIES_KEY, properties);
216 final ObjectNode content = JsonNodeFactory.instance.objectNode();
217 final ObjectNode jsonTypeValue = JsonNodeFactory.instance.objectNode();
218 jsonTypeValue.set(SCHEMA_KEY, jsonSchema);
219 content.set(MediaType.APPLICATION_JSON, jsonTypeValue);
221 final ObjectNode xmlSchema = JsonNodeFactory.instance.objectNode();
222 xmlSchema.put(TYPE_KEY, OBJECT);
223 final ObjectNode xml = JsonNodeFactory.instance.objectNode();
224 xml.put(NAME_KEY, INPUT);
225 xmlSchema.set(XML_KEY, xml);
226 final ObjectNode xmlTypeValue = JsonNodeFactory.instance.objectNode();
227 xmlTypeValue.set(SCHEMA_KEY, xmlSchema);
228 content.set(MediaType.APPLICATION_XML, xmlTypeValue);
230 payload.set(CONTENT_KEY, content);
231 payload.put(DESCRIPTION_KEY, inputName);
232 requestBody = payload;
234 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
235 final String description = String.format("RPC %s success", operationName);
237 if (!output.getChildNodes().isEmpty()) {
238 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
239 final String defName = parentName + "_" + operationName + OUTPUT_SUFFIX + TOP
240 + definitionNames.getDiscriminator(output);
241 schema.put(REF_KEY, COMPONENTS_PREFIX + defName);
242 responses.set(String.valueOf(Response.Status.OK.getStatusCode()), buildResponse(description, schema));
244 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse(description));
246 final String desc = operDef.getDescription().orElse("");
247 final List<String> tags = List.of(deviceName + " " + moduleName);
248 return new Operation.Builder()
250 .parameters(parameters)
251 .requestBody(requestBody)
252 .responses(responses)
258 private static ObjectNode createRequestBodyParameter(final String defName, final String xmlDefName,
259 final String name, final String summary) {
260 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
261 final ObjectNode content = JsonNodeFactory.instance.objectNode();
262 if (summary != null && summary.contains(HttpMethod.PATCH)) {
263 content.set("application/yang-data+json", buildMimeTypeValue(defName));
264 content.set("application/yang-data+xml", buildMimeTypeValue(xmlDefName));
266 content.set(MediaType.APPLICATION_JSON, buildMimeTypeValue(defName));
267 content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(xmlDefName));
269 payload.set(CONTENT_KEY, content);
270 payload.put(DESCRIPTION_KEY, name);
274 private static ObjectNode createPutRequestBodyParameter(final String defName, final String name,
275 final boolean isList) {
276 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
277 final ObjectNode content = JsonNodeFactory.instance.objectNode();
278 final ObjectNode properties = JsonNodeFactory.instance.objectNode();
280 final ObjectNode list = JsonNodeFactory.instance.objectNode();
281 final ObjectNode listValue = JsonNodeFactory.instance.objectNode();
282 listValue.put(TYPE_KEY, "array");
283 listValue.set("items", buildRefSchema(defName));
284 list.set(name, listValue);
285 properties.set(PROPERTIES_KEY, list);
287 final ObjectNode container = JsonNodeFactory.instance.objectNode();
288 container.set(name, buildRefSchema(defName));
289 properties.set(PROPERTIES_KEY, container);
291 final ObjectNode jsonSchema = JsonNodeFactory.instance.objectNode();
292 jsonSchema.set(SCHEMA_KEY, properties);
293 content.set(MediaType.APPLICATION_JSON, jsonSchema);
294 content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(defName));
295 payload.set(CONTENT_KEY, content);
296 payload.put(DESCRIPTION_KEY, name);
300 private static ObjectNode buildRefSchema(final String defName) {
301 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
302 schema.put(REF_KEY, COMPONENTS_PREFIX + defName);
306 private static ObjectNode buildMimeTypeValue(final String defName) {
307 final ObjectNode mimeTypeValue = JsonNodeFactory.instance.objectNode();
308 mimeTypeValue.set(SCHEMA_KEY, buildRefSchema(defName));
309 return mimeTypeValue;
312 public static ObjectNode buildResponse(final String description, final ObjectNode schema,
313 final ObjectNode xmlSchema) {
314 final ObjectNode response = JsonNodeFactory.instance.objectNode();
315 if (!schema.isEmpty()) {
316 final ObjectNode content = JsonNodeFactory.instance.objectNode();
317 final ObjectNode body = JsonNodeFactory.instance.objectNode();
318 final ObjectNode xmlBody = JsonNodeFactory.instance.objectNode();
320 body.set(SCHEMA_KEY, schema);
321 xmlBody.set(SCHEMA_KEY, xmlSchema);
322 content.set(MediaType.APPLICATION_JSON, body);
323 content.set(MediaType.APPLICATION_XML, xmlBody);
325 response.set(CONTENT_KEY, content);
328 response.put(DESCRIPTION_KEY, description);
332 private static ObjectNode buildResponse(final String description) {
333 final ObjectNode response = JsonNodeFactory.instance.objectNode();
334 response.put(DESCRIPTION_KEY, description);
338 private static ObjectNode buildResponse(final String description, final ObjectNode schema) {
339 final ObjectNode response = JsonNodeFactory.instance.objectNode();
340 final ObjectNode content = JsonNodeFactory.instance.objectNode();
341 final ObjectNode body = JsonNodeFactory.instance.objectNode();
342 for (final String mimeType : MIME_TYPES) {
343 content.set(mimeType, body);
345 body.set(SCHEMA_KEY, schema);
346 response.set(CONTENT_KEY, content);
347 response.put(DESCRIPTION_KEY, description);