001/*-
002 * #%L
003 * Smile CDR - CDR
004 * %%
005 * Copyright (C) 2016 - 2026 Smile CDR, Inc.
006 * %%
007 * All rights reserved.
008 * #L%
009 */
010package ca.cdr.test.app.clients;
011
012import ca.cdr.api.model.json.easyshare.CreateShlResponseJson;
013import ca.cdr.api.model.json.easyshare.ShlManifestRequestJson;
014import ca.cdr.api.model.json.easyshare.ShlManifestResponseJson;
015import ca.cdr.test.app.clients.common.HttpClientOwnership;
016import ca.cdr.test.app.clients.common.RequestFactoryUtil;
017import ca.cdr.test.app.clients.common.SmileTestHttpClient;
018import jakarta.annotation.Nonnull;
019import jakarta.annotation.Nullable;
020import org.apache.commons.lang3.Validate;
021import org.springframework.http.MediaType;
022import org.springframework.http.client.support.BasicAuthenticationInterceptor;
023import org.springframework.util.LinkedMultiValueMap;
024import org.springframework.util.MultiValueMap;
025import org.springframework.web.client.RestClient;
026
027import java.time.Instant;
028import java.util.Objects;
029
030public class SmartHealthLinkClient implements AutoCloseable {
031
032        private final RestClient myRestClient;
033
034        @Nullable
035        private final HttpClientOwnership myOwnership;
036
037        SmartHealthLinkClient(RestClient theRestClient) {
038                this(theRestClient, null);
039        }
040
041        private SmartHealthLinkClient(RestClient theRestClient, @Nullable HttpClientOwnership theOwnership) {
042                myRestClient = theRestClient;
043                myOwnership = theOwnership;
044        }
045
046        public static @Nonnull SmartHealthLinkClient open(
047                        @Nonnull String theBaseUrl, @Nonnull String theUsername, @Nonnull String thePassword) {
048                return authenticatedClient(HttpClientOwnership.resolve(null), theBaseUrl, theUsername, thePassword);
049        }
050
051        public static @Nonnull SmartHealthLinkClient openAnonymous(@Nonnull String theBaseUrl) {
052                HttpClientOwnership ownership = HttpClientOwnership.resolve(null);
053                return new SmartHealthLinkClient(builderFor(theBaseUrl, ownership.client()).build(), ownership);
054        }
055
056        public static @Nonnull SmartHealthLinkClient issuingOn(
057                        @Nonnull SmileTestHttpClient theHttpClient,
058                        @Nonnull String theBaseUrl,
059                        @Nonnull String theUsername,
060                        @Nonnull String thePassword) {
061                return authenticatedClient(
062                                HttpClientOwnership.resolve(theHttpClient), theBaseUrl, theUsername, thePassword);
063        }
064
065        private static @Nonnull SmartHealthLinkClient authenticatedClient(
066                        @Nonnull HttpClientOwnership theOwnership,
067                        @Nonnull String theBaseUrl,
068                        @Nonnull String theUsername,
069                        @Nonnull String thePassword) {
070                RestClient restClient = builderFor(theBaseUrl, theOwnership.client())
071                                .requestInterceptor(new BasicAuthenticationInterceptor(theUsername, thePassword))
072                                .build();
073                return new SmartHealthLinkClient(restClient, theOwnership);
074        }
075
076        private static @Nonnull RestClient.Builder builderFor(
077                        String theBaseUrl, @Nonnull SmileTestHttpClient theHttpClient) {
078                return RestClient.builder()
079                                .baseUrl(theBaseUrl)
080                                .requestFactory(RequestFactoryUtil.wrap(theHttpClient))
081                                .defaultRequest(r -> r.accept(MediaType.APPLICATION_JSON));
082        }
083
084        @Nonnull
085        public CreateShlResponseJson createSmartHealthLink(
086                        @Nonnull String theLabel, @Nonnull String theEntryContentType, @Nonnull String theEntryContent) {
087                return createSmartHealthLink(theLabel, theEntryContentType, theEntryContent, null, null, null);
088        }
089
090        @Nonnull
091        public CreateShlResponseJson createSmartHealthLink(
092                        @Nonnull String theLabel,
093                        @Nonnull String theEntryContentType,
094                        @Nonnull String theEntryContent,
095                        @Nullable String thePasscode,
096                        @Nullable Instant theExpiry,
097                        @Nullable String theSubjectFhirResourceId) {
098                Validate.notBlank(theLabel, "Label is required");
099                Validate.notBlank(theEntryContentType, "Entry content type is required");
100                Validate.notBlank(theEntryContent, "Entry content is required");
101
102                MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
103                form.add("label", theLabel);
104                form.add("entryContentType", theEntryContentType);
105                form.add("entryContentString", theEntryContent);
106                if (thePasscode != null) {
107                        form.add("passcode", thePasscode);
108                }
109                if (theExpiry != null) {
110                        form.add("expiryTime", theExpiry.toString());
111                }
112                if (theSubjectFhirResourceId != null) {
113                        form.add("subjectFhirResourceId", theSubjectFhirResourceId);
114                }
115
116                CreateShlResponseJson result = myRestClient
117                                .post()
118                                .uri("shl/manifest")
119                                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
120                                .body(form)
121                                .retrieve()
122                                .body(CreateShlResponseJson.class);
123                return Objects.requireNonNull(result);
124        }
125
126        @Nonnull
127        public ShlManifestResponseJson fetchManifest(
128                        @Nonnull String thePublicId, @Nullable String theRecipient, @Nullable String thePasscode) {
129                Validate.notBlank(thePublicId, "Public ID is required");
130
131                ShlManifestRequestJson request = new ShlManifestRequestJson();
132                request.setRecipient(theRecipient);
133                request.setPasscode(thePasscode);
134
135                ShlManifestResponseJson result = myRestClient
136                                .post()
137                                .uri("shl/manifest/{publicId}", thePublicId)
138                                .contentType(MediaType.APPLICATION_JSON)
139                                .body(request)
140                                .retrieve()
141                                .body(ShlManifestResponseJson.class);
142                return Objects.requireNonNull(result);
143        }
144
145        @Nonnull
146        public byte[] fetchFile(@Nonnull String thePublicId, @Nonnull String theFileId) {
147                Validate.notBlank(thePublicId, "Public ID is required");
148                Validate.notBlank(theFileId, "File ID is required");
149
150                byte[] result = myRestClient
151                                .get()
152                                .uri("shl/manifest/{publicId}/file/{fileId}", thePublicId, theFileId)
153                                .accept(MediaType.ALL)
154                                .retrieve()
155                                .body(byte[].class);
156                return Objects.requireNonNull(result);
157        }
158
159        @Override
160        public void close() {
161                if (myOwnership != null) {
162                        myOwnership.closeIfOwned();
163                }
164        }
165}