d651560773035c4b4d12c5b21934154b9b08e7b3
[netconf.git] / restconf / restconf-openapi / src / main / java / org / opendaylight / restconf / openapi / model / SchemaEntity.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 java.util.Objects.requireNonNullElse;
12 import static org.opendaylight.restconf.openapi.model.PropertyEntity.isSchemaNodeMandatory;
13
14 import com.fasterxml.jackson.core.JsonGenerator;
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.List;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.restconf.openapi.impl.DefinitionNames;
22 import org.opendaylight.restconf.openapi.impl.SchemasStream;
23 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
24 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
26 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
30 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
31
32 /**
33  * Archetype for a Schema.
34  */
35 public final class SchemaEntity extends OpenApiEntity {
36     private final @NonNull SchemaNode value;
37     private final @NonNull String title;
38     private final @NonNull String discriminator;
39     private final @NonNull String type;
40     private final @NonNull SchemaInferenceStack stack;
41     private final boolean isParentConfig;
42     private final SchemasStream.EntityType entityType;
43     private final @NonNull String parentName;
44     private final @NonNull DefinitionNames definitionNames;
45
46     public SchemaEntity(final @NonNull SchemaNode value, final @NonNull String title, final String discriminator,
47             @NonNull final String type, @NonNull final SchemaInferenceStack context, final String parentName,
48             final boolean isParentConfig, @NonNull final DefinitionNames definitionNames,
49             final SchemasStream.EntityType entityType) {
50         this.value = requireNonNull(value);
51         this.title = requireNonNull(title);
52         this.type = requireNonNull(type);
53         this.stack = requireNonNull(context.copy());
54         this.parentName = requireNonNull(parentName);
55         this.isParentConfig = isParentConfig;
56         this.definitionNames = definitionNames;
57         this.entityType = entityType;
58         this.discriminator = requireNonNullElse(discriminator, "");
59     }
60
61     @Override
62     public void generate(final @NonNull JsonGenerator generator) throws IOException {
63         generator.writeObjectFieldStart(title() + discriminator);
64         generator.writeStringField("title", title());
65         generator.writeStringField("type", type());
66         final var description = description();
67         if (description != null) {
68             generator.writeStringField("description", description);
69         }
70         final var reference = reference();
71         if (reference != null) {
72             generator.writeStringField("$ref", reference);
73         }
74         generateEnum(generator);
75         generateDiscriminator(generator);
76         generateExamples(generator);
77         generateExternalDocs(generator);
78         generateProperties(generator);
79         generateXml(generator);
80         generator.writeEndObject();
81     }
82
83     private @NonNull String title() {
84         return title;
85     }
86
87     private @NonNull String type() {
88         return type;
89     }
90
91     private @Nullable String description() {
92         // FIXME: Ensure the method returns null if the description is not available.
93         return value.getDescription()
94             .orElse(value instanceof InputSchemaNode || value instanceof OutputSchemaNode ? null : "");
95     }
96
97     private @Nullable String reference() {
98         return null;
99     }
100
101     private void generateEnum(final @NonNull JsonGenerator generator) throws IOException {
102         // No-op
103     }
104
105     private void generateRequired(final @NonNull JsonGenerator generator, final List<String> required)
106             throws IOException {
107         if (!required.isEmpty()) {
108             generator.writeArrayFieldStart("required");
109             for (final var req : required) {
110                 generator.writeString(req);
111             }
112             generator.writeEndArray();
113         }
114     }
115
116     private void generateDiscriminator(final @NonNull JsonGenerator generator) throws IOException {
117         // No-op
118     }
119
120     private void generateExamples(final @NonNull JsonGenerator generator) throws IOException {
121         // No-op
122     }
123
124     private void generateExternalDocs(final @NonNull JsonGenerator generator) throws IOException {
125         // No-op
126     }
127
128     private void generateProperties(final @NonNull JsonGenerator generator) throws IOException {
129         final var required = new ArrayList<String>();
130         generator.writeObjectFieldStart("properties");
131         stack.enterSchemaTree(value.getQName());
132         switch (entityType) {
133             case RPC:
134                 for (final var childNode : ((ContainerLike) value).getChildNodes()) {
135                     new PropertyEntity(childNode, generator, stack, required, parentName, isParentConfig,
136                         definitionNames);
137                 }
138                 break;
139             default:
140                 final var childNodes = new HashMap<String, DataSchemaNode>();
141                 for (final var childNode : ((DataNodeContainer) value).getChildNodes()) {
142                     childNodes.put(childNode.getQName().getLocalName(), childNode);
143                 }
144                 for (final var childNode : childNodes.values()) {
145                     if (shouldBeAddedAsProperty(childNode)) {
146                         new PropertyEntity(childNode, generator, stack, required, parentName + "_"
147                             + value.getQName().getLocalName(), ((DataSchemaNode) value).isConfiguration(),
148                             definitionNames);
149                     }
150                 }
151         }
152         stack.exit();
153         generator.writeEndObject();
154         generateRequired(generator, required);
155     }
156
157     private boolean shouldBeAddedAsProperty(final DataSchemaNode childNode) {
158         if (childNode instanceof ContainerSchemaNode) {
159             return childNode.isConfiguration() || isSchemaNodeMandatory(childNode)
160                 || !((DataSchemaNode) value).isConfiguration();
161         }
162         return childNode.isConfiguration() || !((DataSchemaNode) value).isConfiguration();
163     }
164
165     private void generateXml(final @NonNull JsonGenerator generator) throws IOException {
166         generator.writeObjectFieldStart("xml");
167         generator.writeStringField("name", value.getQName().getLocalName());
168         generator.writeStringField("namespace", value.getQName().getNamespace().toString());
169         generator.writeEndObject();
170     }
171 }