All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.fitko.fitconnect.core.cases.CaseApiService Maven / Gradle / Ivy

Go to download

Library that provides client access to the FIT-Connect api-endpoints for sending, subscribing and routing

The newest version!
package dev.fitko.fitconnect.core.cases;

import com.nimbusds.jwt.SignedJWT;
import dev.fitko.fitconnect.api.domain.model.cases.Case;
import dev.fitko.fitconnect.api.domain.model.cases.Cases;
import dev.fitko.fitconnect.api.domain.model.event.Event;
import dev.fitko.fitconnect.api.domain.model.event.EventLog;
import dev.fitko.fitconnect.api.domain.model.event.EventLogEntry;
import dev.fitko.fitconnect.api.domain.model.event.Status;
import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
import dev.fitko.fitconnect.api.domain.model.reply.Reply;
import dev.fitko.fitconnect.api.domain.model.reply.SentReply;
import dev.fitko.fitconnect.api.domain.model.submission.SentSubmission;
import dev.fitko.fitconnect.api.domain.model.submission.Submission;
import dev.fitko.fitconnect.api.domain.validation.ValidationContext;
import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
import dev.fitko.fitconnect.api.exceptions.internal.AuthenticationTagsEmptyException;
import dev.fitko.fitconnect.api.exceptions.internal.EventLogException;
import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
import dev.fitko.fitconnect.api.exceptions.internal.SubmitEventNotFoundException;
import dev.fitko.fitconnect.api.services.auth.OAuthService;
import dev.fitko.fitconnect.api.services.events.CaseService;
import dev.fitko.fitconnect.api.services.events.EventLogVerificationService;
import dev.fitko.fitconnect.api.services.http.HttpClient;
import dev.fitko.fitconnect.core.http.HttpHeaders;
import dev.fitko.fitconnect.core.http.MimeTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

import static dev.fitko.fitconnect.core.utils.EventLogUtil.eventSubjectEqualsId;
import static dev.fitko.fitconnect.core.utils.EventLogUtil.eventToLogEntry;
import static dev.fitko.fitconnect.core.utils.EventLogUtil.getAuthTags;
import static dev.fitko.fitconnect.core.utils.EventLogUtil.getFilteredJwtsFromEventLog;
import static dev.fitko.fitconnect.core.utils.EventLogUtil.getJWTSFromEvents;
import static dev.fitko.fitconnect.core.utils.EventLogUtil.mapEventLogToEntries;

public class CaseApiService implements CaseService {

    public static final String EVENTS_PATH = "/v1/cases/%s/events";
    public static final String CASES_PATH = "/v1/cases";
    public static final String CASE_PATH = "/v1/cases/%s";

    private static final Logger LOGGER = LoggerFactory.getLogger(CaseApiService.class);

    private final OAuthService authService;
    private final HttpClient httpClient;
    private final EventLogVerificationService eventLogVerifier;
    private final String baseUrl;

    public CaseApiService(final OAuthService authService,
                          final HttpClient httpClient,
                          final EventLogVerificationService eventLogVerifier,
                          final String baseUrl) {
        this.authService = authService;
        this.httpClient = httpClient;
        this.eventLogVerifier = eventLogVerifier;

        this.baseUrl = baseUrl;
    }

    @Override
    public List getEventLog(final UUID caseId, final UUID destinationId) throws RestApiException, EventLogException {
        final EventLog eventLog = loadEventLog(caseId);
        final List events = getJWTSFromEvents(eventLog.getEventLogs());
        final ValidationContext ctx = ValidationContext.withoutAuthTagValidation(destinationId, caseId);
        checkForValidationErrors(eventLogVerifier.validateEventLogs(ctx, events));
        return mapEventLogToEntries(events);
    }

    @Override
    public AuthenticationTags getAuthenticationTags(final Submission submission) throws RestApiException, EventLogException {

        final SignedJWT submitEvent = getEvent(submission.getCaseId(), Event.SUBMIT_SUBMISSION, submission.getSubmissionId());
        final AuthenticationTags authenticationTags = getAuthTagsFromEvent(submitEvent);

        final var ctx = ValidationContext.withAuthTagValidation(submission.getDestinationId(), submission.getCaseId(), authenticationTags);
        checkForValidationErrors(eventLogVerifier.validateEventLogs(ctx, List.of(submitEvent)));

        return authenticationTags;
    }

    @Override
    public AuthenticationTags getAuthenticationTags(final Reply reply) throws RestApiException, EventLogException {

        final SignedJWT submitEvent = getEvent(reply.getCaseId(), Event.SUBMIT_REPLY, reply.getReplyId());
        final AuthenticationTags authenticationTags = getAuthTagsFromEvent(submitEvent);

        final var ctx = ValidationContext.withAuthTagValidation(null, reply.getCaseId(), authenticationTags);
        checkForValidationErrors(eventLogVerifier.validateEventLogs(ctx, List.of(submitEvent)));

        return authenticationTags;
    }

    @Override
    public void sendEvent(final UUID caseId, final String signedAndSerializedSET) {
        final String url = String.format(baseUrl + EVENTS_PATH, caseId);
        try {
            httpClient.post(url, getHttpHeaders(MimeTypes.APPLICATION_JOSE), signedAndSerializedSET, Void.class);
        } catch (final RestApiException e) {
            throw new RestApiException("Sending event failed", e);
        }
    }

