Search in sources :

Example 1 with MessageCommand

use of org.eclipse.ditto.messages.model.signals.commands.MessageCommand in project ditto by eclipse.

the class OutboundMappingProcessorTest method testOutboundLiveMessageWithRequestedAcksWhichAreIssuedByTargetDontContainRequestedAcks.

@Test
@SuppressWarnings({ "unchecked", "rawtypes", "java:S3740" })
public void testOutboundLiveMessageWithRequestedAcksWhichAreIssuedByTargetDontContainRequestedAcks() {
    new TestKit(actorSystem) {

        {
            final AcknowledgementLabel customAckLabel = AcknowledgementLabel.of("custom:ack");
            final AcknowledgementLabel targetIssuedAckLabel = AcknowledgementLabel.of("issued:ack");
            final DittoHeaders dittoHeaders = DittoHeaders.newBuilder().channel(TopicPath.Channel.LIVE.getName()).acknowledgementRequest(AcknowledgementRequest.of(targetIssuedAckLabel), AcknowledgementRequest.of(customAckLabel)).build();
            final Message<Object> message = Message.newBuilder(MessageHeaders.newBuilder(MessageDirection.TO, TestConstants.Things.THING_ID, "ditto").acknowledgementRequest(AcknowledgementRequest.of(targetIssuedAckLabel), AcknowledgementRequest.of(customAckLabel)).build()).build();
            final MessageCommand signal = SendThingMessage.of(TestConstants.Things.THING_ID, message, dittoHeaders);
            final OutboundSignal outboundSignal = Mockito.mock(OutboundSignal.class);
            final MappingOutcome.Visitor<OutboundSignal.Mapped, Void> mock = Mockito.mock(MappingOutcome.Visitor.class);
            when(outboundSignal.getSource()).thenReturn(signal);
            when(outboundSignal.getTargets()).thenReturn(List.of(ConnectivityModelFactory.newTargetBuilder().address("test").issuedAcknowledgementLabel(targetIssuedAckLabel).authorizationContext(AuthorizationContext.newInstance(DittoAuthorizationContextType.UNSPECIFIED, AuthorizationSubject.newInstance("issuer:subject"))).topics(Topic.TWIN_EVENTS).build()));
            underTest.process(outboundSignal).forEach(outcome -> outcome.accept(mock));
            final ArgumentCaptor<OutboundSignal.Mapped> captor = ArgumentCaptor.forClass(OutboundSignal.Mapped.class);
            verify(mock, times(1)).onMapped(any(String.class), captor.capture());
            verify(mock, times(0)).onError(any(String.class), any(Exception.class), any(), any());
            verify(mock, times(0)).onDropped(any(String.class), any());
            assertThat(captor.getAllValues()).allSatisfy(em -> assertThat(em.getAdaptable().getDittoHeaders().getAcknowledgementRequests()).containsAll(List.of(AcknowledgementRequest.of(customAckLabel), AcknowledgementRequest.of(targetIssuedAckLabel))));
        }
    };
}
Also used : MappingOutcome(org.eclipse.ditto.connectivity.service.messaging.mappingoutcome.MappingOutcome) MessageCommand(org.eclipse.ditto.messages.model.signals.commands.MessageCommand) TestKit(akka.testkit.javadsl.TestKit) AcknowledgementLabel(org.eclipse.ditto.base.model.acks.AcknowledgementLabel) OutboundSignal(org.eclipse.ditto.connectivity.api.OutboundSignal) DittoHeaders(org.eclipse.ditto.base.model.headers.DittoHeaders) WithDittoHeaders(org.eclipse.ditto.base.model.headers.WithDittoHeaders) JsonObject(org.eclipse.ditto.json.JsonObject) Test(org.junit.Test)

Example 2 with MessageCommand

use of org.eclipse.ditto.messages.model.signals.commands.MessageCommand in project ditto by eclipse.

the class LiveSignalEnforcement method doEnforce.

