Skip to content
Snippets Groups Projects
SubscriberClientIT.java 21.25 KiB
package dev.fitko.fitconnect.integrationtests;

import dev.fitko.fitconnect.api.config.ApplicationConfig;
import dev.fitko.fitconnect.api.domain.model.attachment.Attachment;
import dev.fitko.fitconnect.api.domain.model.event.Event;
import dev.fitko.fitconnect.api.domain.model.event.EventState;
import dev.fitko.fitconnect.api.domain.model.event.Status;
import dev.fitko.fitconnect.api.domain.model.event.problems.data.DataEncryptionIssue;
import dev.fitko.fitconnect.api.domain.model.event.problems.data.IncorrectDataAuthenticationTag;
import dev.fitko.fitconnect.api.domain.model.event.problems.other.TechnicalError;
import dev.fitko.fitconnect.api.domain.model.event.problems.submission.InvalidEventLog;
import dev.fitko.fitconnect.api.domain.model.submission.ServiceType;
import dev.fitko.fitconnect.api.domain.model.submission.SubmissionForPickup;
import dev.fitko.fitconnect.api.domain.sender.SendableSubmission;
import dev.fitko.fitconnect.api.domain.subscriber.ReceivedSubmission;
import dev.fitko.fitconnect.api.exceptions.client.FitConnectSubscriberException;
import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
import dev.fitko.fitconnect.client.SenderClient;
import dev.fitko.fitconnect.client.SubscriberClient;
import dev.fitko.fitconnect.client.bootstrap.ClientFactory;
import dev.fitko.fitconnect.integrationtests.condition.EnableIfEnvironmentVariablesAreSet;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isOneOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

@EnableIfEnvironmentVariablesAreSet
public class SubscriberClientIT extends IntegrationTestBase {

    @BeforeEach
    public void cleanup() {
        cleanupTestSubmissions();
    }

    @Test
    void testListSubmissionsForDestination() throws IOException {

        // Given
        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment();

        final var senderClient = ClientFactory.createSenderClient(config);
        final var subscriberClient = ClientFactory.createSubscriberClient(config);
        final UUID destinationId = UUID.fromString(System.getenv("TEST_DESTINATION_ID"));
        final String leikaKey = "urn:de:fim:leika:leistung:99400048079000";
        final String serviceName = "Test Service";

        final String submissionData = getResourceAsString("/submission_data.json");
        final URI submissionDataSchemaUri = URI.create("https://schema.fitko.de/fim/s00000114_1.1.schema.json");

        final var submissionOne = SendableSubmission.Builder()
                .setDestination(destinationId).setServiceType(leikaKey, serviceName)
                .setJsonData(submissionData, submissionDataSchemaUri)
                .build();

        final var submissionTwo = SendableSubmission.Builder()
                .setDestination(destinationId).setServiceType(leikaKey, serviceName)
                .setJsonData(submissionData, submissionDataSchemaUri)
                .build();

        final var sentSubmissionOne = senderClient.send(submissionOne);
        final var sentSubmissionTwo = senderClient.send(submissionTwo);

        assertNotNull(sentSubmissionOne);
        assertNotNull(sentSubmissionTwo);

        // When
        final Set<SubmissionForPickup> submissions = subscriberClient.getAvailableSubmissionsForDestination(destinationId);

        // Then
        Assertions.assertFalse(submissions.isEmpty());

        final List<UUID> submissionIds = submissions.stream().map(SubmissionForPickup::getSubmissionId).collect(Collectors.toList());

        assertThat(submissionIds, Matchers.hasItems(sentSubmissionOne.getSubmissionId(), sentSubmissionTwo.getSubmissionId()));
    }