    @Override
    public Status getStatus(final SentSubmission sentSubmission) {
        final SignedJWT latestEvent = getLogFilteredById(sentSubmission.getCaseId(), sentSubmission.getSubmissionId());
        final ValidationContext contextWithoutAuthTagValidation = ValidationContext.withoutAuthTagValidation(sentSubmission.getDestinationId(), sentSubmission.getCaseId());
        return getVerifiedStatus(contextWithoutAuthTagValidation, latestEvent);
    }

    @Override
    public Status getStatus(final SentReply reply) {
        final SignedJWT latestEvent = getLogFilteredById(reply.getCaseId(), reply.getReplyId());
        //FIXME check if destination id check is needed here for replies.
        final ValidationContext contextWithoutAuthTagValidation = ValidationContext.withoutAuthTagValidation(reply.getCaseId());
        return getVerifiedStatus(contextWithoutAuthTagValidation, latestEvent);
    }

    @Override
    public Status getSubmissionSubmitState(final UUID caseId, final UUID submissionId) {
        final SignedJWT submitEvent = getEvent(caseId, Event.SUBMIT_SUBMISSION, submissionId);
        final var ctx = ValidationContext.withoutAuthTagValidation(caseId);
        checkForValidationErrors(eventLogVerifier.validateEventLogs(ctx, List.of(submitEvent)));
        final EventLogEntry eventLogEntry = eventToLogEntry(submitEvent);
        return Status.fromEventLogEntry(eventLogEntry);
    }

    @Override
    public Cases listCases(final int limit, final int offset) {
        return loadCases(limit, offset);
    }

    @Override
    public Case getCase(final UUID caseId) {
        final String url = String.format(baseUrl + CASE_PATH, caseId);
        try {
            return httpClient.get(url, getHttpHeaders(MimeTypes.APPLICATION_JSON), Case.class).getBody();
        } catch (final RestApiException e) {
            throw new RestApiException("Case query failed", e);
        }
    }

    private SignedJWT getEvent(final UUID caseId, final Event event, final UUID subjectFilter) {
        final EventLog eventLog = loadEventLog(caseId);
        final List submitEvents = getFilteredJwtsFromEventLog(subjectFilter, event, eventLog);

        if (submitEvents.size() != 1) {
            throw new SubmitEventNotFoundException("Event log does not contain exactly one submit event");
        }

        return submitEvents.stream().findFirst().get();
    }

    private AuthenticationTags getAuthTagsFromEvent(final SignedJWT submitEvent) {
        final AuthenticationTags authenticationTags = getAuthTags(submitEvent);
        if (authenticationTags == null || authenticationTags.getMetadata() == null && authenticationTags.getData() == null) {
            throw new AuthenticationTagsEmptyException("Authentication tags are empty");
        }
        return authenticationTags;
    }

    private EventLog loadEventLog(final UUID caseId) {
        final String url = String.format(baseUrl + EVENTS_PATH, caseId);
        try {
            return httpClient.get(url, getHttpHeaders(MimeTypes.APPLICATION_JSON), EventLog.class).getBody();
        } catch (final RestApiException e) {
            throw new RestApiException("EventLog query failed", e);
        }
    }

    private Cases loadCases(final int limit, final int offset) {
        final String urlWithQueryParams = baseUrl + CASES_PATH + "?limit=" + limit + "&offset=" + offset;
        try {
            return httpClient.get(urlWithQueryParams, getHttpHeaders(MimeTypes.APPLICATION_JSON), Cases.class).getBody();
        } catch (final RestApiException e) {
            throw new RestApiException("Cases query failed", e);
        }
    }

    private Map getHttpHeaders(final String mediaType) {
        return new HashMap<>(Map.of(
                HttpHeaders.AUTHORIZATION, "Bearer " + authService.getCurrentToken().getAccessToken(),
                HttpHeaders.CONTENT_TYPE, mediaType,
                HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.toString()));
    }

    private Status getVerifiedStatus(final ValidationContext validationContext, final SignedJWT latestEvent) {
        if (latestEvent == null) {
            LOGGER.info("No events found");
            return new Status();
        }
        checkForValidationErrors(eventLogVerifier.validateEventLogs(validationContext, List.of(latestEvent)));
        final EventLogEntry eventLogEntry = eventToLogEntry(latestEvent);
        return Status.fromEventLogEntry(eventLogEntry);
    }

    private SignedJWT getLogFilteredById(final UUID caseId, final UUID id) {
        final EventLog eventLog = loadEventLog(caseId);
        return getJWTSFromEvents(eventLog.getEventLogs()).stream()
                .filter(eventSubjectEqualsId(id))
                .reduce((first, second) -> second)
                .orElse(null);
    }

    private void checkForValidationErrors(final List validationResults) {
        if (!validationResults.isEmpty()) {
            throw new EventLogException(joinMessages(validationResults));
        }
    }

    private String joinMessages(final List validationResults) {
        return validationResults.stream()
                .map(ValidationResult::getError)
                .map(Exception::getMessage)
                .collect(Collectors.joining("\n"));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy