Repository: incubator-unomi
Updated Branches:
  refs/heads/UNOMI-28-ES-2-X-UPGRADE 3ebc65be1 -> 12641b9b3


UNOMI-48: Add/improve validation done at scoring/segment deletion


Project: http://git-wip-us.apache.org/repos/asf/incubator-unomi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-unomi/commit/30a51a23
Tree: http://git-wip-us.apache.org/repos/asf/incubator-unomi/tree/30a51a23
Diff: http://git-wip-us.apache.org/repos/asf/incubator-unomi/diff/30a51a23

Branch: refs/heads/UNOMI-28-ES-2-X-UPGRADE
Commit: 30a51a23dc73c6c6c794056aeeed2108c146bc60
Parents: 1c5d74a
Author: Quentin Lamerand <qlamer...@jahia.com>
Authored: Fri Aug 12 16:15:56 2016 +0200
Committer: Quentin Lamerand <qlamer...@jahia.com>
Committed: Fri Aug 12 16:15:56 2016 +0200

----------------------------------------------------------------------
 .../unomi/api/segments/DependentMetadata.java   |  53 +++++
 .../unomi/api/services/SegmentService.java      |  41 ++--
 .../cxs/conditions/scoringCondition.json        |  40 ++++
 .../unomi/rest/ScoringServiceEndPoint.java      |  29 ++-
 .../unomi/rest/SegmentServiceEndPoint.java      |  23 +-
 .../services/services/SegmentServiceImpl.java   | 230 ++++++++++++++++---
 6 files changed, 358 insertions(+), 58 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/30a51a23/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java
----------------------------------------------------------------------
diff --git 
a/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java 
b/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java
new file mode 100644
index 0000000..a6693f3
--- /dev/null
+++ b/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java
@@ -0,0 +1,53 @@
+/*
+ * 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.unomi.api.segments;
+
+import org.apache.unomi.api.Metadata;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+import java.util.List;
+
+@XmlRootElement
+public class DependentMetadata implements Serializable {
+
+    private List<Metadata> segments;
+
+    private List<Metadata> scorings;
+
+    public DependentMetadata(List<Metadata> segments, List<Metadata> scorings) 
{
+        this.segments = segments;
+        this.scorings = scorings;
+    }
+
+    public List<Metadata> getSegments() {
+        return segments;
+    }
+
+    public void setSegments(List<Metadata> segments) {
+        this.segments = segments;
+    }
+
+    public List<Metadata> getScorings() {
+        return scorings;
+    }
+
+    public void setScorings(List<Metadata> scorings) {
+        this.scorings = scorings;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/30a51a23/api/src/main/java/org/apache/unomi/api/services/SegmentService.java
----------------------------------------------------------------------
diff --git 
a/api/src/main/java/org/apache/unomi/api/services/SegmentService.java 
b/api/src/main/java/org/apache/unomi/api/services/SegmentService.java
index 8c1db29..a1affd6 100644
--- a/api/src/main/java/org/apache/unomi/api/services/SegmentService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/SegmentService.java
@@ -22,6 +22,7 @@ import org.apache.unomi.api.Metadata;
 import org.apache.unomi.api.PartialList;
 import org.apache.unomi.api.Profile;
 import org.apache.unomi.api.query.Query;
+import org.apache.unomi.api.segments.DependentMetadata;
 import org.apache.unomi.api.segments.Scoring;
 import org.apache.unomi.api.segments.Segment;
 import org.apache.unomi.api.segments.SegmentsAndScores;
@@ -88,25 +89,23 @@ public interface SegmentService {
     /**
      * Removes the segment definition identified by the specified identifier. 
We can specify that we want the operation to be validated beforehand so that we 
can
      * know if any other segment that might use the segment we're trying to 
delete as a condition might be impacted. If {@code validate} is set to {@code 
false}, no
-     * validation is performed. If set to {@code true}, we will first check if 
any segment depends on the one we're trying to delete and if so we will not 
delete the
-     * segment but rather return the list of the metadata of the impacted 
segments. If no dependents are found, then we properly delete the segment.
+     * validation is performed. If set to {@code true}, we will first check if 
any segment or scoring depends on the segment we're trying to delete and if so 
we will not delete the
+     * segment but rather return the list of the metadata of the impacted 
items. If no dependents are found, then we properly delete the segment.
      *
      * @param segmentId the identifier of the segment we want to delete
      * @param validate  whether or not to perform validation
-     * @return a list of impacted segment metadata if any or an empty list if 
no such impacted segments are found or validation was skipped
+     * @return a list of impacted segment metadata if any or an empty list if 
none were found or validation was skipped
      */