    @Test
    void testReceiveSingleSubmission() throws IOException {

        // Given
        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment();

        final var senderClient = ClientFactory.createSenderClient(config);
        final var subscriberClient = ClientFactory.createSubscriberClient(config);

        final UUID destinationId = UUID.fromString(System.getenv("TEST_DESTINATION_ID"));
        final String leikaKey = "urn:de:fim:leika:leistung:99400048079000";
        final String serviceName = "Test Service";

        final String submissionData = getResourceAsString("/submission_data.json");

        final var submission = SendableSubmission.Builder()
                .setDestination(destinationId).setServiceType(leikaKey, serviceName)
                .setJsonData(submissionData, URI.create("https://schema.fitko.de/fim/s00000114_1.1.schema.json"))
                .addAttachment(Attachment.fromByteArray("foo".getBytes(), "plain/text"))
                .build();

        final var sentSubmission = senderClient.send(submission);

        assertNotNull(sentSubmission);

        // When
        final ReceivedSubmission receivedSubmission = subscriberClient.requestSubmission(sentSubmission.getSubmissionId());

        // Then
        assertNotNull(receivedSubmission);
        assertThat(receivedSubmission.getAttachments(), hasSize(1));
        assertThat(receivedSubmission.getAttachments().get(0).getDataAsString(StandardCharsets.UTF_8), is("foo"));
        assertThat(receivedSubmission.getDataAsString(), is(submissionData));
        assertThat(receivedSubmission.getServiceType(), is(new ServiceType(serviceName, leikaKey)));

    }

    @Test
    void testReceiveSubmissionWithUrnSchemaValidation(@TempDir final Path tempDir) throws IOException {

        // Given

        // Prepare locally set submission data schema for a urn schema in given path
        final String schemaResource = getResourceAsString("/submission-data-schema/submission_data_schema.json");
        final Path schemaPath = Files.writeString(Path.of(tempDir.toString(), "submission_data_schema.json"), schemaResource);
        final String schemaUri = "urn:de:fitko:test:schema";

        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment(true, Map.of(schemaUri, schemaPath.toString()));

        final String submissionData = getResourceAsString("/submission_data.json");

        final var submission = SendableSubmission.Builder()
                .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
                .setServiceType("urn:de:fim:leika:leistung:99400048079001", "Test Service")
                .setJsonData(submissionData, URI.create("urn:de:fitko:test:schema"))
                .build();

        // When
        final var sentSubmission = ClientFactory.createSenderClient(config).send(submission);

        // Then
        assertNotNull(sentSubmission);

        // When
        final ReceivedSubmission receivedSubmission = ClientFactory.createSubscriberClient(config).requestSubmission(sentSubmission.getSubmissionId());

        // Then
        assertNotNull(receivedSubmission);
    }

