2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2025-2026 OpenInfra Foundation Europe
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.ncmp.impl.data.policyexecutor;
23 import static java.util.Collections.emptyMap;
24 import static org.onap.cps.ncmp.api.data.models.OperationType.CREATE;
25 import static org.onap.cps.ncmp.api.data.models.OperationType.UPDATE;
27 import java.util.HashMap;
28 import java.util.List;
30 import lombok.RequiredArgsConstructor;
31 import lombok.extern.slf4j.Slf4j;
32 import org.onap.cps.ncmp.api.data.models.OperationType;
33 import org.onap.cps.ncmp.api.exceptions.ProvMnSException;
34 import org.onap.cps.ncmp.impl.provmns.ParameterHelper;
35 import org.onap.cps.ncmp.impl.provmns.RequestParameters;
36 import org.onap.cps.ncmp.impl.provmns.model.PatchItem;
37 import org.onap.cps.utils.JsonObjectMapper;
38 import org.springframework.http.HttpStatus;
39 import org.springframework.stereotype.Service;
43 @RequiredArgsConstructor
44 public class OperationDetailsFactory {
46 public static final OperationDetails DELETE_OPERATION_DETAILS = new OperationDetails("delete", "", emptyMap());
48 private static final String ATTRIBUTE_NAME_SEPARATOR = "/";
49 private static final String REGEX_FOR_LEADING_AND_TRAILING_SEPARATORS = "(^/)|(/$)";
51 private final JsonObjectMapper jsonObjectMapper;
54 * Create OperationDetails object from ProvMnS request details.
56 * @param requestParameters request parameters including uri-ldn-first-part, className and id
57 * @param patchItem provided request payload
58 * @return OperationDetails object
60 public OperationDetails buildOperationDetails(final RequestParameters requestParameters,
61 final PatchItem patchItem) {
62 final OperationDetails operationDetails;
63 switch (patchItem.getOp()) {
65 operationDetails = buildOperationDetailsForPatchItem(CREATE, requestParameters, patchItem);
68 if (patchItem.getPath().contains("#/attributes")) {
69 operationDetails = buildOperationDetailsForPatchItemWithHash(requestParameters, patchItem);
71 operationDetails = buildOperationDetailsForPatchItem(UPDATE, requestParameters, patchItem);
75 operationDetails = DELETE_OPERATION_DETAILS;
78 throw new ProvMnSException("PATCH", HttpStatus.UNPROCESSABLE_ENTITY,
79 "Unsupported Patch Operation Type: " + patchItem.getOp().getValue(), patchItem.getOp().getValue());
81 return operationDetails;
85 * Build a OperationDetails object from ProvMnS request details.
87 * @param operationType Type of operation create, update.
88 * @param requestParameters request parameters including uri-ldn-first-part, className and id
89 * @param resourceAsObject provided request payload
90 * @return OperationDetails object
92 public OperationDetails buildOperationDetails(final OperationType operationType,
93 final RequestParameters requestParameters,
94 final Object resourceAsObject) {
95 final ResourceObjectDetails resourceObjectDetails = createResourceObjectDetails(resourceAsObject,
97 final OperationEntry operationEntry = new OperationEntry(resourceObjectDetails.id(),
98 resourceObjectDetails.attributes());
99 final Map<String, List<OperationEntry>> changeRequestAsMap =
100 Map.of(resourceObjectDetails.objectClass(), List.of(operationEntry));
101 final String targetIdentifier = ParameterHelper.extractParentFdn(requestParameters.fdn());
102 return new OperationDetails(operationType.getOperationName(), targetIdentifier, changeRequestAsMap);
106 * Build OperationDetails for a specific patch item.
108 * @param operationType the type of operation (CREATE, UPDATE)
109 * @param requestParameters request parameters including uri-ldn-first-part, className and id
110 * @param patchItem the patch item containing operation details
111 * @return OperationDetails object for the patch item
113 public OperationDetails buildOperationDetailsForPatchItem(final OperationType operationType,
114 final RequestParameters requestParameters,
115 final PatchItem patchItem) {
116 final Map<String, Object> resourceAsObject = new HashMap<>(2);
117 resourceAsObject.put("id", requestParameters.id());
118 resourceAsObject.put("attributes", patchItem.getValue());
119 return buildOperationDetails(operationType, requestParameters, resourceAsObject);
122 private OperationDetails buildOperationDetailsForPatchItemWithHash(final RequestParameters requestParameters,
123 final PatchItem patchItem) {
124 final Map<String, Object> attributeHierarchyAsMap = createNestedMap(patchItem);
125 final OperationEntry operationEntry = new OperationEntry(requestParameters.id(), attributeHierarchyAsMap);
126 final String targetIdentifier = ParameterHelper.extractParentFdn(requestParameters.fdn());
127 final Map<String, List<OperationEntry>> operationEntriesPerObjectClass = new HashMap<>();
128 operationEntriesPerObjectClass.put(requestParameters.className(), List.of(operationEntry));
129 return new OperationDetails(UPDATE.getOperationName(), targetIdentifier, operationEntriesPerObjectClass);
132 @SuppressWarnings("unchecked")
133 private ResourceObjectDetails createResourceObjectDetails(final Object resourceAsObject,
134 final RequestParameters requestParameters) {
135 final String resourceAsJson = jsonObjectMapper.asJsonString(resourceAsObject);
136 final Map<String, Object> resourceAsMap = jsonObjectMapper.convertJsonString(resourceAsJson, Map.class);
137 return new ResourceObjectDetails(requestParameters.id(),
138 requestParameters.className(),
139 resourceAsMap.get("attributes"));
142 private Map<String, Object> createNestedMap(final PatchItem patchItem) {
143 final Map<String, Object> attributeHierarchyMap = new HashMap<>();
144 Map<String, Object> currentLevel = attributeHierarchyMap;
145 final String[] attributeHierarchyNames = patchItem.getPath().split("#/attributes")[1]
146 .replaceAll(REGEX_FOR_LEADING_AND_TRAILING_SEPARATORS, "")
147 .split(ATTRIBUTE_NAME_SEPARATOR);
148 for (int level = 0; level < attributeHierarchyNames.length; level++) {
149 final String attributeName = attributeHierarchyNames[level];
150 if (isLastLevel(attributeHierarchyNames, level)) {
151 currentLevel.put(attributeName, patchItem.getValue());
153 final Map<String, Object> nextLevel = new HashMap<>();
154 currentLevel.put(attributeName, nextLevel);
155 currentLevel = nextLevel;
158 return attributeHierarchyMap;
161 private boolean isLastLevel(final String[] attributeNamesArray, final int level) {
162 return level == attributeNamesArray.length - 1;