-    List<Metadata> removeSegmentDefinition(String segmentId, boolean validate);
+    DependentMetadata removeSegmentDefinition(String segmentId, boolean 
validate);
 
     /**
-     * Retrieves the list of segment metadata of segments depending on the 
segment identified by the specified identifier. A segment is depending on 
another one if it includes
-     * that segment as part of its condition for profile matching.
+     * Retrieves the list of Segment and Scoring metadata depending on the 
specified segment.
+     * A segment or scoring is depending on a segment if it includes a 
profileSegmentCondition with a test on this segment.
      *
-     * TODO: Rename to something clearer, maybe getDependentSegmentMetadata?
-     *
-     * @param segmentId the identifier of the segment which impact we want to 
evaluate
-     * @return a list of metadata of segments depending on the specified 
segment
+     * @param segmentId the segment identifier
+     * @return a list of Segment/Scoring Metadata depending on the specified 
segment
      */
-    List<Metadata> getImpactedSegmentMetadata(String segmentId);
+    DependentMetadata getSegmentDependentMetadata(String segmentId);
 
     /**
      * Retrieves a list of profiles matching the conditions defined by the 
segment identified by the specified identifier, ordered according to the 
specified {@code sortBy}
@@ -197,10 +196,24 @@ public interface SegmentService {
     void createScoringDefinition(String scope, String scoringId, String name, 
String description);
 
     /**
-     * Deletes the scoring identified by the specified identifier from the 
context server.
+     * Removes the scoring definition identified by the specified identifier. 
We can specify that we want the operation to be validated beforehand so that we 
can
+     * know if any other segment that might use the segment we're trying to 
delete as a condition might be impacted. If {@code validate} is set to {@code 
false}, no
+     * validation is performed. If set to {@code true}, we will first check if 
any segment or scoring depends on the scoring we're trying to delete and if so 
we will not delete the
+     * scoring but rather return the list of the metadata of the impacted 
items. If no dependents are found, then we properly delete the scoring.
+     *
+     * @param scoringId the identifier of the scoring we want to delete
+     * @param validate  whether or not to perform validation
+     * @return a list of impacted items metadata if any or an empty list if 
none were found or validation was skipped
+     */
+    DependentMetadata removeScoringDefinition(String scoringId, boolean 
validate);
+
+    /**
+     * Retrieves the list of Segment and Scoring metadata depending on the 
specified scoring.
+     * A segment or scoring is depending on a segment if it includes a 
scoringCondition with a test on this scoring.
      *
-     * @param scoringId the identifier of the scoring to be deleted
+     * @param scoringId the segment identifier
+     * @return a list of Segment/Scoring Metadata depending on the specified 
scoring
      */
-    void removeScoringDefinition(String scoringId);
+    DependentMetadata getScoringDependentMetadata(String scoringId);
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/30a51a23/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/scoringCondition.json
----------------------------------------------------------------------
diff --git 
a/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/scoringCondition.json
 
