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