Make Content-Type header default to JSON for CPS APIs 30/139530/1
authordanielhanrahan <daniel.hanrahan@est.tech>
Fri, 22 Nov 2024 09:20:03 +0000 (09:20 +0000)
committerdanielhanrahan <daniel.hanrahan@est.tech>
Fri, 22 Nov 2024 10:19:20 +0000 (10:19 +0000)
Recent changes for XML support have made Content-Type header
mandatory, where before it was not. This change makes CPS
default to JSON if Content-Type is not specified.

Issue-ID: CPS-2517
Signed-off-by: danielhanrahan <daniel.hanrahan@est.tech>
Change-Id: Ic3718bfe7aedd6fe9dbd978f520179b184c9c932

cps-rest/docs/openapi/components.yml
cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java
cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java
cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
docs/api/swagger/cps/openapi.yaml

index 728295f..1db4185 100644 (file)
@@ -291,12 +291,13 @@ components:
       name: Content-Type
       in: header
       description: Content type in header
+      required: false
       schema:
         type: string
         enum:
           - application/json
           - application/xml
-      required: true
+        default: application/json
     descendantsInQuery:
       name: descendants
       in: query
index 6d22581..dda88e0 100755 (executable)
@@ -72,9 +72,9 @@ public class DataRestController implements CpsDataApi {
     @Override
     public ResponseEntity<String> createNode(final String apiVersion,
                                              final String dataspaceName, final String anchorName,
-                                             final String contentTypeInHeader,
                                              final String nodeData, final String parentNodeXpath,
-                                             final Boolean dryRunEnabled, final String observedTimestamp) {
+                                             final Boolean dryRunEnabled, final String observedTimestamp,
+                                             final String contentTypeInHeader) {
         final ContentType contentType = ContentType.fromString(contentTypeInHeader);
         if (Boolean.TRUE.equals(dryRunEnabled)) {
             cpsDataService.validateData(dataspaceName, anchorName, parentNodeXpath, nodeData, contentType);
@@ -92,19 +92,18 @@ public class DataRestController implements CpsDataApi {
     }
 
     @Override
-    public ResponseEntity<Void> deleteDataNode(final String apiVersion,
-        final String dataspaceName, final String anchorName,
-        final String xpath, final String observedTimestamp) {
-        cpsDataService.deleteDataNode(dataspaceName, anchorName, xpath,
-            toOffsetDateTime(observedTimestamp));
+    public ResponseEntity<Void> deleteDataNode(final String apiVersion, final String dataspaceName,
+                                               final String anchorName, final String xpath,
+                                               final String observedTimestamp) {
+        cpsDataService.deleteDataNode(dataspaceName, anchorName, xpath, toOffsetDateTime(observedTimestamp));
         return new ResponseEntity<>(HttpStatus.NO_CONTENT);
     }
 
     @Override
     public ResponseEntity<String> addListElements(final String apiVersion, final String dataspaceName,
                                                   final String anchorName, final String parentNodeXpath,
-                                                  final String contentTypeInHeader, final String nodeData,
-                                                  final String observedTimestamp) {
+                                                  final String nodeData, final String observedTimestamp,
+                                                  final String contentTypeInHeader) {
         final ContentType contentType = ContentType.fromString(contentTypeInHeader);
         cpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath,
                 nodeData, toOffsetDateTime(observedTimestamp), contentType);
@@ -115,7 +114,9 @@ public class DataRestController implements CpsDataApi {
     @Timed(value = "cps.data.controller.datanode.get.v1",
             description = "Time taken to get data node")
     public ResponseEntity<Object> getNodeByDataspaceAndAnchor(final String dataspaceName,
-        final String anchorName, final String xpath, final Boolean includeDescendants) {
+                                                              final String anchorName,
+                                                              final String xpath,
+                                                              final Boolean includeDescendants) {
         final FetchDescendantsOption fetchDescendantsOption = Boolean.TRUE.equals(includeDescendants)
             ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS : FetchDescendantsOption.OMIT_DESCENDANTS;
         final DataNode dataNode = cpsDataService.getDataNodes(dataspaceName, anchorName, xpath,
@@ -129,8 +130,9 @@ public class DataRestController implements CpsDataApi {
     @Timed(value = "cps.data.controller.datanode.get.v2",
             description = "Time taken to get data node")
     public ResponseEntity<Object> getNodeByDataspaceAndAnchorV2(final String dataspaceName, final String anchorName,
-                                                                final String contentTypeInHeader, final String xpath,
-                                                                final String fetchDescendantsOptionAsString) {
+                                                                final String xpath,
+                                                                final String fetchDescendantsOptionAsString,
+                                                                final String contentTypeInHeader) {
         final ContentType contentType = ContentType.fromString(contentTypeInHeader);
         final FetchDescendantsOption fetchDescendantsOption =
                 FetchDescendantsOption.getFetchDescendantsOption(fetchDescendantsOptionAsString);
@@ -148,9 +150,9 @@ public class DataRestController implements CpsDataApi {
 
     @Override
     public ResponseEntity<Object> updateNodeLeaves(final String apiVersion, final String dataspaceName,
-                                                   final String anchorName, final String contentTypeInHeader,
-                                                   final String nodeData, final String parentNodeXpath,
-                                                   final String observedTimestamp) {
+                                                   final String anchorName, final String nodeData,
+                                                   final String parentNodeXpath, final String observedTimestamp,
+                                                   final String contentTypeInHeader) {
         final ContentType contentType = ContentType.fromString(contentTypeInHeader);
         cpsDataService.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath,
                 nodeData, toOffsetDateTime(observedTimestamp), contentType);
@@ -159,9 +161,9 @@ public class DataRestController implements CpsDataApi {
 
     @Override
     public ResponseEntity<Object> replaceNode(final String apiVersion, final String dataspaceName,
-                                             final String anchorName, final String contentTypeInHeader,
-                                             final String nodeData, final String parentNodeXpath,
-                                              final String observedTimestamp) {
+                                              final String anchorName, final String nodeData,
+                                              final String parentNodeXpath, final String observedTimestamp,
+                                              final String contentTypeInHeader) {
         final ContentType contentType = ContentType.fromString(contentTypeInHeader);
         cpsDataService.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath,
                         nodeData, toOffsetDateTime(observedTimestamp), contentType);
@@ -169,10 +171,9 @@ public class DataRestController implements CpsDataApi {
     }
 
     @Override
-    public ResponseEntity<Object> replaceListContent(final String apiVersion,
-                                                     final String dataspaceName, final String anchorName,
-                                                     final String parentNodeXpath, final Object jsonData,
-        final String observedTimestamp) {
+    public ResponseEntity<Object> replaceListContent(final String apiVersion, final String dataspaceName,
+                                                     final String anchorName, final String parentNodeXpath,
+                                                     final Object jsonData, final String observedTimestamp) {
         cpsDataService.replaceListContent(dataspaceName, anchorName, parentNodeXpath,
                 jsonObjectMapper.asJsonString(jsonData), toOffsetDateTime(observedTimestamp));
         return new ResponseEntity<>(HttpStatus.OK);
@@ -180,7 +181,7 @@ public class DataRestController implements CpsDataApi {
 
     @Override
     public ResponseEntity<Void> deleteListOrListElement(final String dataspaceName, final String anchorName,
-        final String listElementXpath, final String observedTimestamp) {
+                                                        final String listElementXpath, final String observedTimestamp) {
         cpsDataService
             .deleteListOrListElement(dataspaceName, anchorName, listElementXpath, toOffsetDateTime(observedTimestamp));
         return new ResponseEntity<>(HttpStatus.NO_CONTENT);
@@ -211,10 +212,10 @@ public class DataRestController implements CpsDataApi {
     @Timed(value = "cps.data.controller.get.delta",
             description = "Time taken to get delta between anchors")
     public ResponseEntity<Object> getDeltaByDataspaceAndAnchors(final String dataspaceName,
-                                                                           final String sourceAnchorName,
-                                                                           final String targetAnchorName,
-                                                                           final String xpath,
-                                                                           final String descendants) {
+                                                                final String sourceAnchorName,
+                                                                final String targetAnchorName,
+                                                                final String xpath,
+                                                                final String descendants) {
         final FetchDescendantsOption fetchDescendantsOption =
                 FetchDescendantsOption.getFetchDescendantsOption(descendants);
 
index 6823f6b..b425333 100644 (file)
@@ -60,7 +60,9 @@ public class QueryRestController implements CpsQueryApi {
     @Timed(value = "cps.data.controller.datanode.query.v1",
             description = "Time taken to query data nodes")
     public ResponseEntity<Object> getNodesByDataspaceAndAnchorAndCpsPath(final String dataspaceName,
-        final String anchorName, final String cpsPath, final Boolean includeDescendants) {
+                                                                         final String anchorName,
+                                                                         final String cpsPath,
+                                                                         final Boolean includeDescendants) {
         final FetchDescendantsOption fetchDescendantsOption = Boolean.TRUE.equals(includeDescendants)
             ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS : FetchDescendantsOption.OMIT_DESCENDANTS;
         return executeNodesByDataspaceQueryAndCreateResponse(dataspaceName, anchorName, cpsPath,
@@ -71,8 +73,10 @@ public class QueryRestController implements CpsQueryApi {
     @Timed(value = "cps.data.controller.datanode.query.v2",
             description = "Time taken to query data nodes")
     public ResponseEntity<Object> getNodesByDataspaceAndAnchorAndCpsPathV2(final String dataspaceName,
-        final String anchorName, final String contentTypeInHeader, final String cpsPath,
-                                        final String fetchDescendantsOptionAsString) {
+                                                                           final String anchorName,
+                                                                           final String cpsPath,
+                                                                           final String fetchDescendantsOptionAsString,
+                                                                           final String contentTypeInHeader) {
         final ContentType contentType = ContentType.fromString(contentTypeInHeader);
         final FetchDescendantsOption fetchDescendantsOption =
             FetchDescendantsOption.getFetchDescendantsOption(fetchDescendantsOptionAsString);
@@ -83,9 +87,11 @@ public class QueryRestController implements CpsQueryApi {
     @Override
     @Timed(value = "cps.data.controller.datanode.query.across.anchors",
             description = "Time taken to query data nodes across anchors")
-    public ResponseEntity<Object> getNodesByDataspaceAndCpsPath(final String dataspaceName, final String cpsPath,
+    public ResponseEntity<Object> getNodesByDataspaceAndCpsPath(final String dataspaceName,
+                                                                final String cpsPath,
                                                                 final String fetchDescendantsOptionAsString,
-                                                                final Integer pageIndex, final Integer pageSize) {
+                                                                final Integer pageIndex,
+                                                                final Integer pageSize) {
         final FetchDescendantsOption fetchDescendantsOption =
                 FetchDescendantsOption.getFetchDescendantsOption(fetchDescendantsOptionAsString);
         final PaginationOption paginationOption = (pageIndex == null || pageSize == null)
@@ -111,7 +117,7 @@ public class QueryRestController implements CpsQueryApi {
     }
 
     private Integer getTotalPages(final String dataspaceName, final String cpsPath,
-                                                          final PaginationOption paginationOption) {
+                                  final PaginationOption paginationOption) {
         if (paginationOption == PaginationOption.NO_PAGINATION) {
             return 1;
         }
index 27738b0..72ae4c7 100755 (executable)
@@ -328,6 +328,23 @@ class DataRestControllerSpec extends Specification {
             assert numberOfDataTrees == 2
     }
 
+    def 'Get all the data trees using V2 without Content-Type defaults to json'() {
+        given: 'the service returns all data node leaves'
+            def xpath = '/'
+            def endpoint = "$dataNodeBaseEndpointV2/anchors/$anchorName/node"
+            mockCpsDataService.getDataNodes(dataspaceName, anchorName, xpath, OMIT_DESCENDANTS) >> [dataNodeWithLeavesNoChildren, dataNodeWithLeavesNoChildren2]
+        when: 'V2 of get request is performed through REST API without specifying content-type header'
+            def response =
+                    mvc.perform(get(endpoint)
+                            .param('xpath', xpath))
+                            .andReturn().response
+        then: 'a success response is returned'
+            response.status == HttpStatus.OK.value()
+        and: 'the response contains the datanode in json array format'
+            response.getContentAsString() == '[{"parent-1":{"leaf":"value","leafList":["leaveListElement1","leaveListElement2"]}},' +
+                    '{"parent-2":{"leaf":"value"}}]'
+    }
+
     def 'Get all the data trees as XML with root node xPath using V2'() {
         given: 'the service returns all data node leaves'
             def xpath = '/'
index 3b6bd43..812a2e4 100644 (file)
@@ -1165,8 +1165,9 @@ paths:
       - description: Content type in header
         in: header
         name: Content-Type
-        required: true
+        required: false
         schema:
+          default: application/json
           enum:
           - application/json
           - application/xml
@@ -1363,8 +1364,9 @@ paths:
       - description: Content type in header
         in: header
         name: Content-Type
-        required: true
+        required: false
         schema:
+          default: application/json
           enum:
           - application/json
           - application/xml
@@ -1490,8 +1492,9 @@ paths:
       - description: Content type in header
         in: header
         name: Content-Type
-        required: true
+        required: false
         schema:
+          default: application/json
           enum:
           - application/json
           - application/xml
@@ -1617,8 +1620,9 @@ paths:
       - description: Content type in header
         in: header
         name: Content-Type
-        required: true
+        required: false
         schema:
+          default: application/json
           enum:
           - application/json
           - application/xml
@@ -1810,8 +1814,9 @@ paths:
       - description: Content type in header
         in: header
         name: Content-Type
-        required: true
+        required: false
         schema:
+          default: application/json
           enum:
           - application/json
           - application/xml
@@ -2286,8 +2291,9 @@ paths:
       - description: Content type in header
         in: header
         name: Content-Type
-        required: true
+        required: false
         schema:
+          default: application/json
           enum:
           - application/json
           - application/xml
@@ -2591,8 +2597,9 @@ components:
       description: Content type in header
       in: header
       name: Content-Type
-      required: true
+      required: false
       schema:
+        default: application/json
         enum:
         - application/json
         - application/xml