b/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/scoringCondition.json
new file mode 100644
index 0000000..6ef37fc
--- /dev/null
+++ 
b/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/scoringCondition.json
@@ -0,0 +1,40 @@
+{
+  "metadata": {
+    "id": "scoringCondition",
+    "name": "scoringCondition",
+    "description": "",
+    "tags": [
+      "aggregated",
+      "profileCondition"
+    ],
+    "readOnly": true
+  },
+  "parentCondition": {
+    "type": "profilePropertyCondition",
+    "parameterValues": {
+      "propertyName": "script::'scores.'+scoringPlanId",
+      "propertyValueInteger": "parameter::scoreValue",
+      "comparisonOperator": "parameter::comparisonOperator"
+    }
+  },
+  "parameters": [
+    {
+      "id": "scoringPlanId",
+      "type": "string",
+      "multivalued": false,
+      "defaultValue": ""
+    },
+    {
+      "id": "scoreValue",
+      "type": "integer",
+      "multivalued": false,
+      "defaultValue": 0
+    },
+    {
+      "id": "comparisonOperator",
+      "type": "string",
+      "multivalued": false,
+      "defaultValue": "greaterThanOrEqualTo"
+    }
+  ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/30a51a23/rest/src/main/java/org/apache/unomi/rest/ScoringServiceEndPoint.java
----------------------------------------------------------------------
diff --git 
a/rest/src/main/java/org/apache/unomi/rest/ScoringServiceEndPoint.java 
b/rest/src/main/java/org/apache/unomi/rest/ScoringServiceEndPoint.java
index 0865b8d..e8307bd 100644
--- a/rest/src/main/java/org/apache/unomi/rest/ScoringServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/ScoringServiceEndPoint.java
@@ -22,6 +22,7 @@ import org.apache.unomi.api.Item;
 import org.apache.unomi.api.Metadata;
 import org.apache.unomi.api.PartialList;
 import org.apache.unomi.api.query.Query;
+import org.apache.unomi.api.segments.DependentMetadata;
 import org.apache.unomi.api.segments.Scoring;
 import org.apache.unomi.api.services.SegmentService;
 
@@ -117,14 +118,32 @@ public class ScoringServiceEndPoint {
     }
 
     /**
-     * Deletes the scoring identified by the specified identifier from the 
context server.
+     * Removes the scoring definition identified by the specified identifier. 
We can specify that we want the operation to be validated beforehand so that we 
can
+     * know if any other segment that might use the segment we're trying to 
delete as a condition might be impacted. If {@code validate} is set to {@code 
false}, no
+     * validation is performed. If set to {@code true}, we will first check if 
any segment or scoring depends on the scoring we're trying to delete and if so 
we will not delete the
+     * scoring but rather return the list of the metadata of the impacted 
items. If no dependents are found, then we properly delete the scoring.
      *
-     * @param scoringId the identifier of the scoring to be deleted
+     * @param scoringId the identifier of the scoring we want to delete
+     * @param validate  whether or not to perform validation
+     * @return a list of impacted items metadata if any or an empty list if 
none were found or validation was skipped
      */
-    @DELETE
+   @DELETE
     @Path("/{scoringID}")
-    public void removeScoringDefinition(@PathParam("scoringID") String 
scoringId) {
-        segmentService.removeScoringDefinition(scoringId);
+    public DependentMetadata removeScoringDefinition(@PathParam("scoringID") 
String scoringId, @QueryParam("validate") boolean validate) {
+        return segmentService.removeScoringDefinition(scoringId, validate);
+    }
+
+    /**
+     * Retrieves the list of Segment and Scoring metadata depending on the 
specified scoring.
+     * A segment or scoring is depending on a segment if it includes a 
scoringCondition with a test on this scoring.
+     *
+     * @param scoringId the segment identifier
+     * @return a list of Segment/Scoring Metadata depending on the specified 
scoring
+     */
+    @GET
+    @Path("/{scoringID}/impacted")
+    public DependentMetadata 
getScoringDependentMetadata(@PathParam("scoringID") String scoringId) {
+        return segmentService.getScoringDependentMetadata(scoringId);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/30a51a23/rest/src/main/java/org/apache/unomi/rest/SegmentServiceEndPoint.java
----------------------------------------------------------------------
diff --git 
a/rest/src/main/java/org/apache/unomi/rest/SegmentServiceEndPoint.java 
b/rest/src/main/java/org/apache/unomi/rest/SegmentServiceEndPoint.java
index c74c370..7c1e573 100644
--- a/rest/src/main/java/org/apache/unomi/rest/SegmentServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/SegmentServiceEndPoint.java
@@ -22,6 +22,7 @@ import org.apache.unomi.api.Metadata;
 import org.apache.unomi.api.PartialList;
 import org.apache.unomi.api.Profile;
 import org.apache.unomi.api.query.Query;
+import org.apache.unomi.api.segments.DependentMetadata;
 import org.apache.unomi.api.segments.Segment;
 import org.apache.unomi.api.services.SegmentService;
 
@@ -110,18 +111,16 @@ public class SegmentServiceEndPoint {
     }
 
     /**
-     * Retrieves the list of segment metadata of segments depending on the 
segment identified by the specified identifier. A segment is depending on 
another one if it includes
-     * that segment as part of its condition for profile matching.
+     * Retrieves the list of Segment and Scoring metadata depending on the 
specified segment.
+     * A segment or scoring is depending on a segment if it includes a 
profileSegmentCondition with a test on this segment.
      *
-     * TODO: rename?
-     *
-     * @param segmentId the identifier of the segment which impact we want to 
evaluate
-     * @return a list of metadata of segments depending on the specified 
segment
+     * @param segmentId the segment identifier
+     * @return a list of Segment/Scoring Metadata depending on the specified 
segment
      */
     @GET
     @Path("/{segmentID}/impacted")
-    public List<Metadata> getSegmentImpacted(@PathParam("segmentID") String 
segmentId) {
-        return segmentService.getImpactedSegmentMetadata(segmentId);
+    public DependentMetadata 
getSegmentDependentMetadata(@PathParam("segmentID") String segmentId) {
+        return segmentService.getSegmentDependentMetadata(segmentId);
     }
 
     /**
@@ -162,16 +161,16 @@ public class SegmentServiceEndPoint {
     /**
      * Removes the segment definition identified by the specified identifier. 
We can specify that we want the operation to be validated beforehand so that we 
can
      * know if any other segment that might use the segment we're trying to 
delete as a condition might be impacted. If {@code validate} is set to {@code 
false}, no
-     * validation is performed. If set to {@code true}, we will first check if 
any segment depends on the one we're trying to delete and if so we will not 
delete the
-     * segment but rather return the list of the metadata of the impacted 
segments. If no dependents are found, then we properly delete the segment.
+     * validation is performed. If set to {@code true}, we will first check if 
any segment or scoring depends on the segment we're trying to delete and if so 
we will not delete the
+     * segment but rather return the list of the metadata of the impacted 
items. If no dependents are found, then we properly delete the segment.
      *
      * @param segmentId the identifier of the segment we want to delete
      * @param validate  whether or not to perform validation
-     * @return a list of impacted segment metadata if any or an empty list if 
no such impacted segments are found or validation was skipped
+     * @return a list of impacted segment metadata if any or an empty list if 
none were found or validation was skipped
      */
     @DELETE
     @Path("/{segmentID}")
-    public List<Metadata> removeSegmentDefinition(@PathParam("segmentID") 
String segmentId, @QueryParam("validate") boolean validate) {
+    public DependentMetadata removeSegmentDefinition(@PathParam("segmentID") 
String segmentId, @QueryParam("validate") boolean validate) {
         return segmentService.removeSegmentDefinition(segmentId, validate);
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/30a51a23/services/src/main/java/org/apache/unomi/services/services/SegmentServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/services/src/main/java/org/apache/unomi/services/services/SegmentServiceImpl.java
 
b/services/src/main/java/org/apache/unomi/services/services/SegmentServiceImpl.java
index 6149f62..bd68971 100644
--- 
a/services/src/main/java/org/apache/unomi/services/services/SegmentServiceImpl.java
+++ 
b/services/src/main/java/org/apache/unomi/services/services/SegmentServiceImpl.java
@@ -21,13 +21,9 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import org.apache.unomi.api.*;
 import org.apache.unomi.api.actions.Action;
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.api.conditions.ConditionType;
 import org.apache.unomi.api.query.Query;
 import org.apache.unomi.api.rules.Rule;
-import org.apache.unomi.api.segments.Scoring;
-import org.apache.unomi.api.segments.ScoringElement;
-import org.apache.unomi.api.segments.Segment;
-import org.apache.unomi.api.segments.SegmentsAndScores;
+import org.apache.unomi.api.segments.*;
 import org.apache.unomi.api.services.DefinitionsService;
 import org.apache.unomi.api.services.EventService;
 import org.apache.unomi.api.services.RulesService;
@@ -253,23 +249,26 @@ public class SegmentServiceImpl implements 
SegmentService, SynchronousBundleList
         updateExistingProfilesForSegment(segment);
     }
 
-    private void checkIfSegmentIsImpacted(Segment segment, Condition 
condition, String segmentToDeleteId, Set<Segment> impactedSegments) {
+    private boolean checkSegmentDeletionImpact(Condition condition, String 
segmentToDeleteId) {
         if(condition != null) {
             @SuppressWarnings("unchecked")
             final List<Condition> subConditions = (List<Condition>) 
condition.getParameter("subConditions");
             if (subConditions != null) {
                 for (Condition subCondition : subConditions) {
-                    checkIfSegmentIsImpacted(segment, subCondition, 
segmentToDeleteId, impactedSegments);
+                    if (checkSegmentDeletionImpact(subCondition, 
segmentToDeleteId)) {
+                        return true;
+                    }
                 }
             } else if 
("profileSegmentCondition".equals(condition.getConditionTypeId())) {
                 @SuppressWarnings("unchecked")
                 final List<String> referencedSegmentIds = (List<String>) 
condition.getParameter("segments");
 
                 if (referencedSegmentIds.indexOf(segmentToDeleteId) >= 0) {
-                    impactedSegments.add(segment);
+                    return true;
                 }
             }
         }
+        return false;
     }
 
     /**
@@ -280,13 +279,13 @@ public class SegmentServiceImpl implements 
SegmentService, SynchronousBundleList
      * @param segmentId the segment id to remove in the condition
      * @return updated condition
      */
-    private Condition updateImpactedCondition(Condition condition, String 
segmentId) {
+    private Condition updateSegmentDependentCondition(Condition condition, 
String segmentId) {
         if ("booleanCondition".equals(condition.getConditionTypeId())) {
             @SuppressWarnings("unchecked")
             final List<Condition> subConditions = (List<Condition>) 
condition.getParameter("subConditions");
             List<Condition> updatedSubConditions = new LinkedList<>();
             for (Condition subCondition : subConditions) {
-                Condition updatedCondition = 
updateImpactedCondition(subCondition, segmentId);
+                Condition updatedCondition = 
updateSegmentDependentCondition(subCondition, segmentId);
                 if(updatedCondition != null) {
                     updatedSubConditions.add(updatedCondition);
                 }
@@ -316,26 +315,45 @@ public class SegmentServiceImpl implements 
SegmentService, SynchronousBundleList
         return condition;
     }
 
-    private Set<Segment> getImpactedSegments(String segmentId) {
+    private Set<Segment> getSegmentDependentSegments(String segmentId) {
         Set<Segment> impactedSegments = new HashSet<>(this.allSegments.size());
         for (Segment segment : this.allSegments) {
-            checkIfSegmentIsImpacted(segment, segment.getCondition(), 
segmentId, impactedSegments);
+            if (checkSegmentDeletionImpact(segment.getCondition(), segmentId)) 
{
+                impactedSegments.add(segment);
+            }
         }
         return impactedSegments;
     }
 
-    public List<Metadata> getImpactedSegmentMetadata(String segmentId) {
-        List<Metadata> details = new LinkedList<>();
-        for (Segment definition : getImpactedSegments(segmentId)) {
-            details.add(definition.getMetadata());
+    private Set<Scoring> getSegmentDependentScorings(String segmentId) {
+        Set<Scoring> impactedScoring = new HashSet<>(this.allScoring.size());
+        for (Scoring scoring : this.allScoring) {
+            for (ScoringElement element : scoring.getElements()) {
+                if (checkSegmentDeletionImpact(element.getCondition(), 
segmentId)) {
+                    impactedScoring.add(scoring);
+                    break;
+                }
+            }
         }
+        return impactedScoring;
+    }
 
-        return details;
+    public DependentMetadata getSegmentDependentMetadata(String segmentId) {
+        List<Metadata> segments = new LinkedList<>();
+        List<Metadata> scorings = new LinkedList<>();
+        for (Segment definition : getSegmentDependentSegments(segmentId)) {
+            segments.add(definition.getMetadata());
+        }
+        for (Scoring definition : getSegmentDependentScorings(segmentId)) {
+            scorings.add(definition.getMetadata());
+        }
+        return new DependentMetadata(segments, scorings);
     }
 
-    public List<Metadata> removeSegmentDefinition(String segmentId, boolean 
validate) {
-        Set<Segment> impactedSegments = getImpactedSegments(segmentId);
-        if (!validate || impactedSegments.isEmpty()) {
+    public DependentMetadata removeSegmentDefinition(String segmentId, boolean 
validate) {
+        Set<Segment> impactedSegments = getSegmentDependentSegments(segmentId);
+        Set<Scoring> impactedScorings = getSegmentDependentScorings(segmentId);
+        if (!validate || (impactedSegments.isEmpty() && 
impactedScorings.isEmpty())) {
             // update profiles
             Condition segmentCondition = new Condition();
             
segmentCondition.setConditionType(definitionsService.getConditionType("profilePropertyCondition"));
@@ -351,7 +369,7 @@ public class SegmentServiceImpl implements SegmentService, 
SynchronousBundleList
 
             // update impacted segments
             for (Segment segment : impactedSegments) {
-                Condition updatedCondition = 
updateImpactedCondition(segment.getCondition(), segmentId);
+                Condition updatedCondition = 
updateSegmentDependentCondition(segment.getCondition(), segmentId);
                 segment.setCondition(updatedCondition);
                 if(updatedCondition == null) {
                     
clearAutoGeneratedRules(persistenceService.query("linkedItems", 
segment.getMetadata().getId(), null, Rule.class), 
segment.getMetadata().getId());
@@ -360,16 +378,38 @@ public class SegmentServiceImpl implements 
SegmentService, SynchronousBundleList
                 setSegmentDefinition(segment);
             }
 
+            // update impacted scorings
+            for (Scoring scoring : impactedScorings) {
+                List<ScoringElement> updatedScoringElements = new 
ArrayList<>();
+                for (ScoringElement scoringElement : scoring.getElements()) {
+                    Condition updatedCondition = 
updateSegmentDependentCondition(scoringElement.getCondition(), segmentId);
+                    if (updatedCondition != null) {
+                        scoringElement.setCondition(updatedCondition);
+                        updatedScoringElements.add(scoringElement);
+                    }
+                }
+                scoring.setElements(updatedScoringElements);
+                if (updatedScoringElements.isEmpty()) {
+                    
clearAutoGeneratedRules(persistenceService.query("linkedItems", 
scoring.getMetadata().getId(), null, Rule.class), 
scoring.getMetadata().getId());
+                    scoring.getMetadata().setEnabled(false);
+                }
+                setScoringDefinition(scoring);
+            }
+
             persistenceService.remove(segmentId, Segment.class);
             List<Rule> previousRules = persistenceService.query("linkedItems", 
segmentId, null, Rule.class);
             clearAutoGeneratedRules(previousRules, segmentId);
         }
 
-        List<Metadata> metadata = new LinkedList<>();
+        List<Metadata> segments = new LinkedList<>();
+        List<Metadata> scorings = new LinkedList<>();
         for (Segment definition : impactedSegments) {
-            metadata.add(definition.getMetadata());
+            segments.add(definition.getMetadata());
+        }
+        for (Scoring definition : impactedScorings) {
+            scorings.add(definition.getMetadata());
         }
-        return metadata;
+        return new DependentMetadata(segments, scorings);
     }
 
 
@@ -504,10 +544,146 @@ public class SegmentServiceImpl implements 
SegmentService, SynchronousBundleList
         setScoringDefinition(scoring);
     }
 
-    public void removeScoringDefinition(String scoringId) {
-        persistenceService.remove(scoringId, Scoring.class);
+    private boolean checkScoringDeletionImpact(Condition condition, String 
scoringToDeleteId) {
+        if(condition != null) {
+            @SuppressWarnings("unchecked")
+            final List<Condition> subConditions = (List<Condition>) 
condition.getParameter("subConditions");
+            if (subConditions != null) {
+                for (Condition subCondition : subConditions) {
+                    if (checkScoringDeletionImpact(subCondition, 
scoringToDeleteId)) {
+                        return true;
+                    }
+                }
+            } else if 
("scoringCondition".equals(condition.getConditionTypeId())) {
+                if 
(scoringToDeleteId.equals(condition.getParameter("scoringPlanId"))) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return an updated condition that do not contain a condition on the 
scoringId anymore
+     * it's remove the unnecessary boolean condition (if a condition is the 
only one of a boolean the boolean will be remove and the subcondition returned)
+     * it's return null when there is no more condition after (if the 
condition passed was only a scoring condition on the scoringId)
+     * @param condition the condition to update
+     * @param scoringId the scoring id to remove in the condition
+     * @return updated condition
+     */
+    private Condition updateScoringDependentCondition(Condition condition, 
String scoringId) {
+        if ("booleanCondition".equals(condition.getConditionTypeId())) {
+            @SuppressWarnings("unchecked")
+            final List<Condition> subConditions = (List<Condition>) 
condition.getParameter("subConditions");
+            List<Condition> updatedSubConditions = new LinkedList<>();
+            for (Condition subCondition : subConditions) {
+                Condition updatedCondition = 
updateScoringDependentCondition(subCondition, scoringId);
+                if(updatedCondition != null) {
+                    updatedSubConditions.add(updatedCondition);
+                }
+            }
+            if(!updatedSubConditions.isEmpty()){
+                if(updatedSubConditions.size() == 1) {
+                    return updatedSubConditions.get(0);
+                } else {
+                    condition.setParameter("subConditions", 
updatedSubConditions);
+                    return condition;
+                }
+            } else {
+                return null;
+            }
+        } else if ("scoringCondition".equals(condition.getConditionTypeId())
+                && scoringId.equals(condition.getParameter("scoringPlanId"))) {
+            return null;
+        }
+        return condition;
+    }
+
+    private Set<Segment> getScoringDependentSegments(String scoringId) {
+        Set<Segment> impactedSegments = new HashSet<>(this.allSegments.size());
+        for (Segment segment : this.allSegments) {
+            if (checkScoringDeletionImpact(segment.getCondition(), scoringId)) 
{
+                impactedSegments.add(segment);
+            }
+        }
+        return impactedSegments;
+    }
+
+    private Set<Scoring> getScoringDependentScorings(String scoringId) {
+        Set<Scoring> impactedScoring = new HashSet<>(this.allScoring.size());
+        for (Scoring scoring : this.allScoring) {
+            for (ScoringElement element : scoring.getElements()) {
+                if (checkScoringDeletionImpact(element.getCondition(), 
scoringId)) {
+                    impactedScoring.add(scoring);
+                    break;
+                }
+            }
+        }
+        return impactedScoring;
+    }
+
+    public DependentMetadata getScoringDependentMetadata(String scoringId) {
+        List<Metadata> segments = new LinkedList<>();
+        List<Metadata> scorings = new LinkedList<>();
+        for (Segment definition : getScoringDependentSegments(scoringId)) {
+            segments.add(definition.getMetadata());
+        }
+        for (Scoring definition : getScoringDependentScorings(scoringId)) {
+            scorings.add(definition.getMetadata());
+        }
+        return new DependentMetadata(segments, scorings);
+    }
+
+    public DependentMetadata removeScoringDefinition(String scoringId, boolean 
validate) {
+        Set<Segment> impactedSegments = getScoringDependentSegments(scoringId);
+        Set<Scoring> impactedScorings = getScoringDependentScorings(scoringId);
+        if (!validate || (impactedSegments.isEmpty() && 
impactedScorings.isEmpty())) {
+            // update profiles
+            updateExistingProfilesForRemovedScoring(scoringId);
+
+            // update impacted segments
+            for (Segment segment : impactedSegments) {
+                Condition updatedCondition = 
updateScoringDependentCondition(segment.getCondition(), scoringId);
+                segment.setCondition(updatedCondition);
+                if(updatedCondition == null) {
+                    
clearAutoGeneratedRules(persistenceService.query("linkedItems", 
segment.getMetadata().getId(), null, Rule.class), 
segment.getMetadata().getId());
+                    segment.getMetadata().setEnabled(false);
+                }
+                setSegmentDefinition(segment);
+            }
+
+            // update impacted scorings
+            for (Scoring scoring : impactedScorings) {
+                List<ScoringElement> updatedScoringElements = new 
ArrayList<>();
+                for (ScoringElement scoringElement : scoring.getElements()) {
+                    Condition updatedCondition = 
updateScoringDependentCondition(scoringElement.getCondition(), scoringId);
+                    if (updatedCondition != null) {
+                        scoringElement.setCondition(updatedCondition);
+                        updatedScoringElements.add(scoringElement);
+                    }
+                }
+                scoring.setElements(updatedScoringElements);
+                if (updatedScoringElements.isEmpty()) {
+                    
clearAutoGeneratedRules(persistenceService.query("linkedItems", 
scoring.getMetadata().getId(), null, Rule.class), 
scoring.getMetadata().getId());
+                    scoring.getMetadata().setEnabled(false);
+                }
+                setScoringDefinition(scoring);
+            }
+
+            persistenceService.remove(scoringId, Scoring.class);
+            List<Rule> previousRules = persistenceService.query("linkedItems", 
scoringId, null, Rule.class);
+            clearAutoGeneratedRules(previousRules, scoringId);
+        }
 
-        updateExistingProfilesForRemovedScoring(scoringId);
+        List<Metadata> segments = new LinkedList<>();
+        List<Metadata> scorings = new LinkedList<>();
+        for (Segment definition : impactedSegments) {
+            segments.add(definition.getMetadata());
+        }
+        for (Scoring definition : impactedScorings) {
+            scorings.add(definition.getMetadata());
+        }
+        return new DependentMetadata(segments, scorings);
     }
 
     public void updateAutoGeneratedRules(Metadata metadata, Condition 
condition) {

Reply via email to