ac22978c6b3cb34d5acc465bc305741d3b44e43d
[netconf.git] / restconf / restconf-openapi / src / main / java / org / opendaylight / restconf / openapi / model / PostEntity.java
1 /*
2  * Copyright (c) 2023 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;
9
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;
14
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;
31
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 = """
36         \n
37         Note:
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.
40         """;
41
42     private final @Nullable DocumentedNode parentNode;
43
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;
49     }
50
51     protected @NonNull String operation() {
52         return "post";
53     }
54
55     @NonNull String summary() {
56         if (parentNode instanceof Module) {
57             return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), moduleName());
58         }
59         if (parentNode != null && !(schema() instanceof OperationDefinition)) {
60             return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(),
61                 ((DataSchemaNode) parentNode).getQName().getLocalName());
62         }
63         return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), nodeName());
64     }
65
66     @Override
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                 final var ref = processOperationsRef(rpc, operationName, "_output");
74                 generator.writeObjectFieldStart(String.valueOf(OK.getStatusCode()));
75                 generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
76
77                 generator.writeObjectFieldStart(CONTENT);
78                 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
79                 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
80                 generator.writeEndObject();
81
82                 generator.writeEndObject();
83
84             } else {
85                 generator.writeObjectFieldStart(String.valueOf(NO_CONTENT.getStatusCode()));
86                 generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
87                 generator.writeEndObject();
88
89             }
90         } else {
91             generator.writeObjectFieldStart(String.valueOf(CREATED.getStatusCode()));
92             generator.writeStringField(DESCRIPTION, "Created");
93             generator.writeEndObject();
94         }
95         generator.writeEndObject();
96     }
97
98     @Override
99     void generateRequestBody(final @NonNull JsonGenerator generator) throws IOException {
100         generator.writeObjectFieldStart(REQUEST_BODY);
101         if (schema() instanceof OperationDefinition rpc) {
102             final var input = rpc.getInput();
103             final var operationName = rpc.getQName().getLocalName();
104             generator.writeStringField(DESCRIPTION, operationName + INPUT_SUFFIX);
105             generator.writeObjectFieldStart(CONTENT);
106             if (!input.getChildNodes().isEmpty()) {
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);
119             } else {
120                 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
121                 generator.writeObjectFieldStart(SCHEMA);
122
123                 generator.writeObjectFieldStart(PROPERTIES);
124                 generator.writeObjectFieldStart(INPUT_KEY);
125                 generator.writeStringField(TYPE, OBJECT);
126                 generator.writeEndObject();
127                 generator.writeEndObject();
128
129                 generator.writeStringField(TYPE, OBJECT);
130                 generator.writeEndObject();
131                 generator.writeEndObject();
132
133                 generator.writeObjectFieldStart(MediaType.APPLICATION_XML);
134                 generator.writeObjectFieldStart(SCHEMA);
135
136                 generator.writeObjectFieldStart("xml");
137                 generator.writeStringField(NAME, INPUT_KEY);
138                 generator.writeStringField("namespace", input.getQName().getNamespace().toString());
139                 generator.writeEndObject();
140
141                 generator.writeStringField(TYPE, OBJECT);
142                 generator.writeEndObject();
143                 generator.writeEndObject();
144             }
145             generator.writeEndObject();
146         } else {
147             generator.writeStringField(DESCRIPTION, nodeName());
148             generator.writeObjectFieldStart(CONTENT);
149             final var ref = COMPONENTS_PREFIX + moduleName() + "_" + refPath();
150             var childConfig = true;
151             if (schema() instanceof DataNodeContainer dataSchema) {
152                 final var child = dataSchema.getChildNodes().stream()
153                     .filter(n -> n instanceof ListSchemaNode || n instanceof ContainerSchemaNode)
154                     .findFirst().orElse(null);
155                 if (child != null) {
156                     childConfig = child.isConfiguration();
157                 }
158             }
159             if (parentNode == null && !childConfig) {
160                 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
161             } else {
162                 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
163                 generator.writeObjectFieldStart(SCHEMA);
164
165                 generator.writeObjectFieldStart(PROPERTIES);
166                 generator.writeObjectFieldStart(nodeName());
167                 if (schema() instanceof ListSchemaNode) {
168                     generator.writeStringField(TYPE, ARRAY);
169                     generator.writeObjectFieldStart(ITEMS);
170                     generator.writeStringField(REF, ref);
171                     generator.writeStringField(TYPE, OBJECT);
172                     generator.writeEndObject();
173                 } else {
174                     generator.writeStringField(REF, ref);
175                     generator.writeStringField(TYPE, OBJECT);
176                 }
177                 generator.writeEndObject();
178                 generator.writeEndObject();
179                 generator.writeEndObject();
180                 generator.writeEndObject();
181             }
182             generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
183             generator.writeEndObject();
184         }
185         generator.writeEndObject();
186     }
187
188     @Override
189     public void generateBasics(@NonNull JsonGenerator generator) throws IOException {
190         final var description = description();
191         if (schema() instanceof OperationDefinition) {
192             generator.writeStringField(DESCRIPTION, description);
193         } else {
194             generator.writeStringField(DESCRIPTION, description + POST_DESCRIPTION);
195         }
196         generator.writeStringField(SUMMARY, summary());
197     }
198
199     @Override
200     @NonNull String description() {
201         if (parentNode != null && !(schema() instanceof OperationDefinition)) {
202             return parentNode.getDescription().orElse("");
203         } else {
204             return super.description();
205         }
206     }
207
208     private String processOperationsRef(final OperationDefinition def, final String operationName, final String suf) {
209         if (def instanceof ActionDefinition && parentNode != null) {
210             final var parentName = ((DataSchemaNode) parentNode).getQName().getLocalName();
211             final boolean hasChildNodes = suf.equals(INPUT_SUFFIX) ? !def.getInput().getChildNodes().isEmpty()
212                     : !def.getOutput().getChildNodes().isEmpty();
213             if (hasChildNodes) {
214                 return COMPONENTS_PREFIX + moduleName() + "_" + parentName + "_" + operationName + suf;
215             }
216         }
217         return COMPONENTS_PREFIX + moduleName() + "_" + operationName + suf;
218     }
219 }