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) {