use of akka.http.javadsl.model.sse.ServerSentEvent in project ditto by eclipse.
the class ThingsSseRouteBuilder method createSseRoute.
private Route createSseRoute(final RequestContext ctx, final CompletionStage<DittoHeaders> dittoHeadersStage, final JsonPointer fieldPointer, final Map<String, String> parameters) {
@Nullable final var filterString = parameters.get(PARAM_FILTER);
final List<String> namespaces = getNamespaces(parameters.get(PARAM_NAMESPACES));
final List<ThingId> targetThingIds = getThingIds(parameters.get(ThingsParameter.IDS.toString()));
@Nullable final ThingFieldSelector fields = getFieldSelector(parameters.get(ThingsParameter.FIELDS.toString()));
@Nullable final ThingFieldSelector extraFields = getFieldSelector(parameters.get(PARAM_EXTRA_FIELDS));
final CompletionStage<SignalEnrichmentFacade> facadeStage = signalEnrichmentProvider == null ? CompletableFuture.completedStage(null) : signalEnrichmentProvider.getFacade(ctx.getRequest());
final var sseSourceStage = facadeStage.thenCompose(facade -> dittoHeadersStage.thenCompose(dittoHeaders -> sseAuthorizationEnforcer.checkAuthorization(ctx, dittoHeaders).thenApply(unused -> {
if (filterString != null) {
// will throw an InvalidRqlExpressionException if the RQL expression was not valid:
queryFilterCriteriaFactory.filterCriteria(filterString, dittoHeaders);
}
final Source<SessionedJsonifiable, SupervisedStream.WithQueue> publisherSource = SupervisedStream.sourceQueue(10);
return publisherSource.viaMat(KillSwitches.single(), Keep.both()).mapMaterializedValue(pair -> {
final SupervisedStream.WithQueue withQueue = pair.first();
final KillSwitch killSwitch = pair.second();
final String connectionCorrelationId = dittoHeaders.getCorrelationId().orElseThrow(() -> new IllegalStateException("Expected correlation-id in SSE DittoHeaders: " + dittoHeaders));
final var jsonSchemaVersion = dittoHeaders.getSchemaVersion().orElse(JsonSchemaVersion.LATEST);
sseConnectionSupervisor.supervise(withQueue.getSupervisedStream(), connectionCorrelationId, dittoHeaders);
final var authorizationContext = dittoHeaders.getAuthorizationContext();
final var connect = new Connect(withQueue.getSourceQueue(), connectionCorrelationId, STREAMING_TYPE_SSE, jsonSchemaVersion, null, Set.of(), authorizationContext);
final var startStreaming = StartStreaming.getBuilder(StreamingType.EVENTS, connectionCorrelationId, authorizationContext).withNamespaces(namespaces).withFilter(filterString).withExtraFields(extraFields).build();
Patterns.ask(streamingActor, connect, LOCAL_ASK_TIMEOUT).thenApply(ActorRef.class::cast).thenAccept(streamingSessionActor -> streamingSessionActor.tell(startStreaming, ActorRef.noSender())).exceptionally(e -> {
killSwitch.abort(e);
return null;
});
return NotUsed.getInstance();
}).mapAsync(streamingConfig.getParallelism(), jsonifiable -> postprocess(jsonifiable, facade, targetThingIds, namespaces, fieldPointer, fields)).mapConcat(jsonValues -> jsonValues).map(jsonValue -> {
THINGS_SSE_COUNTER.increment();
return ServerSentEvent.create(jsonValue.toString());
}).log("SSE " + PATH_THINGS).viaMat(eventSniffer.toAsyncFlow(ctx.getRequest()), Keep.none()).keepAlive(Duration.ofSeconds(1), ServerSentEvent::heartbeat);
})));
return completeOKWithFuture(sseSourceStage, EventStreamMarshalling.toEventStream());
}
use of akka.http.javadsl.model.sse.ServerSentEvent in project ditto by eclipse.
the class ThingsSseRouteBuilder method createMessagesSseRoute.
private Route createMessagesSseRoute(final RequestContext ctx, final CompletionStage<DittoHeaders> dittoHeadersStage, final String thingId, final String messagePath) {
final List<ThingId> targetThingIds = List.of(ThingId.of(thingId));
final CompletionStage<SignalEnrichmentFacade> facadeStage = signalEnrichmentProvider == null ? CompletableFuture.completedStage(null) : signalEnrichmentProvider.getFacade(ctx.getRequest());
final var sseSourceStage = facadeStage.thenCompose(facade -> dittoHeadersStage.thenCompose(dittoHeaders -> sseAuthorizationEnforcer.checkAuthorization(ctx, dittoHeaders).thenApply(unused -> {
final Source<SessionedJsonifiable, SupervisedStream.WithQueue> publisherSource = SupervisedStream.sourceQueue(10);
return publisherSource.viaMat(KillSwitches.single(), Keep.both()).mapMaterializedValue(pair -> {
final SupervisedStream.WithQueue withQueue = pair.first();
final KillSwitch killSwitch = pair.second();
final String connectionCorrelationId = dittoHeaders.getCorrelationId().orElseThrow(() -> new IllegalStateException("Expected correlation-id in SSE DittoHeaders: " + dittoHeaders));
final var jsonSchemaVersion = dittoHeaders.getSchemaVersion().orElse(JsonSchemaVersion.LATEST);
sseConnectionSupervisor.supervise(withQueue.getSupervisedStream(), connectionCorrelationId, dittoHeaders);
final var authorizationContext = dittoHeaders.getAuthorizationContext();
final var connect = new Connect(withQueue.getSourceQueue(), connectionCorrelationId, STREAMING_TYPE_SSE, jsonSchemaVersion, null, Set.of(), authorizationContext);
final String resourcePathRqlStatement;
if (INBOX_OUTBOX_WITH_SUBJECT_PATTERN.matcher(messagePath).matches()) {
resourcePathRqlStatement = String.format("eq(resource:path,'%s')", messagePath);
} else {
resourcePathRqlStatement = String.format("like(resource:path,'%s*')", messagePath);
}
final var startStreaming = StartStreaming.getBuilder(StreamingType.MESSAGES, connectionCorrelationId, authorizationContext).withFilter(MessageFormat.format("and(" + "eq(entity:id,''{0}'')," + "{1}" + ")", thingId, resourcePathRqlStatement)).build();
Patterns.ask(streamingActor, connect, LOCAL_ASK_TIMEOUT).thenApply(ActorRef.class::cast).thenAccept(streamingSessionActor -> streamingSessionActor.tell(startStreaming, ActorRef.noSender())).exceptionally(e -> {
killSwitch.abort(e);
return null;
});
return NotUsed.getInstance();
}).filter(jsonifiable -> jsonifiable.getJsonifiable() instanceof MessageCommand).mapAsync(streamingConfig.getParallelism(), jsonifiable -> postprocessMessages(targetThingIds, (MessageCommand<?, ?>) jsonifiable.getJsonifiable(), facade, jsonifiable)).mapConcat(messages -> messages).map(message -> {
THINGS_SSE_COUNTER.increment();
final Optional<Charset> charset = determineCharsetFromContentType(message.getContentType());
final String data = message.getRawPayload().map(body -> new String(body.array(), charset.orElse(StandardCharsets.UTF_8))).orElse("");
return ServerSentEvent.create(data, message.getSubject());
}).log("SSE " + PATH_THINGS + "/" + messagePath).keepAlive(Duration.ofSeconds(1), ServerSentEvent::heartbeat);
})));
return completeOKWithFuture(sseSourceStage, EventStreamMarshalling.toEventStream());
}
Aggregations