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;
25 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion;
26 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.URIType;
27 import org.opendaylight.netconf.sal.rest.doc.impl.DefinitionNames;
28 import org.opendaylight.netconf.sal.rest.doc.util.JsonUtil;
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.OperationDefinition;
32 import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
34 public final class OperationBuilder {
35 public static final String BODY = "body";
36 public static final String CONFIG = "_config";
37 public static final String CONFIG_QUERY_PARAM = "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 NONCONFIG_QUERY_PARAM = "nonconfig";
47 public static final String PARAMETERS_KEY = "parameters";
48 public static final String POST_SUFFIX = "_post";
49 public static final String PROPERTIES_KEY = "properties";
50 public static final String REF_KEY = "$ref";
51 public static final String REQUEST_BODY_KEY = "requestBody";
52 public static final String RESPONSES_KEY = "responses";
53 public static final String SCHEMA_KEY = "schema";
54 public static final String SUMMARY_KEY = "summary";
55 public static final String SUMMARY_SEPARATOR = " - ";
56 public static final String TAGS_KEY = "tags";
57 public static final String TOP = "_TOP";
58 public static final String XML_KEY = "xml";
59 public static final String XML_SUFFIX = "_xml";
60 private static final String CONTENT = "content";
61 private static final ArrayNode CONSUMES_PUT_POST;
62 private static final String ENUM_KEY = "enum";
63 private static final List<String> MIME_TYPES = ImmutableList.of(MediaType.APPLICATION_XML,
64 MediaType.APPLICATION_JSON);
65 private static final String OBJECT = "object";
66 private static final String REQUIRED_KEY = "required";
67 private static final String STRING = "string";
68 private static final String TYPE_KEY = "type";
69 private static final String QUERY = "query";
72 CONSUMES_PUT_POST = JsonNodeFactory.instance.arrayNode();
73 for (final String mimeType : MIME_TYPES) {
74 CONSUMES_PUT_POST.add(mimeType);
78 private OperationBuilder() {
82 public static ObjectNode buildPost(final String parentName, final String nodeName, final String discriminator,
83 final String moduleName, final Optional<String> deviceName,
84 final String description, final ArrayNode pathParams,
85 final OAversion oaversion) {
86 final ObjectNode value = JsonNodeFactory.instance.objectNode();
87 value.put(DESCRIPTION_KEY, description);
88 value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.POST, moduleName, deviceName, nodeName));
89 value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
90 final ArrayNode parameters = JsonUtil.copy(pathParams);
91 final ObjectNode ref = JsonNodeFactory.instance.objectNode();
92 final String cleanDefName = parentName + CONFIG + "_" + nodeName + POST_SUFFIX;
93 final String defName = cleanDefName + discriminator;
94 final String xmlDefName = cleanDefName + XML_SUFFIX + discriminator;
95 ref.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
96 insertRequestBodyParameter(parameters, value, defName, xmlDefName, nodeName + CONFIG, oaversion);
97 value.set(PARAMETERS_KEY, parameters);
99 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
100 responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
101 buildResponse(Response.Status.CREATED.getReasonPhrase(), Optional.empty(), oaversion));
103 value.set(RESPONSES_KEY, responses);
104 setConsumesIfNeeded(value, oaversion);
108 public static ObjectNode buildGet(final DataSchemaNode node, final String moduleName,
109 final Optional<String> deviceName, final ArrayNode pathParams,
110 final String defName, final boolean isConfig,
111 final URIType uriType, final OAversion oaversion) {
112 final ObjectNode value = JsonNodeFactory.instance.objectNode();
113 value.put(DESCRIPTION_KEY, node.getDescription().orElse(""));
114 value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.GET, moduleName, deviceName,
115 node.getQName().getLocalName()));
116 value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
117 final ArrayNode parameters = JsonUtil.copy(pathParams);
119 addQueryParameters(parameters, isConfig, uriType, oaversion);
121 value.set(PARAMETERS_KEY, parameters);
123 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
124 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
125 schema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
126 responses.set(String.valueOf(Response.Status.OK.getStatusCode()),
127 buildResponse(Response.Status.OK.getReasonPhrase(), Optional.of(schema), oaversion));
129 value.set(RESPONSES_KEY, responses);
133 private static void addQueryParameters(final ArrayNode parameters, final boolean isConfig,
134 final ApiDocServiceImpl.URIType uriType, final OAversion oaversion) {
135 if (uriType.equals(ApiDocServiceImpl.URIType.RFC8040)) {
136 final ObjectNode contentParam = JsonNodeFactory.instance.objectNode();
137 final ArrayNode cases = JsonNodeFactory.instance.arrayNode();
138 cases.add(NONCONFIG_QUERY_PARAM);
140 cases.add(CONFIG_QUERY_PARAM);
142 contentParam.put(REQUIRED_KEY, true);
144 contentParam.put(IN_KEY, QUERY);
145 contentParam.put(NAME_KEY, CONTENT);
147 final ObjectNode typeParent = getTypeParentNode(contentParam, oaversion);
148 typeParent.put(TYPE_KEY, STRING);
149 typeParent.set(ENUM_KEY, cases);
151 parameters.add(contentParam);
155 public static ObjectNode buildPut(final String parentName, final String nodeName, final String discriminator,
156 final String moduleName, final Optional<String> deviceName,
157 final String description, final ArrayNode pathParams,
158 final OAversion oaversion) {
159 final ObjectNode value = JsonNodeFactory.instance.objectNode();
160 value.put(DESCRIPTION_KEY, description);
161 value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.PUT, moduleName, deviceName, nodeName));
162 value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
163 final ArrayNode parameters = JsonUtil.copy(pathParams);
164 final String defName = parentName + CONFIG + "_" + nodeName + TOP;
165 final String xmlDefName = parentName + CONFIG + "_" + nodeName;
166 insertRequestBodyParameter(parameters, value, defName, xmlDefName, nodeName + CONFIG, oaversion);
167 value.set(PARAMETERS_KEY, parameters);
169 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
170 responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
171 buildResponse(Response.Status.CREATED.getReasonPhrase(), Optional.empty(), oaversion));
172 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()),
173 buildResponse("Updated", Optional.empty(), oaversion));
175 value.set(RESPONSES_KEY, responses);
176 setConsumesIfNeeded(value, oaversion);
180 public static ObjectNode buildDelete(final DataSchemaNode node, final String moduleName,
181 final Optional<String> deviceName, final ArrayNode pathParams,
182 final OAversion oaversion) {
183 final ObjectNode value = JsonNodeFactory.instance.objectNode();
184 value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.DELETE, moduleName, deviceName,
185 node.getQName().getLocalName()));
186 value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
187 value.put(DESCRIPTION_KEY, node.getDescription().orElse(""));
188 final ArrayNode parameters = JsonUtil.copy(pathParams);
189 value.set(PARAMETERS_KEY, parameters);
191 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
192 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()),
193 buildResponse("Deleted", Optional.empty(), oaversion));
195 value.set(RESPONSES_KEY, responses);
199 public static ObjectNode buildPostOperation(final OperationDefinition operDef, final String moduleName,
200 final Optional<String> deviceName, final String parentName,
201 final DefinitionNames definitionNames, final OAversion oaversion) {
202 final ObjectNode postOperation = JsonNodeFactory.instance.objectNode();
203 final ArrayNode parameters = JsonNodeFactory.instance.arrayNode();
204 final String operName = operDef.getQName().getLocalName();
205 final String inputName = operName + INPUT_SUFFIX;
207 final InputSchemaNode input = operDef.getInput();
208 final OutputSchemaNode output = operDef.getOutput();
209 if (!input.getChildNodes().isEmpty()) {
210 final String discriminator = definitionNames.getDiscriminator(input);
211 final String clearDefName = parentName + "_" + operName + INPUT_SUFFIX;
212 final String defName = clearDefName + discriminator;
213 final String defTopName = clearDefName + TOP + discriminator;
214 insertRequestBodyParameter(parameters, postOperation, defTopName, defName, inputName, oaversion);
216 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
217 final ObjectNode jsonSchema = JsonNodeFactory.instance.objectNode();
218 final ObjectNode properties = JsonNodeFactory.instance.objectNode();
219 final ObjectNode inputSchema = JsonNodeFactory.instance.objectNode();
220 inputSchema.put(TYPE_KEY, OBJECT);
221 properties.set(INPUT_KEY, inputSchema);
222 jsonSchema.put(TYPE_KEY, OBJECT);
223 jsonSchema.set(PROPERTIES_KEY, properties);
224 if (oaversion.equals(OAversion.V3_0)) {
225 final ObjectNode content = JsonNodeFactory.instance.objectNode();
226 final ObjectNode jsonTypeValue = JsonNodeFactory.instance.objectNode();
227 jsonTypeValue.set(SCHEMA_KEY, jsonSchema);
228 content.set(MediaType.APPLICATION_JSON, jsonTypeValue);
230 final ObjectNode xmlSchema = JsonNodeFactory.instance.objectNode();
231 xmlSchema.put(TYPE_KEY, OBJECT);
232 final ObjectNode xml = JsonNodeFactory.instance.objectNode();
233 xml.put(NAME_KEY, INPUT);
234 xmlSchema.set(XML_KEY, xml);
235 final ObjectNode xmlTypeValue = JsonNodeFactory.instance.objectNode();
236 xmlTypeValue.set(SCHEMA_KEY, xmlSchema);
237 content.set(MediaType.APPLICATION_XML, xmlTypeValue);
239 payload.set(CONTENT_KEY, content);
240 payload.put(DESCRIPTION_KEY, inputName);
241 postOperation.set(REQUEST_BODY_KEY, payload);
243 payload.put(IN_KEY, BODY);
244 payload.put(NAME_KEY, inputName);
245 payload.set(SCHEMA_KEY, jsonSchema);
246 parameters.add(payload);
250 setConsumesIfNeeded(postOperation, oaversion);
251 postOperation.set(PARAMETERS_KEY, parameters);
252 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
253 final String description = String.format("RPC %s success", operName);
255 if (!output.getChildNodes().isEmpty()) {
256 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
257 final String defName = parentName + "_" + operName + OUTPUT_SUFFIX + TOP
258 + definitionNames.getDiscriminator(output);
259 schema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
260 responses.set(String.valueOf(Response.Status.OK.getStatusCode()), buildResponse(description,
261 Optional.of(schema), oaversion));
263 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse(description,
264 Optional.empty(), oaversion));
266 postOperation.set(RESPONSES_KEY, responses);
267 postOperation.put(DESCRIPTION_KEY, operDef.getDescription().orElse(""));
268 postOperation.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.POST, moduleName, deviceName, operName));
269 postOperation.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
270 return postOperation;
273 private static void insertRequestBodyParameter(final ArrayNode parameters, final ObjectNode operation,
274 final String defName, final String xmlDefName,
275 final String name, final OAversion oaversion) {
276 final ObjectNode payload = JsonNodeFactory.instance.objectNode();
277 if (oaversion.equals(OAversion.V3_0)) {
278 final ObjectNode content = JsonNodeFactory.instance.objectNode();
279 content.set(MediaType.APPLICATION_JSON, buildMimeTypeValue(defName));
280 content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(xmlDefName));
281 payload.set(CONTENT_KEY, content);
282 payload.put(DESCRIPTION_KEY, name);
283 operation.set(REQUEST_BODY_KEY, payload);
285 payload.put(IN_KEY, BODY);
286 payload.put(NAME_KEY, name);
287 payload.set(SCHEMA_KEY, buildRefSchema(defName, OAversion.V2_0));
288 parameters.add(payload);
292 private static ObjectNode buildRefSchema(final String defName, final OAversion oaversion) {
293 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
294 schema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
298 private static ObjectNode buildMimeTypeValue(final String defName) {
299 final ObjectNode mimeTypeValue = JsonNodeFactory.instance.objectNode();
300 mimeTypeValue.set(SCHEMA_KEY, buildRefSchema(defName, OAversion.V3_0));
301 return mimeTypeValue;
304 public static ObjectNode buildResponse(final String description, final Optional<ObjectNode> schema,
305 final OAversion oaversion) {
306 final ObjectNode response = JsonNodeFactory.instance.objectNode();
308 if (schema.isPresent()) {
309 final ObjectNode schemaValue = schema.get();
310 if (oaversion.equals(OAversion.V3_0)) {
311 final ObjectNode content = JsonNodeFactory.instance.objectNode();
312 final ObjectNode body = JsonNodeFactory.instance.objectNode();
313 for (final String mimeType : MIME_TYPES) {
314 content.set(mimeType, body);
316 body.set(SCHEMA_KEY, schemaValue);
317 response.set(CONTENT_KEY, content);
319 response.set(SCHEMA_KEY, schemaValue);
322 response.put(DESCRIPTION_KEY, description);
326 private static void setConsumesIfNeeded(final ObjectNode operation, final OAversion oaversion) {
327 if (oaversion.equals(OAversion.V2_0)) {
328 operation.set(CONSUMES_KEY, CONSUMES_PUT_POST);
332 private static String buildSummaryValue(final String httpMethod, final String moduleName,
333 final Optional<String> deviceName, final String nodeName) {
334 return httpMethod + SUMMARY_SEPARATOR + deviceName.map(s -> s + SUMMARY_SEPARATOR).orElse("")
335 + moduleName + SUMMARY_SEPARATOR + nodeName;
338 public static ArrayNode buildTagsValue(final Optional<String> deviceName, final String moduleName) {
339 final ArrayNode tagsValue = JsonNodeFactory.instance.arrayNode();
340 tagsValue.add(deviceName.map(s -> "mounted " + s).orElse("controller") + " " + moduleName);
344 public static String getAppropriateModelPrefix(final OAversion oaversion) {
345 if (oaversion.equals(OAversion.V3_0)) {
346 return COMPONENTS_PREFIX;
348 return DEFINITIONS_PREFIX;
351 public static ObjectNode getTypeParentNode(final ObjectNode parameter, final OAversion oaversion) {
352 if (oaversion.equals(OAversion.V3_0)) {
353 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
354 parameter.set(SCHEMA_KEY, schema);