Attach a (JSON) data instance for a container with children to a given Anchor 93/117193/9
authorniamhcore <niamh.core@est.tech>
Thu, 28 Jan 2021 16:11:52 +0000 (16:11 +0000)
committerniamhcore <niamh.core@est.tech>
Mon, 1 Feb 2021 14:18:35 +0000 (14:18 +0000)
Issue-ID: CPS-26

Signed-off-by: niamhcore <niamh.core@est.tech>
Change-Id: I38fc1b1a6ccf84e64eff3218372b40c8fa2491ba

cps-rest/docs/api/swagger/cpsData.yml
cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java
cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy
cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy
cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java [new file with mode: 0644]
cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
cps-service/src/main/java/org/onap/cps/utils/YangUtils.java
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy

index c33cf16..dcdb99a 100644 (file)
@@ -20,46 +20,47 @@ nodesByDataspaceAndAnchor:
         $ref: 'components.yaml#/components/responses/NotFound'
     x-codegen-request-body-name: xpath
 
-nodesByDataspace:
-  get:
+  post:
     tags:
       - cps-data
-    summary: Get all nodes for a given dataspace using an xpath or schema node identifier - DRAFT
-    operationId: getNode
+    summary: Create a node for a given anchor for the given dataspace
+    operationId: createNode
     parameters:
       - $ref: 'components.yaml#/components/parameters/dataspaceNameInPath'
+      - $ref: 'components.yaml#/components/parameters/anchorNameInPath'
+    requestBody:
+      required: true
+      content:
+        application/json:
+          schema:
+            type: string
     responses:
-      200:
-        $ref: 'components.yaml#/components/responses/Ok'
+      201:
+        $ref: 'components.yaml#/components/responses/Created'
       400:
         $ref: 'components.yaml#/components/responses/BadRequest'
       401:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
-      404:
-        $ref: 'components.yaml#/components/responses/NotFound'
-    x-codegen-request-body-name: requestBody
 
-  post:
+nodesByDataspace:
+  get:
     tags:
       - cps-data
-    summary: Create a node for a given anchor for the given dataspace - DRAFT
-    operationId: createNode
+    summary: Get all nodes for a given dataspace using an xpath or schema node identifier - DRAFT
+    operationId: getNode
     parameters:
       - $ref: 'components.yaml#/components/parameters/dataspaceNameInPath'
-    requestBody:
-      content:
-        multipart/form-data:
-          schema:
-            $ref: 'components.yaml#/components/schemas/MultipartFile'
-      required: true
     responses:
-      201:
-        $ref: 'components.yaml#/components/responses/Created'
+      200:
+        $ref: 'components.yaml#/components/responses/Ok'
       400:
         $ref: 'components.yaml#/components/responses/BadRequest'
       401:
         $ref: 'components.yaml#/components/responses/Unauthorized'
       403:
-        $ref: 'components.yaml#/components/responses/Forbidden'
\ No newline at end of file
+        $ref: 'components.yaml#/components/responses/Forbidden'
+      404:
+        $ref: 'components.yaml#/components/responses/NotFound'
+    x-codegen-request-body-name: requestBody
\ No newline at end of file
index 2ecbd4f..07f5553 100644 (file)
 package org.onap.cps.rest.controller;
 
 import javax.validation.Valid;
