74782cf6705c875e9471f3405da9d34b855d2055
[netconf.git] / restconf / restconf-openapi / src / test / java / org / opendaylight / restconf / openapi / SchemaObjectsTest.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;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.mockito.Mockito.mock;
13 import static org.mockito.Mockito.when;
14
15 import java.util.List;
16 import java.util.Optional;
17 import javax.ws.rs.core.UriInfo;
18 import org.junit.BeforeClass;
19 import org.junit.Test;
20 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
21 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
22 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
23 import org.opendaylight.restconf.openapi.impl.MountPointOpenApiGeneratorRFC8040;
24 import org.opendaylight.restconf.openapi.model.MediaTypeObject;
25 import org.opendaylight.restconf.openapi.model.OpenApiObject;
26 import org.opendaylight.restconf.openapi.model.Operation;
27 import org.opendaylight.restconf.openapi.model.Schema;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
31
32 public class SchemaObjectsTest {
33     private static final String HTTP_URL = "http://localhost/path";
34     private static final YangInstanceIdentifier INSTANCE_ID = YangInstanceIdentifier.builder()
35             .node(QName.create("", "nodes"))
36             .node(QName.create("", "node"))
37             .nodeWithKey(QName.create("", "node"), QName.create("" , "id"), "123").build();
38
39     private static OpenApiObject mountPointApi;
40
41     @BeforeClass
42     public static void beforeClass() throws Exception {
43         final var schemaService = mock(DOMSchemaService.class);
44         final var context = YangParserTestUtils.parseYangResourceDirectory("/yang");
45         when(schemaService.getGlobalContext()).thenReturn(context);
46         final UriInfo mockInfo = DocGenTestHelper.createMockUriInfo(HTTP_URL);
47         final DOMMountPointService service = mock(DOMMountPointService.class);
48         final DOMMountPoint mountPoint = mock(DOMMountPoint.class);
49         when(mountPoint.getService(DOMSchemaService.class)).thenReturn(Optional.of(schemaService));
50         when(service.getMountPoint(INSTANCE_ID)).thenReturn(Optional.of(mountPoint));
51         final var openApi = new MountPointOpenApiGeneratorRFC8040(schemaService, service).getMountPointOpenApi();
52         openApi.onMountPointCreated(INSTANCE_ID);
53
54         mountPointApi = openApi.getMountPointApi(mockInfo, 1L, null);
55         assertNotNull("Failed to find MountPoint API", mountPointApi);
56     }
57
58     @Test
59     public void testIfOperationsUseOneSchema() {
60         final var schemas = mountPointApi.components().schemas();
61         for (final var pathsEntry : mountPointApi.paths().entrySet()) {
62             final var path = pathsEntry.getValue();
63             if (path.post() == null || path.put() == null || path.patch() == null || path.delete() == null) {
64                 // skip operational data
65                 continue;
66             }
67             for (final var operation : List.of(path.put(), path.patch(), path.post())) {
68                 final var schema = schemas.get(extractSchemaName(operation));
69                 assertNotNull("Schema for \"" + operation + "\" is missing.", schema);
70             }
71         }
72     }
73
74     /**
75      * Extract schema name used for operation.
76      * <p>
77      * We assume that for all content types of operation (XML, JSON) the same schema is used. We extract its name from
78      * the schema reference used in operation.
79      * </p>
80      * @param operation for which we want to find schema
81      * @return name of the schema used for operation
82      */
83     private static String extractSchemaName(final Operation operation) {
84         // Find distinct schema refs
85         final var references = operation.requestBody().content().values().stream()
86             .map(MediaTypeObject::schema)
87             .map(SchemaObjectsTest::getRef)
88             .distinct()
89             .toList();
90         // Assert all schema refs are same
91         assertEquals("Inconsistent schemas for operation: " + operation.summary(), 1, references.size());
92         return references.get(0).replaceAll("#/components/schemas/", "");
93     }
94
95     private static String getRef(final Schema schema) {
96         final String ref;
97         if (schema.ref() != null) {
98             ref = schema.ref();
99         } else {
100             final var properties = schema.properties();
101             if (properties != null && !properties.isEmpty()) {
102                 final var property = schema.properties().values().iterator().next();
103                 if (property.type().equals("array")) {
104                     ref = property.items().ref();
105                 } else {
106                     ref = property.ref();
107                 }
108             } else {
109                 ref = null;
110             }
111         }
112         return ref;
113     }
114 }