From cc22de6bdf205696d47069cd79b8accf9acfa6f1 Mon Sep 17 00:00:00 2001 From: JvD_Ericsson Date: Mon, 27 Jan 2025 13:56:39 +0000 Subject: [PATCH] TEIV: yang-parser updates Issue-ID: SMO-177 Change-Id: Id24d75f4f80e666e95b450910e9125ae9984286b Signed-off-by: JvD_Ericsson --- .../exposure/schemas/02_postSchemas.groovy | 2 +- .../contracts/schemas/00_getAllSchemas.groovy | 578 ----------- .../contracts/schemas/01_getSchemaContent.groovy | 251 ----- .../contracts/schemas/02_postSchemas.groovy | 1037 -------------------- .../contracts/schemas/03_deleteSchemas.groovy | 66 -- .../parser/findings/ParserFindingType.java | 7 +- .../oran/smo/yangtools/parser/model/YangModel.java | 35 +- .../smo/yangtools/parser/model/schema/Schema.java | 1 + .../model/statements/yang/test/NamespaceTest.java | 59 ++ .../namespace-test/modA_nsA.yang | 7 + .../namespace-test/modA_nsB.yang | 7 + .../namespace-test/modB_nsA.yang | 7 + .../namespace-test/modB_nsB.yang | 7 + 13 files changed, 123 insertions(+), 1941 deletions(-) delete mode 100644 teiv/src/test/resources/contracts/schemas/00_getAllSchemas.groovy delete mode 100644 teiv/src/test/resources/contracts/schemas/01_getSchemaContent.groovy delete mode 100644 teiv/src/test/resources/contracts/schemas/02_postSchemas.groovy delete mode 100644 teiv/src/test/resources/contracts/schemas/03_deleteSchemas.groovy create mode 100644 yang-parser/src/test/java/org/oran/smo/yangtools/parser/model/statements/yang/test/NamespaceTest.java create mode 100644 yang-parser/src/test/resources/model-statements-yang/namespace-test/modA_nsA.yang create mode 100644 yang-parser/src/test/resources/model-statements-yang/namespace-test/modA_nsB.yang create mode 100644 yang-parser/src/test/resources/model-statements-yang/namespace-test/modB_nsA.yang create mode 100644 yang-parser/src/test/resources/model-statements-yang/namespace-test/modB_nsB.yang diff --git a/teiv/src/test/resources/contracts/exposure/schemas/02_postSchemas.groovy b/teiv/src/test/resources/contracts/exposure/schemas/02_postSchemas.groovy index 1823d82..949cc6b 100644 --- a/teiv/src/test/resources/contracts/exposure/schemas/02_postSchemas.groovy +++ b/teiv/src/test/resources/contracts/exposure/schemas/02_postSchemas.groovy @@ -642,7 +642,7 @@ import org.springframework.cloud.contract.spec.Contract p('module o-ran-smo-teiv-ran { ' + ' ' + ' yang-version 1.1; ' + - ' namespace "urn:o-ran-smo-teiv-ran"; ' + + ' namespace "urn:o-ran:smo-teiv-ran"; ' + ' prefix module; ' + ' ' + ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + diff --git a/teiv/src/test/resources/contracts/schemas/00_getAllSchemas.groovy b/teiv/src/test/resources/contracts/schemas/00_getAllSchemas.groovy deleted file mode 100644 index 729b26b..0000000 --- a/teiv/src/test/resources/contracts/schemas/00_getAllSchemas.groovy +++ /dev/null @@ -1,578 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Ericsson - * Modifications Copyright (C) 2024 OpenInfra Foundation Europe - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package contracts.schemas - -import org.springframework.cloud.contract.spec.Contract - -[ - Contract.make { - description "SUCCESS - 200: Get a list of all schemas" - request { - method GET() - url("/topology-inventory/v1alpha11/schemas") - } - response { - status OK() - headers { - contentType('application/json') - } - body('''{ - "items": [ - { - "name": "_3gpp-common-yang-extensions", - "domain": "", - "revision": "2019-06-23", - "content": { - "href": "/schemas/_3gpp-common-yang-extensions/content" - } - }, - { - "name": "_3gpp-common-yang-types", - "domain": "", - "revision": "2022-07-26", - "content": { - "href": "/schemas/_3gpp-common-yang-types/content" - } - }, - { - "name": "ietf-geo-location", - "domain": "", - "revision": "2022-02-11", - "content": { - "href": "/schemas/ietf-geo-location/content" - } - }, - { - "name": "ietf-inet-types", - "domain": "", - "revision": "2013-07-15", - "content": { - "href": "/schemas/ietf-inet-types/content" - } - }, - { - "name": "ietf-yang-types", - "domain": "", - "revision": "2013-07-15", - "content": { - "href": "/schemas/ietf-yang-types/content" - } - }, - { - "name": "o-ran-smo-teiv-cloud", - "domain": "CLOUD", - "revision": "2024-10-04", - "content": { - "href": "/schemas/o-ran-smo-teiv-cloud/content" - } - }, - { - "name": "o-ran-smo-teiv-common-yang-extensions", - "domain": "", - "revision": "2024-05-24", - "content": { - "href": "/schemas/o-ran-smo-teiv-common-yang-extensions/content" - } - }, - { - "name": "o-ran-smo-teiv-common-yang-types", - "domain": "", - "revision": "2024-10-04", - "content": { - "href": "/schemas/o-ran-smo-teiv-common-yang-types/content" - } - }, - { - "name": "o-ran-smo-teiv-equipment", - "domain": "EQUIPMENT", - "revision": "2024-10-21", - "content": { - "href": "/schemas/o-ran-smo-teiv-equipment/content" - } - }, - { - "name": "o-ran-smo-teiv-oam", - "domain": "OAM", - "revision": "2024-10-04", - "content": { - "href": "/schemas/o-ran-smo-teiv-oam/content" - } - }, - { - "name": "o-ran-smo-teiv-ran", - "domain": "RAN", - "revision": "2024-10-08", - "content": { - "href": "/schemas/o-ran-smo-teiv-ran/content" - } - }, - { - "name": "o-ran-smo-teiv-rel-cloud-ran", - "domain": "REL_CLOUD_RAN", - "revision": "2024-10-04", - "content": { - "href": "/schemas/o-ran-smo-teiv-rel-cloud-ran/content" - } - }, - { - "name": "o-ran-smo-teiv-rel-equipment-ran", - "domain": "REL_EQUIPMENT_RAN", - "revision": "2024-10-08", - "content": { - "href": "/schemas/o-ran-smo-teiv-rel-equipment-ran/content" - } - }, - { - "name": "o-ran-smo-teiv-rel-oam-cloud", - "domain": "REL_OAM_CLOUD", - "revision": "2024-10-04", - "content": { - "href": "/schemas/o-ran-smo-teiv-rel-oam-cloud/content" - } - }, - { - "name": "o-ran-smo-teiv-rel-oam-ran", - "domain": "REL_OAM_RAN", - "revision": "2024-10-04", - "content": { - "href": "/schemas/o-ran-smo-teiv-rel-oam-ran/content" - } - }, - { - "name": "test-existing-rapp-module", - "domain": "", - "revision": "2024-05-02", - "content": { - "href": "/schemas/test-existing-rapp-module/content" - } - }, - { - "name": "test-module-for-deletion", - "domain": "", - "revision": "2024-05-02", - "content": { - "href": "/schemas/test-module-for-deletion/content" - } - }, - { - "name":"test-module-in-deleting-state", - "domain":"", - "revision":"2024-05-02", - "content":{ - "href":"/schemas/test-module-in-deleting-state/content" - } - }, - { - "name":"module-rapp-module", - "domain":"", - "revision":"2024-05-01", - "content":{ - "href":"/schemas/module-rapp-module/content" - } - }, - { - "name": "test-built-in-module", - "domain": "TEST", - "revision": "2024-05-24", - "content": { - "href": "/schemas/test-built-in-module/content" - } - }, - { - "name": "test-app-module", - "domain": "", - "revision": "2024-05-24", - "content": { - "href": "/schemas/test-app-module/content" - } - }, - { - "name": "test-app-for-deletion-module", - "domain": "", - "revision": "2024-05-24", - "content": { - "href": "/schemas/test-app-for-deletion-module/content" - } - } - ], - "self": { - "href": "/schemas?offset=0&limit=500" - }, - "first": { - "href": "/schemas?offset=0&limit=500" - }, - "prev": { - "href": "/schemas?offset=0&limit=500" - }, - "next": { - "href": "/schemas?offset=0&limit=500" - }, - "last": { - "href": "/schemas?offset=0&limit=500" - }, - "totalCount": 22 - }''') - bodyMatchers { - jsonPath('$.items', byType { - occurrence(22) - }) - jsonPath('$.items[0].name', byEquality()) - jsonPath('$.items[0].domain', byEquality()) - jsonPath('$.items[0].revision', byEquality()) - jsonPath('$.items[0].content.href', byEquality()) - jsonPath('$.items[1].name', byEquality()) - jsonPath('$.items[1].domain', byEquality()) - jsonPath('$.items[1].revision', byEquality()) - jsonPath('$.items[1].content.href', byEquality()) - jsonPath('$.items[2].name', byEquality()) - jsonPath('$.items[2].domain', byEquality()) - jsonPath('$.items[2].revision', byEquality()) - jsonPath('$.items[2].content.href', byEquality()) - jsonPath('$.items[3].name', byEquality()) - jsonPath('$.items[3].domain', byEquality()) - jsonPath('$.items[3].revision', byEquality()) - jsonPath('$.items[3].content.href', byEquality()) - jsonPath('$.items[4].name', byEquality()) - jsonPath('$.items[4].domain', byEquality()) - jsonPath('$.items[4].revision', byEquality()) - jsonPath('$.items[4].content.href', byEquality()) - jsonPath('$.items[5].name', byEquality()) - jsonPath('$.items[5].domain', byEquality()) - jsonPath('$.items[5].revision', byEquality()) - jsonPath('$.items[5].content.href', byEquality()) - jsonPath('$.items[6].name', byEquality()) - jsonPath('$.items[6].domain', byEquality()) - jsonPath('$.items[6].revision', byEquality()) - jsonPath('$.items[6].content.href', byEquality()) - jsonPath('$.items[7].name', byEquality()) - jsonPath('$.items[7].domain', byEquality()) - jsonPath('$.items[7].revision', byEquality()) - jsonPath('$.items[7].content.href', byEquality()) - jsonPath('$.items[8].name', byEquality()) - jsonPath('$.items[8].domain', byEquality()) - jsonPath('$.items[8].revision', byEquality()) - jsonPath('$.items[8].content.href', byEquality()) - jsonPath('$.items[9].name', byEquality()) - jsonPath('$.items[9].domain', byEquality()) - jsonPath('$.items[9].revision', byEquality()) - jsonPath('$.items[9].content.href', byEquality()) - jsonPath('$.items[10].name', byEquality()) - jsonPath('$.items[10].domain', byEquality()) - jsonPath('$.items[10].revision', byEquality()) - jsonPath('$.items[10].content.href', byEquality()) - jsonPath('$.items[11].name', byEquality()) - jsonPath('$.items[11].domain', byEquality()) - jsonPath('$.items[11].revision', byEquality()) - jsonPath('$.items[11].content.href', byEquality()) - jsonPath('$.items[12].name', byEquality()) - jsonPath('$.items[12].domain', byEquality()) - jsonPath('$.items[12].revision', byEquality()) - jsonPath('$.items[12].content.href', byEquality()) - jsonPath('$.items[13].name', byEquality()) - jsonPath('$.items[13].domain', byEquality()) - jsonPath('$.items[13].revision', byEquality()) - jsonPath('$.items[13].content.href', byEquality()) - jsonPath('$.items[14].name', byEquality()) - jsonPath('$.items[14].domain', byEquality()) - jsonPath('$.items[14].revision', byEquality()) - jsonPath('$.items[14].content.href', byEquality()) - jsonPath('$.items[16].name', byEquality()) - jsonPath('$.items[16].domain', byEquality()) - jsonPath('$.items[16].revision', byEquality()) - jsonPath('$.items[16].content.href', byEquality()) - jsonPath('$.items[17].name', byEquality()) - jsonPath('$.items[17].domain', byEquality()) - jsonPath('$.items[17].revision', byEquality()) - jsonPath('$.items[17].content.href', byEquality()) - jsonPath('$.items[18].name', byEquality()) - jsonPath('$.items[18].domain', byEquality()) - jsonPath('$.items[18].revision', byEquality()) - jsonPath('$.items[18].content.href', byEquality()) - jsonPath('$.items[19].name', byEquality()) - jsonPath('$.items[19].domain', byEquality()) - jsonPath('$.items[19].revision', byEquality()) - jsonPath('$.items[19].content.href', byEquality()) - jsonPath('$.items[20].name', byEquality()) - jsonPath('$.items[20].domain', byEquality()) - jsonPath('$.items[20].revision', byEquality()) - jsonPath('$.items[20].content.href', byEquality()) - jsonPath('$.items[21].name', byEquality()) - jsonPath('$.items[21].domain', byEquality()) - jsonPath('$.items[21].revision', byEquality()) - jsonPath('$.items[21].content.href', byEquality()) - } - } - }, - Contract.make { - description "SUCCESS - 200: Get a list of all schemas with offset as 0 and limit as 1." - request { - method GET() - url("/topology-inventory/v1alpha11/schemas?offset=0&limit=1") - } - response { - status OK() - headers { - contentType('application/json') - } - body('''{ - "items": [ - { - "name": "_3gpp-common-yang-extensions", - "domain": "", - "revision": "2019-06-23", - "content": { - "href": "/schemas/_3gpp-common-yang-extensions/content" - } - } - ], - "self": { - "href": "/schemas?offset=0&limit=1" - }, - "first": { - "href": "/schemas?offset=0&limit=1" - }, - "prev": { - "href": "/schemas?offset=0&limit=1" - }, - "next": { - "href": "/schemas?offset=1&limit=1" - }, - "last": { - "href": "/schemas?offset=21&limit=1" - }, - "totalCount": 22 - }''') - } - }, - Contract.make { - description "SUCCESS - 200: Get a list of all schemas with offset as 3 and limit as 3." - request { - method GET() - url("/topology-inventory/v1alpha11/schemas?offset=3&limit=3") - } - response { - status OK() - headers { - contentType('application/json') - } - body('''{ - "items": [ - { - "name": "ietf-inet-types", - "domain": "", - "revision": "2013-07-15", - "content": { - "href": "/schemas/ietf-inet-types/content" - } - }, - { - "name": "ietf-yang-types", - "domain": "", - "revision": "2013-07-15", - "content": { - "href": "/schemas/ietf-yang-types/content" - } - }, - { - "name": "o-ran-smo-teiv-cloud", - "domain": "CLOUD", - "revision": "2024-10-04", - "content": { - "href": "/schemas/o-ran-smo-teiv-cloud/content" - } - } - ], - "self": { - "href": "/schemas?offset=3&limit=3" - }, - "first": { - "href": "/schemas?offset=0&limit=3" - }, - "prev": { - "href": "/schemas?offset=0&limit=3" - }, - "next": { - "href": "/schemas?offset=6&limit=3" - }, - "last": { - "href": "/schemas?offset=21&limit=3" - }, - "totalCount": 22 - }''') - } - }, - Contract.make { - description "SUCCESS - 200: Get a list of all schemas with RAN domain" - request { - method GET() - url("/topology-inventory/v1alpha11/schemas?domain=RAN&offset=0&limit=100") - } - response { - status OK() - headers { - contentType('application/json') - } - body('''{ - "items": [ - { - "name": "o-ran-smo-teiv-ran", - "domain": "RAN", - "revision": "2024-10-08", - "content": { - "href": "/schemas/o-ran-smo-teiv-ran/content" - } - } - ], - "self": { - "href": "/schemas?offset=0&limit=100&domain=RAN" - }, - "first": { - "href": "/schemas?offset=0&limit=100&domain=RAN" - }, - "prev": { - "href": "/schemas?offset=0&limit=100&domain=RAN" - }, - "next": { - "href": "/schemas?offset=0&limit=100&domain=RAN" - }, - "last": { - "href": "/schemas?offset=0&limit=100&domain=RAN" - }, - "totalCount": 1 - }''') - } - }, - Contract.make { - description "SUCCESS - 200: Get a list of all schemas with domain name containing RAN" - request { - method GET() - url("/topology-inventory/v1alpha11/schemas?domain=.*RAN.*") - } - response { - status OK() - headers { - contentType('application/json') - } - body('''{ - "items": [ - { - "name": "o-ran-smo-teiv-rel-oam-ran", - "domain": "REL_OAM_RAN", - "revision": "2024-10-04", - "content": { - "href": "/schemas/o-ran-smo-teiv-rel-oam-ran/content" - } - }, - { - "name": "o-ran-smo-teiv-rel-cloud-ran", - "domain": "REL_CLOUD_RAN", - "revision": "2024-10-04", - "content": { - "href": "/schemas/o-ran-smo-teiv-rel-cloud-ran/content" - } - }, - { - "name": "o-ran-smo-teiv-ran", - "domain": "RAN", - "revision": "2024-10-08", - "content": { - "href": "/schemas/o-ran-smo-teiv-ran/content" - } - }, - { - "name": "o-ran-smo-teiv-rel-equipment-ran", - "domain": "REL_EQUIPMENT_RAN", - "revision": "2024-10-04", - "content": { - "href": "/schemas/o-ran-smo-teiv-rel-equipment-ran/content" - } - } - ], - "self": { - "href": "/schemas?offset=0&limit=500&domain=.*RAN.*" - }, - "first": { - "href": "/schemas?offset=0&limit=500&domain=.*RAN.*" - }, - "prev": { - "href": "/schemas?offset=0&limit=500&domain=.*RAN.*" - }, - "next": { - "href": "/schemas?offset=0&limit=500&domain=.*RAN.*" - }, - "last": { - "href": "/schemas?offset=0&limit=500&domain=.*RAN.*" - }, - "totalCount": 4 - }''') - } - }, - Contract.make { - description "SUCCESS - 200: Get a list of all schemas with invalid domain" - request { - method GET() - url("/topology-inventory/v1alpha11/schemas?domain=INVALID") - } - response { - status OK() - headers { - contentType('application/json') - } - body('''{ - "items": [], - "self": { - "href": "/schemas?offset=0&limit=500&domain=INVALID" - }, - "first": { - "href": "/schemas?offset=0&limit=500&domain=INVALID" - }, - "prev": { - "href": "/schemas?offset=0&limit=500&domain=INVALID" - }, - "next": { - "href": "/schemas?offset=0&limit=500&domain=INVALID" - }, - "last": { - "href": "/schemas?offset=0&limit=500&domain=INVALID" - }, - "totalCount": 0 - }''') - } - }, - Contract.make { - description "ERROR - 400: Get a list of all schemas with offset greater than totalCount" - request { - method GET() - url("/topology-inventory/v1alpha11/schemas?domain=RAN.*&offset=100") - } - response { - status BAD_REQUEST() - headers { - contentType('application/json') - } - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid Value", - "details": "Offset cannot be larger than 0" - }''') - } - } -] diff --git a/teiv/src/test/resources/contracts/schemas/01_getSchemaContent.groovy b/teiv/src/test/resources/contracts/schemas/01_getSchemaContent.groovy deleted file mode 100644 index 2a6ba12..0000000 --- a/teiv/src/test/resources/contracts/schemas/01_getSchemaContent.groovy +++ /dev/null @@ -1,251 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Ericsson - * Modifications Copyright (C) 2024 OpenInfra Foundation Europe - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package contracts.schemas - -import org.springframework.cloud.contract.spec.Contract - -[ - Contract.make { - description "SUCCESS - 200: Get schema with name o-ran-smo-teiv-rel-oam-ran" - request { - method GET() - url("/topology-inventory/v1alpha11/schemas/o-ran-smo-teiv-rel-oam-ran/content") - } - response { - status OK() - body("module o-ran-smo-teiv-rel-oam-ran {\n" + - " yang-version 1.1;\n" + - " namespace \"urn:o-ran:smo-teiv-rel-oam-ran\";\n" + - " prefix or-teiv-rel-oamran;\n" + - "\n" + - " import o-ran-smo-teiv-common-yang-types { prefix or-teiv-types; }\n" + - "\n" + - " import o-ran-smo-teiv-common-yang-extensions { prefix or-teiv-yext; }\n" + - "\n" + - " import o-ran-smo-teiv-oam { prefix or-teiv-oam; }\n" + - "\n" + - " import o-ran-smo-teiv-ran { prefix or-teiv-ran; }\n" + - "\n" + - " organization \"ORAN\";\n" + - " contact \"The Authors\";\n" + - " description\n" + - " \"RAN O&M to Logical topology model.\n" + - "\n" + - " This model contains the RAN O&M to Logical topology relations\n" + - "\n" + - " Copyright (C) 2024 Ericsson\n" + - " Modifications Copyright (C) 2024 OpenInfra Foundation Europe\n" + - "\n" + - " Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\n" + - " you may not use this file except in compliance with the License.\n" + - " You may obtain a copy of the License at\n" + - "\n" + - " http://www.apache.org/licenses/LICENSE-2.0\n" + - "\n" + - " Unless required by applicable law or agreed to in writing, software\n" + - " distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\n" + - " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + - " See the License for the specific language governing permissions and\n" + - " limitations under the License.\n" + - "\n" + - " SPDX-License-Identifier: Apache-2.0\";\n" + - "\n" + - " revision \"2024-10-04\" {\n" + - " description \"Added grouping, Origin_Relationship_Mapping_Grp to the topology object.\";\n" + - " or-teiv-yext:label 0.5.0;\n" + - " }\n" + - "\n" + - " revision \"2024-07-15\" {\n" + - " description \"This revision aligns O-RAN Work Group 10 Stage 2 Specification (O-RAN.WG10.TE&IV-CIMI.0-R004.v02.00)\";\n" + - " or-teiv-yext:label 0.4.0;\n" + - " }\n" + - "\n" + - " revision \"2024-05-24\" {\n" + - " description \"Initial revision.\";\n" + - " or-teiv-yext:label 0.3.0;\n" + - " }\n" + - "\n" + - " or-teiv-yext:domain REL_OAM_RAN;\n" + - "\n" + - " or-teiv-yext:biDirectionalTopologyRelationship MANAGEDELEMENT_MANAGES_ODUFUNCTION { // 1 to 0..n\n" + - "\n" + - " description\n" + - " \"The aSide of this relationship is an instance of the ManagedElement type.\n" + - " The bSide of this relationship is an instance of the ODUFunction type.\n" + - " The ManagedElement represents the node that manages the ODUFunction.\n" + - " A ManagedElement instance can manage many ODUFunctions.\n" + - " An ODUFunction instance must be managed by one ManagedElement.\n" + - " \";\n" + - "\n" + - " uses or-teiv-types:Top_Grp_Type;\n" + - " uses or-teiv-types:Origin_Relationship_Mapping_Grp;\n" + - " key id;\n" + - "\n" + - " leaf-list managed-oduFunction {\n" + - " description \"Managed Element manages O-DU Function.\";\n" + - " or-teiv-yext:aSide or-teiv-oam:ManagedElement;\n" + - " type instance-identifier;\n" + - " }\n" + - "\n" + - " leaf managed-by-managedElement {\n" + - " description \"O-DU Function managed by Managed Element.\";\n" + - " or-teiv-yext:bSide or-teiv-ran:ODUFunction;\n" + - " type instance-identifier;\n" + - " mandatory true;\n" + - " }\n" + - " }\n" + - "\n" + - " or-teiv-yext:biDirectionalTopologyRelationship MANAGEDELEMENT_MANAGES_OCUCPFUNCTION { // 1 to 0..n\n" + - "\n" + - " description\n" + - " \"The aSide of this relationship is an instance of the ManagedElement type.\n" + - " The bSide of this relationship is an instance of the OCUCPFunction type.\n" + - " The ManagedElement represents the node that manages the OCUCPFunction.\n" + - " A ManagedElement instance can manage many OCUCPFunctions.\n" + - " An OCUCPFunction instance must be managed by one ManagedElement.\n" + - " \";\n" + - "\n" + - " uses or-teiv-types:Top_Grp_Type;\n" + - " uses or-teiv-types:Origin_Relationship_Mapping_Grp;\n" + - " key id;\n" + - "\n" + - " leaf-list managed-ocucpFunction {\n" + - " description \"Managed Element manages O-CU-CP Function.\";\n" + - " or-teiv-yext:aSide or-teiv-oam:ManagedElement;\n" + - " type instance-identifier;\n" + - " }\n" + - "\n" + - " leaf managed-by-managedElement {\n" + - " description \"O-CU-CP Function managed by Managed Element.\";\n" + - " or-teiv-yext:bSide or-teiv-ran:OCUCPFunction;\n" + - " type instance-identifier;\n" + - " mandatory true;\n" + - " }\n" + - " }\n" + - "\n" + - " or-teiv-yext:biDirectionalTopologyRelationship MANAGEDELEMENT_MANAGES_OCUUPFUNCTION { // 1 to 0..n\n" + - "\n" + - " description\n" + - " \"The aSide of this relationship is an instance of the ManagedElement type.\n" + - " The bSide of this relationship is an instance of the OCUUPFunction type.\n" + - " The ManagedElement represents the node that manages the OCUUPFunction.\n" + - " A ManagedElement instance can manage many OCUUPFunctions.\n" + - " An OCUUPFunction instance must be managed by one ManagedElement.\n" + - " \";\n" + - "\n" + - " uses or-teiv-types:Top_Grp_Type;\n" + - " uses or-teiv-types:Origin_Relationship_Mapping_Grp;\n" + - " key id;\n" + - "\n" + - " leaf-list managed-ocuupFunction {\n" + - " description \"Managed Element manages O-CU-UP Function.\";\n" + - " or-teiv-yext:aSide or-teiv-oam:ManagedElement;\n" + - " type instance-identifier;\n" + - " }\n" + - "\n" + - " leaf managed-by-managedElement {\n" + - " description \"O-CU-UP Function managed by Managed Element.\";\n" + - " or-teiv-yext:bSide or-teiv-ran:OCUUPFunction;\n" + - " type instance-identifier;\n" + - " mandatory true;\n" + - " }\n" + - " }\n" + - "\n" + - " or-teiv-yext:biDirectionalTopologyRelationship MANAGEDELEMENT_MANAGES_ORUFUNCTION { // 1 to 0..n\n" + - "\n" + - " description\n" + - " \"The aSide of this relationship is an instance of the ManagedElement type.\n" + - " The bSide of this relationship is an instance of the ORUFunction type.\n" + - " The ManagedElement represents the node that manages the ORUFunction.\n" + - " A ManagedElement instance can manage many ORUFunction.\n" + - " An ORUFunction instance must be managed by one ManagedElement.\n" + - " \";\n" + - "\n" + - " uses or-teiv-types:Top_Grp_Type;\n" + - " uses or-teiv-types:Origin_Relationship_Mapping_Grp;\n" + - " key id;\n" + - "\n" + - " leaf-list managed-oruFunction {\n" + - " description \"Managed Element manages O-RU Function.\";\n" + - " or-teiv-yext:aSide or-teiv-oam:ManagedElement;\n" + - " type instance-identifier;\n" + - " }\n" + - "\n" + - " leaf managed-by-managedElement {\n" + - " description \"O-RU Function managed by Managed Element.\";\n" + - " or-teiv-yext:bSide or-teiv-ran:ORUFunction;\n" + - " type instance-identifier;\n" + - " mandatory true;\n" + - " }\n" + - " }\n" + - "\n" + - " or-teiv-yext:biDirectionalTopologyRelationship MANAGEDELEMENT_MANAGES_NEARRTRICFUNCTION { // 1 to 0..n\n" + - "\n" + - " description\n" + - " \"The aSide of this relationship is an instance of the ManagedElement type.\n" + - " The bSide of this relationship is an instance of the NearRTRICFunction type.\n" + - " The ManagedElement represents the node that manages the NearRTRICFunction.\n" + - " A ManagedElement instance can manage many NearRTRICFunction.\n" + - " An NearRTRICFunction instance must be managed by one ManagedElement.\n" + - " \";\n" + - "\n" + - " uses or-teiv-types:Top_Grp_Type;\n" + - " uses or-teiv-types:Origin_Relationship_Mapping_Grp;\n" + - " key id;\n" + - "\n" + - " leaf-list managed-nearRTRICFunction {\n" + - " description \"Managed Element manages Near RT RIC Function.\";\n" + - " or-teiv-yext:aSide or-teiv-oam:ManagedElement;\n" + - " type instance-identifier;\n" + - " }\n" + - "\n" + - " leaf managed-by-managedElement {\n" + - " description \"Near RT RIC Function managed by Managed Element.\";\n" + - " or-teiv-yext:bSide or-teiv-ran:NearRTRICFunction;\n" + - " type instance-identifier;\n" + - " mandatory true;\n" + - " }\n" + - " }\n" + - "}") - headers { - contentType('text/plain') - } - } - }, - Contract.make { - description "ERROR - 400: Get schema content with invalid name invalid" - request { - method GET() - url("/topology-inventory/v1alpha11/schemas/invalid/content") - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid schema name", - "details": "Invalid schema name: invalid" - }''') - headers { - contentType('application/json') - } - } - } -] diff --git a/teiv/src/test/resources/contracts/schemas/02_postSchemas.groovy b/teiv/src/test/resources/contracts/schemas/02_postSchemas.groovy deleted file mode 100644 index b2506ec..0000000 --- a/teiv/src/test/resources/contracts/schemas/02_postSchemas.groovy +++ /dev/null @@ -1,1037 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Ericsson - * Modifications Copyright (C) 2024 OpenInfra Foundation Europe - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package contracts.schemas - -import org.springframework.cloud.contract.spec.Contract - -[ - Contract.make { - description "SUCCESS - 201: Create a new classifiers + decorators schema with name module-rapp-model1" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model1 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model1"; ' + - ' prefix model1; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /model:decorators { ' + - ' leaf urban { ' + - ' type string; ' + - ' } ' + - ' leaf rural { ' + - ' type boolean; ' + - ' } ' + - ' leaf weekend { ' + - ' type uint32; ' + - ' } ' + - ' } ' + - ' ' + - ' identity geo-classifier { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - ' identity classifierTest1 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest2 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest3 { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - '}')))) - } - response { - status CREATED() - } - }, - Contract.make { - description "SUCCESS - 201: Create a new decorators without classifier schema with name module-rapp-model2" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model2 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model2"; ' + - ' prefix model2; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /model:decorators { ' + - ' leaf urban { ' + - ' type string; ' + - ' } ' + - ' leaf rural { ' + - ' type boolean; ' + - ' } ' + - ' leaf weekend { ' + - ' type uint32; ' + - ' } ' + - ' } ' + - ' ' + - '}')))) - } - response { - status CREATED() - } - }, - Contract.make { - description "SUCCESS - 201: Create a new classifiers without decorator schema with name module-rapp-model3" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model3 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model3"; ' + - ' prefix model3; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' identity classifierTest1 { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - ' identity classifierTest2 { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - ' identity classifierTest3 { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - '}')))) - } - response { - status CREATED() - } - }, - Contract.make { - description "ERROR - 400: Create a new classifiers and decorators schema, exception thrown due to wrong inheritance for the classifiers" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model4 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model4"; ' + - ' prefix model4; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /model:decorators { ' + - ' leaf urban { ' + - ' type string; ' + - ' } ' + - ' leaf rural { ' + - ' type boolean; ' + - ' } ' + - ' leaf weekend { ' + - ' type uint32; ' + - ' } ' + - ' } ' + - ' ' + - ' identity classifierTest1 { ' + - ' base model-test; ' + - ' } ' + - ' ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid schema name", - "details": "Invalid schema name: Invalid classifier classifierTest1" - }''') - } - }, - Contract.make { - description "ERROR - 400: Create a new classifiers and decorators schema, exception thrown due to wrong inheritance for the classifiers" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model5 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model5"; ' + - ' prefix model5; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /model:decorators { ' + - ' leaf test1 { ' + - ' type string; ' + - ' } ' + - ' leaf test2 { ' + - ' type boolean; ' + - ' } ' + - ' leaf test3 { ' + - ' type uint32; ' + - ' } ' + - ' } ' + - ' ' + - ' identity geo-classifier { ' + - ' base wrong-classifier; ' + - ' } ' + - ' ' + - ' identity urban { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity rural { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid schema name", - "details": "Invalid schema name: Invalid classifier geo-classifier" - }''') - } - }, - Contract.make { - description "ERROR - 400: Create a new classifiers and decorators schema, exception thrown due to wrong inheritance for the classifier" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model6 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model6"; ' + - ' prefix model6; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /model:decorators { ' + - ' leaf urban { ' + - ' type string; ' + - ' } ' + - ' leaf rural { ' + - ' type boolean; ' + - ' } ' + - ' leaf weekend { ' + - ' type uint32; ' + - ' } ' + - ' } ' + - ' ' + - ' identity classifierTest1 { ' + - ' base model-test; ' + - ' } ' + - ' ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid schema name", - "details": "Invalid schema name: Invalid classifier classifierTest1" - }''') - } - }, - Contract.make { - description "ERROR - 400: Create a new classifier and decorator schema with already existing schema name" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module test-app-module { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:test-app-module"; ' + - ' prefix module; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /model:decorators { ' + - ' leaf urban { ' + - ' type string; ' + - ' } ' + - ' leaf rural { ' + - ' type boolean; ' + - ' } ' + - ' leaf weekend { ' + - ' type uint32; ' + - ' } ' + - ' } ' + - ' ' + - ' identity geo-classifier { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - ' identity classifierTest1 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest2 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest3 { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: Schema already exists" - }''') - } - }, - Contract.make { - description "ERROR - 400: Invalid file type" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.json')), - contentType: p('application/json'), - content: $(c(regex(nonEmpty())), - p('{"sample1":"test", "sample2":99}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: Invalid file" - }''') - } - }, - Contract.make { - description "ERROR - 400: Empty file" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty()))))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: Missing content at the beginning of the document." - }''') - } - }, - Contract.make { - description "ERROR - 400: Invalid leaf 1" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-x { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-module-x"; ' + - ' prefix module-x; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix test; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-06-10" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /test:decorators { ' + - ' leaf select*fromocucpfunction { ' + - ' type string; ' + - ' } ' + - ' leaf vendor { ' + - ' type string; ' + - ' } ' + - ' } ' + - ' ' + - ' identity Outdoor { ' + - ' base test:classifiers; ' + - ' } ' + - ' ' + - ' identity Rural { ' + - ' base test:classifiers; ' + - ' } ' + - ' ' + - ' identity Weekend { ' + - ' base test:classifiers; ' + - ' } ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: 'select*fromocucpfunction' is not a valid YANG identifier." - }''') - } - }, - Contract.make { - description "ERROR - 400: Invalid leaf 2" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-x2 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-module-x2"; ' + - ' prefix module-x2; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix test; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-06-10" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /test:decorators { ' + - ' leaf location { ' + - ' type string; ' + - ' } ' + - ' leaf vendor { ' + - ' type string; ' + - ' } ' + - ' } ' + - ' ' + - ' identity UPDATEties_model.module_referenceSETstatusDELETINGWHEREnameodu-function-model { ' + - ' base test:classifiers; ' + - ' } ' + - ' ' + - ' identity Rural { ' + - ' base test:classifiers; ' + - ' } ' + - ' ' + - ' identity Weekend { ' + - ' base test:classifiers; ' + - ' } ' + - ' ' + - '}}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: Unexpected content at end of document. Check curly braces balance throughout document." - }''') - } - }, - Contract.make { - description "ERROR - 400: Create a new classifier and decorator schema without any given decorator or classifier" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model7 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model7"; ' + - ' prefix model7; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /model:decorators { ' + - ' ' + - ' } ' + - ' ' + - ' identity classifierTest1 { ' + - ' base test-classifier; ' + - ' } ' + - ' ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: Encountered '{}', which does nothing. Replace with ';' or un-comment the contents." - }''') - } - }, - Contract.make { - description "ERROR - 400: Create a new classifier and decorator schema without any given decorator or classifier 2" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-module8 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-module8"; ' + - ' prefix module8; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: Invalid schema" - }''') - } - }, - Contract.make { - description "ERROR - 400: Create a new classifier and decorator schema missing leaf type for the decorators" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model9 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model9"; ' + - ' prefix model9; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /model:decorators { ' + - ' leaf urban { ' + - ' ' + - ' } ' + - ' leaf rural { ' + - ' type boolean; ' + - ' } ' + - ' leaf weekend { ' + - ' type uint32; ' + - ' } ' + - ' } ' + - ' ' + - ' identity geo-classifier { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - ' identity classifierTest1 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest2 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest3 { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: Encountered '{}', which does nothing. Replace with ';' or un-comment the contents." - }''') - } - }, - Contract.make { - description "ERROR - 400: Create a new classifier and decorator schema with wrong decorator type" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model10 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model10"; ' + - ' prefix model10; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /model:decorators { ' + - ' leaf urban { ' + - ' type wrong; ' + - ' } ' + - ' leaf rural { ' + - ' type boolean; ' + - ' } ' + - ' leaf weekend { ' + - ' type uint32; ' + - ' } ' + - ' } ' + - ' ' + - ' identity geo-classifier { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - ' identity classifierTest1 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest2 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest3 { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: Cannot resolve typedef 'wrong'." - }''') - } - }, - Contract.make { - description "ERROR - 400: Create a new classifier and decorator schema with wrong syntax (missing imports)" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model11 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model11"; ' + - ' prefix model11; ' + - ' ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' } ' + - ' ' + - ' identity geo-classifier { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: Invalid schema" - }''') - } - }, - Contract.make { - description "ERROR - 400: Create a new classifier and decorator schema with wrong syntax (missing revision)" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model12 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model12"; ' + - ' prefix model12; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' augment /model:decorators { ' + - ' leaf urban { ' + - ' type string; ' + - ' } ' + - ' leaf rural { ' + - ' type boolean; ' + - ' } ' + - ' leaf weekend { ' + - ' type uint32; ' + - ' } ' + - ' } ' + - ' ' + - ' identity geo-classifier { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - ' identity classifierTest1 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest2 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest3 { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: (Sub-)Module does not have a 'revision' statement." - }''') - } - }, - Contract.make { - description "ERROR - 400: Create a new classifier and decorator schema with wrong syntax (missing yang version, namespace, prefix)" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-model13 { ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix model; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /model:decorators { ' + - ' leaf urban { ' + - ' type string; ' + - ' } ' + - ' leaf rural { ' + - ' type boolean; ' + - ' } ' + - ' leaf weekend { ' + - ' type uint32; ' + - ' } ' + - ' } ' + - ' ' + - ' identity geo-classifier { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - ' identity classifierTest1 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest2 { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity classifierTest3 { ' + - ' base model:classifiers; ' + - ' } ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: Invalid schema" - }''') - } - }, - Contract.make { - description "ERROR - 400: Create a new classifier and decorator schema with wrong syntax (prefix is different then the inheritances)" - request { - method POST() - url("/topology-inventory/v1alpha11/schemas") - headers { - contentType(multipartFormData()) - } - multipart( - file: named( - name: $(c(regex(nonEmpty())), p('file.yang')), - contentType: p('application/yang'), - content: $(c(regex(nonEmpty())), - p('module module-rapp-module14 { ' + - ' ' + - ' yang-version 1.1; ' + - ' namespace "urn:module-rapp-model14"; ' + - ' prefix module14; ' + - ' ' + - ' import o-ran-smo-teiv-common-yang-types { prefix testModel; } ' + - ' import o-ran-smo-teiv-common-yang-extensions {prefix or-teiv-yext; } ' + - ' ' + - ' revision "2024-05-08" { ' + - ' description ' + - ' "Initial revision."; ' + - ' or-teiv-yext:label 0.3.0; ' + - ' } ' + - ' ' + - ' augment /model:decorators { ' + - ' leaf test1 { ' + - ' type string; ' + - ' } ' + - ' leaf test2 { ' + - ' type boolean; ' + - ' } ' + - ' leaf test3 { ' + - ' type uint32; ' + - ' } ' + - ' } ' + - ' ' + - ' identity geo-classifier { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity urban { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - ' identity rural { ' + - ' base geo-classifier; ' + - ' } ' + - ' ' + - '}')))) - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid file input", - "details": "Invalid file input: Path to schema node '/model:decorators', part of 'augment' statement, cannot be resolved." - }''') - } - } -] diff --git a/teiv/src/test/resources/contracts/schemas/03_deleteSchemas.groovy b/teiv/src/test/resources/contracts/schemas/03_deleteSchemas.groovy deleted file mode 100644 index 5c486fa..0000000 --- a/teiv/src/test/resources/contracts/schemas/03_deleteSchemas.groovy +++ /dev/null @@ -1,66 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Ericsson - * Modifications Copyright (C) 2024 OpenInfra Foundation Europe - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ -package contracts.schemas - -import org.springframework.cloud.contract.spec.Contract - -[ - Contract.make { - description "SUCCESS - 204: Delete an existing classifier schema - test-app-for-deletion-module" - request { - method DELETE() - url("/topology-inventory/v1alpha11/schemas/test-app-for-deletion-module") - } - response { - status NO_CONTENT() - } - }, - Contract.make { - description "ERROR - 400: Delete a schema that does not exists" - request { - method DELETE() - url("/topology-inventory/v1alpha11/schemas/does-not-exist-rapp-module") - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid schema name", - "details": "Invalid schema name: does-not-exist-rapp-module" - }''') - } - }, - Contract.make { - description "ERROR - 400: Delete a schema that is in deleting status" - request { - method DELETE() - url("/topology-inventory/v1alpha11/schemas/test-module-in-deleting-state") - } - response { - status BAD_REQUEST() - body('''{ - "status": "BAD_REQUEST", - "message": "Invalid schema name", - "details": "Invalid schema name: test-module-in-deleting-state" - }''') - } - } -] diff --git a/yang-parser/src/main/java/org/oran/smo/yangtools/parser/findings/ParserFindingType.java b/yang-parser/src/main/java/org/oran/smo/yangtools/parser/findings/ParserFindingType.java index 907f3cd..50b01ec 100644 --- a/yang-parser/src/main/java/org/oran/smo/yangtools/parser/findings/ParserFindingType.java +++ b/yang-parser/src/main/java/org/oran/smo/yangtools/parser/findings/ParserFindingType.java @@ -64,6 +64,11 @@ public enum ParserFindingType { * There is a mismatch in conformance type between a module and its submodules. */ P006_IMPLEMENT_IMPORT_MISMATCH, + /** + * Two or more modules are in the input with the same module name but different namespace; + * or the other way around. This is a serious issue. + */ + P007_MODULE_NAMESPACE_MISMATCH, /** * Fail-fast. Denotes that during parsing some issues were found that are so severe that it @@ -142,7 +147,7 @@ public enum ParserFindingType { */ P037_UNRESOLVABLE_INCLUDE, /** - * Multiple revisions of a subnmodule are in the input. Not allowed. + * Multiple revisions of a submodule are in the input. Not allowed. */ P038_AMBIGUOUS_INCLUDE, /** diff --git a/yang-parser/src/main/java/org/oran/smo/yangtools/parser/model/YangModel.java b/yang-parser/src/main/java/org/oran/smo/yangtools/parser/model/YangModel.java index b4b14e2..d6336d4 100644 --- a/yang-parser/src/main/java/org/oran/smo/yangtools/parser/model/YangModel.java +++ b/yang-parser/src/main/java/org/oran/smo/yangtools/parser/model/YangModel.java @@ -184,7 +184,7 @@ public class YangModel { */ extractModuleIdentity(context, yangDomDocumentRoot); extractPrefixes(yangDomDocumentRoot); - extractNamespace(yangDomDocumentRoot, namespaceResolver); + extractNamespace(context, yangDomDocumentRoot, namespaceResolver); if (createTypeSafeStatementTree) { /* @@ -320,8 +320,8 @@ public class YangModel { } } - private void extractNamespace(final YangDomDocumentRoot docRoot, final ModuleAndNamespaceResolver namespaceResolver) { - + private void extractNamespace(final ParserExecutionContext context, final YangDomDocumentRoot docRoot, + final ModuleAndNamespaceResolver namespaceResolver) { if (docRoot.getChildren().isEmpty()) { return; } @@ -335,10 +335,31 @@ public class YangModel { if (namespaceChildren.size() == 1) { final YangDomElement namespaceDomElement = namespaceChildren.get(0); if (namespaceDomElement.getValue() != null) { - namespaceResolver.recordModuleMapping(this.moduleIdentity.getModuleName(), namespaceDomElement - .getValue()); - namespaceResolver.recordNamespaceMapping(namespaceDomElement.getValue(), this.moduleIdentity - .getModuleName()); + final String moduleName = this.moduleIdentity.getModuleName(); + final String namespace = namespaceDomElement.getValue(); + + /* + * If the module already exists in the resolver (this is the case when multiple revisions of the + * same module are in the input, with only one of these being conformance IMPLEMENT), then the + * namespaces must match, so we check for that - same the other way around. If they don't match + * we will issue a finding here - this is a serious issue. + */ + final String alreadyMappedModuleName = namespaceResolver.getModuleForNamespace(namespace); + final String alreadyMappedNamespace = namespaceResolver.getNamespaceForModule(moduleName); + + if (alreadyMappedModuleName != null && !alreadyMappedModuleName.equals(moduleName)) { + context.addFinding(new Finding(this, ParserFindingType.P007_MODULE_NAMESPACE_MISMATCH, + "Same namespace '" + namespace + "' is used by multiple different modules.")); + } else if (alreadyMappedNamespace != null && !alreadyMappedNamespace.equals(namespace)) { + context.addFinding(new Finding(this, ParserFindingType.P007_MODULE_NAMESPACE_MISMATCH, + "Module '" + moduleName + "' is multiple times in the input, with different namespaces.")); + } else { + /* + * All good. + */ + namespaceResolver.recordModuleMapping(moduleName, namespace); + namespaceResolver.recordNamespaceMapping(namespace, moduleName); + } } } } diff --git a/yang-parser/src/main/java/org/oran/smo/yangtools/parser/model/schema/Schema.java b/yang-parser/src/main/java/org/oran/smo/yangtools/parser/model/schema/Schema.java index d5ba911..aad192b 100644 --- a/yang-parser/src/main/java/org/oran/smo/yangtools/parser/model/schema/Schema.java +++ b/yang-parser/src/main/java/org/oran/smo/yangtools/parser/model/schema/Schema.java @@ -209,6 +209,7 @@ public class Schema { .toString(), ParserFindingType.P001_BASIC_FILE_READ_ERROR.toString(), ParserFindingType.P003_DUPLICATE_INPUT .toString(), ParserFindingType.P004_SAME_MODULE_DUPLICATE_IMPLEMENTS.toString(), ParserFindingType.P005_NO_IMPLEMENTS.toString(), ParserFindingType.P006_IMPLEMENT_IMPORT_MISMATCH.toString(), + ParserFindingType.P007_MODULE_NAMESPACE_MISMATCH.toString(), ParserFindingType.P013_INVALID_SYNTAX_AT_DOCUMENT_ROOT.toString(), ParserFindingType.P014_INVALID_SYNTAX_AT_DOCUMENT_END.toString(), ParserFindingType.P031_PREFIX_NOT_UNIQUE .toString(), ParserFindingType.P032_MISSING_REVISION.toString(), ParserFindingType.P035_AMBIGUOUS_IMPORT diff --git a/yang-parser/src/test/java/org/oran/smo/yangtools/parser/model/statements/yang/test/NamespaceTest.java b/yang-parser/src/test/java/org/oran/smo/yangtools/parser/model/statements/yang/test/NamespaceTest.java new file mode 100644 index 0000000..b2921a1 --- /dev/null +++ b/yang-parser/src/test/java/org/oran/smo/yangtools/parser/model/statements/yang/test/NamespaceTest.java @@ -0,0 +1,59 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Ericsson + * Modifications Copyright (C) 2024 OpenInfra Foundation Europe + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.oran.smo.yangtools.parser.model.statements.yang.test; + +import org.junit.Test; +import org.oran.smo.yangtools.parser.findings.ParserFindingType; +import org.oran.smo.yangtools.parser.testutils.YangTestCommon; + +import java.util.Arrays; +import java.util.Collections; + +public class NamespaceTest extends YangTestCommon { + + private static final String TEST_DIR = "src/test/resources/model-statements-yang/namespace-test/"; + + @Test + public void test___modA_nsA___modB_nsB__ok() { + + parseAbsoluteImplementsYangModels(Arrays.asList(TEST_DIR + "modA_nsA.yang", TEST_DIR + "modB_nsB.yang")); + + assertNoFindings(); + } + + @Test + public void test___modA_nsA___modA_nsB___namespace_different() { + + parseYangModels(Collections.emptyList(), Arrays.asList(TEST_DIR + "modA_nsA.yang"), Collections.emptyList(), Arrays + .asList(TEST_DIR + "modA_nsB.yang")); + + assertHasFindingOfType(ParserFindingType.P007_MODULE_NAMESPACE_MISMATCH.toString()); + } + + @Test + public void test___modA_nsA___modB_nsA___module_different() { + + parseYangModels(Collections.emptyList(), Arrays.asList(TEST_DIR + "modA_nsA.yang"), Collections.emptyList(), Arrays + .asList(TEST_DIR + "modB_nsA.yang")); + + assertHasFindingOfType(ParserFindingType.P007_MODULE_NAMESPACE_MISMATCH.toString()); + } +} diff --git a/yang-parser/src/test/resources/model-statements-yang/namespace-test/modA_nsA.yang b/yang-parser/src/test/resources/model-statements-yang/namespace-test/modA_nsA.yang new file mode 100644 index 0000000..5bcf81f --- /dev/null +++ b/yang-parser/src/test/resources/model-statements-yang/namespace-test/modA_nsA.yang @@ -0,0 +1,7 @@ +module modA { + + namespace "nsA"; + + prefix "this"; + revision "2024-09-26"; +} \ No newline at end of file diff --git a/yang-parser/src/test/resources/model-statements-yang/namespace-test/modA_nsB.yang b/yang-parser/src/test/resources/model-statements-yang/namespace-test/modA_nsB.yang new file mode 100644 index 0000000..522791b --- /dev/null +++ b/yang-parser/src/test/resources/model-statements-yang/namespace-test/modA_nsB.yang @@ -0,0 +1,7 @@ +module modA { + + namespace "nsB"; + + prefix "this"; + revision "2024-09-30"; +} \ No newline at end of file diff --git a/yang-parser/src/test/resources/model-statements-yang/namespace-test/modB_nsA.yang b/yang-parser/src/test/resources/model-statements-yang/namespace-test/modB_nsA.yang new file mode 100644 index 0000000..23df3f1 --- /dev/null +++ b/yang-parser/src/test/resources/model-statements-yang/namespace-test/modB_nsA.yang @@ -0,0 +1,7 @@ +module modB { + + namespace "nsA"; + + prefix "this"; + revision "2024-09-30"; +} \ No newline at end of file diff --git a/yang-parser/src/test/resources/model-statements-yang/namespace-test/modB_nsB.yang b/yang-parser/src/test/resources/model-statements-yang/namespace-test/modB_nsB.yang new file mode 100644 index 0000000..cc75642 --- /dev/null +++ b/yang-parser/src/test/resources/model-statements-yang/namespace-test/modB_nsB.yang @@ -0,0 +1,7 @@ +module modB { + + namespace "nsB"; + + prefix "this"; + revision "2024-09-26"; +} \ No newline at end of file -- 2.16.6