558513280c91a8bcc4040b451cd7fad462796e37
[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 package org.opendaylight.restconf.openapi.model.builder;
9
10 import static org.opendaylight.restconf.openapi.impl.DefinitionGenerator.INPUT;
11 import static org.opendaylight.restconf.openapi.impl.DefinitionGenerator.INPUT_SUFFIX;
12 import static org.opendaylight.restconf.openapi.impl.DefinitionGenerator.OUTPUT_SUFFIX;
13
14 import com.fasterxml.jackson.databind.node.ArrayNode;
15 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
16 import com.fasterxml.jackson.databind.node.ObjectNode;
17 import java.util.ArrayList;
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.NonNull;
23 import org.opendaylight.restconf.openapi.impl.DefinitionNames;
24 import org.opendaylight.restconf.openapi.model.Operation;
25 import org.opendaylight.restconf.openapi.model.Parameter;
26 import org.opendaylight.restconf.openapi.model.Schema;
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.ListSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
31 import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
32
33 public final class OperationBuilder {
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 INPUT_KEY = "input";
38     public static final String NAME_KEY = "name";
39     public static final String PROPERTIES_KEY = "properties";
40     public static final String REF_KEY = "$ref";
41     public static final String SCHEMA_KEY = "schema";
42     public static final String SUMMARY_TEMPLATE = "%s - %s - %s - %s";
43     public static final String TOP = "_TOP";
44     public static final String XML_KEY = "xml";
45     private static final List<String> MIME_TYPES = List.of(MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON);
46     private static final String OBJECT = "object";
47     private static final String TYPE_KEY = "type";
48
49     private OperationBuilder() {
50         // Hidden on purpose
51     }
52
53     public static Operation buildPost(final String parentName, final String nodeName, final String discriminator,
54             final String moduleName, final @NonNull String deviceName, final String description,
55             final List<Parameter> pathParams) {
56         final var summary = SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName, moduleName, nodeName);
57         final List<String> tags = List.of(deviceName + " " + moduleName);
58         final List<Parameter> parameters = new ArrayList<>(pathParams);
59         final ObjectNode ref = JsonNodeFactory.instance.objectNode();
60         final String cleanDefName = parentName + "_" + nodeName;
61         final String defName = cleanDefName + discriminator;
62         final String xmlDefName = cleanDefName + discriminator;
63         ref.put(REF_KEY, COMPONENTS_PREFIX + defName);
64         final ObjectNode requestBody = createRequestBodyParameter(defName, xmlDefName, nodeName, summary);
65         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
66         responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
67                 buildResponse(Response.Status.CREATED.getReasonPhrase()));
68
69         return new Operation.Builder()
70             .tags(tags)
71             .parameters(parameters)
72             .requestBody(requestBody)
73             .responses(responses)
74             .description(description)
75             .summary(summary)
76             .build();
77     }
78
79     public static Operation buildGet(final DataSchemaNode node, final String parentName, final String moduleName,
80             final @NonNull String deviceName, final List<Parameter> pathParams, final boolean isConfig) {
81         final String nodeName = node.getQName().getLocalName();
82         final String defName = parentName + "_" + nodeName;
83         final String description = node.getDescription().orElse("");
84         final String summary = SUMMARY_TEMPLATE.formatted(HttpMethod.GET, deviceName, moduleName,
85                 node.getQName().getLocalName());
86         final List<String> tags = List.of(deviceName + " " + moduleName);
87         final List<Parameter> parameters = new ArrayList<>(pathParams);
88         parameters.add(buildQueryParameters(isConfig));
89         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
90
91         final boolean isList = node instanceof ListSchemaNode;
92         final ObjectNode response;
93         if (isConfig) {
94             response = createRequestBodyParameter1(defName, nodeName, isList, summary,
95                 String.valueOf(Response.Status.OK.getStatusCode()));
96         } else {
97             response = JsonNodeFactory.instance.objectNode();
98             response.put(DESCRIPTION_KEY, Response.Status.OK.getReasonPhrase());
99         }
100         responses.set(String.valueOf(Response.Status.OK.getStatusCode()), response);
101
102         return new Operation.Builder()
103             .tags(tags)
104             .parameters(parameters)
105             .responses(responses)
106             .description(description)
107             .summary(summary)
108             .build();
109     }
110
111     private static Parameter buildQueryParameters(final boolean isConfig) {
112         final ArrayNode cases = JsonNodeFactory.instance.arrayNode()
113             .add("config")
114             .add("nonconfig")
115             .add("all");
116
117         return new Parameter.Builder()
118             .in("query")
119             .name("content")
120             .required(!isConfig)
121             .schema(new Schema.Builder().type("string").schemaEnum(cases).build())
122             .build();
123     }
124
125     public static Operation buildPut(final DataSchemaNode node, final String parentName, final String moduleName,
126             final @NonNull String deviceName, final List<Parameter> pathParams, final String fullName) {
127         final String nodeName = node.getQName().getLocalName();
128         final String summary = SUMMARY_TEMPLATE.formatted(HttpMethod.PUT, moduleName, deviceName, nodeName);
129         final List<String> tags = List.of(deviceName + " " + moduleName);
130         final List<Parameter> parameters = new ArrayList<>(pathParams);
131         final String defName = parentName + "_" + nodeName;
132         final boolean isList = node instanceof ListSchemaNode;
133         final ObjectNode requestBody = createRequestBodyParameter1(defName, fullName, isList, summary, nodeName);
134
135         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
136         responses.set(String.valueOf(Response.Status.CREATED.getStatusCode()),
137             buildResponse(Response.Status.CREATED.getReasonPhrase()));
138         responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse("Updated"));
139
140         return new Operation.Builder()
141             .tags(tags)
142             .parameters(parameters)
143             .requestBody(requestBody)
144             .responses(responses)
145             .description(node.getDescription().orElse(""))
146             .summary(summary)
147             .build();
148     }
149
150     public static Operation buildPatch(final DataSchemaNode node, final String parentName, final String moduleName,
151             final @NonNull String deviceName, final List<Parameter> pathParams, final String fullName) {
152         final String nodeName = node.getQName().getLocalName();
153         final String summary = SUMMARY_TEMPLATE.formatted(HttpMethod.PATCH, moduleName, deviceName, nodeName);
154         final List<String> tags = List.of(deviceName + " " + moduleName);
155         final List<Parameter> parameters = new ArrayList<>(pathParams);
156         final String defName = parentName + "_" + nodeName;
157         final boolean isList = node instanceof ListSchemaNode;
158         final ObjectNode requestBody = createRequestBodyParameter1(defName, fullName, isList, summary, nodeName);
159
160         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
161         responses.set(String.valueOf(Response.Status.OK.getStatusCode()),
162                 buildResponse(Response.Status.OK.getReasonPhrase()));
163         responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse("Updated"));
164
165         return new Operation.Builder()
166             .tags(tags)
167             .parameters(parameters)
168             .requestBody(requestBody)
169             .responses(responses)
170             .description(node.getDescription().orElse(""))
171             .summary(summary)
172             .build();
173     }
174
175     public static Operation buildDelete(final DataSchemaNode node, final String moduleName,
176             final @NonNull String deviceName, final List<Parameter> pathParams) {
177         final String summary = SUMMARY_TEMPLATE.formatted(HttpMethod.DELETE, deviceName, moduleName,
178                 node.getQName().getLocalName());
179         final List<String> tags = List.of(deviceName + " " + moduleName);
180         final String description = node.getDescription().orElse("");
181         final List<Parameter> parameters = new ArrayList<>(pathParams);
182
183         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
184         responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse("Deleted"));
185
186         return new Operation.Builder()
187             .tags(tags)
188             .parameters(parameters)
189             .responses(responses)
190             .description(description)
191             .summary(summary)
192             .build();
193     }
194
195     public static Operation buildPostOperation(final OperationDefinition operDef, final String moduleName,
196             final @NonNull String deviceName, final String parentName, final DefinitionNames definitionNames,
197             final List<Parameter> parentPathParameters) {
198         final List<Parameter> parameters = new ArrayList<>(parentPathParameters);
199         final String operationName = operDef.getQName().getLocalName();
200         final String inputName = operationName + INPUT_SUFFIX;
201         final String summary = SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName, moduleName, operationName);
202
203         final InputSchemaNode input = operDef.getInput();
204         final OutputSchemaNode output = operDef.getOutput();
205         ObjectNode requestBody;
206         if (!input.getChildNodes().isEmpty()) {
207             final String discriminator = definitionNames.getDiscriminator(input);
208             final String clearDefName = parentName + "_" + operationName + INPUT_SUFFIX;
209             final String defName = clearDefName + discriminator;
210             final String defTopName = clearDefName + TOP + discriminator;
211             requestBody = createRequestBodyParameter(defTopName, defName, inputName, summary);
212         } else {
213             final ObjectNode payload = JsonNodeFactory.instance.objectNode();
214             final ObjectNode jsonSchema = JsonNodeFactory.instance.objectNode();
215             final ObjectNode properties = JsonNodeFactory.instance.objectNode();
216             final ObjectNode inputSchema = JsonNodeFactory.instance.objectNode();
217             inputSchema.put(TYPE_KEY, OBJECT);
218             properties.set(INPUT_KEY, inputSchema);
219             jsonSchema.put(TYPE_KEY, OBJECT);
220             jsonSchema.set(PROPERTIES_KEY, properties);
221             final ObjectNode content = JsonNodeFactory.instance.objectNode();
222             final ObjectNode jsonTypeValue = JsonNodeFactory.instance.objectNode();
223             jsonTypeValue.set(SCHEMA_KEY, jsonSchema);
224             content.set(MediaType.APPLICATION_JSON, jsonTypeValue);
225
226             final ObjectNode xmlSchema = JsonNodeFactory.instance.objectNode();
227             xmlSchema.put(TYPE_KEY, OBJECT);
228             final ObjectNode xml = JsonNodeFactory.instance.objectNode();
229             xml.put(NAME_KEY, INPUT);
230             xmlSchema.set(XML_KEY, xml);
231             final ObjectNode xmlTypeValue = JsonNodeFactory.instance.objectNode();
232             xmlTypeValue.set(SCHEMA_KEY, xmlSchema);
233             content.set(MediaType.APPLICATION_XML, xmlTypeValue);
234
235             payload.set(CONTENT_KEY, content);
236             payload.put(DESCRIPTION_KEY, inputName);
237             requestBody = payload;
238         }
239         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
240         final String description = String.format("RPC %s success", operationName);
241
242         if (!output.getChildNodes().isEmpty()) {
243             final ObjectNode schema = JsonNodeFactory.instance.objectNode();
244             final String defName = parentName + "_" + operationName + OUTPUT_SUFFIX + TOP
245                     + definitionNames.getDiscriminator(output);
246             schema.put(REF_KEY, COMPONENTS_PREFIX + defName);
247             responses.set(String.valueOf(Response.Status.OK.getStatusCode()), buildResponse(description, schema));
248         } else {
249             responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse(description));
250         }
251         final String desc = operDef.getDescription().orElse("");
252         final List<String> tags = List.of(deviceName + " " + moduleName);
253         return new Operation.Builder()
254             .tags(tags)
255             .parameters(parameters)
256             .requestBody(requestBody)
257             .responses(responses)
258             .description(desc)
259             .summary(summary)
260             .build();
261     }
262
263     private static ObjectNode createRequestBodyParameter(final String defName, final String xmlDefName,
264             final String name, final String summary) {
265         final ObjectNode payload = JsonNodeFactory.instance.objectNode();
266         final ObjectNode content = JsonNodeFactory.instance.objectNode();
267         if (summary != null && summary.contains(HttpMethod.PATCH)) {
268             content.set("application/yang-data+json", buildMimeTypeValue(defName));
269             content.set("application/yang-data+xml", buildMimeTypeValue(xmlDefName));
270         } else {
271             content.set(MediaType.APPLICATION_JSON, buildMimeTypeValue(defName));
272             content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(xmlDefName));
273         }
274         payload.set(CONTENT_KEY, content);
275         payload.put(DESCRIPTION_KEY, name);
276         return payload;
277     }
278
279     // TODO change name after this method will be finished.(for now it's just PUT and PATCH but there might be more
280     //  in next patches)
281     private static ObjectNode createRequestBodyParameter1(final String defName, final String name,
282             final boolean isList, final String summary, final String description) {
283         final ObjectNode payload = JsonNodeFactory.instance.objectNode();
284         final ObjectNode content = JsonNodeFactory.instance.objectNode();
285         final ObjectNode properties = JsonNodeFactory.instance.objectNode();
286         if (isList) {
287             final ObjectNode list = JsonNodeFactory.instance.objectNode();
288             final ObjectNode listValue = JsonNodeFactory.instance.objectNode();
289             listValue.put(TYPE_KEY, "array");
290             listValue.set("items", buildRefSchema(defName));
291             list.set(name, listValue);
292             properties.set(PROPERTIES_KEY, list);
293         } else {
294             final ObjectNode container = JsonNodeFactory.instance.objectNode();
295             container.set(name, buildRefSchema(defName));
296             properties.set(PROPERTIES_KEY, container);
297         }
298         final ObjectNode jsonSchema = JsonNodeFactory.instance.objectNode();
299         jsonSchema.set(SCHEMA_KEY, properties);
300         if (summary != null && summary.contains(HttpMethod.PATCH)) {
301             content.set("application/yang-data+json", jsonSchema);
302             content.set("application/yang-data+xml", buildMimeTypeValue(defName));
303         } else {
304             content.set(MediaType.APPLICATION_JSON, jsonSchema);
305             content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(defName));
306         }
307         payload.set(CONTENT_KEY, content);
308         payload.put(DESCRIPTION_KEY, description);
309         return payload;
310     }
311
312     private static ObjectNode buildRefSchema(final String defName) {
313         final ObjectNode schema = JsonNodeFactory.instance.objectNode();
314         schema.put(REF_KEY, COMPONENTS_PREFIX + defName);
315         return schema;
316     }
317
318     private static ObjectNode buildMimeTypeValue(final String defName) {
319         final ObjectNode mimeTypeValue = JsonNodeFactory.instance.objectNode();
320         mimeTypeValue.set(SCHEMA_KEY, buildRefSchema(defName));
321         return mimeTypeValue;
322     }
323
324     public static ObjectNode buildResponse(final String description, final ObjectNode schema,
325             final ObjectNode xmlSchema) {
326         final ObjectNode response = JsonNodeFactory.instance.objectNode();
327         if (!schema.isEmpty()) {
328             final ObjectNode content = JsonNodeFactory.instance.objectNode();
329             final ObjectNode body = JsonNodeFactory.instance.objectNode();
330             final ObjectNode xmlBody = JsonNodeFactory.instance.objectNode();
331
332             body.set(SCHEMA_KEY, schema);
333             xmlBody.set(SCHEMA_KEY, xmlSchema);
334             content.set(MediaType.APPLICATION_JSON, body);
335             content.set(MediaType.APPLICATION_XML, xmlBody);
336
337             response.set(CONTENT_KEY, content);
338         }
339
340         response.put(DESCRIPTION_KEY, description);
341         return response;
342     }
343
344     private static ObjectNode buildResponse(final String description) {
345         final ObjectNode response = JsonNodeFactory.instance.objectNode();
346         response.put(DESCRIPTION_KEY, description);
347         return response;
348     }
349
350     private static ObjectNode buildResponse(final String description, final ObjectNode schema) {
351         final ObjectNode response = JsonNodeFactory.instance.objectNode();
352         final ObjectNode content = JsonNodeFactory.instance.objectNode();
353         final ObjectNode body = JsonNodeFactory.instance.objectNode();
354         for (final String mimeType : MIME_TYPES) {
355             content.set(mimeType, body);
356         }
357         body.set(SCHEMA_KEY, schema);
358         response.set(CONTENT_KEY, content);
359         response.put(DESCRIPTION_KEY, description);
360         return response;
361     }
362 }