3739523dd52ff5c81b01cbc2d64a5042239aca29
[netconf.git] /
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.restconf.openapi.model.builder;
10
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;
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 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;
29
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";
55
56     static {
57         CONSUMES_PUT_POST = JsonNodeFactory.instance.arrayNode();
58         for (final String mimeType : MIME_TYPES) {
59             CONSUMES_PUT_POST.add(mimeType);
60         }
61     }
62
63     private OperationBuilder() {
64
65     }
66
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()));
82
83         return new Operation.Builder()
84             .tags(tags)
85             .parameters(parameters)
86             .requestBody(requestBody)
87             .responses(responses)
88             .description(description)
89             .summary(summary)
90             .build();
91     }
92
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);
107
108         responses.set(String.valueOf(Response.Status.OK.getStatusCode()),
109                 buildResponse(Response.Status.OK.getReasonPhrase(), schema, xmlSchema));
110
111         return new Operation.Builder()
112             .tags(tags)
113             .parameters(parameters)
114             .responses(responses)
115             .description(description)
116             .summary(summary)
117             .build();
118     }
119
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);
124         if (isConfig) {
125             cases.add(CONFIG_QUERY_PARAM);
126         } else {
127             contentParam.put(REQUIRED_KEY, true);
128         }
129         contentParam.put(IN_KEY, QUERY);
130         contentParam.put(NAME_KEY, CONTENT);
131
132         final ObjectNode typeParent = getTypeParentNode(contentParam);
133         typeParent.put(TYPE_KEY, STRING);
134         typeParent.set(ENUM_KEY, cases);
135
136         parameters.add(contentParam);
137     }
138
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);
148
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"));
153
154         return new Operation.Builder()
155             .tags(tags)
156             .parameters(parameters)
157             .requestBody(requestBody)
158             .responses(responses)
159             .description(description)
160             .summary(summary)
161             .build();
162     }
163
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);
172
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"));
177
178         return new Operation.Builder()
179             .tags(tags)
180             .parameters(parameters)
181             .requestBody(requestBody)
182             .responses(responses)
183             .description(description)
184             .summary(summary)
185             .build();
186     }
187
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);
195
196         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
197         responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse("Deleted"));
198
199         return new Operation.Builder()
200             .tags(tags)
201             .parameters(parameters)
202             .responses(responses)
203             .description(description)
204             .summary(summary)
205             .build();
206     }
207
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);
215
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);
225         } else {
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);
238
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);
247
248             payload.set(CONTENT_KEY, content);
249             payload.put(DESCRIPTION_KEY, inputName);
250             requestBody = payload;
251         }
252         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
253         final String description = String.format("RPC %s success", operationName);
254
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));
261         } else {
262             responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse(description));
263         }
264         final String desc = operDef.getDescription().orElse("");
265         final ArrayNode tags = buildTagsValue(deviceName, moduleName);
266         return new Operation.Builder()
267             .tags(tags)
268             .parameters(parameters)
269             .requestBody(requestBody)
270             .responses(responses)
271             .description(desc)
272             .summary(summary)
273             .build();
274     }
275
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));
283         } else {
284             content.set(MediaType.APPLICATION_JSON, buildMimeTypeValue(defName));
285             content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(xmlDefName));
286         }
287         payload.set(CONTENT_KEY, content);
288         payload.put(DESCRIPTION_KEY, name);
289         return payload;
290     }
291
292     private static ObjectNode buildRefSchema(final String defName) {
293         final ObjectNode schema = JsonNodeFactory.instance.objectNode();
294         schema.put(REF_KEY, COMPONENTS_PREFIX + defName);
295         return schema;
296     }
297
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;
302     }
303
304     public static ObjectNode buildResponse(final String description, final ObjectNode schema,
305             final ObjectNode xmlSchema) {
306         final ObjectNode response = JsonNodeFactory.instance.objectNode();
307
308         final ObjectNode content = JsonNodeFactory.instance.objectNode();
309         final ObjectNode body = JsonNodeFactory.instance.objectNode();
310         final ObjectNode xmlBody = JsonNodeFactory.instance.objectNode();
311
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);
316
317         response.set(CONTENT_KEY, content);
318
319         response.put(DESCRIPTION_KEY, description);
320         return response;
321     }
322
323     private static ObjectNode buildResponse(final String description) {
324         final ObjectNode response = JsonNodeFactory.instance.objectNode();
325         response.put(DESCRIPTION_KEY, description);
326         return response;
327     }
328
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);
335         }
336         body.set(SCHEMA_KEY, schema);
337         response.set(CONTENT_KEY, content);
338         response.put(DESCRIPTION_KEY, description);
339         return response;
340     }
341
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;
347         }
348         return httpMethod + SUMMARY_SEPARATOR + deviceName + SUMMARY_SEPARATOR
349                 + moduleName + SUMMARY_SEPARATOR + nodeName;
350     }
351
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);
356         }
357         return JsonNodeFactory.instance.arrayNode().add("mounted " + deviceName + " " + moduleName);
358     }
359
360     public static ObjectNode getTypeParentNode(final ObjectNode parameter) {
361         final ObjectNode schema = JsonNodeFactory.instance.objectNode();
362         parameter.set(SCHEMA_KEY, schema);
363         return schema;
364     }
365 }