Remove Bierman02 support from sal-rest-docgen
[netconf.git] / restconf / sal-rest-docgen / src / main / java / org / opendaylight / netconf / sal / rest / doc / model / builder / OperationBuilder.java
1 /*
2  * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.netconf.sal.rest.doc.model.builder;
10
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;
14
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.DataSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
30 import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
31
32 public final class OperationBuilder {
33     public static final String BODY = "body";
34     public static final String CONFIG = "_config";
35     public static final String CONFIG_QUERY_PARAM = "config";
36     public static final String CONSUMES_KEY = "consumes";
37     public static final String CONTENT_KEY = "content";
38     public static final String COMPONENTS_PREFIX = "#/components/schemas/";
39     public static final String DEFINITIONS_PREFIX = "#/definitions/";
40     public static final String DESCRIPTION_KEY = "description";
41     public static final String IN_KEY = "in";
42     public static final String INPUT_KEY = "input";
43     public static final String NAME_KEY = "name";
44     public static final String NONCONFIG_QUERY_PARAM = "nonconfig";
45     public static final String PARAMETERS_KEY = "parameters";
46     public static final String POST_SUFFIX = "_post";
47     public static final String PROPERTIES_KEY = "properties";
48     public static final String REF_KEY = "$ref";
49     public static final String REQUEST_BODY_KEY = "requestBody";
50     public static final String RESPONSES_KEY = "responses";
51     public static final String SCHEMA_KEY = "schema";
52     public static final String SUMMARY_KEY = "summary";
53     public static final String SUMMARY_SEPARATOR = " - ";
54     public static final String TAGS_KEY = "tags";
55     public static final String TOP = "_TOP";
56     public static final String XML_KEY = "xml";
57     public static final String XML_SUFFIX = "_xml";
58     private static final String CONTENT = "content";
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 STRING = "string";
66     private static final String TYPE_KEY = "type";
67     private static final String QUERY = "query";
68
69     static {
70         CONSUMES_PUT_POST = JsonNodeFactory.instance.arrayNode();
71         for (final String mimeType : MIME_TYPES) {
72             CONSUMES_PUT_POST.add(mimeType);
73         }
74     }
75
76     private OperationBuilder() {
77
78     }
79
80     public static ObjectNode buildPost(final String parentName, final String nodeName, final String discriminator,
81                                        final String moduleName, final Optional<String> deviceName,
82                                        final String description, final ArrayNode pathParams,
83                                        final OAversion oaversion) {
84         final ObjectNode value = JsonNodeFactory.instance.objectNode();
85         value.put(DESCRIPTION_KEY, description);
86         value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.POST, moduleName, deviceName, nodeName));
87         value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
88         final ArrayNode parameters = JsonUtil.copy(pathParams);
89         final ObjectNode ref = JsonNodeFactory.instance.objectNode();
90         final String cleanDefName = parentName + CONFIG + "_" + nodeName + POST_SUFFIX;
91         final String defName = cleanDefName + discriminator;
92         final String xmlDefName = cleanDefName + XML_SUFFIX + discriminator;
93         ref.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
94         insertRequestBodyParameter(parameters, value, defName, xmlDefName, nodeName + CONFIG, oaversion);
95         value.set(PARAMETERS_KEY, parameters);
96
97         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
98         responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
99                 buildResponse(Response.Status.CREATED.getReasonPhrase(), Optional.empty(), oaversion));
100
101         value.set(RESPONSES_KEY, responses);
102         setConsumesIfNeeded(value, oaversion);
103         return value;
104     }
105
106     public static ObjectNode buildGet(final DataSchemaNode node, final String moduleName,
107                                       final Optional<String> deviceName, final ArrayNode pathParams,
108                                       final String defName, final boolean isConfig, final OAversion oaversion) {
109         final ObjectNode value = JsonNodeFactory.instance.objectNode();
110         value.put(DESCRIPTION_KEY, node.getDescription().orElse(""));
111         value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.GET, moduleName, deviceName,
112                 node.getQName().getLocalName()));
113         value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
114         final ArrayNode parameters = JsonUtil.copy(pathParams);
115
116         addQueryParameters(parameters, isConfig, oaversion);
117
118         value.set(PARAMETERS_KEY, parameters);
119
120         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
121         final ObjectNode schema = JsonNodeFactory.instance.objectNode();
122         schema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
123         responses.set(String.valueOf(Response.Status.OK.getStatusCode()),
124                 buildResponse(Response.Status.OK.getReasonPhrase(), Optional.of(schema), oaversion));
125
126         value.set(RESPONSES_KEY, responses);
127         return value;
128     }
129
130     private static void addQueryParameters(final ArrayNode parameters, final boolean isConfig,
131                                            final OAversion oaversion) {
132         final ObjectNode contentParam = JsonNodeFactory.instance.objectNode();
133         final ArrayNode cases = JsonNodeFactory.instance.arrayNode();
134         cases.add(NONCONFIG_QUERY_PARAM);
135         if (isConfig) {
136             cases.add(CONFIG_QUERY_PARAM);
137         } else {
138             contentParam.put(REQUIRED_KEY, true);
139         }
140         contentParam.put(IN_KEY, QUERY);
141         contentParam.put(NAME_KEY, CONTENT);
142
143         final ObjectNode typeParent = getTypeParentNode(contentParam, oaversion);
144         typeParent.put(TYPE_KEY, STRING);
145         typeParent.set(ENUM_KEY, cases);
146
147         parameters.add(contentParam);
148     }
149
150     public static ObjectNode buildPut(final String parentName, final String nodeName, final String discriminator,
151                                       final String moduleName, final Optional<String> deviceName,
152                                       final String description, final ArrayNode pathParams,
153                                       final OAversion oaversion) {
154         final ObjectNode value = JsonNodeFactory.instance.objectNode();
155         value.put(DESCRIPTION_KEY, description);
156         value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.PUT, moduleName, deviceName, nodeName));
157         value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
158         final ArrayNode parameters = JsonUtil.copy(pathParams);
159         final String defName = parentName + CONFIG + "_" + nodeName + TOP;
160         final String xmlDefName = parentName + CONFIG + "_" + nodeName;
161         insertRequestBodyParameter(parameters, value, defName, xmlDefName, nodeName + CONFIG, oaversion);
162         value.set(PARAMETERS_KEY, parameters);
163
164         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
165         responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
166                 buildResponse(Response.Status.CREATED.getReasonPhrase(), Optional.empty(), oaversion));
167         responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()),
168                 buildResponse("Updated", Optional.empty(), oaversion));
169
170         value.set(RESPONSES_KEY, responses);
171         setConsumesIfNeeded(value, oaversion);
172         return value;
173     }
174
175     public static ObjectNode buildDelete(final DataSchemaNode node, final String moduleName,
176                                          final Optional<String> deviceName, final ArrayNode pathParams,
177                                          final OAversion oaversion) {
178         final ObjectNode value = JsonNodeFactory.instance.objectNode();
179         value.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.DELETE, moduleName, deviceName,
180                 node.getQName().getLocalName()));
181         value.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
182         value.put(DESCRIPTION_KEY, node.getDescription().orElse(""));
183         final ArrayNode parameters = JsonUtil.copy(pathParams);
184         value.set(PARAMETERS_KEY, parameters);
185
186         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
187         responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()),
188                 buildResponse("Deleted", Optional.empty(), oaversion));
189
190         value.set(RESPONSES_KEY, responses);
191         return value;
192     }
193
194     public static ObjectNode buildPostOperation(final OperationDefinition operDef, final String moduleName,
195                                                 final Optional<String> deviceName, final String parentName,
196                                                 final DefinitionNames definitionNames, final OAversion oaversion) {
197         final ObjectNode postOperation = JsonNodeFactory.instance.objectNode();
198         final ArrayNode parameters = JsonNodeFactory.instance.arrayNode();
199         final String operName = operDef.getQName().getLocalName();
200         final String inputName = operName + INPUT_SUFFIX;
201
202         final InputSchemaNode input = operDef.getInput();
203         final OutputSchemaNode output = operDef.getOutput();
204         if (!input.getChildNodes().isEmpty()) {
205             final String discriminator = definitionNames.getDiscriminator(input);
206             final String clearDefName = parentName + "_" + operName + INPUT_SUFFIX;
207             final String defName = clearDefName + discriminator;
208             final String defTopName = clearDefName + TOP + discriminator;
209             insertRequestBodyParameter(parameters, postOperation, defTopName, defName, inputName, oaversion);
210         } else {
211             final ObjectNode payload = JsonNodeFactory.instance.objectNode();
212             final ObjectNode jsonSchema = JsonNodeFactory.instance.objectNode();
213             final ObjectNode properties = JsonNodeFactory.instance.objectNode();
214             final ObjectNode inputSchema = JsonNodeFactory.instance.objectNode();
215             inputSchema.put(TYPE_KEY, OBJECT);
216             properties.set(INPUT_KEY, inputSchema);
217             jsonSchema.put(TYPE_KEY, OBJECT);
218             jsonSchema.set(PROPERTIES_KEY, properties);
219             if (oaversion.equals(OAversion.V3_0)) {
220                 final ObjectNode content = JsonNodeFactory.instance.objectNode();
221                 final ObjectNode jsonTypeValue = JsonNodeFactory.instance.objectNode();
222                 jsonTypeValue.set(SCHEMA_KEY, jsonSchema);
223                 content.set(MediaType.APPLICATION_JSON, jsonTypeValue);
224
225                 final ObjectNode xmlSchema = JsonNodeFactory.instance.objectNode();
226                 xmlSchema.put(TYPE_KEY, OBJECT);
227                 final ObjectNode xml = JsonNodeFactory.instance.objectNode();
228                 xml.put(NAME_KEY, INPUT);
229                 xmlSchema.set(XML_KEY, xml);
230                 final ObjectNode xmlTypeValue = JsonNodeFactory.instance.objectNode();
231                 xmlTypeValue.set(SCHEMA_KEY, xmlSchema);
232                 content.set(MediaType.APPLICATION_XML, xmlTypeValue);
233
234                 payload.set(CONTENT_KEY, content);
235                 payload.put(DESCRIPTION_KEY, inputName);
236                 postOperation.set(REQUEST_BODY_KEY, payload);
237             } else {
238                 payload.put(IN_KEY, BODY);
239                 payload.put(NAME_KEY, inputName);
240                 payload.set(SCHEMA_KEY, jsonSchema);
241                 parameters.add(payload);
242             }
243
244         }
245         setConsumesIfNeeded(postOperation, oaversion);
246         postOperation.set(PARAMETERS_KEY, parameters);
247         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
248         final String description = String.format("RPC %s success", operName);
249
250         if (!output.getChildNodes().isEmpty()) {
251             final ObjectNode schema = JsonNodeFactory.instance.objectNode();
252             final String defName = parentName + "_" + operName + OUTPUT_SUFFIX + TOP
253                     + definitionNames.getDiscriminator(output);
254             schema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
255             responses.set(String.valueOf(Response.Status.OK.getStatusCode()), buildResponse(description,
256                     Optional.of(schema), oaversion));
257         } else {
258             responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse(description,
259                     Optional.empty(), oaversion));
260         }
261         postOperation.set(RESPONSES_KEY, responses);
262         postOperation.put(DESCRIPTION_KEY, operDef.getDescription().orElse(""));
263         postOperation.put(SUMMARY_KEY, buildSummaryValue(HttpMethod.POST, moduleName, deviceName, operName));
264         postOperation.set(TAGS_KEY, buildTagsValue(deviceName, moduleName));
265         return postOperation;
266     }
267
268     private static void insertRequestBodyParameter(final ArrayNode parameters, final ObjectNode operation,
269                                                    final String defName, final String xmlDefName,
270                                                    final String name, final OAversion oaversion) {
271         final ObjectNode payload = JsonNodeFactory.instance.objectNode();
272         if (oaversion.equals(OAversion.V3_0)) {
273             final ObjectNode content = JsonNodeFactory.instance.objectNode();
274             content.set(MediaType.APPLICATION_JSON, buildMimeTypeValue(defName));
275             content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(xmlDefName));
276             payload.set(CONTENT_KEY, content);
277             payload.put(DESCRIPTION_KEY, name);
278             operation.set(REQUEST_BODY_KEY, payload);
279         } else {
280             payload.put(IN_KEY, BODY);
281             payload.put(NAME_KEY, name);
282             payload.set(SCHEMA_KEY, buildRefSchema(defName, OAversion.V2_0));
283             parameters.add(payload);
284         }
285     }
286
287     private static ObjectNode buildRefSchema(final String defName, final OAversion oaversion) {
288         final ObjectNode schema = JsonNodeFactory.instance.objectNode();
289         schema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + defName);
290         return schema;
291     }
292
293     private static ObjectNode buildMimeTypeValue(final String defName) {
294         final ObjectNode mimeTypeValue = JsonNodeFactory.instance.objectNode();
295         mimeTypeValue.set(SCHEMA_KEY, buildRefSchema(defName, OAversion.V3_0));
296         return mimeTypeValue;
297     }
298
299     public static ObjectNode buildResponse(final String description, final Optional<ObjectNode> schema,
300                                            final OAversion oaversion) {
301         final ObjectNode response = JsonNodeFactory.instance.objectNode();
302
303         if (schema.isPresent()) {
304             final ObjectNode schemaValue = schema.get();
305             if (oaversion.equals(OAversion.V3_0)) {
306                 final ObjectNode content = JsonNodeFactory.instance.objectNode();
307                 final ObjectNode body = JsonNodeFactory.instance.objectNode();
308                 for (final String mimeType : MIME_TYPES) {
309                     content.set(mimeType, body);
310                 }
311                 body.set(SCHEMA_KEY, schemaValue);
312                 response.set(CONTENT_KEY, content);
313             } else {
314                 response.set(SCHEMA_KEY, schemaValue);
315             }
316         }
317         response.put(DESCRIPTION_KEY, description);
318         return response;
319     }
320
321     private static void setConsumesIfNeeded(final ObjectNode operation, final OAversion oaversion) {
322         if (oaversion.equals(OAversion.V2_0)) {
323             operation.set(CONSUMES_KEY, CONSUMES_PUT_POST);
324         }
325     }
326
327     private static String buildSummaryValue(final String httpMethod, final String moduleName,
328                                             final Optional<String> deviceName, final String nodeName) {
329         return httpMethod + SUMMARY_SEPARATOR + deviceName.map(s -> s + SUMMARY_SEPARATOR).orElse("")
330                 + moduleName + SUMMARY_SEPARATOR + nodeName;
331     }
332
333     public static ArrayNode buildTagsValue(final Optional<String> deviceName, final String moduleName) {
334         final ArrayNode tagsValue = JsonNodeFactory.instance.arrayNode();
335         tagsValue.add(deviceName.map(s -> "mounted " + s).orElse("controller") + " " + moduleName);
336         return tagsValue;
337     }
338
339     public static String getAppropriateModelPrefix(final OAversion oaversion) {
340         if (oaversion.equals(OAversion.V3_0)) {
341             return COMPONENTS_PREFIX;
342         }
343         return DEFINITIONS_PREFIX;
344     }
345
346     public static ObjectNode getTypeParentNode(final ObjectNode parameter, final OAversion oaversion) {
347         if (oaversion.equals(OAversion.V3_0)) {
348             final ObjectNode schema = JsonNodeFactory.instance.objectNode();
349             parameter.set(SCHEMA_KEY, schema);
350             return schema;
351         }
352         return parameter;
353     }
354 }