diff options
author | Arpit Singh <as00745003@techmahindra.com> | 2024-08-09 12:23:49 +0530 |
---|---|---|
committer | Arpit Singh <as00745003@techmahindra.com> | 2024-10-09 09:28:30 +0530 |
commit | 07acbb4ddd713f74406b156cbcac2507f96f3b08 (patch) | |
tree | 8f4122b9c3ecb994b9bb203cdd8b3fd1ad191f64 /cps-service/src/main | |
parent | e2517a8b993ed884edb251b91ce600d0a1a9fefe (diff) |
Implementation of Data validation feature in Create a Node API
Added support to validate JSON/XML data without the need of persisting
it in the databse.
- added "dryRunInQuery" flag as a new query parameter
- added new method as part of CpsDataService layer to perform data
validation
- added new method in yang parser "validateData" to validate
data without persisting it
Issue-ID: CPS-2361
Change-Id: I43dd33cc6120576d0fac606d5c4b0168d107311d
Signed-off-by: Arpit Singh <as00745003@techmahindra.com>
Diffstat (limited to 'cps-service/src/main')
4 files changed, 82 insertions, 13 deletions
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java index 68e1880d77..b3eff8eb26 100644 --- a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java @@ -322,4 +322,18 @@ public interface CpsDataService { Map<String, String> yangResourcesNameToContentMap, String targetData, FetchDescendantsOption fetchDescendantsOption); + + + /** + * Validates JSON or XML data by parsing it using the schema associated to an anchor within the given dataspace. + * Validation is performed without persisting the data. + * + * @param dataspaceName the name of the dataspace where the anchor is located. + * @param anchorName the name of the anchor used to validate the data. + * @param parentNodeXpath the xpath of the parent node where the data is to be validated. + * @param nodeData the JSON or XML data to be validated. + * @param contentType the content type of the data (e.g., JSON or XML). + */ + void validateData(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData, + ContentType contentType); } 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 index eed4f09bf0..b1b545be68 100644 --- 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 @@ -64,6 +64,7 @@ import org.springframework.stereotype.Service; public class CpsDataServiceImpl implements CpsDataService { private static final String ROOT_NODE_XPATH = "/"; + private static final String PARENT_NODE_XPATH_FOR_ROOT_NODE_XPATH = ""; private static final long DEFAULT_LOCK_TIMEOUT_IN_MILLISECONDS = 300L; private static final String NO_DATA_NODES = "No data nodes."; @@ -358,6 +359,14 @@ public class CpsDataServiceImpl implements CpsDataService { sendDataUpdatedEvent(anchor, listNodeXpath, Operation.DELETE, observedTimestamp); } + @Override + public void validateData(final String dataspaceName, final String anchorName, final String parentNodeXpath, + final String nodeData, final ContentType contentType) { + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); + final String xpath = ROOT_NODE_XPATH.equals(parentNodeXpath) ? PARENT_NODE_XPATH_FOR_ROOT_NODE_XPATH : + CpsPathUtil.getNormalizedXpath(parentNodeXpath); + yangParser.validateData(contentType, nodeData, anchor, xpath); + } private Collection<DataNode> rebuildSourceDataNodes(final String xpath, final Anchor sourceAnchor, final Collection<DataNode> sourceDataNodes) { @@ -422,7 +431,8 @@ public class CpsDataServiceImpl implements CpsDataService { final String nodeData, final ContentType contentType) { if (ROOT_NODE_XPATH.equals(parentNodeXpath)) { - final ContainerNode containerNode = yangParser.parseData(contentType, nodeData, anchor, ""); + final ContainerNode containerNode = yangParser.parseData(contentType, nodeData, + anchor, PARENT_NODE_XPATH_FOR_ROOT_NODE_XPATH); final Collection<DataNode> dataNodes = new DataNodeBuilder() .withContainerNode(containerNode) .buildCollection(); @@ -450,7 +460,7 @@ public class CpsDataServiceImpl implements CpsDataService { if (isRootNodeXpath(xpath)) { final ContainerNode containerNode = yangParser.parseData(contentType, nodeData, - yangResourcesNameToContentMap, ""); + yangResourcesNameToContentMap, PARENT_NODE_XPATH_FOR_ROOT_NODE_XPATH); final Collection<DataNode> dataNodes = new DataNodeBuilder() .withContainerNode(containerNode) .buildCollection(); diff --git a/cps-service/src/main/java/org/onap/cps/utils/YangParser.java b/cps-service/src/main/java/org/onap/cps/utils/YangParser.java index dc23c6bc4a..168e0999d5 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/YangParser.java +++ b/cps-service/src/main/java/org/onap/cps/utils/YangParser.java @@ -21,6 +21,9 @@ package org.onap.cps.utils; +import static org.onap.cps.utils.YangParserHelper.VALIDATE_AND_PARSE; +import static org.onap.cps.utils.YangParserHelper.VALIDATE_ONLY; + import io.micrometer.core.annotation.Timed; import java.util.Map; import lombok.RequiredArgsConstructor; @@ -57,11 +60,12 @@ public class YangParser { final String parentNodeXpath) { final SchemaContext schemaContext = getSchemaContext(anchor); try { - return yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath); + return yangParserHelper + .parseData(contentType, nodeData, schemaContext, parentNodeXpath, VALIDATE_AND_PARSE); } catch (final DataValidationException e) { invalidateCache(anchor); } - return yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath); + return yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath, VALIDATE_AND_PARSE); } /** @@ -78,7 +82,31 @@ public class YangParser { final Map<String, String> yangResourcesNameToContentMap, final String parentNodeXpath) { final SchemaContext schemaContext = getSchemaContext(yangResourcesNameToContentMap); - return yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath); + return yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath, VALIDATE_AND_PARSE); + } + + /** + * Parses data to validate it, using the schema context for given anchor. + * + * @param anchor the anchor used for node data validation + * @param parentNodeXpath the xpath of the parent node + * @param nodeData JSON or XML data string to validate + * @param contentType the content type of the data (e.g., JSON or XML) + * @throws DataValidationException if validation fails + */ + public void validateData(final ContentType contentType, + final String nodeData, + final Anchor anchor, + final String parentNodeXpath) { + final SchemaContext schemaContext = getSchemaContext(anchor); + try { + yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath, VALIDATE_ONLY); + } catch (final DataValidationException e) { + invalidateCache(anchor); + log.error("Data validation failed for anchor: {}, xpath: {}, details: {}", anchor, parentNodeXpath, + e.getMessage()); + } + yangParserHelper.parseData(contentType, nodeData, schemaContext, parentNodeXpath, VALIDATE_ONLY); } private SchemaContext getSchemaContext(final Anchor anchor) { diff --git a/cps-service/src/main/java/org/onap/cps/utils/YangParserHelper.java b/cps-service/src/main/java/org/onap/cps/utils/YangParserHelper.java index d95aceaf79..5612945ea9 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/YangParserHelper.java +++ b/cps-service/src/main/java/org/onap/cps/utils/YangParserHelper.java @@ -72,6 +72,9 @@ public class YangParserHelper { static final String DATA_ROOT_NODE_NAMESPACE = "urn:ietf:params:xml:ns:netconf:base:1.0"; static final String DATA_ROOT_NODE_TAG_NAME = "data"; + static final String DATA_VALIDATION_FAILURE_MESSAGE = "Data Validation Failed"; + static final boolean VALIDATE_ONLY = true; + static final boolean VALIDATE_AND_PARSE = false; /** * Parses data into NormalizedNode according to given schema context. @@ -85,11 +88,20 @@ public class YangParserHelper { public ContainerNode parseData(final ContentType contentType, final String nodeData, final SchemaContext schemaContext, - final String parentNodeXpath) { + final String parentNodeXpath, + final boolean validateOnly) { if (contentType == ContentType.JSON) { - return parseJsonData(nodeData, schemaContext, parentNodeXpath); + final ContainerNode validatedAndParsedJson = parseJsonData(nodeData, schemaContext, parentNodeXpath); + if (validateOnly) { + return null; + } + return validatedAndParsedJson; + } + final NormalizedNodeResult normalizedNodeResult = parseXmlData(nodeData, schemaContext, parentNodeXpath); + if (validateOnly) { + return null; } - return parseXmlData(nodeData, schemaContext, parentNodeXpath); + return buildContainerNodeFormNormalizedNodeResult(normalizedNodeResult); } private ContainerNode parseJsonData(final String jsonData, @@ -124,13 +136,13 @@ public class YangParserHelper { jsonParserStream.parse(jsonReader); } catch (final IOException | JsonSyntaxException | IllegalStateException | IllegalArgumentException exception) { throw new DataValidationException( - "Data Validation Failed", "Failed to parse json data. " + exception.getMessage(), exception); + DATA_VALIDATION_FAILURE_MESSAGE, "Failed to parse json data. " + exception.getMessage(), exception); } return dataContainerNodeBuilder.build(); } @SuppressFBWarnings(value = "DCN_NULLPOINTER_EXCEPTION", justification = "Problem originates in 3PP code") - private ContainerNode parseXmlData(final String xmlData, + private NormalizedNodeResult parseXmlData(final String xmlData, final SchemaContext schemaContext, final String parentNodeXpath) { final XMLInputFactory factory = XMLInputFactory.newInstance(); @@ -167,12 +179,17 @@ public class YangParserHelper { } catch (final XMLStreamException | URISyntaxException | IOException | SAXException | NullPointerException | ParserConfigurationException | TransformerException exception) { throw new DataValidationException( - "Data Validation Failed", "Failed to parse xml data: " + exception.getMessage(), exception); + DATA_VALIDATION_FAILURE_MESSAGE, "Failed to parse xml data: " + exception.getMessage(), exception); } + return normalizedNodeResult; + } + + private ContainerNode buildContainerNodeFormNormalizedNodeResult(final NormalizedNodeResult normalizedNodeResult) { + final DataContainerChild dataContainerChild = - (DataContainerChild) getFirstChildXmlRoot(normalizedNodeResult.getResult()); + (DataContainerChild) getFirstChildXmlRoot(normalizedNodeResult.getResult()); final YangInstanceIdentifier.NodeIdentifier nodeIdentifier = - new YangInstanceIdentifier.NodeIdentifier(dataContainerChild.getIdentifier().getNodeType()); + new YangInstanceIdentifier.NodeIdentifier(dataContainerChild.getIdentifier().getNodeType()); return Builders.containerBuilder().withChild(dataContainerChild).withNodeIdentifier(nodeIdentifier).build(); } |