2 * Copyright (c) 2023 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;
10 import static javax.ws.rs.core.Response.Status.CREATED;
11 import static javax.ws.rs.core.Response.Status.NO_CONTENT;
12 import static javax.ws.rs.core.Response.Status.OK;
14 import com.fasterxml.jackson.core.JsonGenerator;
15 import java.io.IOException;
16 import java.util.List;
17 import javax.ws.rs.HttpMethod;
18 import javax.ws.rs.core.MediaType;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
22 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
24 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
26 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.Module;
28 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
29 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
31 public final class PostEntity extends OperationEntity {
33 private final DocumentedNode parentNode;
34 private static final String INPUT_SUFFIX = "_input";
35 private static final String INPUT_KEY = "input";
36 private static final String POST_DESCRIPTION = """
39 In example payload, you can see only the first data node child of the resource to be created, following the
40 guidelines of RFC 8040, which allows us to create only one resource in POST request.
43 public PostEntity(final SchemaNode schema, final String deviceName, final String moduleName,
44 final List<ParameterEntity> parameters, final String refPath, final DocumentedNode parentNode) {
45 super(schema, deviceName, moduleName, parameters, refPath);
46 this.parentNode = parentNode;
49 protected String operation() {
53 @Nullable String summary() {
54 if (parentNode instanceof Module) {
55 return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), moduleName());
57 if (parentNode != null && !(schema() instanceof OperationDefinition)) {
58 return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(),
59 ((DataSchemaNode) parentNode).getQName().getLocalName());
61 return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), nodeName());
65 void generateResponses(final @NonNull JsonGenerator generator) throws IOException {
66 generator.writeObjectFieldStart(RESPONSES);
67 if (schema() instanceof OperationDefinition rpc) {
68 final var output = rpc.getOutput();
69 final var operationName = rpc.getQName().getLocalName();
70 if (!output.getChildNodes().isEmpty()) {
71 // TODO: add proper discriminator from DefinitionNames when schemas re-implementation is done
72 final var ref = processOperationsRef(rpc, operationName, "_output");
73 generator.writeObjectFieldStart(String.valueOf(OK.getStatusCode()));
74 generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
76 generator.writeObjectFieldStart(CONTENT);
77 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
78 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
79 generator.writeEndObject();
81 generator.writeEndObject();
84 generator.writeObjectFieldStart(String.valueOf(NO_CONTENT.getStatusCode()));
85 generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
86 generator.writeEndObject();
90 generator.writeObjectFieldStart(String.valueOf(CREATED.getStatusCode()));
91 generator.writeStringField(DESCRIPTION, "Created");
92 generator.writeEndObject();
94 generator.writeEndObject();
98 void generateRequestBody(final @NonNull JsonGenerator generator) throws IOException {
99 generator.writeObjectFieldStart(REQUEST_BODY);
100 if (schema() instanceof OperationDefinition rpc) {
101 final var input = rpc.getInput();
102 final var operationName = rpc.getQName().getLocalName();
103 generator.writeStringField(DESCRIPTION, operationName + INPUT_SUFFIX);
104 generator.writeObjectFieldStart(CONTENT);
105 if (!input.getChildNodes().isEmpty()) {
106 // TODO: add proper discriminator from DefinitionNames when schemas re-implementation is done
107 final var ref = processOperationsRef(rpc, operationName, INPUT_SUFFIX);
108 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
109 generator.writeObjectFieldStart(SCHEMA);
110 generator.writeObjectFieldStart("properties");
111 generator.writeObjectFieldStart(INPUT_KEY);
112 generator.writeStringField("$ref", ref);
113 generator.writeStringField(TYPE, OBJECT);
114 generator.writeEndObject();
115 generator.writeEndObject();
116 generator.writeEndObject();
117 generator.writeEndObject();
118 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
120 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
121 generator.writeObjectFieldStart(SCHEMA);
123 generator.writeObjectFieldStart(PROPERTIES);
124 generator.writeObjectFieldStart(INPUT_KEY);
125 generator.writeStringField(TYPE, OBJECT);
126 generator.writeEndObject();
127 generator.writeEndObject();
129 generator.writeStringField(TYPE, OBJECT);
130 generator.writeEndObject();
131 generator.writeEndObject();
133 generator.writeObjectFieldStart(MediaType.APPLICATION_XML);
134 generator.writeObjectFieldStart(SCHEMA);
136 generator.writeObjectFieldStart("xml");
137 generator.writeStringField(NAME, INPUT_KEY);
138 generator.writeStringField("namespace", input.getQName().getNamespace().toString());
139 generator.writeEndObject();
141 generator.writeStringField(TYPE, OBJECT);
142 generator.writeEndObject();
143 generator.writeEndObject();
145 generator.writeEndObject();
147 generator.writeStringField(DESCRIPTION, nodeName());
148 generator.writeObjectFieldStart(CONTENT);
149 final var ref = COMPONENTS_PREFIX + moduleName() + "_" + refPath();
150 //TODO: Remove if and fix this weird logic of top level nodes
151 var childConfig = true;
152 if (schema() instanceof DataNodeContainer dataSchema) {
153 final var child = dataSchema.getChildNodes().stream()
154 .filter(n -> n instanceof ListSchemaNode || n instanceof ContainerSchemaNode)
155 .findFirst().orElse(null);
157 childConfig = child.isConfiguration();
160 if (parentNode == null && !childConfig) {
161 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
163 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
164 generator.writeObjectFieldStart(SCHEMA);
166 generator.writeObjectFieldStart(PROPERTIES);
167 generator.writeObjectFieldStart(nodeName());
168 if (schema() instanceof ListSchemaNode) {
169 generator.writeStringField(TYPE, ARRAY);
170 generator.writeObjectFieldStart(ITEMS);
171 generator.writeStringField(REF, ref);
172 generator.writeStringField(TYPE, OBJECT);
173 generator.writeEndObject();
175 generator.writeStringField(REF, ref);
176 generator.writeStringField(TYPE, OBJECT);
178 generator.writeEndObject();
179 generator.writeEndObject();
180 generator.writeEndObject();
181 generator.writeEndObject();
183 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
184 generator.writeEndObject();
186 generator.writeEndObject();
191 public void generateBasics(@NonNull JsonGenerator generator) throws IOException {
192 final var description = description();
193 if (schema() instanceof OperationDefinition) {
194 generator.writeStringField(DESCRIPTION, description);
196 generator.writeStringField(DESCRIPTION, description + POST_DESCRIPTION);
198 final var summary = summary();
199 if (summary != null) {
200 generator.writeStringField(SUMMARY, summary);
205 @Nullable String description() {
206 if (parentNode != null && !(schema() instanceof OperationDefinition)) {
207 return parentNode.getDescription().orElse("");
209 return super.description();
213 private String processOperationsRef(final OperationDefinition def, final String operationName, final String suf) {
214 final var ref = COMPONENTS_PREFIX + moduleName() + "_" + operationName + suf;
215 if (def instanceof ActionDefinition && parentNode != null) {
216 final var parentName = ((DataSchemaNode) parentNode).getQName().getLocalName();
217 if (!operationName.contains(parentName)) {
218 return COMPONENTS_PREFIX + moduleName() + "_" + parentName + "_" + operationName + suf;