diff --git a/common/src/main/java/org/apache/atlas/repository/Constants.java b/common/src/main/java/org/apache/atlas/repository/Constants.java
index 9d3c7a3fde..ab19d31562 100644
--- a/common/src/main/java/org/apache/atlas/repository/Constants.java
+++ b/common/src/main/java/org/apache/atlas/repository/Constants.java
@@ -370,6 +370,8 @@ public final class Constants {
public static final String STORM_SOURCE = "storm";
public static final String FILE_SPOOL_SOURCE = "file_spool";
public static final String ASSET_POLICY_GUIDS = "assetPolicyGUIDs";
+
+ public static final String NON_COMPLIANT_ASSET_POLICY_GUIDS = "nonComplaintAssetPolicyGUIDs";
public static final String ASSET_POLICIES_COUNT = "assetPoliciesCount";
diff --git a/intg/src/main/java/org/apache/atlas/model/instance/MoveBusinessPolicyRequest.java b/intg/src/main/java/org/apache/atlas/model/instance/MoveBusinessPolicyRequest.java
new file mode 100644
index 0000000000..6fc640030c
--- /dev/null
+++ b/intg/src/main/java/org/apache/atlas/model/instance/MoveBusinessPolicyRequest.java
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.atlas.model.instance;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
+
+import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
+import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.PUBLIC_ONLY;
+
+/**
+ * Request to link/unlink policies from asset.
+ */
+@JsonAutoDetect(getterVisibility = PUBLIC_ONLY, setterVisibility = PUBLIC_ONLY, fieldVisibility = NONE)
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.PROPERTY)
+public class MoveBusinessPolicyRequest implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private Set policyIds;
+ private String type;
+
+ public Set getPolicyIds() {
+ return policyIds;
+ }
+
+ public void setPolicyIds(Set policyIds) {
+ this.policyIds = policyIds;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("MoveBusinessPolicyRequest{");
+ sb.append("policyIds=").append(policyIds);
+ sb.append(", type='").append(type).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public enum Type {
+ GOVERNED ("governed"), NON_COMPLIANT ("non_compliant");
+ private final String description;
+ Type(String description) {
+ this.description = description;
+ }
+
+ // Getter method to retrieve the description
+ public String getDescription() {
+ return description;
+ }
+ }
+
+
+
+}
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasEntityStore.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasEntityStore.java
index 24ca97d731..9f1a3f7db1 100644
--- a/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasEntityStore.java
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasEntityStore.java
@@ -371,4 +371,5 @@ EntityMutationResponse deleteByUniqueAttributes(List objectIds)
void unlinkBusinessPolicy(String policyId, Set unlinkGuids) throws AtlasBaseException;
+ void moveBusinessPolicy(Set policyId, String assetId, String type) throws AtlasBaseException;
}
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java
index c0bb42298c..180efa3af2 100644
--- a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java
@@ -2822,6 +2822,34 @@ private void handleBusinessPolicyMutation(List vertices) throws Atl
}
+ @Override
+ @GraphTransaction
+ public void moveBusinessPolicy(Set policyIds, String assetId, String type) throws AtlasBaseException {
+ // Start performance metric recording
+ AtlasPerfMetrics.MetricRecorder metric = RequestContext.get().startMetricRecord("moveBusinessPolicy.GraphTransaction");
+
+ try {
+ // Attempt to move the business policy using the entityGraphMapper
+ AtlasVertex vertex = entityGraphMapper.moveBusinessPolicy(policyIds, assetId, type);
+
+ if (vertex == null) {
+ LOG.warn("No vertex found for assetId: {}", assetId);
+ return;
+ }
+
+ handleBusinessPolicyMutation(Collections.singletonList(vertex));
+ } catch (Exception e) {
+ // Log the error with context and rethrow it wrapped in an AtlasBaseException
+ LOG.error("Error during moveBusinessPolicy for assetId: {}", assetId, e);
+ throw new AtlasBaseException(e);
+ } finally {
+ // End the performance metric recording
+ RequestContext.get().endMetricRecord(metric);
+ }
+ }
+
+
+
}
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphMapper.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphMapper.java
index 865d58a2b2..16d080c286 100644
--- a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphMapper.java
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityGraphMapper.java
@@ -28,15 +28,7 @@
import org.apache.atlas.exception.EntityNotFoundException;
import org.apache.atlas.model.TimeBoundary;
import org.apache.atlas.model.TypeCategory;
-import org.apache.atlas.model.instance.AtlasClassification;
-import org.apache.atlas.model.instance.AtlasEntity;
-import org.apache.atlas.model.instance.AtlasEntityHeader;
-import org.apache.atlas.model.instance.AtlasObjectId;
-import org.apache.atlas.model.instance.AtlasRelatedObjectId;
-import org.apache.atlas.model.instance.AtlasRelationship;
-import org.apache.atlas.model.instance.AtlasStruct;
-import org.apache.atlas.model.instance.EntityMutationResponse;
-import org.apache.atlas.model.instance.EntityMutations;
+import org.apache.atlas.model.instance.*;
import org.apache.atlas.model.instance.EntityMutations.EntityOperation;
import org.apache.atlas.model.tasks.AtlasTask;
import org.apache.atlas.model.typedef.AtlasEntityDef;
@@ -4654,6 +4646,55 @@ public List unlinkBusinessPolicy(String policyId, Set unlin
}).collect(Collectors.toList());
}
+ public AtlasVertex moveBusinessPolicy(Set policyIds, String assetId, String type) {
+ // Retrieve the AtlasVertex for the given assetId
+ AtlasVertex assetVertex = AtlasGraphUtilsV2.findByGuid(graph, assetId);
+
+ // Get the sets of governed and non-compliant policy GUIDs
+ Set governedPolicies = assetVertex.getMultiValuedSetProperty(ASSET_POLICY_GUIDS, String.class);
+ Set nonCompliantPolicies = assetVertex.getMultiValuedSetProperty(NON_COMPLIANT_ASSET_POLICY_GUIDS, String.class);
+
+ // Determine if the type is governed or non-compliant and move policies accordingly
+ boolean isGoverned = MoveBusinessPolicyRequest.Type.GOVERNED.getDescription().equals(type);
+
+ policyIds.forEach(policyId -> {
+ if (isGoverned) {
+ assetVertex.setProperty(ASSET_POLICY_GUIDS, policyId);
+ AtlasGraphUtilsV2.removeItemFromListPropertyValue(assetVertex, NON_COMPLIANT_ASSET_POLICY_GUIDS, policyId);
+ } else {
+ assetVertex.setProperty(NON_COMPLIANT_ASSET_POLICY_GUIDS, policyId);
+ AtlasGraphUtilsV2.removeItemFromListPropertyValue(assetVertex, ASSET_POLICY_GUIDS, policyId);
+ }
+ });
+
+ // Update the sets after processing
+ if (isGoverned) {
+ governedPolicies.addAll(policyIds);
+ nonCompliantPolicies.removeAll(policyIds);
+ } else {
+ nonCompliantPolicies.addAll(policyIds);
+ governedPolicies.removeAll(policyIds);
+ }
+
+ // Update the modification metadata
+ updateModificationMetadata(assetVertex);
+
+ // Create a differential AtlasEntity to reflect the changes
+ AtlasEntity diffEntity = new AtlasEntity(assetVertex.getProperty(TYPE_NAME_PROPERTY_KEY, String.class));
+ diffEntity.setGuid(assetVertex.getProperty(GUID_PROPERTY_KEY, String.class));
+ diffEntity.setAttribute(ASSET_POLICY_GUIDS, governedPolicies);
+ diffEntity.setAttribute(NON_COMPLIANT_ASSET_POLICY_GUIDS, nonCompliantPolicies);
+ diffEntity.setUpdatedBy(assetVertex.getProperty(MODIFIED_BY_KEY, String.class));
+ diffEntity.setUpdateTime(new Date(RequestContext.get().getRequestTime()));
+
+ // Cache the differential entity for further processing
+ RequestContext.get().cacheDifferentialEntity(diffEntity);
+
+ return assetVertex;
+ }
+
+
+
private void cacheDifferentialEntity(AtlasVertex ev, Set existingValues) {
AtlasEntity diffEntity = new AtlasEntity(ev.getProperty(TYPE_NAME_PROPERTY_KEY, String.class));
@@ -4667,4 +4708,7 @@ private void cacheDifferentialEntity(AtlasVertex ev, Set existingValues)
requestContext.cacheDifferentialEntity(diffEntity);
}
+
+
+
}
diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/BusinessPolicyREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/BusinessPolicyREST.java
index ac0a52f9f5..990fd0c25d 100644
--- a/webapp/src/main/java/org/apache/atlas/web/rest/BusinessPolicyREST.java
+++ b/webapp/src/main/java/org/apache/atlas/web/rest/BusinessPolicyREST.java
@@ -5,6 +5,7 @@
import org.apache.atlas.annotation.Timed;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.instance.LinkBusinessPolicyRequest;
+import org.apache.atlas.model.instance.MoveBusinessPolicyRequest;
import org.apache.atlas.repository.store.graph.AtlasEntityStore;
import org.apache.atlas.utils.AtlasPerfMetrics;
import org.apache.atlas.utils.AtlasPerfTracer;
@@ -112,4 +113,45 @@ public void unlinkBusinessPolicy(@PathParam("policyId") final String policyGuid,
RequestContext.get().endMetricRecord(metric);
}
}
+
+
+ @POST
+ @Path("/move/{assetId}")
+ @Timed
+ public void moveBusinessPolicy(
+ @PathParam("assetId") String assetId,
+ final MoveBusinessPolicyRequest request
+ ) throws AtlasBaseException {
+ // Start performance metric recording
+ AtlasPerfMetrics.MetricRecorder metric = RequestContext.get().startMetricRecord("moveBusinessPolicy");
+
+ // Ensure the current user is authorized to move the policy
+ String currentUser = RequestContext.getCurrentUser();
+ if (!ARGO_SERVICE_USER_NAME.equals(currentUser)) {
+ throw new AtlasBaseException(AtlasErrorCode.UNAUTHORIZED_ACCESS, currentUser, "moveBusinessPolicy");
+ }
+
+ // Set request context parameters for the current operation
+ RequestContext requestContext = RequestContext.get();
+ requestContext.setIncludeClassifications(false);
+ requestContext.setIncludeMeanings(false);
+ requestContext.getRequestContextHeaders().put("x-atlan-route", "business-policy-rest");
+
+ AtlasPerfTracer perf = null;
+ try {
+ // Start performance tracing if enabled
+ if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+ perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "BusinessPolicyREST.moveBusinessPolicy(" + assetId + ")");
+ }
+
+ // Move the business policy using the entitiesStore
+ entitiesStore.moveBusinessPolicy(request.getPolicyIds(), assetId, request.getType());
+ } finally {
+ // Log performance trace and end metric recording
+ AtlasPerfTracer.log(perf);
+ requestContext.endMetricRecord(metric);
+ }
+ }
+
}
+