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
8 package org.opendaylight.restconf.openapi.model.builder;
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;
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;
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";
49 private OperationBuilder() {
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()));
69 return new Operation.Builder()
71 .parameters(parameters)
72 .requestBody(requestBody)
74 .description(description)
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();
91 final boolean isList = node instanceof ListSchemaNode;
92 final ObjectNode response;
94 response = createRequestBodyParameter1(defName, nodeName, isList, summary,
95 String.valueOf(Response.Status.OK.getStatusCode()));
97 response = JsonNodeFactory.instance.objectNode();
98 response.put(DESCRIPTION_KEY, Response.Status.OK.getReasonPhrase());
100 responses.set(String.valueOf(Response.Status.OK.getStatusCode()), response);
102 return new Operation.Builder()
104 .parameters(parameters)
105 .responses(responses)
106 .description(description)
111 private static Parameter buildQueryParameters(final boolean isConfig) {
112 final ArrayNode cases = JsonNodeFactory.instance.arrayNode()
117 return new Parameter.Builder()
121 .schema(new Schema.Builder().type("string").schemaEnum(cases).build())
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);
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"));
140 return new Operation.Builder()
142 .parameters(parameters)
143 .requestBody(requestBody)
144 .responses(responses)
145 .description(node.getDescription().orElse(""))
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);
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"));
165 return new Operation.Builder()
167 .parameters(parameters)
168 .requestBody(requestBody)
169 .responses(responses)
170 .description(node.getDescription().orElse(""))
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);
183 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
184 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse("Deleted"));
186 return new Operation.Builder()
188 .parameters(parameters)
189 .responses(responses)
190 .description(description)
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);
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);
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);
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);
235 payload.set(CONTENT_KEY, content);
236 payload.put(DESCRIPTION_KEY, inputName);
237 requestBody = payload;
239 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
240 final String description = String.format("RPC %s success", operationName);
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));
249 responses.set(String.valueOf(Response.Status.NO_CONTENT.getStatusCode()), buildResponse(description));
251 final String desc = operDef.getDescription().orElse("");
252 final List<String> tags = List.of(deviceName + " " + moduleName);
253 return new Operation.Builder()
255 .parameters(parameters)
256 .requestBody(requestBody)
257 .responses(responses)
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));
271 content.set(MediaType.APPLICATION_JSON, buildMimeTypeValue(defName));
272 content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(xmlDefName));
274 payload.set(CONTENT_KEY, content);
275 payload.put(DESCRIPTION_KEY, name);
279 // TODO change name after this method will be finished.(for now it's just PUT and PATCH but there might be more
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();
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);
294 final ObjectNode container = JsonNodeFactory.instance.objectNode();
295 container.set(name, buildRefSchema(defName));
296 properties.set(PROPERTIES_KEY, container);
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));
304 content.set(MediaType.APPLICATION_JSON, jsonSchema);
305 content.set(MediaType.APPLICATION_XML, buildMimeTypeValue(defName));
307 payload.set(CONTENT_KEY, content);
308 payload.put(DESCRIPTION_KEY, description);
312 private static ObjectNode buildRefSchema(final String defName) {
313 final ObjectNode schema = JsonNodeFactory.instance.objectNode();
314 schema.put(REF_KEY, COMPONENTS_PREFIX + defName);
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;
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();
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);
337 response.set(CONTENT_KEY, content);
340 response.put(DESCRIPTION_KEY, description);
344 private static ObjectNode buildResponse(final String description) {
345 final ObjectNode response = JsonNodeFactory.instance.objectNode();
346 response.put(DESCRIPTION_KEY, description);
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);
357 body.set(SCHEMA_KEY, schema);
358 response.set(CONTENT_KEY, content);
359 response.put(DESCRIPTION_KEY, description);