use of org.eclipse.ditto.connectivity.model.FilteredTopic in project ditto by eclipse.
the class SignalFilter method matchesFilterBeforeEnrichment.
private static boolean matchesFilterBeforeEnrichment(final FilteredTopic filteredTopic, final Signal<?> signal) {
final Optional<String> filterOptional = filteredTopic.getFilter();
if (filterOptional.isPresent()) {
// match filter ignoring "extraFields"
final TopicPath topicPath = DITTO_PROTOCOL_ADAPTER.toTopicPath(signal);
final PlaceholderResolver<TopicPath> topicPathPlaceholderResolver = PlaceholderFactory.newPlaceholderResolver(TOPIC_PATH_PLACEHOLDER, topicPath);
final PlaceholderResolver<WithResource> resourcePlaceholderResolver = PlaceholderFactory.newPlaceholderResolver(RESOURCE_PLACEHOLDER, signal);
final PlaceholderResolver<Object> timePlaceholderResolver = PlaceholderFactory.newPlaceholderResolver(TIME_PLACEHOLDER, new Object());
final Criteria criteria = parseCriteria(filterOptional.get(), signal.getDittoHeaders(), topicPathPlaceholderResolver, resourcePlaceholderResolver, timePlaceholderResolver);
final Set<JsonPointer> extraFields = filteredTopic.getExtraFields().map(JsonFieldSelector::getPointers).orElse(Collections.emptySet());
if (signal instanceof ThingEvent) {
return ThingEventToThingConverter.thingEventToThing((ThingEvent<?>) signal).filter(thing -> Thing3ValuePredicateVisitor.couldBeTrue(criteria, extraFields, thing, topicPathPlaceholderResolver, resourcePlaceholderResolver, timePlaceholderResolver)).isPresent();
} else {
final Thing emptyThing = Thing.newBuilder().build();
return Thing3ValuePredicateVisitor.couldBeTrue(criteria, extraFields, emptyThing, topicPathPlaceholderResolver, resourcePlaceholderResolver, timePlaceholderResolver);
}
} else {
return true;
}
}
use of org.eclipse.ditto.connectivity.model.FilteredTopic in project ditto by eclipse.
the class OutboundMappingProcessorActor method splitTargetsByExtraFields.
/**
* Split the targets of an outbound signal into 2 parts: those without extra fields and those with.
*
* @param outboundSignal The outbound signal.
* @return A pair of lists. The first list contains targets without matching extra fields.
* The second list contains targets together with their extra fields matching the outbound signal.
*/
private static Pair<List<Target>, List<Pair<Target, FilteredTopic>>> splitTargetsByExtraFields(final OutboundSignal outboundSignal) {
final Optional<StreamingType> streamingTypeOptional = StreamingType.fromSignal(outboundSignal.getSource());
if (streamingTypeOptional.isPresent()) {
// Find targets with a matching topic with extra fields
final StreamingType streamingType = streamingTypeOptional.get();
final List<Target> targetsWithoutExtraFields = new ArrayList<>(outboundSignal.getTargets().size());
final List<Pair<Target, FilteredTopic>> targetsWithExtraFields = new ArrayList<>(outboundSignal.getTargets().size());
for (final Target target : outboundSignal.getTargets()) {
final Optional<FilteredTopic> matchingExtraFields = target.getTopics().stream().filter(filteredTopic -> filteredTopic.getExtraFields().isPresent() && streamingType == StreamingType.fromTopic(filteredTopic.getTopic().getPubSubTopic())).findAny();
if (matchingExtraFields.isPresent()) {
targetsWithExtraFields.add(Pair.create(target, matchingExtraFields.get()));
} else {
targetsWithoutExtraFields.add(target);
}
}
return Pair.create(targetsWithoutExtraFields, targetsWithExtraFields);
} else {
// The outbound signal has no streaming type: Do not attach extra fields.
return Pair.create(outboundSignal.getTargets(), Collections.emptyList());
}
}
use of org.eclipse.ditto.connectivity.model.FilteredTopic in project ditto by eclipse.
the class OutboundMappingProcessorActor method enrichAndFilterSignal.
// Called inside stream; must be thread-safe
// precondition: whenever filteredTopic != null, it contains an extra fields
private CompletionStage<Collection<OutboundSignalWithSender>> enrichAndFilterSignal(final Pair<OutboundSignalWithSender, FilteredTopic> outboundSignalWithExtraFields) {
final OutboundSignalWithSender outboundSignal = outboundSignalWithExtraFields.first();
final FilteredTopic filteredTopic = outboundSignalWithExtraFields.second();
final Optional<JsonFieldSelector> extraFieldsOptional = Optional.ofNullable(filteredTopic).flatMap(FilteredTopic::getExtraFields);
if (extraFieldsOptional.isEmpty()) {
return CompletableFuture.completedFuture(Collections.singletonList(outboundSignal));
}
final JsonFieldSelector extraFields = extraFieldsOptional.get();
final Target target = outboundSignal.getTargets().get(0);
final DittoHeaders headers = DittoHeaders.newBuilder().authorizationContext(target.getAuthorizationContext()).schemaVersion(JsonSchemaVersion.LATEST).build();
return extractEntityId(outboundSignal.delegate.getSource()).filter(ThingId.class::isInstance).map(ThingId.class::cast).map(thingId -> signalEnrichmentFacade.retrievePartialThing(thingId, extraFields, headers, outboundSignal.getSource())).map(partialThingCompletionStage -> partialThingCompletionStage.thenApply(outboundSignal::setExtra)).orElse(CompletableFuture.completedStage(outboundSignal)).thenApply(outboundSignalWithExtra -> applyFilter(outboundSignalWithExtra, filteredTopic)).exceptionally(error -> {
logger.withCorrelationId(outboundSignal.getSource()).warning("Could not retrieve extra data due to: {} {}", error.getClass().getSimpleName(), error.getMessage());
// recover from all errors to keep message-mapping-stream running despite enrichment failures
return recoverFromEnrichmentError(outboundSignal, target, error);
});
}
use of org.eclipse.ditto.connectivity.model.FilteredTopic in project ditto by eclipse.
the class OutboundMappingProcessorActor method applyFilter.
private Collection<OutboundSignalWithSender> applyFilter(final OutboundSignalWithSender outboundSignalWithExtra, final FilteredTopic filteredTopic) {
final Optional<String> filter = filteredTopic.getFilter();
final Optional<JsonFieldSelector> extraFields = filteredTopic.getExtraFields();
if (filter.isPresent() && extraFields.isPresent()) {
// evaluate filter criteria again if signal enrichment is involved.
final Signal<?> signal = outboundSignalWithExtra.getSource();
final TopicPath topicPath = DITTO_PROTOCOL_ADAPTER.toTopicPath(signal);
final PlaceholderResolver<TopicPath> topicPathPlaceholderResolver = PlaceholderFactory.newPlaceholderResolver(TOPIC_PATH_PLACEHOLDER, topicPath);
final PlaceholderResolver<WithResource> resourcePlaceholderResolver = PlaceholderFactory.newPlaceholderResolver(RESOURCE_PLACEHOLDER, signal);
final PlaceholderResolver<Object> timePlaceholderResolver = PlaceholderFactory.newPlaceholderResolver(TIME_PLACEHOLDER, new Object());
final DittoHeaders dittoHeaders = signal.getDittoHeaders();
final Criteria criteria = QueryFilterCriteriaFactory.modelBased(RqlPredicateParser.getInstance(), topicPathPlaceholderResolver, resourcePlaceholderResolver, timePlaceholderResolver).filterCriteria(filter.get(), dittoHeaders);
return outboundSignalWithExtra.getExtra().flatMap(extra -> ThingEventToThingConverter.mergeThingWithExtraFields(signal, extraFields.get(), extra).filter(ThingPredicateVisitor.apply(criteria, topicPathPlaceholderResolver, resourcePlaceholderResolver, timePlaceholderResolver)).map(thing -> outboundSignalWithExtra)).map(Collections::singletonList).orElse(List.of());
} else {
// no signal enrichment: filtering is already done in SignalFilter since there is no ignored field
return Collections.singletonList(outboundSignalWithExtra);
}
}
Aggregations