11172bfd5709e5996e0dce2710ebc607133c572c
[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.ContainerSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
23 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
25 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.Module;
27 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
28 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
29
30 public final class PostEntity extends OperationEntity {
31
32     private final DocumentedNode parentNode;
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     public PostEntity(final SchemaNode schema, final String deviceName, final String moduleName,
43             final List<ParameterEntity> parameters, final String refPath, final DocumentedNode parentNode) {
44         super(schema, deviceName, moduleName, parameters, refPath);
45         this.parentNode = parentNode;
46     }
47
48     protected String operation() {
49         return "post";
50     }
51
52     @Nullable String summary() {
53         if (parentNode instanceof Module) {
54             return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), moduleName());
55         }
56         if (parentNode != null) {
57             return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(),
58                 ((DataSchemaNode) parentNode).getQName().getLocalName());
59         }
60         return SUMMARY_TEMPLATE.formatted(HttpMethod.POST, deviceName(), moduleName(), nodeName());
61     }
62
63     @Override
64     void generateResponses(final @NonNull JsonGenerator generator) throws IOException {
65         generator.writeObjectFieldStart(RESPONSES);
66         if (schema() instanceof OperationDefinition rpc) {
67             final var output = rpc.getOutput();
68             final var operationName = rpc.getQName().getLocalName();
69             if (!output.getChildNodes().isEmpty()) {
70                 // TODO: add proper discriminator from DefinitionNames when schemas re-implementation is done
71                 final var ref = COMPONENTS_PREFIX + moduleName() + "_" + operationName + "_output";
72                 generator.writeObjectFieldStart(String.valueOf(OK.getStatusCode()));
73                 generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
74
75                 generator.writeObjectFieldStart(CONTENT);
76                 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_JSON, ref);
77                 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
78                 generator.writeEndObject();
79
80                 generator.writeEndObject();
81
82             } else {
83                 generator.writeObjectFieldStart(String.valueOf(NO_CONTENT.getStatusCode()));
84                 generator.writeStringField(DESCRIPTION, String.format("RPC %s success", operationName));
85                 generator.writeEndObject();
86
87             }
88         } else {
89             generator.writeObjectFieldStart(String.valueOf(CREATED.getStatusCode()));
90             generator.writeStringField(DESCRIPTION, "Created");
91             generator.writeEndObject();
92         }
93         generator.writeEndObject();
94     }
95
96     @Override
97     void generateRequestBody(final @NonNull JsonGenerator generator) throws IOException {
98         generator.writeObjectFieldStart(REQUEST_BODY);
99         if (schema() instanceof OperationDefinition rpc) {
100             final var input = rpc.getInput();
101             final var operationName = rpc.getQName().getLocalName();
102             generator.writeStringField(DESCRIPTION, operationName + INPUT_SUFFIX);
103             generator.writeObjectFieldStart(CONTENT);
104             if (!input.getChildNodes().isEmpty()) {
105                 // TODO: add proper discriminator from DefinitionNames when schemas re-implementation is done
106                 final var ref = COMPONENTS_PREFIX + moduleName() + "_" + operationName + INPUT_SUFFIX;
107                 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
108                 generator.writeObjectFieldStart(SCHEMA);
109                 generator.writeObjectFieldStart("properties");
110                 generator.writeObjectFieldStart(INPUT_KEY);
111                 generator.writeStringField("$ref", ref);
112                 generator.writeStringField(TYPE, OBJECT);
113                 generator.writeEndObject();
114                 generator.writeEndObject();
115                 generator.writeEndObject();
116                 generator.writeEndObject();
117                 generateMediaTypeSchemaRef(generator, MediaType.APPLICATION_XML, ref);
118             } else {
119                 generator.writeObjectFieldStart(MediaType.APPLICATION_JSON);
120                 generator.writeObjectFieldStart(SCHEMA);
121
122                 generator.writeObjectFieldStart(PROPERTIES);
123                 generator.writeObjectFieldStart(INPUT_KEY);
124                 generator.writeStringField(TYPE, OBJECT);
125                 generator.writeEndObject();
126                 generator.writeEndObject();
127
128                 generator.writeStringField(TYPE, OBJECT);
129                 generator.writeEndObject();
130                 generator.writeEndObject();
131
132                 generator.writeObjectFieldStart(MediaType.APPLICATION_XML);
133                 generator.writeObjectFieldStart(SCHEMA);
134
135                 generator.writeObjectFieldStart("xml");
136                 generator.writeStringField(NAME, INPUT_KEY);
137                 generator.writeStringField("namespace", input.getQName().getNamespace().toString());
138                 generator.writeEndObject();
139
140                 generator.writeStringField(TYPE, OBJECT);
141                 generator.writeEndObject();
142                 generator.writeEndObject();
143             }
144             generator.writeEndObject();
145         } else {
146             generator.writeStringField(DESCRIPTION, nodeName());
147             generator.writeObjectFieldStart(CONTENT);
148             final var ref = COMPONENTS_PREFIX + moduleName() + "_" + refPath();
149             //TODO: Remove if and fix this weird logic of top level nodes
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
189     @Override
190     public void generateBasics(@NonNull JsonGenerator generator) throws IOException {
191         final var description = description();
192         if (schema() instanceof OperationDefinition) {
193             generator.writeStringField(DESCRIPTION, description);
194         } else {
195             generator.writeStringField(DESCRIPTION, description + POST_DESCRIPTION);
196         }
197         final var summary = summary();
198         if (summary != null) {
199             generator.writeStringField(SUMMARY, summary);
200         }
201     }
202
203     @Override
204     @Nullable String description() {
205         if (parentNode != null) {
206             return parentNode.getDescription().orElse("");
207         } else {
208             return super.description();
209         }
210     }
211 }