private CompletionStage<Contextual<WithDittoHeaders>> doEnforce(final SignalWithEntityId<?> liveSignal, final Entry<Enforcer> enforcerEntry) {
    final CompletionStage<Contextual<WithDittoHeaders>> result;
    final var correlationIdOpt = SignalInformationPoint.getCorrelationId(liveSignal);
    if (enforcerEntry.exists() && correlationIdOpt.isPresent()) {
        final Enforcer enforcer = enforcerEntry.getValueOrThrow();
        if (liveSignal instanceof SendClaimMessage) {
            // claim messages require no enforcement, publish them right away:
            result = publishMessageCommand((MessageCommand<?, ?>) liveSignal, enforcer);
        } else if (SignalInformationPoint.isCommandResponse(liveSignal)) {
            result = enforceLiveCommandResponse((CommandResponse<?>) liveSignal, correlationIdOpt.get());
        } else {
            final var streamingType = StreamingType.fromSignal(liveSignal);
            if (streamingType.isPresent()) {
                result = enforceLiveSignal(streamingType.get(), liveSignal, enforcer);
            } else {
                log().error("Unsupported Signal in LiveSignalEnforcement: <{}>", liveSignal);
                throw GatewayInternalErrorException.newBuilder().dittoHeaders(liveSignal.getDittoHeaders()).build();
            }
        }
    } else {
        // drop live command to nonexistent things and respond with error.
        log(liveSignal).info("Command of type <{}> with ID <{}> could not be dispatched as no enforcer " + "could be looked up! Answering with ThingNotAccessibleException.", liveSignal.getType(), liveSignal.getEntityId());
        throw ThingNotAccessibleException.newBuilder(ThingId.of(entityId().getId())).dittoHeaders(liveSignal.getDittoHeaders()).build();
    }
    return result;
}
Also used : MessageCommand(org.eclipse.ditto.messages.model.signals.commands.MessageCommand) SendClaimMessage(org.eclipse.ditto.messages.model.signals.commands.SendClaimMessage) Enforcer(org.eclipse.ditto.policies.model.enforcers.Enforcer)

Example 3 with MessageCommand

use of org.eclipse.ditto.messages.model.signals.commands.MessageCommand 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());
}
Also used : PFBuilder(akka.japi.pf.PFBuilder) Arrays(java.util.Arrays) StreamingType(org.eclipse.ditto.internal.utils.pubsub.StreamingType) AbstractRoute(org.eclipse.ditto.gateway.service.endpoints.routes.AbstractRoute) ConditionChecker.checkNotNull(org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull) RequestContext(akka.http.javadsl.server.RequestContext) DittoMetrics(org.eclipse.ditto.internal.utils.metrics.DittoMetrics) RqlPredicateParser(org.eclipse.ditto.rql.parser.RqlPredicateParser) KillSwitch(akka.stream.KillSwitch) ServerSentEvent(akka.http.javadsl.model.sse.ServerSentEvent) DittoRuntimeException(org.eclipse.ditto.base.model.exceptions.DittoRuntimeException) DittoHeaders(org.eclipse.ditto.base.model.headers.DittoHeaders) Keep(akka.stream.javadsl.Keep) ActorRef(akka.actor.ActorRef) Duration(java.time.Duration) Map(java.util.Map) Patterns(akka.pattern.Patterns) Message(org.eclipse.ditto.messages.model.Message) JsonValue(org.eclipse.ditto.json.JsonValue) PartialFunction(scala.PartialFunction) StreamingConfig(org.eclipse.ditto.gateway.service.util.config.streaming.StreamingConfig) MessageCommand(org.eclipse.ditto.messages.model.signals.commands.MessageCommand) QueryFilterCriteriaFactory(org.eclipse.ditto.rql.query.filter.QueryFilterCriteriaFactory) SupervisedStream(org.eclipse.ditto.gateway.service.streaming.actors.SupervisedStream) Collection(java.util.Collection) Set(java.util.Set) ThingFieldSelector(org.eclipse.ditto.things.model.ThingFieldSelector) UUID(java.util.UUID) ThingId(org.eclipse.ditto.things.model.ThingId) StreamingSession(org.eclipse.ditto.gateway.service.streaming.actors.StreamingSession) Collectors(java.util.stream.Collectors) StandardCharsets(java.nio.charset.StandardCharsets) EventStreamMarshalling(akka.http.javadsl.marshalling.sse.EventStreamMarshalling) List(java.util.List) CompletionStage(java.util.concurrent.CompletionStage) Stream(java.util.stream.Stream) NotUsed(akka.NotUsed) ThingsParameter(org.eclipse.ditto.gateway.service.endpoints.routes.things.ThingsParameter) Accept(akka.http.javadsl.model.headers.Accept) JsonFieldSelector(org.eclipse.ditto.json.JsonFieldSelector) ResourcePlaceholder(org.eclipse.ditto.protocol.placeholders.ResourcePlaceholder) Optional(java.util.Optional) Pattern(java.util.regex.Pattern) SignalEnrichmentFacade(org.eclipse.ditto.internal.models.signalenrichment.SignalEnrichmentFacade) Connect(org.eclipse.ditto.gateway.service.streaming.Connect) SessionedJsonifiable(org.eclipse.ditto.gateway.service.streaming.actors.SessionedJsonifiable) StartStreaming(org.eclipse.ditto.gateway.service.streaming.StartStreaming) Route(akka.http.javadsl.server.Route) Counter(org.eclipse.ditto.internal.utils.metrics.instruments.counter.Counter) Source(akka.stream.javadsl.Source) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) EventSniffer(org.eclipse.ditto.gateway.service.endpoints.utils.EventSniffer) StatusCodes(akka.http.javadsl.model.StatusCodes) OptionalInt(java.util.OptionalInt) Supplier(java.util.function.Supplier) ActorSelection(akka.actor.ActorSelection) GatewaySignalEnrichmentProvider(org.eclipse.ditto.gateway.service.endpoints.utils.GatewaySignalEnrichmentProvider) MessageFormat(java.text.MessageFormat) JsonObject(org.eclipse.ditto.json.JsonObject) PathMatchers(akka.http.javadsl.server.PathMatchers) SignalEnrichmentFailedException(org.eclipse.ditto.base.model.exceptions.SignalEnrichmentFailedException) Charset(java.nio.charset.Charset) UriEncoding(org.eclipse.ditto.base.service.UriEncoding) StreamSupport(java.util.stream.StreamSupport) ThingEvent(org.eclipse.ditto.things.model.signals.events.ThingEvent) JsonPointer(org.eclipse.ditto.json.JsonPointer) Nullable(javax.annotation.Nullable) Thing(org.eclipse.ditto.things.model.Thing) TopicPathPlaceholder(org.eclipse.ditto.protocol.placeholders.TopicPathPlaceholder) JsonSchemaVersion(org.eclipse.ditto.base.model.json.JsonSchemaVersion) HttpHeader(akka.http.javadsl.model.HttpHeader) KillSwitches(akka.stream.KillSwitches) TimePlaceholder(org.eclipse.ditto.placeholders.TimePlaceholder) RouteDirectives(akka.http.javadsl.server.directives.RouteDirectives) MediaTypes(akka.http.javadsl.model.MediaTypes) SearchSource(org.eclipse.ditto.internal.utils.search.SearchSource) Collections(java.util.Collections) NotThreadSafe(javax.annotation.concurrent.NotThreadSafe) KillSwitch(akka.stream.KillSwitch) ActorRef(akka.actor.ActorRef) MessageCommand(org.eclipse.ditto.messages.model.signals.commands.MessageCommand) SignalEnrichmentFacade(org.eclipse.ditto.internal.models.signalenrichment.SignalEnrichmentFacade) Connect(org.eclipse.ditto.gateway.service.streaming.Connect) ServerSentEvent(akka.http.javadsl.model.sse.ServerSentEvent) Charset(java.nio.charset.Charset) SupervisedStream(org.eclipse.ditto.gateway.service.streaming.actors.SupervisedStream) SessionedJsonifiable(org.eclipse.ditto.gateway.service.streaming.actors.SessionedJsonifiable) ThingId(org.eclipse.ditto.things.model.ThingId)

