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
9 package org.opendaylight.restconf.openapi.model.builder;
11 import static org.opendaylight.restconf.openapi.impl.DefinitionGenerator.INPUT;
12 import static org.opendaylight.restconf.openapi.impl.DefinitionGenerator.INPUT_SUFFIX;
13 import static org.opendaylight.restconf.openapi.impl.DefinitionGenerator.OUTPUT_SUFFIX;
15 import com.fasterxml.jackson.databind.node.ArrayNode;
16 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
17 import com.fasterxml.jackson.databind.node.ObjectNode;
18 import java.util.List;
19 import java.util.Optional;
20 import javax.ws.rs.HttpMethod;
21 import javax.ws.rs.core.MediaType;
22 import javax.ws.rs.core.Response;
23 import org.opendaylight.restconf.openapi.impl.DefinitionNames;
24 import org.opendaylight.restconf.openapi.model.Operation;
25 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
28 import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
30 public final class OperationBuilder {
31 public static final String CONFIG = "_config";
32 public static final String CONFIG_QUERY_PARAM = "config";
33 public static final String CONTENT_KEY = "content";
34 public static final String COMPONENTS_PREFIX = "#/components/schemas/";
35 public static final String DESCRIPTION_KEY = "description";
36 public static final String IN_KEY = "in";
37 public static final String INPUT_KEY = "input";
38 public static final String NAME_KEY = "name";
39 public static final String NONCONFIG_QUERY_PARAM = "nonconfig";
40 public static final String PROPERTIES_KEY = "properties";
41 public static final String REF_KEY = "$ref";
42 public static final String SCHEMA_KEY = "schema";
43 public static final String SUMMARY_SEPARATOR = " - ";
44 public static final String TOP = "_TOP";
45 public static final String XML_KEY = "xml";
46 private static final String CONTENT = "content";
47 private static final ArrayNode CONSUMES_PUT_POST;
48 private static final String ENUM_KEY = "enum";
49 private static final List<String> MIME_TYPES = List.of(MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON);
50 private static final String OBJECT = "object";
51 private static final String REQUIRED_KEY = "required";
52 private static final String STRING = "string";
53 private static final String TYPE_KEY = "type";
54 private static final String QUERY = "query";
57 CONSUMES_PUT_POST = JsonNodeFactory.instance.arrayNode();
58 for (final String mimeType : MIME_TYPES) {
59 CONSUMES_PUT_POST.add(mimeType);
63 private OperationBuilder() {
67 public static Operation buildPost(final String parentName, final String nodeName, final String discriminator,
68 final String moduleName, final Optional<String> deviceName, final String description,
69 final ArrayNode pathParams) {
70 final var summary = buildSummaryValue(HttpMethod.POST, moduleName, deviceName, nodeName);
71 final ArrayNode tags = buildTagsValue(deviceName, moduleName);
72 final ArrayNode parameters = JsonNodeFactory.instance.arrayNode().addAll(pathParams);
73 final ObjectNode ref = JsonNodeFactory.instance.objectNode();
74 final String cleanDefName = parentName + CONFIG + "_" + nodeName;
75 final String defName = cleanDefName + discriminator;
76 final String xmlDefName = cleanDefName + discriminator;
77 ref.put(REF_KEY, COMPONENTS_PREFIX + defName);
78 final ObjectNode requestBody = createRequestBodyParameter(defName, xmlDefName, nodeName + CONFIG, summary);
79 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
80 responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
81 buildResponse(Response.Status.CREATED.getReasonPhrase(), Optional.empty()));
83 return new Operation.Builder()
85 .parameters(parameters)
86 .requestBody(requestBody)
88 .description(description)
93 public static Operation buildGet(final DataSchemaNode node, final String moduleName,
94 final Optional<String> deviceName, final ArrayNode pathParams, final String defName,
95 final String defNameTop, final boolean isConfig) {
96 final String description = node.getDescription().orElse("");
97 final String summary = buildSummaryValue(HttpMethod.GET, moduleName, deviceName,
98 node.getQName().getLocalName());
99 final ArrayNode tags = buildTagsValue(deviceName, moduleName);
100 final ArrayNode parameters = JsonNodeFactory.instance.arrayNode().addAll(pathParams);
101 addQueryParameters(parameters, isConfig);
102 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
103 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
104 final ObjectNode xmlSchema = JsonNodeFactory.instance.objectNode();
105 schema.put(REF_KEY, COMPONENTS_PREFIX + defNameTop);
106 xmlSchema.put(REF_KEY, COMPONENTS_PREFIX + defName);
108 responses.set(String.valueOf(Response.Status.OK.getStatusCode()),
109 buildResponse(Response.Status.OK.getReasonPhrase(), schema, xmlSchema));
111 return new Operation.Builder()
113 .parameters(parameters)
114 .responses(responses)
115 .description(description)
120 private static void addQueryParameters(final ArrayNode parameters, final boolean isConfig) {
121 final ObjectNode contentParam = JsonNodeFactory.instance.objectNode();
122 final ArrayNode cases = JsonNodeFactory.instance.arrayNode();
123 cases.add(NONCONFIG_QUERY_PARAM);
125 cases.add(CONFIG_QUERY_PARAM);
127 contentParam.put(REQUIRED_KEY, true);
129 contentParam.put(IN_KEY, QUERY);
130 contentParam.put(NAME_KEY, CONTENT);
132 final ObjectNode typeParent = getTypeParentNode(contentParam);
133 typeParent.put(TYPE_KEY, STRING);
134 typeParent.set(ENUM_KEY, cases);
136 parameters.add(contentParam);
139 public static Operation buildPut(final String parentName, final String nodeName, final String discriminator,
140 final String moduleName, final Optional<String> deviceName, final String description,
141 final ArrayNode pathParams) {
142 final String summary = buildSummaryValue(HttpMethod.PUT, moduleName, deviceName, nodeName);
143 final ArrayNode tags = buildTagsValue(deviceName, moduleName);
144 final ArrayNode parameters = JsonNodeFactory.instance.arrayNode().addAll(pathParams);
145 final String defName = parentName + CONFIG + "_" + nodeName + TOP;
146 final String xmlDefName = parentName + CONFIG + "_" + nodeName;
147 final ObjectNode requestBody = createRequestBodyParameter(defName, xmlDefName, nodeName + CONFIG, summary);
149 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
150 responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
151 buildResponse(Response.Status.CREATED.getReasonPhrase(), Optional.empty()));
152 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()),
153 buildResponse("Updated", Optional.empty()));
155 return new Operation.Builder()
157 .parameters(parameters)
158 .requestBody(requestBody)
159 .responses(responses)
160 .description(description)
165 public static Operation buildPatch(final String parentName, final String nodeName, final String moduleName,
166 final Optional<String> deviceName, final String description, final ArrayNode pathParams) {
167 final String summary = buildSummaryValue(HttpMethod.PATCH, moduleName, deviceName, nodeName);
168 final ArrayNode tags = buildTagsValue(deviceName, moduleName);
169 final ArrayNode parameters = JsonNodeFactory.instance.arrayNode().addAll(pathParams);
170 final String defName = parentName + CONFIG + "_" + nodeName + TOP;
171 final String xmlDefName = parentName + CONFIG + "_" + nodeName;
172 final ObjectNode requestBody = createRequestBodyParameter(defName, xmlDefName, nodeName + CONFIG, summary);
174 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
175 responses.set(String.valueOf(Response.Status.OK.getStatusCode()),
176 buildResponse(Response.Status.OK.getReasonPhrase(), Optional.empty()));
177 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()),
178 buildResponse("Updated", Optional.empty()));
180 return new Operation.Builder()
182 .parameters(parameters)
183 .requestBody(requestBody)
184 .responses(responses)
185 .description(description)
190 public static Operation buildDelete(final DataSchemaNode node, final String moduleName,
191 final Optional<String> deviceName, final ArrayNode pathParams) {
192 final String summary = buildSummaryValue(HttpMethod.DELETE, moduleName, deviceName,
193 node.getQName().getLocalName());
194 final ArrayNode tags = buildTagsValue(deviceName, moduleName);
195 final String description = node.getDescription().orElse("");
196 final ArrayNode parameters = JsonNodeFactory.instance.arrayNode().addAll(pathParams);
198 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
199 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()),
200 buildResponse("Deleted", Optional.empty()));
202 return new Operation.Builder()
204 .parameters(parameters)
205 .responses(responses)
206 .description(description)
211 public static Operation buildPostOperation(final OperationDefinition operDef, final String moduleName,
212 final Optional<String> deviceName, final String parentName, final DefinitionNames definitionNames,
213 final ArrayNode parentPathParameters) {
214 final ArrayNode parameters = JsonNodeFactory.instance.arrayNode().addAll(parentPathParameters);
215 final String operationName = operDef.getQName().getLocalName();
216 final String inputName = operationName + INPUT_SUFFIX;
217 final String summary = buildSummaryValue(HttpMethod.POST, moduleName, deviceName, operationName);
219 final InputSchemaNode input = operDef.getInput();
220 final OutputSchemaNode output = operDef.getOutput();
221 ObjectNode requestBody;
222 if (!input.getChildNodes().isEmpty()) {
223 final String discriminator = definitionNames.getDiscriminator(input);
224 final String clearDefName = parentName + "_" + operationName + INPUT_SUFFIX;
225 final String defName = clearDefName + discriminator;
226 final String defTopName = clearDefName + TOP + discriminator;
227 requestBody = createRequestBodyParameter(defTopName, defName, inputName, summary);
229 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
230 final ObjectNode jsonSchema = JsonNodeFactory.instance.objectNode();
231 final ObjectNode properties = JsonNodeFactory.instance.objectNode();
232 final ObjectNode inputSchema = JsonNodeFactory.instance.objectNode();
233 inputSchema.put(TYPE_KEY, OBJECT);
234 properties.set(INPUT_KEY, inputSchema);
235 jsonSchema.put(TYPE_KEY, OBJECT);
236 jsonSchema.set(PROPERTIES_KEY, properties);
237 final ObjectNode content = JsonNodeFactory.instance.objectNode();
238 final ObjectNode jsonTypeValue = JsonNodeFactory.instance.objectNode();
239 jsonTypeValue.set(SCHEMA_KEY, jsonSchema);
240 content.set(MediaType.APPLICATION_JSON, jsonTypeValue);
242 final ObjectNode xmlSchema = JsonNodeFactory.instance.objectNode();
243 xmlSchema.put(TYPE_KEY, OBJECT);
244 final ObjectNode xml = JsonNodeFactory.instance.objectNode();
245 xml.put(NAME_KEY, INPUT);
246 xmlSchema.set(XML_KEY, xml);
247 final ObjectNode xmlTypeValue = JsonNodeFactory.instance.objectNode();
248 xmlTypeValue.set(SCHEMA_KEY, xmlSchema);
249 content.set(MediaType.APPLICATION_XML, xmlTypeValue);
251 payload.set(CONTENT_KEY, content);
252 payload.put(DESCRIPTION_KEY, inputName);
253 requestBody = payload;
255 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
256 final String description = String.format("RPC %s success", operationName);
258 if (!output.getChildNodes().isEmpty()) {
259 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
260 final String defName = parentName + "_" + operationName + OUTPUT_SUFFIX + TOP
261 + definitionNames.getDiscriminator(output);
262 schema.put(REF_KEY, COMPONENTS_PREFIX + defName);
263 responses.set(String.valueOf(Response.Status.OK.getStatusCode()), buildResponse(description,
264 Optional.of(schema)));
266 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse(description,
269 final String desc = operDef.getDescription().orElse("");
270 final ArrayNode tags = buildTagsValue(deviceName, moduleName);
271 return new Operation.Builder()
273 .parameters(parameters)
274 .requestBody(requestBody)
275 .responses(responses)
281 private static ObjectNode createRequestBodyParameter(final String defName, final String xmlDefName,
282 final String name, final String summary) {
283 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
284 final ObjectNode content = JsonNodeFactory.instance.objectNode();
285 if (summary != null && summary.contains(HttpMethod.PATCH)) {
286 content.set("application/yang-data+json", buildMimeTypeValue(defName));
287 content.set("application/yang-data+xml", buildMimeTypeValue(xmlDefName));
289 content.set(MediaType.APPLICATION_JSON, buildMimeTypeValue(defName));
290 content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(xmlDefName));
292 payload.set(CONTENT_KEY, content);
293 payload.put(DESCRIPTION_KEY, name);
297 private static ObjectNode buildRefSchema(final String defName) {
298 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
299 schema.put(REF_KEY, COMPONENTS_PREFIX + defName);
303 private static ObjectNode buildMimeTypeValue(final String defName) {
304 final ObjectNode mimeTypeValue = JsonNodeFactory.instance.objectNode();
305 mimeTypeValue.set(SCHEMA_KEY, buildRefSchema(defName));
306 return mimeTypeValue;
309 public static ObjectNode buildResponse(final String description, final ObjectNode schema,
310 final ObjectNode xmlSchema) {
311 final ObjectNode response = JsonNodeFactory.instance.objectNode();
313 final ObjectNode content = JsonNodeFactory.instance.objectNode();
314 final ObjectNode body = JsonNodeFactory.instance.objectNode();
315 final ObjectNode xmlBody = JsonNodeFactory.instance.objectNode();
317 body.set(SCHEMA_KEY, schema);
318 xmlBody.set(SCHEMA_KEY, xmlSchema);
319 content.set(MediaType.APPLICATION_JSON, body);
320 content.set(MediaType.APPLICATION_XML, xmlBody);
322 response.set(CONTENT_KEY, content);
324 response.put(DESCRIPTION_KEY, description);
328 public static ObjectNode buildResponse(final String description, final Optional<ObjectNode> schema) {
329 final ObjectNode response = JsonNodeFactory.instance.objectNode();
331 if (schema.isPresent()) {
332 final ObjectNode schemaValue = schema.orElseThrow();
333 final ObjectNode content = JsonNodeFactory.instance.objectNode();
334 final ObjectNode body = JsonNodeFactory.instance.objectNode();
335 for (final String mimeType : MIME_TYPES) {
336 content.set(mimeType, body);
338 body.set(SCHEMA_KEY, schemaValue);
339 response.set(CONTENT_KEY, content);
341 response.put(DESCRIPTION_KEY, description);
345 private static String buildSummaryValue(final String httpMethod, final String moduleName,
346 final Optional<String> deviceName, final String nodeName) {
347 return httpMethod + SUMMARY_SEPARATOR + deviceName.map(s -> s + SUMMARY_SEPARATOR).orElse("")
348 + moduleName + SUMMARY_SEPARATOR + nodeName;
351 public static ArrayNode buildTagsValue(final Optional<String> deviceName, final String moduleName) {
352 final ArrayNode tagsValue = JsonNodeFactory.instance.arrayNode();
353 tagsValue.add(deviceName.map(s -> "mounted " + s).orElse("controller") + " " + moduleName);
357 public static ObjectNode getTypeParentNode(final ObjectNode parameter) {
358 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
359 parameter.set(SCHEMA_KEY, schema);