blob: 7a044aff60ca59291de545a4979fca07d19ee7f5 [file] [log] [blame]
/*
* 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.graphql.condition.parsers;
import graphql.schema.DataFetchingEnvironment;
import org.apache.unomi.api.conditions.Condition;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SegmentConditionParser {
private static final Predicate<Condition> IS_BOOLEAN_CONDITION_TYPE =
condition -> "booleanCondition".equals(condition.getConditionTypeId());
private static final Predicate<Condition> IS_PROFILE_PROPERTY_CONDITION_TYPE =
condition -> "profilePropertyCondition".equals(condition.getConditionTypeId());
private static final Predicate<Condition> IS_PROFILE_SEGMENT_CONDITION_TYPE =
condition -> "profileSegmentCondition".equals(condition.getConditionTypeId());
private static final Predicate<Condition> IS_PAST_EVENT_CONDITION_TYPE =
condition -> "pastEventCondition".equals(condition.getConditionTypeId());
private static final Predicate<Condition> IS_PROFILE_USER_LIST_CONDITION_TYPE =
condition -> "profileUserListCondition".equals(condition.getConditionTypeId());
private final Condition segmentCondition;
private final DataFetchingEnvironment environment;
private Map<String, ConditionDecorator> conditionsContext = new LinkedHashMap<>();
private Map<FilterType, List<ConditionDecorator>> groupedConditionsByFilterType = new LinkedHashMap<>();
private Map<FilterType, String> rootConditionIdPerFilterType = new LinkedHashMap<>();
public SegmentConditionParser(final Condition segmentCondition, final DataFetchingEnvironment environment) {
this.segmentCondition = segmentCondition;
this.environment = environment;
}
public Map<String, Object> parse() {
populateConditionsContext(segmentCondition);
groupConditionsByFilterType();
findRootConditionsByFilterType();
return doParse();
}
private Map<String, Object> doParse() {
final Map<String, Object> dataHolder = new LinkedHashMap<>();
rootConditionIdPerFilterType.forEach((filterType, conditionId) -> {
final ConditionDecorator conditionDecorator = conditionsContext.get(conditionId);
final List<Condition> conditionDecorators = groupedConditionsByFilterType.get(filterType).stream()
.map(ConditionDecorator::getCondition)
.collect(Collectors.toList());
switch (filterType) {
case CONSENTS_CONTAINS:
dataHolder.put(filterType.getValue(),
new SegmentProfileConsentsConditionParser(conditionDecorators).parse());
break;
case SEGMENTS_CONTAINS:
dataHolder.put(filterType.getValue(),
new SegmentProfileSegmentsConditionParser(conditionDecorators).parse());
break;
case PROFILE_IDS_CONTAINS:
dataHolder.put(filterType.getValue(),
new SegmentProfileIDsConditionParser(conditionDecorators).parse());
break;
case LISTS_CONTAINS:
dataHolder.put(filterType.getValue(),
new SegmentProfileListConditionParser(conditionDecorators).parse());
break;
case EVENTS:
dataHolder.put(filterType.getValue(),
new SegmentProfileEventsConditionParser(conditionDecorator.getCondition(), environment).parse());
break;
case PROPERTIES:
dataHolder.put(filterType.getValue(),
new SegmentProfilePropertiesConditionParser(conditionDecorator.getCondition(), environment).parse());
break;
case INTERESTS:
dataHolder.put(filterType.getValue(),
new SegmentProfileInterestsConditionParser(conditionDecorator.getCondition()).parse());
break;
default: {
// do nothing
}
}
});
return dataHolder;
}
private void groupConditionsByFilterType() {
conditionsContext.entrySet().stream()
.filter(entry -> !IS_BOOLEAN_CONDITION_TYPE.test(entry.getValue().getCondition()))
.forEach(entry -> {
final FilterType filterType = entry.getValue().getFilterType();
if (!groupedConditionsByFilterType.containsKey(filterType)) {
groupedConditionsByFilterType.put(filterType, new ArrayList<>());
}
groupedConditionsByFilterType.get(filterType).add(entry.getValue());
});
}
private void findRootConditionsByFilterType() {
groupedConditionsByFilterType.forEach((filterType, conditionDecorators) -> {
if (conditionDecorators != null && !conditionDecorators.isEmpty()) {
ConditionDecorator member = conditionDecorators.get(0);
do {
if (member.getParentId() != null && conditionsContext.get(member.getParentId()).getParentId() != null) {
member = conditionsContext.get(member.getParentId());
}
}
while (conditionsContext.get(member.getParentId()).getParentId() != null);
rootConditionIdPerFilterType.put(filterType, member.getId());
}
});
}
private void populateConditionsContext(final Condition condition) {
populateConditionsContext(null, condition);
}
@SuppressWarnings("unchecked")
private void populateConditionsContext(final ConditionDecorator parentDecorator, final Condition condition) {
final ConditionDecorator conditionDecorator = createConditionDecorator(parentDecorator, condition);
conditionsContext.put(conditionDecorator.getId(), conditionDecorator);
if (IS_BOOLEAN_CONDITION_TYPE.test(condition)) {
final List<Condition> subConditions = (List<Condition>) condition.getParameter("subConditions");
conditionDecorator.setSubConditions(subConditions);
subConditions.forEach(subCondition -> populateConditionsContext(conditionDecorator, subCondition));
}
}
private ConditionDecorator createConditionDecorator(final ConditionDecorator parentDecorator, final Condition condition) {
final ConditionDecorator decorator = new ConditionDecorator();
decorator.setId(UUID.randomUUID().toString());
decorator.setCondition(condition);
if (parentDecorator != null) {
decorator.setParentId(parentDecorator.getId());
}
if (IS_BOOLEAN_CONDITION_TYPE.test(condition)) {
decorator.setOperator(condition.getParameter("operator").toString());
return decorator;
}
if (IS_PROFILE_PROPERTY_CONDITION_TYPE.test(condition)) {
final Object propertyName = condition.getParameter("propertyName");
if (propertyName != null) {
if (propertyName.toString().startsWith("properties.interests.")) {
decorator.setFilterType(FilterType.INTERESTS);
} else if (propertyName.toString().equals("itemId")) {
decorator.setFilterType(FilterType.PROFILE_IDS_CONTAINS);
} else if (propertyName.toString().startsWith("consents.")) {
decorator.setFilterType(FilterType.CONSENTS_CONTAINS);
} else {
decorator.setFilterType(FilterType.PROPERTIES);
}
}
} else if (IS_PROFILE_SEGMENT_CONDITION_TYPE.test(condition)) {
decorator.setFilterType(FilterType.SEGMENTS_CONTAINS);
} else if (IS_PAST_EVENT_CONDITION_TYPE.test(condition)) {
decorator.setFilterType(FilterType.EVENTS);
} else if (IS_PROFILE_USER_LIST_CONDITION_TYPE.test(condition)) {
decorator.setFilterType(FilterType.LISTS_CONTAINS);
} else {
decorator.setFilterType(FilterType.UNKNOWN);
}
return decorator;
}
private class ConditionDecorator {
private String id;
private String parentId;
private String operator;
private FilterType filterType;
private Condition condition;
private List<Condition> subConditions;
String getId() {
return id;
}
ConditionDecorator setId(String id) {
this.id = id;
return this;
}
String getParentId() {
return parentId;
}
ConditionDecorator setParentId(String parentId) {
this.parentId = parentId;
return this;
}
String getOperator() {
return operator;
}
ConditionDecorator setOperator(String operator) {
this.operator = operator;
return this;
}
FilterType getFilterType() {
return filterType;
}
ConditionDecorator setFilterType(FilterType filterType) {
this.filterType = filterType;
return this;
}
Condition getCondition() {
return condition;
}
ConditionDecorator setCondition(Condition condition) {
this.condition = condition;
return this;
}
List<Condition> getSubConditions() {
return subConditions;
}
ConditionDecorator setSubConditions(List<Condition> subConditions) {
this.subConditions = subConditions;
return this;
}
}
private enum FilterType {
CONSENTS_CONTAINS("consents_contains"),
LISTS_CONTAINS("lists_contains"),
PROFILE_IDS_CONTAINS("profileIDs_contains"),
SEGMENTS_CONTAINS("segments_contains"),
PROPERTIES("properties"),
EVENTS("events"),
INTERESTS("interests"),
UNKNOWN("unknown");
private String value;
FilterType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}