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 java.util.Objects.requireNonNull;
11 import static javax.ws.rs.core.Response.Status.CREATED;
12 import static javax.ws.rs.core.Response.Status.NO_CONTENT;
13 import static javax.ws.rs.core.Response.Status.OK;
15 import com.fasterxml.jackson.core.JsonGenerator;
16 import java.io.IOException;
17 import java.util.List;
18 import javax.ws.rs.HttpMethod;
19 import javax.ws.rs.core.MediaType;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
23 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
25 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
27 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.Module;
29 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
30 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
32 public final class PostEntity extends OperationEntity {
33 private static final String INPUT_SUFFIX = "_input";
34 private static final String INPUT_KEY = "input";
35 private static final String POST_DESCRIPTION = """
38 In example payload, you can see only the first data node child of the resource to be created, following the
39 guidelines of RFC 8040, which allows us to create only one resource in POST request.
42 private final @Nullable DocumentedNode parentNode;
44 public PostEntity(final @NonNull SchemaNode schema, final @NonNull String deviceName,
45 final @NonNull String moduleName, final @NonNull List<ParameterEntity> parameters,
46 final @NonNull String refPath, final @Nullable DocumentedNode parentNode) {
47 super(requireNonNull(schema), deviceName, moduleName, requireNonNull(parameters), requireNonNull(refPath));
48 this.parentNode = parentNode;
51 protected @NonNull String operation() {
55 @NonNull String summary() {
56 if (parentNode instanceof Module) {
57 return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), moduleName());
59 if (parentNode != null && !(schema() instanceof OperationDefinition)) {
60 return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(),
61 ((DataSchemaNode) parentNode).getQName().getLocalName());
63 return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), nodeName());
67 void generateResponses(final @NonNull JsonGenerator generator) throws IOException {
68 generator.writeObjectFieldStart(RESPONSES);
69 if (schema() instanceof OperationDefinition rpc) {
70 final var output = rpc.getOutput();
71 final var operationName = rpc.getQName().getLocalName();
72 if (!output.getChildNodes().isEmpty()) {
73 // TODO: add proper discriminator from DefinitionNames when schemas re-implementation is done
74 final var ref = processOperationsRef(rpc, operationName, "_output");
75 generator.writeObjectFieldStart(String.valueOf(OK.getStatusCode()));
76 generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
78 generator.writeObjectFieldStart(CONTENT);
79 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
80 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
81 generator.writeEndObject();
83 generator.writeEndObject();
86 generator.writeObjectFieldStart(String.valueOf(NO_CONTENT.getStatusCode()));
87 generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
88 generator.writeEndObject();
92 generator.writeObjectFieldStart(String.valueOf(CREATED.getStatusCode()));
93 generator.writeStringField(DESCRIPTION, "Created");
94 generator.writeEndObject();
96 generator.writeEndObject();
100 void generateRequestBody(final @NonNull JsonGenerator generator) throws IOException {
101 generator.writeObjectFieldStart(REQUEST_BODY);
102 if (schema() instanceof OperationDefinition rpc) {
103 final var input = rpc.getInput();
104 final var operationName = rpc.getQName().getLocalName();
105 generator.writeStringField(DESCRIPTION, operationName + INPUT_SUFFIX);
106 generator.writeObjectFieldStart(CONTENT);
107 if (!input.getChildNodes().isEmpty()) {
108 // TODO: add proper discriminator from DefinitionNames when schemas re-implementation is done
109 final var ref = processOperationsRef(rpc, operationName, INPUT_SUFFIX);
110 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
111 generator.writeObjectFieldStart(SCHEMA);
112 generator.writeObjectFieldStart("properties");
113 generator.writeObjectFieldStart(INPUT_KEY);
114 generator.writeStringField("$ref", ref);
115 generator.writeStringField(TYPE, OBJECT);
116 generator.writeEndObject();
117 generator.writeEndObject();
118 generator.writeEndObject();
119 generator.writeEndObject();
120 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
122 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
123 generator.writeObjectFieldStart(SCHEMA);
125 generator.writeObjectFieldStart(PROPERTIES);
126 generator.writeObjectFieldStart(INPUT_KEY);
127 generator.writeStringField(TYPE, OBJECT);
128 generator.writeEndObject();
129 generator.writeEndObject();
131 generator.writeStringField(TYPE, OBJECT);
132 generator.writeEndObject();
133 generator.writeEndObject();
135 generator.writeObjectFieldStart(MediaType.APPLICATION_XML);
136 generator.writeObjectFieldStart(SCHEMA);
138 generator.writeObjectFieldStart("xml");
139 generator.writeStringField(NAME, INPUT_KEY);
140 generator.writeStringField("namespace", input.getQName().getNamespace().toString());
141 generator.writeEndObject();
143 generator.writeStringField(TYPE, OBJECT);
144 generator.writeEndObject();
145 generator.writeEndObject();
147 generator.writeEndObject();
149 generator.writeStringField(DESCRIPTION, nodeName());
150 generator.writeObjectFieldStart(CONTENT);
151 final var ref = COMPONENTS_PREFIX + moduleName() + "_" + refPath();
152 //TODO: Remove if and fix this weird logic of top level nodes
153 var childConfig = true;
154 if (schema() instanceof DataNodeContainer dataSchema) {
155 final var child = dataSchema.getChildNodes().stream()
156 .filter(n -> n instanceof ListSchemaNode || n instanceof ContainerSchemaNode)
157 .findFirst().orElse(null);
159 childConfig = child.isConfiguration();
162 if (parentNode == null && !childConfig) {
163 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
165 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
166 generator.writeObjectFieldStart(SCHEMA);
168 generator.writeObjectFieldStart(PROPERTIES);
169 generator.writeObjectFieldStart(nodeName());
170 if (schema() instanceof ListSchemaNode) {
171 generator.writeStringField(TYPE, ARRAY);
172 generator.writeObjectFieldStart(ITEMS);
173 generator.writeStringField(REF, ref);
174 generator.writeStringField(TYPE, OBJECT);
175 generator.writeEndObject();
177 generator.writeStringField(REF, ref);
178 generator.writeStringField(TYPE, OBJECT);
180 generator.writeEndObject();
181 generator.writeEndObject();
182 generator.writeEndObject();
183 generator.writeEndObject();
185 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
186 generator.writeEndObject();
188 generator.writeEndObject();
192 public void generateBasics(@NonNull JsonGenerator generator) throws IOException {
193 final var description = description();
194 if (schema() instanceof OperationDefinition) {
195 generator.writeStringField(DESCRIPTION, description);
197 generator.writeStringField(DESCRIPTION, description + POST_DESCRIPTION);
199 generator.writeStringField(SUMMARY, summary());
203 @NonNull String description() {
204 if (parentNode != null && !(schema() instanceof OperationDefinition)) {
205 return parentNode.getDescription().orElse("");
207 return super.description();
211 private String processOperationsRef(final OperationDefinition def, final String operationName, final String suf) {
212 if (def instanceof ActionDefinition && parentNode != null) {
213 final var parentName = ((DataSchemaNode) parentNode).getQName().getLocalName();
214 final boolean hasChildNodes = suf.equals(INPUT_SUFFIX) ? !def.getInput().getChildNodes().isEmpty()
215 : !def.getOutput().getChildNodes().isEmpty();
217 return COMPONENTS_PREFIX + moduleName() + "_" + parentName + "_" + operationName + suf;
220 return COMPONENTS_PREFIX + moduleName() + "_" + operationName + suf;