responses:
'200':
$ref: 'components.yml#/components/responses/Ok'
+ '204':
+ $ref: 'components.yml#/components/responses/Created'
'400':
$ref: 'components.yml#/components/responses/BadRequest'
'403':
import static org.onap.cps.cpspath.parser.CpsPathUtil.NO_PARENT_PATH;
import static org.onap.cps.cpspath.parser.CpsPathUtil.ROOT_NODE_XPATH;
+import static org.onap.cps.cpspath.parser.CpsPathUtil.isPathToListElement;
import static org.onap.cps.events.model.EventPayload.Action.CREATE;
import static org.onap.cps.events.model.EventPayload.Action.REMOVE;
import static org.onap.cps.events.model.EventPayload.Action.REPLACE;
final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> dataNodes = dataNodeFactory
.createDataNodesWithAnchorParentXpathAndNodeData(anchor, parentNodeXpath, nodeData, contentType);
- cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes);
+ if (ROOT_NODE_XPATH.equals(parentNodeXpath) || !isPathToListElement(parentNodeXpath)) {
+ cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes);
+ } else {
+ cpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, dataNodes);
+ }
sendDataUpdatedEvent(anchor, parentNodeXpath, REPLACE_ACTION, observedTimestamp);
}
'json list' | '/test-tree' | '{"branch": [{"name":"Name1"}, {"name":"Name2"}]}' || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"]
}
+ def 'Replace list data node using singular #scenario node'() {
+ given: 'schema set for given anchor and dataspace references test-tree model'
+ setupSchemaSetMocks('test-tree.yang')
+ when: 'replace data method is invoked with data and list node xpath'
+ objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, '/test-tree/branch[@name=\'Name\']', data, observedTimestamp, contentType)
+ then: 'the persistence service method is invoked with correct parameters'
+ 1 * mockCpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, '/test-tree/branch[@name=\'Name\']',
+ { dataNodes ->{
+ assert dataNodes.size() == 1
+ assert dataNodes.collect { it.getXpath() == '/test-tree/branch[@name=\'Name\']/nest' }
+ }})
+ and: 'the CpsValidator is called on the dataspaceName and AnchorName'
+ 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
+ where: 'the following data was used'
+ scenario | contentType | data
+ 'JSON data' | ContentType.JSON | '{"nest":{"name":"nestName"}}'
+ 'XML data' | ContentType.XML | '<nest><name>nestName</name></nest>'
+ }
+
def 'Replace data node using singular XML data node: #scenario.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
when: 'the webinfo (container) is updated'
json = '{"webinfo": {"domain-name":"newdomain.com" ,"contact-email":"info@newdomain.com" }}'
objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', json, now, ContentType.JSON)
- then: 'webinfo has been updated with teh new details'
+ then: 'webinfo has been updated with the new details'
def result = objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/webinfo', DIRECT_CHILDREN_ONLY)
result.leaves.'domain-name'[0] == 'newdomain.com'
result.leaves.'contact-email'[0] == 'info@newdomain.com'
restoreBookstoreDataAnchor(1)
}
+ def 'Update list items.'() {
+ given: 'list of books'
+ def existingJsonData = '{"books": [ {"title":"Existing Book", "lang":"English"}, {"title":"Another existing book", "lang":"French"} ] }'
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_2 , '/bookstore/categories[@code=\'1\']', existingJsonData, now, ContentType.JSON)
+ when: 'the books list is updated'
+ def updatedJsonData = '{"books": [ {"title":"Existing Book", "lang":"German"}, {"title":"A new book", "lang":"Hindi"} ] }'
+ objectUnderTest.updateDataNodeAndDescendants(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_2, '/bookstore/categories[@code=\'1\']', updatedJsonData, now, ContentType.JSON)
+ then: 'the expected number of updated books are retrieved'
+ def result = objectUnderTest.getDataNodesForMultipleXpaths(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_2, ['/bookstore/categories[@code=\'1\']/books[@title="Existing Book"]', '/bookstore/categories[@code=\'1\']/books[@title="A new book"]', '/bookstore/categories[@code=\'1\']/books[@title="Another existing book"]'], DIRECT_CHILDREN_ONLY)
+ assert result.size() == 2
+ and: 'the updated books have expected xpaths'
+ def xpaths = result*.xpath
+ assert xpaths.containsAll(["/bookstore/categories[@code='1']/books[@title='A new book']", "/bookstore/categories[@code='1']/books[@title='Existing Book']"])
+ and: 'the updated book has expected leaf value'
+ result[1].leaves['lang'] == 'German'
+ and: 'the book that was removed in the updated data is no longer present'
+ assert result.every { it.xpath != "/bookstore/categories[@code='1']/books[@title='Another existing book']" }
+ cleanup:
+ restoreBookstoreDataAnchor(2)
+ }
+
def 'Update bookstore top-level container data node.'() {
when: 'the bookstore top-level container is updated'
def json = '{ "bookstore": { "bookstore-name": "new bookstore" }}'