    @Test
    void testRejectEvent() throws IOException {

        // Given
        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment();

        final String submissionData = getResourceAsString("/submission_data.json");

        final SenderClient senderClient = ClientFactory.createSenderClient(config);

        final var submission = SendableSubmission.Builder()
                .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
                .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
                .setJsonData(submissionData, URI.create("https://schema.fitko.de/fim/s00000114_1.1.schema.json"))
                .addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "plain/text"))
                .build();

        final var sentSubmission = senderClient.send(submission);

        assertNotNull(sentSubmission);

        // When
        final var subscriberClient = ClientFactory.createSubscriberClient(config);

        final var sentSubmissionId = sentSubmission.getSubmissionId();

        // reject and remove
        subscriberClient.requestSubmission(sentSubmissionId).rejectSubmission(List.of(new DataEncryptionIssue()));

        // check event log if reject event was sent
        final Status status = senderClient.getSubmissionStatus(sentSubmission);
        assertThat(status.getState(), is(EventState.REJECTED));

        // second attempt to receive and reject the submission should return an empty result
        // since the submission is gone after being rejected
        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
        assertThat(exception.getCause(), instanceOf(RestApiException.class));
        assertThat(((RestApiException) exception.getCause()).getStatusCode(), is(404));
    }

    @Test
    void testAcceptEvent() throws IOException {

        // Given
        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment();

        final String submissionData = getResourceAsString("/submission_data.json");

        final SenderClient senderClient = ClientFactory.createSenderClient(config);
        final SubscriberClient subscriberClient = ClientFactory.createSubscriberClient(config);

        final var submission = SendableSubmission.Builder()
                .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
                .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
                .setJsonData(submissionData, URI.create("https://schema.fitko.de/fim/s00000114_1.1.schema.json"))
                .addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "plain/text"))
                .build();

        final var sentSubmission = senderClient.send(submission);

        Assertions.assertNotNull(sentSubmission);

        // When
        final var sentSubmissionId = sentSubmission.getSubmissionId();

        // accept and remove
        subscriberClient.requestSubmission(sentSubmissionId).acceptSubmission();

        // check event log if accept event was sent
        final Status status = senderClient.getSubmissionStatus(sentSubmission);
        assertThat(status.getState(), is(EventState.ACCEPTED));

        // second attempt to receive the submission should return an empty result
        // since the submission is gone after being accepted
        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
        assertThat(exception.getCause(), instanceOf(RestApiException.class));
        assertThat(((RestApiException) exception.getCause()).getStatusCode(), is(404));
    }

    @Test
    void testAcceptEventWithProblem() throws IOException {

        // Given
        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment();

        final String submissionData = getResourceAsString("/submission_data.json");

        final var submission = SendableSubmission.Builder()
                .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
                .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
                .setJsonData(submissionData, URI.create("https://schema.fitko.de/fim/s00000114_1.1.schema.json"))
                .addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "plain/text"))
                .build();

        final SenderClient senderClient = ClientFactory.createSenderClient(config);
        final var subscriberClient = ClientFactory.createSubscriberClient(config);

        final var sentSubmission = senderClient.send(submission);

        assertNotNull(sentSubmission);

        // When
        final var sentSubmissionId = sentSubmission.getSubmissionId();

        // accept and remove
        subscriberClient.requestSubmission(sentSubmissionId).acceptSubmission(new IncorrectDataAuthenticationTag());

        // check event log if accept event was sent and contains a problem
        // check event log if accept event was sent
        final Status status = senderClient.getSubmissionStatus(sentSubmission);
        assertThat(status.getState(), is(EventState.ACCEPTED));

        // second attempt to receive the submission should return an empty result
        // since the submission is gone after being accepted
        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> subscriberClient.requestSubmission(sentSubmissionId));
        assertThat(exception.getCause(), instanceOf(RestApiException.class));
        assertThat(((RestApiException) exception.getCause()).getStatusCode(), is(404));
    }

    @Test
    void testRejectEventViaClient() throws IOException {

        // Given
        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment();

        final String submissionData = getResourceAsString("/submission_data.json");

        final SenderClient senderClient = ClientFactory.createSenderClient(config);
        final SubscriberClient subscriberClient = ClientFactory.createSubscriberClient(config);

        final var submission = SendableSubmission.Builder()
                .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
                .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
                .setJsonData(submissionData, URI.create("https://schema.fitko.de/fim/s00000114_1.1.schema.json"))
                .addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "text/plain"))
                .build();

        final var sentSubmission = senderClient.send(submission);

        assertNotNull(sentSubmission);

        // When
        final Optional<SubmissionForPickup> submissionForPickup = subscriberClient.getAvailableSubmissionsForDestination(submission.getDestinationId())
                .stream()
                .filter(s -> s.getSubmissionId().equals(sentSubmission.getSubmissionId()))
                .findFirst();

        assertTrue(submissionForPickup.isPresent());

        subscriberClient.rejectSubmission(submissionForPickup.get(), List.of(new InvalidEventLog()));

        // Then
        assertThat(senderClient.getSubmissionStatus(sentSubmission).getState(), is(EventState.REJECTED));

        // second attempt to receive and reject the submission should return an empty result since the submission is gone after being rejected
        final FitConnectSubscriberException exception = assertThrows(FitConnectSubscriberException.class, () -> subscriberClient.requestSubmission(sentSubmission.getSubmissionId()));
        assertThat(exception.getCause(), instanceOf(RestApiException.class));
        assertThat(((RestApiException) exception.getCause()).getStatusCode(), is(404));
    }

    @Test
    void testReadSubmissionStatus() throws IOException {

        // Given
        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment();

        final String submissionData = getResourceAsString("/submission_data.json");

        final SenderClient senderClient = ClientFactory.createSenderClient(config);
        final SubscriberClient subscriberClient = ClientFactory.createSubscriberClient(config);

        final var submission = SendableSubmission.Builder()
                .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
                .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
                .setJsonData(submissionData, URI.create("https://schema.fitko.de/fim/s00000114_1.1.schema.json"))
                .addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "plain/text"))
                .build();

        final var sentSubmission = senderClient.send(submission);

        Assertions.assertNotNull(sentSubmission);

        final Status statusForSubmission = subscriberClient.getSubmissionStatus(sentSubmission.getDestinationId(), sentSubmission.getCaseId(), sentSubmission.getSubmissionId());

        assertThat(statusForSubmission.getState(), isOneOf(Event.SUBMIT_SUBMISSION.getState(), Event.NOTIFY_SUBMISSION.getState()));
    }

    @Test
    void testReadAllSubmissionsOrderedByIssuedAtTimestamp() throws IOException {

        // Given
        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment();
        final SenderClient senderClient = ClientFactory.createSenderClient(config);
        final SubscriberClient subscriberClient = ClientFactory.createSubscriberClient(config);

        final String submissionData = getResourceAsString("/submission_data.json");
        final UUID destinationId = UUID.fromString(System.getenv("TEST_DESTINATION_ID"));

        // ensure destination is empty
        subscriberClient.getAvailableSubmissionsForDestination(destinationId).forEach(s -> subscriberClient.rejectSubmission(s, List.of(new TechnicalError())));
        assertThat(subscriberClient.getAvailableSubmissionsForDestination(destinationId), hasSize(0));

        final var submission = SendableSubmission.Builder()
                .setDestination(destinationId)
                .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
                .setJsonData(submissionData, URI.create("https://schema.fitko.de/fim/s00000114_1.1.schema.json"))
                .build();

        IntStream.range(0, 5).forEach(index -> {
            try {
                senderClient.send(submission);
                // create some distance between submission dates
                Thread.sleep(1000);
            } catch (final InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        final List<Date> submissionDates = subscriberClient.getAvailableSubmissionsForDestination(destinationId).stream()
                .map(subscriberClient::requestSubmission)
                // receivedSubmission implements comparable on issuedAt
                .sorted()
                .map(ReceivedSubmission::getSubmittedAt)
                .collect(Collectors.toList());

        assertThat(submissionDates, hasSize(5));

        boolean consecutiveSubmissionDates = false;

        for (int i = 0; i < submissionDates.size() - 1; i++) {
            consecutiveSubmissionDates = submissionDates.get(i + 1).toInstant().isAfter(submissionDates.get(i).toInstant());
        }

        assertTrue(consecutiveSubmissionDates);
    }

    @Test
    void testMultiThreadExecution() throws IOException, InterruptedException {

        // Given
        final ApplicationConfig config = getConfigWithCredentialsFromEnvironment();

        final String submissionData = getResourceAsString("/submission_data.json");

        final SenderClient senderClient = ClientFactory.createSenderClient(config);
        final var submission = SendableSubmission.Builder()
                .setDestination(UUID.fromString(System.getenv("TEST_DESTINATION_ID")))
                .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Test Service")
                .setJsonData(submissionData, URI.create("https://schema.fitko.de/fim/s00000114_1.1.schema.json"))
                .addAttachment(Attachment.fromPath(Path.of("src/test/resources/attachment.txt"), "plain/text"))
                .build();

        IntStream.range(0, 10).mapToObj(i -> submission).forEach(senderClient::send);

        final ExecutorService es = Executors.newCachedThreadPool();
        final SubscriberClient subscriberClient = ClientFactory.createSubscriberClient(config);

        for (final SubmissionForPickup submissionForPickup : subscriberClient.getAvailableSubmissionsForDestination(submission.getDestinationId())) {
            es.execute(new SubscriberRunner(subscriberClient, submissionForPickup, "Thread-" + submissionForPickup.getSubmissionId()));
        }

        es.shutdown();
        es.awaitTermination(1, TimeUnit.MINUTES);

    }

    static class SubscriberRunner implements Runnable {
        private final SubscriberClient subscriberClient;
        private final SubmissionForPickup submissionForPickup;
        private final String threadName;

        SubscriberRunner(final SubscriberClient subscriberClient, final SubmissionForPickup submissionForPickup, final String threadName) {
            this.subscriberClient = subscriberClient;
            this.submissionForPickup = submissionForPickup;
            this.threadName = threadName;
            System.out.println("Creating " + threadName);
        }

        public void run() {
            System.out.println("Running " + threadName);
            assertNotNull(subscriberClient.requestSubmission(submissionForPickup));
            System.out.println("Thread " + threadName + " exiting.");
        }
    }
}