001package org.hl7.fhir.r5.utils.client.network;
002
003import lombok.Getter;
004import lombok.Setter;
005
006import org.hl7.fhir.r5.model.Bundle;
007import org.hl7.fhir.r5.model.Resource;
008import org.hl7.fhir.r5.utils.client.EFhirClientException;
009import org.hl7.fhir.utilities.ToolingClientLogger;
010import org.hl7.fhir.utilities.http.HTTPRequest;
011import org.hl7.fhir.utilities.http.HTTPHeader;
012
013import java.io.IOException;
014import java.net.URI;
015import java.util.Collections;
016import java.util.Map;
017import java.util.concurrent.TimeUnit;
018
019public class Client {
020
021  public static final String DEFAULT_CHARSET = "UTF-8";
022  private static final long DEFAULT_TIMEOUT = 5000;
023  @Getter @Setter
024  private ToolingClientLogger logger;
025
026  @Setter @Getter
027  private int retryCount;
028  @Setter @Getter
029  private long timeout = DEFAULT_TIMEOUT;
030
031  @Setter @Getter
032  private String base;
033
034  public <T extends Resource> ResourceRequest<T> issueOptionsRequest(URI optionsUri,
035                                                                     String resourceFormat,
036                                                                     String message,
037                                                                     long timeout) throws IOException {
038    HTTPRequest request = new HTTPRequest()
039      .withUrl(optionsUri.toURL())
040      .withMethod(HTTPRequest.HttpMethod.OPTIONS);
041    return executeFhirRequest(request, resourceFormat, Collections.emptyList(), message, retryCount, timeout);
042  }
043
044  public <T extends Resource> ResourceRequest<T> issueGetResourceRequest(URI resourceUri,
045                                                                         String resourceFormat,
046                                                                         Iterable<HTTPHeader> headers,
047                                                                         String message,
048                                                                         long timeout) throws IOException {
049    HTTPRequest request = new HTTPRequest()
050      .withUrl(resourceUri.toURL())
051      .withMethod(HTTPRequest.HttpMethod.GET);
052
053    return executeFhirRequest(request, resourceFormat, headers, message, retryCount, timeout);
054  }
055
056  public <T extends Resource> ResourceRequest<T> issuePutRequest(URI resourceUri,
057                                                                 byte[] payload,
058                                                                 String resourceFormat,
059                                                                 String message,
060                                                                 long timeout) throws IOException {
061    return issuePutRequest(resourceUri, payload, resourceFormat, Collections.emptyList(), message, timeout);
062  }
063
064  public <T extends Resource> ResourceRequest<T> issuePutRequest(URI resourceUri,
065                                                                 byte[] payload,
066                                                                 String resourceFormat,
067                                                                 Iterable<HTTPHeader> headers,
068                                                                 String message,
069                                                                 long timeout) throws IOException {
070    if (payload == null) throw new EFhirClientException(0, "PUT requests require a non-null payload");
071
072    HTTPRequest request = new HTTPRequest()
073      .withUrl(resourceUri.toURL())
074      .withMethod(HTTPRequest.HttpMethod.PUT)
075      .withBody(payload)
076      .withContentType(getContentTypeWithDefaultCharset(resourceFormat));
077
078    return executeFhirRequest(request, resourceFormat, headers, message, retryCount, timeout);
079  }
080
081  public <T extends Resource> ResourceRequest<T> issuePostRequest(URI resourceUri,
082                                                                  byte[] payload,
083                                                                  String resourceFormat,
084                                                                  String message,
085                                                                  long timeout) throws IOException {
086    return issuePostRequest(resourceUri, payload, resourceFormat, Collections.emptyList(), message, timeout);
087  }
088
089  public <T extends Resource> ResourceRequest<T> issuePostRequest(URI resourceUri,
090                                                                  byte[] payload,
091                                                                  String resourceFormat,
092                                                                  Iterable<HTTPHeader> headers,
093                                                                  String message,
094                                                                  long timeout) throws IOException {
095    if (payload == null) throw new EFhirClientException(0, "POST requests require a non-null payload");
096
097    HTTPRequest request = new HTTPRequest()
098      .withUrl(resourceUri.toURL())
099      .withMethod(HTTPRequest.HttpMethod.POST)
100      .withBody(payload)
101      .withContentType(getContentTypeWithDefaultCharset(resourceFormat));
102
103    return executeFhirRequest(request, resourceFormat, headers, message, retryCount, timeout);
104  }
105
106  public boolean issueDeleteRequest(URI resourceUri) throws IOException {
107    HTTPRequest request = new HTTPRequest()
108      .withUrl(resourceUri.toURL())
109      .withMethod(HTTPRequest.HttpMethod.DELETE);
110    return executeFhirRequest(request, null, Collections.emptyList(), null, retryCount, timeout).isSuccessfulRequest();
111  }
112
113  public Bundle issueGetFeedRequest(URI resourceUri, String resourceFormat) throws IOException {
114    HTTPRequest request = new HTTPRequest()
115      .withUrl(resourceUri.toURL())
116      .withMethod(HTTPRequest.HttpMethod.GET);
117    return executeBundleRequest(request, resourceFormat, Collections.emptyList(), null, retryCount, timeout);
118  }
119
120  public Bundle issuePostFeedRequest(URI resourceUri,
121                                     Map<String, String> parameters,
122                                     String resourceName,
123                                     Resource resource,
124                                     String resourceFormat) throws IOException {
125    String boundary = "----WebKitFormBoundarykbMUo6H8QaUnYtRy";
126    byte[] payload = ByteUtils.encodeFormSubmission(parameters, resourceName, resource, boundary);
127
128    HTTPRequest request = new HTTPRequest()
129      .withUrl(resourceUri.toURL())
130      .withMethod(HTTPRequest.HttpMethod.POST)
131      .withBody(payload)
132      .withContentType(getContentTypeWithDefaultCharset(resourceFormat));
133    return executeBundleRequest(request, resourceFormat, Collections.emptyList(), null, retryCount, timeout);
134  }
135
136  public Bundle postBatchRequest(URI resourceUri,
137                                 byte[] payload,
138                                 String resourceFormat,
139                                 Iterable<HTTPHeader> headers,
140                                 String message,
141                                 int timeout) throws IOException {
142    if (payload == null) throw new EFhirClientException(0, "POST requests require a non-null payload");
143
144    HTTPRequest request = new HTTPRequest()
145      .withUrl(resourceUri.toURL())
146      .withMethod(HTTPRequest.HttpMethod.POST)
147      .withBody(payload)
148      .withContentType(getContentTypeWithDefaultCharset(resourceFormat));
149    return executeBundleRequest(request, resourceFormat, headers, message, retryCount, timeout);
150  }
151
152  private static String getContentTypeWithDefaultCharset(String resourceFormat) {
153    return resourceFormat + ";charset=" + DEFAULT_CHARSET;
154  }
155
156  public <T extends Resource> Bundle executeBundleRequest(HTTPRequest request,
157                                                          String resourceFormat,
158                                                          Iterable<HTTPHeader> headers,
159                                                          String message,
160                                                          int retryCount,
161                                                          long timeout) throws IOException {
162    return new FhirRequestBuilder(request, base)
163      .withLogger(logger)
164      .withResourceFormat(resourceFormat)
165      .withRetryCount(retryCount)
166      .withMessage(message)
167      .withHeaders(headers == null ? Collections.emptyList() : headers)
168      .withTimeout(timeout, TimeUnit.MILLISECONDS)
169      .executeAsBatch();
170  }
171
172  public <T extends Resource> ResourceRequest<T> executeFhirRequest(HTTPRequest request,
173                                                                    String resourceFormat,
174                                                                    Iterable<HTTPHeader> headers,
175                                                                    String message,
176                                                                    int retryCount,
177                                                                    long timeout) throws IOException {
178    return new FhirRequestBuilder(request, base)
179      .withLogger(logger)
180      .withResourceFormat(resourceFormat)
181      .withRetryCount(retryCount)
182      .withMessage(message)
183      .withHeaders(headers == null ? Collections.emptyList() : headers)
184      .withTimeout(timeout, TimeUnit.MILLISECONDS)
185      .execute();
186  }
187}