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 javax.ws.rs.HttpMethod;
20 import javax.ws.rs.core.MediaType;
21 import javax.ws.rs.core.Response;
22 import org.eclipse.jdt.annotation.Nullable;
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 @Nullable 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()));
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 @Nullable 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 @Nullable 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()));
152 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse("Updated"));
154 return new Operation.Builder()
156 .parameters(parameters)
157 .requestBody(requestBody)
158 .responses(responses)
159 .description(description)
164 public static Operation buildPatch(final String parentName, final String nodeName, final String moduleName,
165 final @Nullable String deviceName, final String description, final ArrayNode pathParams) {
166 final String summary = buildSummaryValue(HttpMethod.PATCH, moduleName, deviceName, nodeName);
167 final ArrayNode tags = buildTagsValue(deviceName, moduleName);
168 final ArrayNode parameters = JsonNodeFactory.instance.arrayNode().addAll(pathParams);
169 final String defName = parentName + CONFIG + "_" + nodeName + TOP;
170 final String xmlDefName = parentName + CONFIG + "_" + nodeName;
171 final ObjectNode requestBody = createRequestBodyParameter(defName, xmlDefName, nodeName + CONFIG, summary);
173 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
174 responses.set(String.valueOf(Response.Status.OK.getStatusCode()),
175 buildResponse(Response.Status.OK.getReasonPhrase()));
176 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse("Updated"));
178 return new Operation.Builder()
180 .parameters(parameters)
181 .requestBody(requestBody)
182 .responses(responses)
183 .description(description)
188 public static Operation buildDelete(final DataSchemaNode node, final String moduleName,
189 final @Nullable String deviceName, final ArrayNode pathParams) {
190 final String summary = buildSummaryValue(HttpMethod.DELETE, moduleName, deviceName,
191 node.getQName().getLocalName());
192 final ArrayNode tags = buildTagsValue(deviceName, moduleName);
193 final String description = node.getDescription().orElse("");
194 final ArrayNode parameters = JsonNodeFactory.instance.arrayNode().addAll(pathParams);
196 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
197 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse("Deleted"));
199 return new Operation.Builder()
201 .parameters(parameters)
202 .responses(responses)
203 .description(description)
208 public static Operation buildPostOperation(final OperationDefinition operDef, final String moduleName,
209 final @Nullable String deviceName, final String parentName, final DefinitionNames definitionNames,
210 final ArrayNode parentPathParameters) {
211 final ArrayNode parameters = JsonNodeFactory.instance.arrayNode().addAll(parentPathParameters);
212 final String operationName = operDef.getQName().getLocalName();
213 final String inputName = operationName + INPUT_SUFFIX;
214 final String summary = buildSummaryValue(HttpMethod.POST, moduleName, deviceName, operationName);
216 final InputSchemaNode input = operDef.getInput();
217 final OutputSchemaNode output = operDef.getOutput();
218 ObjectNode requestBody;
219 if (!input.getChildNodes().isEmpty()) {
220 final String discriminator = definitionNames.getDiscriminator(input);
221 final String clearDefName = parentName + "_" + operationName + INPUT_SUFFIX;
222 final String defName = clearDefName + discriminator;
223 final String defTopName = clearDefName + TOP + discriminator;
224 requestBody = createRequestBodyParameter(defTopName, defName, inputName, summary);
226 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
227 final ObjectNode jsonSchema = JsonNodeFactory.instance.objectNode();
228 final ObjectNode properties = JsonNodeFactory.instance.objectNode();
229 final ObjectNode inputSchema = JsonNodeFactory.instance.objectNode();
230 inputSchema.put(TYPE_KEY, OBJECT);
231 properties.set(INPUT_KEY, inputSchema);
232 jsonSchema.put(TYPE_KEY, OBJECT);
233 jsonSchema.set(PROPERTIES_KEY, properties);
234 final ObjectNode content = JsonNodeFactory.instance.objectNode();
235 final ObjectNode jsonTypeValue = JsonNodeFactory.instance.objectNode();
236 jsonTypeValue.set(SCHEMA_KEY, jsonSchema);
237 content.set(MediaType.APPLICATION_JSON, jsonTypeValue);
239 final ObjectNode xmlSchema = JsonNodeFactory.instance.objectNode();
240 xmlSchema.put(TYPE_KEY, OBJECT);
241 final ObjectNode xml = JsonNodeFactory.instance.objectNode();
242 xml.put(NAME_KEY, INPUT);
243 xmlSchema.set(XML_KEY, xml);
244 final ObjectNode xmlTypeValue = JsonNodeFactory.instance.objectNode();
245 xmlTypeValue.set(SCHEMA_KEY, xmlSchema);
246 content.set(MediaType.APPLICATION_XML, xmlTypeValue);
248 payload.set(CONTENT_KEY, content);
249 payload.put(DESCRIPTION_KEY, inputName);
250 requestBody = payload;
252 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
253 final String description = String.format("RPC %s success", operationName);
255 if (!output.getChildNodes().isEmpty()) {
256 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
257 final String defName = parentName + "_" + operationName + OUTPUT_SUFFIX + TOP
258 + definitionNames.getDiscriminator(output);
259 schema.put(REF_KEY, COMPONENTS_PREFIX + defName);
260 responses.set(String.valueOf(Response.Status.OK.getStatusCode()), buildResponse(description, schema));
262 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse(description));
264 final String desc = operDef.getDescription().orElse("");
265 final ArrayNode tags = buildTagsValue(deviceName, moduleName);
266 return new Operation.Builder()
268 .parameters(parameters)
269 .requestBody(requestBody)
270 .responses(responses)
276 private static ObjectNode createRequestBodyParameter(final String defName, final String xmlDefName,
277 final String name, final String summary) {
278 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
279 final ObjectNode content = JsonNodeFactory.instance.objectNode();
280 if (summary != null && summary.contains(HttpMethod.PATCH)) {
281 content.set("application/yang-data+json", buildMimeTypeValue(defName));
282 content.set("application/yang-data+xml", buildMimeTypeValue(xmlDefName));
284 content.set(MediaType.APPLICATION_JSON, buildMimeTypeValue(defName));
285 content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(xmlDefName));
287 payload.set(CONTENT_KEY, content);
288 payload.put(DESCRIPTION_KEY, name);
292 private static ObjectNode buildRefSchema(final String defName) {
293 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
294 schema.put(REF_KEY, COMPONENTS_PREFIX + defName);
298 private static ObjectNode buildMimeTypeValue(final String defName) {
299 final ObjectNode mimeTypeValue = JsonNodeFactory.instance.objectNode();
300 mimeTypeValue.set(SCHEMA_KEY, buildRefSchema(defName));
301 return mimeTypeValue;
304 public static ObjectNode buildResponse(final String description, final ObjectNode schema,
305 final ObjectNode xmlSchema) {
306 final ObjectNode response = JsonNodeFactory.instance.objectNode();
308 final ObjectNode content = JsonNodeFactory.instance.objectNode();
309 final ObjectNode body = JsonNodeFactory.instance.objectNode();
310 final ObjectNode xmlBody = JsonNodeFactory.instance.objectNode();
312 body.set(SCHEMA_KEY, schema);
313 xmlBody.set(SCHEMA_KEY, xmlSchema);
314 content.set(MediaType.APPLICATION_JSON, body);
315 content.set(MediaType.APPLICATION_XML, xmlBody);
317 response.set(CONTENT_KEY, content);
319 response.put(DESCRIPTION_KEY, description);
323 private static ObjectNode buildResponse(final String description) {
324 final ObjectNode response = JsonNodeFactory.instance.objectNode();
325 response.put(DESCRIPTION_KEY, description);
329 private static ObjectNode buildResponse(final String description, final ObjectNode schema) {
330 final ObjectNode response = JsonNodeFactory.instance.objectNode();
331 final ObjectNode content = JsonNodeFactory.instance.objectNode();
332 final ObjectNode body = JsonNodeFactory.instance.objectNode();
333 for (final String mimeType : MIME_TYPES) {
334 content.set(mimeType, body);
336 body.set(SCHEMA_KEY, schema);
337 response.set(CONTENT_KEY, content);
338 response.put(DESCRIPTION_KEY, description);
342 private static String buildSummaryValue(final String httpMethod, final String moduleName,
343 final String deviceName, final String nodeName) {
344 // FIXME eliminate this method
345 if (deviceName == null) {
346 return httpMethod + SUMMARY_SEPARATOR + moduleName + SUMMARY_SEPARATOR + nodeName;
348 return httpMethod + SUMMARY_SEPARATOR + deviceName + SUMMARY_SEPARATOR
349 + moduleName + SUMMARY_SEPARATOR + nodeName;
352 public static ArrayNode buildTagsValue(final @Nullable String deviceName, final String moduleName) {
353 // FIXME eliminate this method
354 if (deviceName == null) {
355 return JsonNodeFactory.instance.arrayNode().add("controller" + " " + moduleName);
357 return JsonNodeFactory.instance.arrayNode().add("mounted " + deviceName + " " + moduleName);
360 public static ObjectNode getTypeParentNode(final ObjectNode parameter) {
361 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
362 parameter.set(SCHEMA_KEY, schema);