Fix wrong path reference - schema for actions
[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 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;
13
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;
30
31 public final class PostEntity extends OperationEntity {
32
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 = """
37         \n
38         Note:
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.
41         """;
42
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;
47     }
48
49     protected String operation() {
50         return "post";
51     }
52
53     @Nullable String summary() {
54         if (parentNode instanceof Module) {
55             return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), moduleName());
56         }
57         if (parentNode != null && !(schema() instanceof OperationDefinition)) {
58             return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(),
59                 ((DataSchemaNode) parentNode).getQName().getLocalName());
60         }
61         return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), nodeName());
62     }
63
64     @Override
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));
75
76                 generator.writeObjectFieldStart(CONTENT);
77                 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
78                 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
79                 generator.writeEndObject();
80
81                 generator.writeEndObject();
82
83             } else {
84                 generator.writeObjectFieldStart(String.valueOf(NO_CONTENT.getStatusCode()));
85                 generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
86                 generator.writeEndObject();
87
88             }
89         } else {
90             generator.writeObjectFieldStart(String.valueOf(CREATED.getStatusCode()));
91             generator.writeStringField(DESCRIPTION, "Created");
92             generator.writeEndObject();
93         }
94         generator.writeEndObject();
95     }
96
97     @Override
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);
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             //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);
156                 if (child != null) {
157                     childConfig = child.isConfiguration();
158                 }
159             }
160             if (parentNode == null && !childConfig) {
161                 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
162             } else {
163                 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
164                 generator.writeObjectFieldStart(SCHEMA);
165
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();
174                 } else {
175                     generator.writeStringField(REF, ref);
176                     generator.writeStringField(TYPE, OBJECT);
177                 }
178                 generator.writeEndObject();
179                 generator.writeEndObject();
180                 generator.writeEndObject();
181                 generator.writeEndObject();
182             }
183             generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
184             generator.writeEndObject();
185         }
186         generator.writeEndObject();
187     }
188
189
190     @Override
191     public void generateBasics(@NonNull JsonGenerator generator) throws IOException {
192         final var description = description();
193         if (schema() instanceof OperationDefinition) {
194             generator.writeStringField(DESCRIPTION, description);
195         } else {
196             generator.writeStringField(DESCRIPTION, description + POST_DESCRIPTION);
197         }
198         final var summary = summary();
199         if (summary != null) {
200             generator.writeStringField(SUMMARY, summary);
201         }
202     }
203
204     @Override
205     @Nullable String description() {
206         if (parentNode != null && !(schema() instanceof OperationDefinition)) {
207             return parentNode.getDescription().orElse("");
208         } else {
209             return super.description();
210         }
211     }
212
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;
219             }
220         }
221         return ref;
222     }
223 }