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.netconf.sal.rest.doc.model.builder;
11 import static org.opendaylight.netconf.sal.rest.doc.impl.DefinitionGenerator.INPUT;
12 import static org.opendaylight.netconf.sal.rest.doc.impl.DefinitionGenerator.INPUT_SUFFIX;
13 import static org.opendaylight.netconf.sal.rest.doc.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 com.google.common.collect.ImmutableList;
19 import java.util.List;
20 import java.util.Optional;
21 import javax.ws.rs.HttpMethod;
22 import javax.ws.rs.core.MediaType;
23 import javax.ws.rs.core.Response;
24 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion;
25 import org.opendaylight.netconf.sal.rest.doc.impl.DefinitionNames;
26 import org.opendaylight.netconf.sal.rest.doc.util.JsonUtil;
27 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
29 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
33 import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
35 public final class OperationBuilder {
36 public static final String BODY = "body";
37 public static final String CONFIG = "_config";
38 public static final String CONSUMES_KEY = "consumes";
39 public static final String CONTENT_KEY = "content";
40 public static final String COMPONENTS_PREFIX = "#/components/schemas/";
41 public static final String DEFINITIONS_PREFIX = "#/definitions/";
42 public static final String DESCRIPTION_KEY = "description";
43 public static final String IN_KEY = "in";
44 public static final String INPUT_KEY = "input";
45 public static final String NAME_KEY = "name";
46 public static final String PARAMETERS_KEY = "parameters";
47 public static final String POST_SUFFIX = "_post";
48 public static final String PROPERTIES_KEY = "properties";
49 public static final String REF_KEY = "$ref";
50 public static final String REQUEST_BODY_KEY = "requestBody";
51 public static final String RESPONSES_KEY = "responses";
52 public static final String SCHEMA_KEY = "schema";
53 public static final String SUMMARY_KEY = "summary";
54 public static final String SUMMARY_SEPARATOR = " - ";
55 public static final String TAGS_KEY = "tags";
56 public static final String TOP = "_TOP";
57 public static final String XML_KEY = "xml";
58 public static final String XML_SUFFIX = "_xml";
59 private static final ArrayNode CONSUMES_PUT_POST;
60 private static final String ENUM_KEY = "enum";
61 private static final List<String> MIME_TYPES = ImmutableList.of(MediaType.APPLICATION_XML,
62 MediaType.APPLICATION_JSON);
63 private static final String OBJECT = "object";
64 private static final String REQUIRED_KEY = "required";
65 private static final String TYPE_KEY = "type";
68 CONSUMES_PUT_POST = JsonNodeFactory.instance.arrayNode();
69 for (final String mimeType : MIME_TYPES) {
70 CONSUMES_PUT_POST.add(mimeType);
74 private OperationBuilder() {
78 public static ObjectNode buildPost(final DataSchemaNode node, final String parentName, final String nodeName,
79 final String discriminator, final String moduleName,
80 final Optional<String> deviceName, final String description,
81 final ArrayNode pathParams, final OAversion oaversion) {
82 final ObjectNode value = JsonNodeFactory.instance.objectNode();
83 value.put(DESCRIPTION_KEY, description);
84 value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.POST, moduleName, deviceName, nodeName));
85 value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
86 final ArrayNode parameters = JsonUtil.copy(pathParams);
87 final ObjectNode ref = JsonNodeFactory.instance.objectNode();
88 final String cleanDefName = parentName + CONFIG + "_" + nodeName + POST_SUFFIX;
89 final String defName = cleanDefName + discriminator;
90 final String xmlDefName = cleanDefName + XML_SUFFIX + discriminator;
91 ref.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
92 final DataSchemaNode childNode = getListOrContainerChildNode(Optional.ofNullable(node));
93 if (childNode != null && childNode.isConfiguration()) {
94 final String childNodeName = childNode.getQName().getLocalName();
95 final String cleanChildDefName = parentName + "_" + nodeName + CONFIG + "_" + childNodeName + POST_SUFFIX;
96 final String childDefName = cleanChildDefName + discriminator;
97 final String childXmlDefName = cleanChildDefName + XML_SUFFIX + discriminator;
98 insertPostRequestBodyParameter(childNode, parameters, value, childDefName, childXmlDefName, childNodeName,
101 insertRequestBodyParameter(parameters, value, defName, xmlDefName, nodeName + CONFIG, oaversion);
103 value.set(PARAMETERS_KEY, parameters);
105 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
106 responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
107 buildResponse(Response.Status.CREATED.getReasonPhrase(), Optional.empty(), oaversion));
109 value.set(RESPONSES_KEY, responses);
110 setConsumesIfNeeded(value, oaversion);
114 public static ObjectNode buildGet(final DataSchemaNode node, final String moduleName,
115 final Optional<String> deviceName, final ArrayNode pathParams,
116 final String defName, final String defNameTop, final boolean isConfig,
117 final OAversion oaversion) {
118 final ObjectNode value = JsonNodeFactory.instance.objectNode();
119 value.put(DESCRIPTION_KEY, node.getDescription().orElse(""));
120 value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.GET, moduleName, deviceName,
121 node.getQName().getLocalName()));
122 value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
123 final ArrayNode parameters = JsonUtil.copy(pathParams);
124 parameters.add(buildQueryParameters(isConfig, oaversion));
126 value.set(PARAMETERS_KEY, parameters);
128 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
129 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
130 final ObjectNode xmlSchema = JsonNodeFactory.instance.objectNode();
131 schema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defNameTop);
132 xmlSchema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
133 responses.set(String.valueOf(Response.Status.OK.getStatusCode()),
134 buildResponse(Response.Status.OK.getReasonPhrase(), schema, xmlSchema, oaversion));
136 value.set(RESPONSES_KEY, responses);
140 private static ObjectNode buildQueryParameters(final boolean isConfig, final OAversion oaversion) {
141 final ObjectNode contentParam = JsonNodeFactory.instance.objectNode();
142 final ArrayNode cases = JsonNodeFactory.instance.arrayNode();
145 cases.add("nonconfig");
148 cases.add("nonconfig");
149 contentParam.put(REQUIRED_KEY, true);
151 contentParam.put(IN_KEY, "query");
152 contentParam.put(NAME_KEY, "content");
154 final ObjectNode typeParent = getTypeParentNode(contentParam, oaversion);
155 typeParent.put(TYPE_KEY, "string");
156 typeParent.set(ENUM_KEY, cases);
161 public static ObjectNode buildPut(final String parentName, final String nodeName, final String discriminator,
162 final String moduleName, final Optional<String> deviceName,
163 final String description, final ArrayNode pathParams,
164 final OAversion oaversion) {
165 final ObjectNode value = JsonNodeFactory.instance.objectNode();
166 value.put(DESCRIPTION_KEY, description);
167 value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.PUT, moduleName, deviceName, nodeName));
168 value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
169 final ArrayNode parameters = JsonUtil.copy(pathParams);
170 final String defName = parentName + CONFIG + "_" + nodeName + TOP;
171 final String xmlDefName = parentName + CONFIG + "_" + nodeName;
172 insertRequestBodyParameter(parameters, value, defName, xmlDefName, nodeName + CONFIG, oaversion);
173 value.set(PARAMETERS_KEY, parameters);
175 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
176 responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
177 buildResponse(Response.Status.CREATED.getReasonPhrase(), Optional.empty(), oaversion));
178 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()),
179 buildResponse("Updated", Optional.empty(), oaversion));
181 value.set(RESPONSES_KEY, responses);
182 setConsumesIfNeeded(value, oaversion);
186 public static ObjectNode buildDelete(final DataSchemaNode node, final String moduleName,
187 final Optional<String> deviceName, final ArrayNode pathParams,
188 final OAversion oaversion) {
189 final ObjectNode value = JsonNodeFactory.instance.objectNode();
190 value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.DELETE, moduleName, deviceName,
191 node.getQName().getLocalName()));
192 value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
193 value.put(DESCRIPTION_KEY, node.getDescription().orElse(""));
194 final ArrayNode parameters = JsonUtil.copy(pathParams);
195 value.set(PARAMETERS_KEY, parameters);
197 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
198 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()),
199 buildResponse("Deleted", Optional.empty(), oaversion));
201 value.set(RESPONSES_KEY, responses);
205 public static ObjectNode buildPostOperation(final OperationDefinition operDef, final String moduleName,
206 final Optional<String> deviceName, final String parentName,
207 final DefinitionNames definitionNames, final OAversion oaversion,
208 final ArrayNode parentPathParameters) {
209 final ObjectNode postOperation = JsonNodeFactory.instance.objectNode();
210 final ArrayNode parameters = JsonNodeFactory.instance.arrayNode().addAll(parentPathParameters);
211 final String operName = operDef.getQName().getLocalName();
212 final String inputName = operName + INPUT_SUFFIX;
214 final InputSchemaNode input = operDef.getInput();
215 final OutputSchemaNode output = operDef.getOutput();
216 if (!input.getChildNodes().isEmpty()) {
217 final String discriminator = definitionNames.getDiscriminator(input);
218 final String clearDefName = parentName + "_" + operName + INPUT_SUFFIX;
219 final String defName = clearDefName + discriminator;
220 final String defTopName = clearDefName + TOP + discriminator;
221 insertRequestBodyParameter(parameters, postOperation, defTopName, defName, inputName, oaversion);
223 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
224 final ObjectNode jsonSchema = JsonNodeFactory.instance.objectNode();
225 final ObjectNode properties = JsonNodeFactory.instance.objectNode();
226 final ObjectNode inputSchema = JsonNodeFactory.instance.objectNode();
227 inputSchema.put(TYPE_KEY, OBJECT);
228 properties.set(INPUT_KEY, inputSchema);
229 jsonSchema.put(TYPE_KEY, OBJECT);
230 jsonSchema.set(PROPERTIES_KEY, properties);
231 if (oaversion.equals(OAversion.V3_0)) {
232 final ObjectNode content = JsonNodeFactory.instance.objectNode();
233 final ObjectNode jsonTypeValue = JsonNodeFactory.instance.objectNode();
234 jsonTypeValue.set(SCHEMA_KEY, jsonSchema);
235 content.set(MediaType.APPLICATION_JSON, jsonTypeValue);
237 final ObjectNode xmlSchema = JsonNodeFactory.instance.objectNode();
238 xmlSchema.put(TYPE_KEY, OBJECT);
239 final ObjectNode xml = JsonNodeFactory.instance.objectNode();
240 xml.put(NAME_KEY, INPUT);
241 xmlSchema.set(XML_KEY, xml);
242 final ObjectNode xmlTypeValue = JsonNodeFactory.instance.objectNode();
243 xmlTypeValue.set(SCHEMA_KEY, xmlSchema);
244 content.set(MediaType.APPLICATION_XML, xmlTypeValue);
246 payload.set(CONTENT_KEY, content);
247 payload.put(DESCRIPTION_KEY, inputName);
248 postOperation.set(REQUEST_BODY_KEY, payload);
250 payload.put(IN_KEY, BODY);
251 payload.put(NAME_KEY, inputName);
252 payload.set(SCHEMA_KEY, jsonSchema);
253 parameters.add(payload);
257 setConsumesIfNeeded(postOperation, oaversion);
258 postOperation.set(PARAMETERS_KEY, parameters);
259 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
260 final String description = String.format("RPC %s success", operName);
262 if (!output.getChildNodes().isEmpty()) {
263 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
264 final String defName = parentName + "_" + operName + OUTPUT_SUFFIX + TOP
265 + definitionNames.getDiscriminator(output);
266 schema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
267 responses.set(String.valueOf(Response.Status.OK.getStatusCode()), buildResponse(description,
268 Optional.of(schema), oaversion));
270 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse(description,
271 Optional.empty(), oaversion));
273 postOperation.set(RESPONSES_KEY, responses);
274 postOperation.put(DESCRIPTION_KEY, operDef.getDescription().orElse(""));
275 postOperation.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.POST, moduleName, deviceName, operName));
276 postOperation.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
277 return postOperation;
280 private static void insertRequestBodyParameter(final ArrayNode parameters, final ObjectNode operation,
281 final String defName, final String xmlDefName,
282 final String name, final OAversion oaversion) {
283 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
284 if (oaversion.equals(OAversion.V3_0)) {
285 final ObjectNode content = JsonNodeFactory.instance.objectNode();
286 content.set(MediaType.APPLICATION_JSON, buildMimeTypeValue(defName));
287 content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(xmlDefName));
288 payload.set(CONTENT_KEY, content);
289 payload.put(DESCRIPTION_KEY, name);
290 operation.set(REQUEST_BODY_KEY, payload);
292 payload.put(IN_KEY, BODY);
293 payload.put(NAME_KEY, name);
294 payload.set(SCHEMA_KEY, buildRefSchema(defName, OAversion.V2_0));
295 parameters.add(payload);
299 private static void insertPostRequestBodyParameter(final DataSchemaNode childNode, final ArrayNode parameters,
300 final ObjectNode operation, final String defName, final String xmlDefName, final String name,
301 final OAversion oaversion) {
302 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
303 if (oaversion.equals(OAversion.V3_0)) {
304 final ObjectNode content = JsonNodeFactory.instance.objectNode();
305 final ObjectNode properties = JsonNodeFactory.instance.objectNode();
306 if (childNode instanceof ListSchemaNode) {
307 final ObjectNode list = JsonNodeFactory.instance.objectNode();
308 final ObjectNode listValue = JsonNodeFactory.instance.objectNode();
309 listValue.put(TYPE_KEY, "array");
310 listValue.set("items", buildRefSchema(defName, oaversion));
311 list.set(name, listValue);
312 properties.set(PROPERTIES_KEY, list);
314 final ObjectNode container = JsonNodeFactory.instance.objectNode();
315 container.set(name, buildRefSchema(defName, oaversion));
316 properties.set(PROPERTIES_KEY, container);
318 final ObjectNode jsonSchema = JsonNodeFactory.instance.objectNode();
319 jsonSchema.set(SCHEMA_KEY, properties);
320 content.set(MediaType.APPLICATION_JSON, jsonSchema);
321 content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(xmlDefName));
322 payload.set(CONTENT_KEY, content);
323 payload.put(DESCRIPTION_KEY, name);
324 operation.set(REQUEST_BODY_KEY, payload);
326 payload.put(IN_KEY, BODY);
327 payload.put(NAME_KEY, name);
328 payload.set(SCHEMA_KEY, buildRefSchema(defName, OAversion.V2_0));
329 parameters.add(payload);
333 private static ObjectNode buildRefSchema(final String defName, final OAversion oaversion) {
334 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
335 schema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
339 private static ObjectNode buildMimeTypeValue(final String defName) {
340 final ObjectNode mimeTypeValue = JsonNodeFactory.instance.objectNode();
341 mimeTypeValue.set(SCHEMA_KEY, buildRefSchema(defName, OAversion.V3_0));
342 return mimeTypeValue;
345 public static ObjectNode buildResponse(final String description, final ObjectNode schema,
346 final ObjectNode xmlSchema, final OAversion oaversion) {
347 final ObjectNode response = JsonNodeFactory.instance.objectNode();
349 if (oaversion.equals(OAversion.V3_0)) {
350 final ObjectNode content = JsonNodeFactory.instance.objectNode();
351 final ObjectNode body = JsonNodeFactory.instance.objectNode();
352 final ObjectNode xmlBody = JsonNodeFactory.instance.objectNode();
354 body.set(SCHEMA_KEY, schema);
355 xmlBody.set(SCHEMA_KEY, xmlSchema);
356 content.set(MediaType.APPLICATION_JSON, body);
357 content.set(MediaType.APPLICATION_XML, xmlBody);
359 response.set(CONTENT_KEY, content);
361 response.set(SCHEMA_KEY, schema);
363 response.put(DESCRIPTION_KEY, description);
367 public static ObjectNode buildResponse(final String description, final Optional<ObjectNode> schema,
368 final OAversion oaversion) {
369 final ObjectNode response = JsonNodeFactory.instance.objectNode();
371 if (schema.isPresent()) {
372 final ObjectNode schemaValue = schema.get();
373 if (oaversion.equals(OAversion.V3_0)) {
374 final ObjectNode content = JsonNodeFactory.instance.objectNode();
375 final ObjectNode body = JsonNodeFactory.instance.objectNode();
376 for (final String mimeType : MIME_TYPES) {
377 content.set(mimeType, body);
379 body.set(SCHEMA_KEY, schemaValue);
380 response.set(CONTENT_KEY, content);
382 response.set(SCHEMA_KEY, schemaValue);
385 response.put(DESCRIPTION_KEY, description);
389 private static void setConsumesIfNeeded(final ObjectNode operation, final OAversion oaversion) {
390 if (oaversion.equals(OAversion.V2_0)) {
391 operation.set(CONSUMES_KEY, CONSUMES_PUT_POST);
395 private static String buildSummaryValue(final String httpMethod, final String moduleName,
396 final Optional<String> deviceName, final String nodeName) {
397 return httpMethod + SUMMARY_SEPARATOR + deviceName.map(s -> s + SUMMARY_SEPARATOR).orElse("")
398 + moduleName + SUMMARY_SEPARATOR + nodeName;
401 public static ArrayNode buildTagsValue(final Optional<String> deviceName, final String moduleName) {
402 final ArrayNode tagsValue = JsonNodeFactory.instance.arrayNode();
403 tagsValue.add(deviceName.map(s -> "mounted " + s).orElse("controller") + " " + moduleName);
407 public static String getAppropriateModelPrefix(final OAversion oaversion) {
408 if (oaversion.equals(OAversion.V3_0)) {
409 return COMPONENTS_PREFIX;
411 return DEFINITIONS_PREFIX;
414 public static ObjectNode getTypeParentNode(final ObjectNode parameter, final OAversion oaversion) {
415 if (oaversion.equals(OAversion.V3_0)) {
416 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
417 parameter.set(SCHEMA_KEY, schema);
423 private static DataSchemaNode getListOrContainerChildNode(final Optional<DataSchemaNode> node) {
424 return node.flatMap(schemaNode -> ((DataNodeContainer) schemaNode).getChildNodes().stream()
425 .filter(n -> n instanceof ListSchemaNode || n instanceof ContainerSchemaNode)
426 .findFirst()).orElse(null);