no.digipost.api.useragreements.client.ApiService.java Source code

Java tutorial

Introduction

Here is the source code for no.digipost.api.useragreements.client.ApiService.java

Source

/**
 * Copyright (C) Posten Norge AS
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package no.digipost.api.useragreements.client;

import no.digipost.api.useragreements.client.response.StreamingRateLimitedResponse;
import no.digipost.cache2.inmemory.SingleCached;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;

import javax.xml.bind.JAXB;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import static java.time.Duration.ofMinutes;
import static java.time.format.DateTimeFormatter.ISO_DATE_TIME;
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
import static no.digipost.api.useragreements.client.Headers.X_Digipost_UserId;
import static no.digipost.api.useragreements.client.response.ResponseUtils.mapOkResponseOrThrowException;
import static no.digipost.api.useragreements.client.response.ResponseUtils.unmarshallEntities;
import static no.digipost.api.useragreements.client.response.ResponseUtils.unmarshallEntity;
import static no.digipost.cache2.inmemory.CacheConfig.expireAfterAccess;
import static no.digipost.cache2.inmemory.CacheConfig.useSoftValues;

public class ApiService {

    public static final String DIGIPOST_MEDIA_TYPE_USERS_V1 = "application/vnd.digipost.user-v1+xml";
    private static final String USER_DOCUMENTS_PATH = "user-documents";
    private static final String USER_AGREEMENTS_PATH = "user-agreements";

    private final URI serviceEndpoint;
    private final BrokerId brokerId;
    private final CloseableHttpClient httpClient;

    public ApiService(final URI serviceEndpoint, final BrokerId brokerId, final CloseableHttpClient httpClient) {
        this.serviceEndpoint = serviceEndpoint;
        this.brokerId = brokerId;
        this.httpClient = httpClient;
    }

    public IdentificationResult identifyUser(final SenderId senderId, final UserId userId,
            final String requestTrackingId, final ResponseHandler<IdentificationResult> handler) {
        return executeHttpRequest(newPostRequest(getEntryPoint().getIdentificationUri(), requestTrackingId,
                new Identification(userId.serialize())), handler);
    }

    public void createAgreement(final SenderId senderId, final Agreement agreement, final String requestTrackingId,
            final ResponseHandler<Void> handler) {
        executeHttpRequest(newPostRequest(new URIBuilder(serviceEndpoint).setPath(userAgreementsPath(senderId)),
                requestTrackingId, agreement), handler);
    }

    public GetAgreementResult getAgreement(final SenderId senderId, final AgreementType agreementType,
            final UserId userId, final String requestTrackingId,
            final ResponseHandler<GetAgreementResult> handler) {
        URIBuilder uriBuilder = new URIBuilder(serviceEndpoint).setPath(userAgreementsPath(senderId))
                .setParameter("user-id", userId.serialize())
                .setParameter("agreement-type", agreementType.getType());
        return executeHttpRequest(newGetRequest(uriBuilder, requestTrackingId), handler);
    }

    public Agreements getAgreements(final SenderId senderId, final UserId userId, final String requestTrackingId,
            final ResponseHandler<Agreements> handler) {
        URIBuilder uriBuilder = new URIBuilder(serviceEndpoint).setPath(userAgreementsPath(senderId))
                .setParameter("user-id", userId.serialize());
        return executeHttpRequest(newGetRequest(uriBuilder, requestTrackingId), handler);
    }

    public void deleteAgrement(final SenderId senderId, final AgreementType agreementType, final UserId userId,
            final String requestTrackingId, final ResponseHandler<Void> handler) {
        URIBuilder uriBuilder = new URIBuilder(serviceEndpoint).setPath(userAgreementsPath(senderId))
                .setParameter("user-id", userId.serialize())
                .setParameter("agreement-type", agreementType.getType());
        HttpDelete deleteAgreementRequest = new HttpDelete(buildUri(uriBuilder));
        executeHttpRequest(withRequestTrackingHeader(deleteAgreementRequest, requestTrackingId), handler);
    }

    public Documents getDocuments(final SenderId senderId, final AgreementType agreementType, final UserId userId,
            final GetDocumentsQuery query, final String requestTrackingId,
            final ResponseHandler<Documents> handler) {
        URIBuilder uriBuilder = new URIBuilder(serviceEndpoint).setPath(userDocumentsPath(senderId))
                .setParameter(UserId.QUERY_PARAM_NAME, userId.serialize())
                .setParameter(AgreementType.QUERY_PARAM_NAME, agreementType.getType());
        setGetDocumentsQueryParams(uriBuilder, query);
        return executeHttpRequest(newGetRequest(uriBuilder, requestTrackingId), handler);
    }

    private void setGetDocumentsQueryParams(final URIBuilder uriBuilder, final GetDocumentsQuery query) {
        if (query.getInvoiceStatus() != null) {
            uriBuilder.setParameter(InvoiceStatus.QUERY_PARAM_NAME, query.getInvoiceStatus().getStatus());
        }
        if (query.getInvoiceDueDateFrom() != null) {
            uriBuilder.setParameter("invoice-due-date-from", query.getInvoiceDueDateFrom().format(ISO_LOCAL_DATE));
        }
        if (query.getInvoiceDueDateTo() != null) {
            uriBuilder.setParameter("invoice-due-date-to", query.getInvoiceDueDateTo().format(ISO_LOCAL_DATE));
        }
        if (query.getDeliveryTimeFrom() != null) {
            uriBuilder.setParameter("delivery-time-from", query.getDeliveryTimeFrom().format(ISO_DATE_TIME));
        }
        if (query.getDeliveryTimeTo() != null) {
            uriBuilder.setParameter("delivery-time-to", query.getDeliveryTimeTo().format(ISO_DATE_TIME));
        }
    }

    public Document getDocument(final SenderId senderId, final AgreementType agreementType, final long documentId,
            final String requestTrackingId, final ResponseHandler<Document> handler) {
        URIBuilder uriBuilder = new URIBuilder(serviceEndpoint)
                .setPath(userDocumentsPath(senderId) + "/" + documentId)
                .setParameter(AgreementType.QUERY_PARAM_NAME, agreementType.getType());
        return executeHttpRequest(newGetRequest(uriBuilder, requestTrackingId), handler);
    }

    public void updateInvoice(final SenderId senderId, final AgreementType agreementType, final long documentId,
            final InvoiceUpdate invoice, final String requestTrackingId, final ResponseHandler<Void> handler) {
        URIBuilder uriBuilder = new URIBuilder(serviceEndpoint)
                .setPath(userDocumentsPath(senderId) + "/" + documentId + "/invoice")
                .setParameter(AgreementType.QUERY_PARAM_NAME, agreementType.getType());
        executeHttpRequest(newPostRequest(uriBuilder, requestTrackingId, invoice), handler);
    }

    public DocumentCount getDocumentCount(final SenderId senderId, final AgreementType agreementType,
            final UserId userId, final GetDocumentsQuery query, final String requestTrackingId,
            final ResponseHandler<DocumentCount> handler) {
        URIBuilder uriBuilder = new URIBuilder(serviceEndpoint).setPath(userDocumentsPath(senderId) + "/count")
                .setParameter(UserId.QUERY_PARAM_NAME, userId.serialize())
                .setParameter(AgreementType.QUERY_PARAM_NAME, agreementType.getType());
        setGetDocumentsQueryParams(uriBuilder, query);
        return executeHttpRequest(newGetRequest(uriBuilder, requestTrackingId), handler);
    }

    public DocumentContent getDocumentContent(final SenderId senderId, final AgreementType agreementType,
            final long documentId, final String requestTrackingId, final ResponseHandler<DocumentContent> handler) {
        URIBuilder uriBuilder = new URIBuilder(serviceEndpoint)
                .setPath(userDocumentsPath(senderId) + "/" + documentId + "/content")
                .setParameter(AgreementType.QUERY_PARAM_NAME, agreementType.getType());
        return executeHttpRequest(newGetRequest(uriBuilder, requestTrackingId), handler);
    }

    public StreamingRateLimitedResponse<UserId> getAgreementOwners(final SenderId senderId,
            final AgreementType agreementType, final Boolean smsNotificationsEnabled,
            final String requestTrackingId) {
        URIBuilder uriBuilder = new URIBuilder(serviceEndpoint)
                .setPath(userAgreementsPath(senderId) + "/agreement-owners")
                .setParameter(AgreementType.QUERY_PARAM_NAME, agreementType.getType());
        if (smsNotificationsEnabled != null) {
            uriBuilder.setParameter("invoice-sms-notification", smsNotificationsEnabled.toString());
        }

        HttpGet request = newGetRequest(uriBuilder, requestTrackingId);
        request.setHeader(X_Digipost_UserId, brokerId.serialize());
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(request);
            return new StreamingRateLimitedResponse<>(
                    mapOkResponseOrThrowException(response, r -> unmarshallEntities(r, AgreementOwners.class)),
                    AgreementOwners::getIdsAsStream);
        } catch (IOException ioe) {
            throw new RuntimeIOException(ioe.getMessage(), ioe);
        } catch (RuntimeException rte) {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    rte.addSuppressed(e);
                }
            }
            throw rte;
        }
    }

    private static String userAgreementsPath(final SenderId senderId) {
        return senderId.serialize() + "/" + USER_AGREEMENTS_PATH;
    }

    private static String userDocumentsPath(final SenderId senderId) {
        return senderId.serialize() + "/" + USER_DOCUMENTS_PATH;
    }

    private <T> T executeHttpRequest(final HttpRequestBase request, final ResponseHandler<T> handler) {
        try {
            request.setHeader(X_Digipost_UserId, brokerId.serialize());
            return httpClient.execute(request, handler);
        } catch (IOException e) {
            throw RuntimeIOException.from(e);
        }
    }

    private HttpGet newGetRequest(final URIBuilder uriBuilder, String requestTrackingId) {
        return newGetRequest(buildUri(uriBuilder), requestTrackingId);
    }

    private HttpGet newGetRequest(final URI uri, String requestTrackingId) {
        return withCommonHeaders(new HttpGet(uri), requestTrackingId);
    }

    private HttpPost newPostRequest(final URIBuilder uriBuilder, String requestTrackingId, Object postBodyEntity) {
        return newPostRequest(buildUri(uriBuilder), requestTrackingId, postBodyEntity);
    }

    private HttpPost newPostRequest(final URI uri, String requestTrackingId, Object postBodyEntity) {
        HttpPost request = withCommonHeaders(new HttpPost(uri), requestTrackingId);
        request.setHeader(HttpHeaders.CONTENT_TYPE, DIGIPOST_MEDIA_TYPE_USERS_V1);
        request.setEntity(marshallJaxbEntity(postBodyEntity));
        return request;
    }

    private static URI buildUri(URIBuilder builder) {
        try {
            return builder.build();
        } catch (URISyntaxException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    private static <REQ extends HttpRequestBase> REQ withCommonHeaders(REQ request, String requestTrackingId) {
        request.setHeader(HttpHeaders.ACCEPT, DIGIPOST_MEDIA_TYPE_USERS_V1);
        return withRequestTrackingHeader(request, requestTrackingId);
    }

    private static <REQ extends HttpRequestBase> REQ withRequestTrackingHeader(REQ request,
            final String requestTrackingId) {
        if (requestTrackingId != null && !requestTrackingId.isEmpty()) {
            request.setHeader("X-Digipost-Request-Id", requestTrackingId);
        }
        return request;
    }

    private static HttpEntity marshallJaxbEntity(final Object obj) {
        ByteArrayOutputStream bao = new ByteArrayOutputStream(1024);
        JAXB.marshal(obj, bao);
        return new ByteArrayEntity(bao.toByteArray());
    }

    public EntryPoint getEntryPoint() {
        try {
            return cachedEntryPoint.get();
        } catch (RuntimeException e) {
            if (e.getCause() instanceof UserAgreementsApiException) {
                throw (UserAgreementsApiException) e.getCause();
            } else {
                throw e;
            }
        }
    }

    private EntryPoint performGetEntryPoint() {
        return executeHttpRequest(newGetRequest(serviceEndpoint, null),
                response -> mapOkResponseOrThrowException(response, r -> unmarshallEntity(r, EntryPoint.class)));
    }

    private final SingleCached<EntryPoint> cachedEntryPoint = new SingleCached<>("digipost-entrypoint",
            this::performGetEntryPoint, expireAfterAccess(ofMinutes(5)), useSoftValues);
}