Example 4 with MessageCommand

use of org.eclipse.ditto.messages.model.signals.commands.MessageCommand in project ditto by eclipse.

the class HttpPublisherActor method toCommandResponseOrAcknowledgement.

private CompletionStage<SendResult> toCommandResponseOrAcknowledgement(final Signal<?> sentSignal, @Nullable final Target autoAckTarget, final HttpResponse response, final int maxTotalMessageSize, final int ackSizeQuota, final AuthorizationContext targetAuthorizationContext) {
    final var autoAckLabel = getAcknowledgementLabel(autoAckTarget);
    final var statusCode = response.status().intValue();
    final HttpStatus httpStatus;
    try {
        httpStatus = HttpStatus.getInstance(statusCode);
    } catch (final HttpStatusCodeOutOfRangeException e) {
        response.discardEntityBytes(materializer);
        final var error = MessageSendingFailedException.newBuilder().message(String.format("Remote server delivered unknown HTTP status code <%d>!", statusCode)).cause(e).build();
        return CompletableFuture.failedFuture(error);
    }
    final var isSentSignalLiveCommand = SignalInformationPoint.isLiveCommand(sentSignal);
    final int maxResponseSize = isSentSignalLiveCommand ? maxTotalMessageSize : ackSizeQuota;
    return getResponseBody(response, maxResponseSize, materializer).thenApply(body -> {
        @Nullable final CommandResponse<?> result;
        final var mergedDittoHeaders = mergeWithResponseHeaders(sentSignal.getDittoHeaders(), response);
        final Optional<EntityId> entityIdOptional = WithEntityId.getEntityIdOfType(EntityId.class, sentSignal);
        if (autoAckLabel.isPresent() && entityIdOptional.isPresent()) {
            final EntityId entityId = entityIdOptional.get();
            if (DittoAcknowledgementLabel.LIVE_RESPONSE.equals(autoAckLabel.get())) {
                // Live-Response is declared as issued ack => parse live response from response
                if (sentSignal instanceof MessageCommand) {
                    result = toMessageCommandResponse((MessageCommand<?, ?>) sentSignal, mergedDittoHeaders, body, httpStatus, targetAuthorizationContext);
                } else if (sentSignal instanceof ThingCommand && SignalInformationPoint.isChannelLive(sentSignal)) {
                    result = toLiveCommandResponse(mergedDittoHeaders, body, targetAuthorizationContext);
                } else {
                    result = null;
                }
            } else {
                // There is an issued ack declared but its not live-response => handle response as acknowledgement.
                result = Acknowledgement.of(autoAckLabel.get(), entityId, httpStatus, mergedDittoHeaders, body);
            }
        } else {
            // No Acks declared as issued acks => Handle response either as live response or as acknowledgement
            // or as fallback build a response for local diagnostics.
            final boolean isDittoProtocolMessage = mergedDittoHeaders.getDittoContentType().filter(org.eclipse.ditto.base.model.headers.contenttype.ContentType::isDittoProtocol).isPresent();
            if (isDittoProtocolMessage && body.isObject()) {
                final CommandResponse<?> parsedResponse = toCommandResponse(body.asObject(), targetAuthorizationContext);
                if (parsedResponse instanceof Acknowledgement) {
                    result = parsedResponse;
                } else if (SignalInformationPoint.isLiveCommandResponse(parsedResponse)) {
                    result = parsedResponse;
                } else {
                    result = null;
                }
            } else {
                result = null;
            }
        }
        final var liveCommandWithEntityId = tryToGetAsLiveCommandWithEntityId(sentSignal);
        if (liveCommandWithEntityId.isPresent() && null != result && SignalInformationPoint.isLiveCommandResponse(result)) {
            // Do only return command response for live commands with a correct response.
            httpPushRoundTripSignalValidator.accept((Command<?>) liveCommandWithEntityId.get(), result);
        }
        if (result == null) {
            connectionLogger.success(InfoProviderFactory.forSignal(sentSignal), "No CommandResponse created from HTTP response with status <{0}> and body <{1}>.", response.status(), body);
        } else {
            connectionLogger.success(InfoProviderFactory.forSignal(result), "CommandResponse <{0}> created from HTTP response with Status <{1}> and body <{2}>.", result, response.status(), body);
        }
        final MessageSendingFailedException sendFailure;
        if (!httpStatus.isSuccess()) {
            final String message = String.format("Got non success status code: <%s> and body: <%s>", httpStatus.getCode(), body);
            sendFailure = MessageSendingFailedException.newBuilder().message(message).dittoHeaders(mergedDittoHeaders).build();
        } else {
            sendFailure = null;
        }
        return new SendResult(result, sendFailure, mergedDittoHeaders);
    });
}
Also used : MessageSendingFailedException(org.eclipse.ditto.connectivity.model.MessageSendingFailedException) HttpStatus(org.eclipse.ditto.base.model.common.HttpStatus) MessageCommand(org.eclipse.ditto.messages.model.signals.commands.MessageCommand) ThingCommand(org.eclipse.ditto.things.model.signals.commands.ThingCommand) SignalInformationPoint(org.eclipse.ditto.internal.models.signal.SignalInformationPoint) SignalWithEntityId(org.eclipse.ditto.base.model.signals.SignalWithEntityId) WithEntityId(org.eclipse.ditto.base.model.entity.id.WithEntityId) EntityId(org.eclipse.ditto.base.model.entity.id.EntityId) Acknowledgement(org.eclipse.ditto.base.model.signals.acks.Acknowledgement) HttpStatusCodeOutOfRangeException(org.eclipse.ditto.base.model.common.HttpStatusCodeOutOfRangeException) SendResult(org.eclipse.ditto.connectivity.service.messaging.SendResult) Nullable(javax.annotation.Nullable)

Aggregations

MessageCommand (org.eclipse.ditto.messages.model.signals.commands.MessageCommand)4 Nullable (javax.annotation.Nullable)2 NotUsed (akka.NotUsed)1 ActorRef (akka.actor.ActorRef)1 ActorSelection (akka.actor.ActorSelection)1 EventStreamMarshalling (akka.http.javadsl.marshalling.sse.EventStreamMarshalling)1 HttpHeader (akka.http.javadsl.model.HttpHeader)1 MediaTypes (akka.http.javadsl.model.MediaTypes)1 StatusCodes (akka.http.javadsl.model.StatusCodes)1 Accept (akka.http.javadsl.model.headers.Accept)1 ServerSentEvent (akka.http.javadsl.model.sse.ServerSentEvent)1 PathMatchers (akka.http.javadsl.server.PathMatchers)1 RequestContext (akka.http.javadsl.server.RequestContext)1 Route (akka.http.javadsl.server.Route)1 RouteDirectives (akka.http.javadsl.server.directives.RouteDirectives)1 PFBuilder (akka.japi.pf.PFBuilder)1 Patterns (akka.pattern.Patterns)1 KillSwitch (akka.stream.KillSwitch)1 KillSwitches (akka.stream.KillSwitches)1 Keep (akka.stream.javadsl.Keep)1