-import org.modelmapper.ModelMapper;
-import org.onap.cps.api.CpsAdminService;
+import javax.validation.constraints.NotNull;
+import org.onap.cps.api.CpsDataService;
 import org.onap.cps.rest.api.CpsDataApi;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.multipart.MultipartFile;
 
 @RestController
 @RequestMapping("${rest.api.base-path}")
 public class DataRestController implements CpsDataApi {
 
     @Autowired
-    private CpsAdminService cpsAdminService;
-
-    @Autowired
-    private ModelMapper modelMapper;
+    private CpsDataService cpsDataService;
 
     @Override
-    public ResponseEntity<String> createNode(@Valid final MultipartFile multipartFile, final String dataspaceName) {
-        return null;
+    public ResponseEntity<String> createNode(@Valid final String jsonData, @NotNull final String dataspaceName,
+        @NotNull @Valid final String anchorName) {
+        cpsDataService.saveData(dataspaceName, anchorName, jsonData);
+        return new ResponseEntity<>(HttpStatus.CREATED);
     }
 
     @Override
@@ -53,4 +52,5 @@ public class DataRestController implements CpsDataApi {
     public ResponseEntity<Object> getNodeByDataspaceAndAnchor(final String dataspaceName, final String anchorName) {
         return null;
     }
+
 }
index 540d622..c1c7c5d 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.cps.rest.controller
 
 import org.modelmapper.ModelMapper
 import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
 import org.onap.cps.spi.exceptions.DataspaceAlreadyDefinedException
 import org.onap.cps.spi.exceptions.SchemaSetInUseException
@@ -55,6 +56,9 @@ class AdminRestControllerSpec extends Specification {
     @SpringBean
     CpsAdminService mockCpsAdminService = Mock()
 
+    @SpringBean
+    CpsDataService mockCpsDataService = Mock()
+
     @SpringBean
     ModelMapper modelMapper = Mock()
 
index edc484b..7dbf6bc 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.cps.rest.exceptions
 import groovy.json.JsonSlurper
 import org.modelmapper.ModelMapper
 import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
 import org.onap.cps.spi.exceptions.AnchorAlreadyDefinedException
 import org.onap.cps.spi.exceptions.CpsException
@@ -55,6 +56,9 @@ class CpsRestExceptionHandlerSpec extends Specification {
     @SpringBean
     CpsModuleService mockCpsModuleService = Mock()
 
+    @SpringBean
+    CpsDataService mockCpsDataService = Mock()
+
     @SpringBean
     ModelMapper modelMapper = Mock()
 
index ebeeb9a..a8f4965 100644 (file)
 
 package org.onap.cps.api;
 
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.onap.cps.spi.exceptions.DataValidationException;
+
 /*
  * Datastore interface for handling CPS data.
  */
 public interface CpsDataService {
-
+    /**
+     * Persists data for the given anchor and dataspace.
+     *
+     * @param dataspaceName dataspace name
+     * @param anchorName    anchor name
+     * @param jsonData      json data
+     * @throws DataValidationException when json data is invalid
+     */
+    void saveData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String jsonData);
 }
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
new file mode 100644 (file)
index 0000000..2a1e18b
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  Modifications Copyright (C) 2020 Bell Canada. All rights reserved.
+ *  ================================================================================
+ *  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.onap.cps.api.impl;
+
+import org.onap.cps.api.CpsAdminService;
+import org.onap.cps.api.CpsDataService;
+import org.onap.cps.api.CpsModuleService;
+import org.onap.cps.spi.CpsDataPersistenceService;
+import org.onap.cps.spi.model.Anchor;
+import org.onap.cps.spi.model.DataNode;
+import org.onap.cps.spi.model.DataNodeBuilder;
+import org.onap.cps.utils.YangUtils;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class CpsDataServiceImpl implements CpsDataService {
+
+    @Autowired
+    private CpsDataPersistenceService cpsDataPersistenceService;
+
+    @Autowired
+    private CpsAdminService cpsAdminService;
+
+    @Autowired
+    private CpsModuleService cpsModuleService;
+
+    @Autowired
+    private YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache;
+
+    @Override
+    public void saveData(final String dataspaceName, final String anchorName, final String jsonData) {
+        final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+        final SchemaContext schemaContext = getSchemaContext(dataspaceName, anchor.getSchemaSetName());
+        final NormalizedNode normalizedNode = YangUtils.parseJsonData(jsonData, schemaContext);
+        final DataNode dataNode = new DataNodeBuilder().withNormalizedNodeTree(normalizedNode).build();
+        cpsDataPersistenceService.storeDataNode(dataspaceName, anchor.getName(), dataNode);
+    }
+
+    private SchemaContext getSchemaContext(final String dataspaceName, final String schemaSetName) {
+        return yangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName).getSchemaContext();
+    }
+}
\ No newline at end of file
index 427ddd6..990b7bb 100644 (file)
@@ -40,9 +40,9 @@ public class CpsModuleServiceImpl implements CpsModuleService {
 
     @Override
     public void createSchemaSet(final String dataspaceName, final String schemaSetName,
-            final Map<String, String> yangResourcesNameToContentMap) {
+        final Map<String, String> yangResourcesNameToContentMap) {
         final YangTextSchemaSourceSet yangTextSchemaSourceSet
-                = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap);
+            = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap);
         cpsModulePersistenceService.storeSchemaSet(dataspaceName, schemaSetName, yangResourcesNameToContentMap);
         yangTextSchemaSourceSetCache.updateCache(dataspaceName, schemaSetName, yangTextSchemaSourceSet);
     }
@@ -50,9 +50,9 @@ public class CpsModuleServiceImpl implements CpsModuleService {
     @Override
     public SchemaSet getSchemaSet(final String dataspaceName, final String schemaSetName) {
         final YangTextSchemaSourceSet yangTextSchemaSourceSet = yangTextSchemaSourceSetCache
-                                                                        .get(dataspaceName, schemaSetName);
+            .get(dataspaceName, schemaSetName);
         return SchemaSet.builder().name(schemaSetName).dataspaceName(dataspaceName)
-                       .moduleReferences(yangTextSchemaSourceSet.getModuleReferences()).build();
+            .moduleReferences(yangTextSchemaSourceSet.getModuleReferences()).build();
     }
 
     @Override
index 1244d54..1ba9432 100644 (file)
@@ -27,6 +27,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.spi.exceptions.DataValidationException;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
@@ -52,17 +53,21 @@ public class YangUtils {
      * @param schemaContext the SchemaContext for the given data
      * @return the NormalizedNode representing the json data
      */
-    public static NormalizedNode parseJsonData(final String jsonData, final SchemaContext schemaContext)
-            throws IOException {
+    public static NormalizedNode parseJsonData(final String jsonData, final SchemaContext schemaContext) {
         final JSONCodecFactory jsonCodecFactory = JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02
                 .getShared(schemaContext);
         final NormalizedNodeResult normalizedNodeResult = new NormalizedNodeResult();
         final NormalizedNodeStreamWriter normalizedNodeStreamWriter = ImmutableNormalizedNodeStreamWriter
                 .from(normalizedNodeResult);
-        try (final JsonParserStream jsonParserStream = JsonParserStream
-                .create(normalizedNodeStreamWriter, jsonCodecFactory)) {
-            final JsonReader jsonReader = new JsonReader(new StringReader(jsonData));
-            jsonParserStream.parse(jsonReader);
+        try {
+            try (final JsonParserStream jsonParserStream = JsonParserStream
+                    .create(normalizedNodeStreamWriter, jsonCodecFactory)) {
+                final JsonReader jsonReader = new JsonReader(new StringReader(jsonData));
+                jsonParserStream.parse(jsonReader);
+            }
+        } catch (final IOException e) {
+            throw new DataValidationException("Failed to parse json data.", String
+                .format("Exception occurred on parsing string %s.", jsonData), e);
         }
         return normalizedNodeResult.getResult();
     }
index 5f2168a..261d174 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.cps.api.impl
 
 import org.onap.cps.TestUtils
 import org.onap.cps.api.CpsAdminService
+import org.onap.cps.spi.CpsDataPersistenceService
 import org.onap.cps.spi.CpsModulePersistenceService
 import org.onap.cps.spi.exceptions.ModelValidationException
 import org.onap.cps.spi.model.ModuleReference
@@ -46,6 +47,8 @@ class CpsModuleServiceImplSpec extends Specification {
     CpsModulePersistenceService mockModuleStoreService = Mock()
     @SpringBean
     CpsAdminService mockCpsAdminService = Mock()
+    @SpringBean
+    CpsDataPersistenceService mockDataPersistenceService = Mock()
     @Autowired
     CpsModuleServiceImpl objectUnderTest = new CpsModuleServiceImpl()
     @SpringBean
@@ -93,14 +96,14 @@ class CpsModuleServiceImplSpec extends Specification {
     }
 
     @Unroll
-    def 'Delete set by name and dataspace with #cascadeDeleteOption.'(){
+    def 'Delete set by name and dataspace with #cascadeDeleteOption.'() {
         when: 'schema set deletion is requested'
             objectUnderTest.deleteSchemaSet(dataspaceName, schemaSetname, cascadeDeleteOption)
         then: 'persistence service method is invoked with same parameters'
             mockModuleStoreService.deleteSchemaSet(dataspaceName, schemaSetname, cascadeDeleteOption)
         where: 'following parameters are used'
-            dataspaceName | schemaSetname | cascadeDeleteOption
-            'dataspace-1'  | 'schemas-set-1' | CASCADE_DELETE_ALLOWED
-            'dataspace-2'  | 'schemas-set-2' | CASCADE_DELETE_PROHIBITED
+            dataspaceName | schemaSetname   | cascadeDeleteOption
+            'dataspace-1' | 'schemas-set-1' | CASCADE_DELETE_ALLOWED
+            'dataspace-2' | 'schemas-set-2' | CASCADE_DELETE_PROHIBITED
     }
 }