001package org.hl7.fhir.convertors.txClient;
002
003import java.io.IOException;
004import java.net.URISyntaxException;
005import java.util.EnumSet;
006import java.util.HashMap;
007import java.util.Map;
008
009import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
010import org.hl7.fhir.exceptions.FHIRException;
011import org.hl7.fhir.r4.model.Resource;
012import org.hl7.fhir.r4.utils.client.EFhirClientException;
013import org.hl7.fhir.r4.utils.client.FHIRToolingClient;
014import org.hl7.fhir.r5.formats.IParser.OutputStyle;
015import org.hl7.fhir.r5.formats.JsonParser;
016import org.hl7.fhir.r5.model.Bundle;
017import org.hl7.fhir.r5.model.CanonicalResource;
018import org.hl7.fhir.r5.model.CapabilityStatement;
019import org.hl7.fhir.r5.model.OperationOutcome;
020import org.hl7.fhir.r5.model.Parameters;
021import org.hl7.fhir.r5.model.TerminologyCapabilities;
022import org.hl7.fhir.r5.model.ValueSet;
023import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
024import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
025import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
026import org.hl7.fhir.r5.utils.client.network.ClientHeaders;
027import org.hl7.fhir.utilities.FhirPublication;
028import org.hl7.fhir.utilities.ToolingClientLogger;
029import org.hl7.fhir.utilities.Utilities;
030import org.hl7.fhir.utilities.http.HTTPHeader;
031
032public class TerminologyClientR4 implements ITerminologyClient {
033
034
035  public static class TerminologyClientR4Factory implements ITerminologyClientFactory {
036
037    @Override
038    public ITerminologyClient makeClient(String id, String url, String userAgent, ToolingClientLogger logger) throws URISyntaxException {
039      return new TerminologyClientR4(id, checkEndsWith("/r4", url), userAgent);
040    }
041
042    private String checkEndsWith(String term, String url) {
043      if (url.endsWith(term))
044        return url;
045      if (Utilities.isTxFhirOrgServer(url)) {
046        return Utilities.pathURL(url, term);
047      }
048      return url;
049    }
050
051    @Override
052    public String getVersion() {
053      return "4.0.1";
054    }
055  }
056  
057  private final FHIRToolingClient client; 
058  private ClientHeaders clientHeaders;
059  private String id;
060  private ITerminologyConversionLogger logger;
061
062  public TerminologyClientR4(String id, String address, String userAgent) throws URISyntaxException {
063    this.client = new FHIRToolingClient(address, userAgent);
064    setClientHeaders(new ClientHeaders());
065    this.id = id;
066  }
067
068  public TerminologyClientR4(String id, String address, String userAgent, ClientHeaders clientHeaders) throws URISyntaxException {
069    this.client = new FHIRToolingClient(address, userAgent);
070    setClientHeaders(clientHeaders);
071    this.id = id;
072  }
073
074  @Override
075  public String getId() {
076    return id;
077  }
078
079
080
081  public EnumSet<FhirPublication> supportableVersions() {
082    // todo
083    return EnumSet.range(FhirPublication.STU3, FhirPublication.R5);
084  }
085  
086  public void setAllowedVersions(EnumSet<FhirPublication> versions) {
087    // todo
088  }
089  
090  public EnumSet<FhirPublication> getAllowedVersions() {
091    return null; // todo
092  }
093  
094  public FhirPublication getActualVersion() {
095    return FhirPublication.R4;
096  }
097  
098  
099  @Override
100  public TerminologyCapabilities getTerminologyCapabilities() throws FHIRException {
101    return (TerminologyCapabilities) convertResource("getTerminologyCapabilities.response", client.getTerminologyCapabilities());
102  }
103
104  @Override
105  public String getAddress() {
106    return client.getAddress();
107  }
108
109  @Override
110  public ValueSet expandValueset(ValueSet vs, Parameters p) throws FHIRException {
111    org.hl7.fhir.r4.model.ValueSet vs2 = vs == null ? null : (org.hl7.fhir.r4.model.ValueSet) convertResource("expandValueset.valueset", vs);
112    org.hl7.fhir.r4.model.Parameters p2 = p == null ? null :  (org.hl7.fhir.r4.model.Parameters) convertResource("expandValueset.parameters", p);
113    try {
114      vs2 = client.expandValueset(vs2, p2); // todo: second parameter
115      return (ValueSet) convertResource("expandValueset.response", vs2);
116    } catch (org.hl7.fhir.r4.utils.client.EFhirClientException e) {
117      if (e.getServerErrors().size() > 0) {
118        throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getCode(), e.getMessage(), (org.hl7.fhir.r5.model.OperationOutcome) convertResource("expandValueset.error", e.getServerErrors().get(0)));
119      } else {
120        throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getCode(), e.getMessage());        
121      }
122    }
123  }
124
125
126  @Override
127  public Parameters validateCS(Parameters pin) throws FHIRException {
128    try {
129      org.hl7.fhir.r4.model.Parameters p2 = (org.hl7.fhir.r4.model.Parameters) convertResource("validateCS.request", pin);
130      p2 = client.operateType(org.hl7.fhir.r4.model.CodeSystem.class, "validate-code", p2);
131      return (Parameters) convertResource("validateCS.response", p2);
132    } catch (EFhirClientException e) {
133      if (e.getServerErrors().size() == 1) {
134        OperationOutcome op =  (OperationOutcome) convertResource("validateCS.error", e.getServerErrors().get(0));
135        throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getCode(), e.getMessage(), op, e);
136      } else {
137        throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getCode(), e.getMessage(), e);        
138      }
139    } catch (IOException e) {
140      throw new FHIRException(e);
141    }
142  }
143
144
145  @Override
146  public Parameters subsumes(Parameters pin) throws FHIRException {
147    try {
148      org.hl7.fhir.r4.model.Parameters p2 = (org.hl7.fhir.r4.model.Parameters) convertResource("subsumes.request", pin);
149      p2 = client.operateType(org.hl7.fhir.r4.model.CodeSystem.class, "subsumes", p2);
150      return (Parameters) convertResource("subsumes.response", p2);
151    } catch (EFhirClientException e) {
152      if (e.getServerErrors().size() == 1) {
153        OperationOutcome op =  (OperationOutcome) convertResource("subsumes.error", e.getServerErrors().get(0));
154        throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getCode(), e.getMessage(), op, e);
155      } else {
156        throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getCode(), e.getMessage(), e);        
157      }
158    } catch (IOException e) {
159      throw new FHIRException(e);
160    }
161  }
162
163  @Override
164  public Parameters validateVS(Parameters pin) throws FHIRException {
165    try {
166      org.hl7.fhir.r4.model.Parameters p2 = (org.hl7.fhir.r4.model.Parameters) convertResource("validateVS.request", pin);
167      p2 = client.operateType(org.hl7.fhir.r4.model.ValueSet.class, "validate-code", p2);
168      return (Parameters) convertResource("validateVS.response", p2);
169    } catch (EFhirClientException e) {
170      if (e.getServerErrors().size() == 1) {
171        OperationOutcome op =  (OperationOutcome) convertResource("validateVS.error", e.getServerErrors().get(0));
172        throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getCode(), e.getMessage(), op, e);
173      } else {
174        throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getCode(), e.getMessage(), e);        
175      }
176    } catch (IOException e) {
177      throw new FHIRException(e);
178    }
179  }
180
181  @Override
182  public ITerminologyClient setTimeoutFactor(int i) {
183    client.setTimeoutFactor(i);
184    return this;
185  }
186
187  @Override
188  public ToolingClientLogger getLogger() {
189    return client.getLogger();
190  }
191
192  @Override
193  public ITerminologyClient setLogger(ToolingClientLogger txLog) {
194    client.setLogger(txLog);
195    return this;
196  }
197
198  @Override
199  public ITerminologyClient setRetryCount(int retryCount) throws FHIRException {
200    client.setRetryCount(retryCount);
201    return this;
202  }
203
204  @Override
205  public CapabilityStatement getCapabilitiesStatementQuick() throws FHIRException {
206    return (CapabilityStatement) convertResource("getCapabilitiesStatementQuick.response", client.getCapabilitiesStatementQuick());
207  }
208
209  @Override
210  public CapabilityStatement getCapabilitiesStatement() throws FHIRException {
211    return (CapabilityStatement) convertResource("getCapabilitiesStatement.response", client.getCapabilitiesStatement());
212  }
213
214  @Override
215  public Parameters lookupCode(Map<String, String> params) throws FHIRException {
216    return (Parameters) convertResource("lookupCode.response", client.lookupCode(params));
217  }
218
219  @Override
220  public Parameters lookupCode(Parameters params) throws FHIRException {
221    return (Parameters) convertResource("lookupCode.response", client.lookupCode((org.hl7.fhir.r4.model.Parameters) convertResource("lookupCode.request", params)));
222  }
223
224  @Override
225  public int getRetryCount() throws FHIRException {
226    return client.getRetryCount();
227  }
228
229  @Override
230  public Bundle validateBatch(Bundle batch) {
231    org.hl7.fhir.r4.model.Bundle result = client.transaction((org.hl7.fhir.r4.model.Bundle) convertResource("validateBatch.request", batch));
232    return result == null ? null : (Bundle) convertResource("validateBatch.response", result);
233  }
234
235  @Override
236  public CanonicalResource read(String type, String id) {
237    Class<Resource> t;
238    try {
239      t = (Class<Resource>) Class.forName("org.hl7.fhir.r4.model." + type);// todo: do we have to deal with any resource renaming? Use cases are limited...
240    } catch (ClassNotFoundException e) {
241      throw new FHIRException("Unable to fetch resources of type " + type + " in R2");
242    }
243    org.hl7.fhir.r4.model.Resource r4 = client.read(t, id);
244    if (r4 == null) {
245      throw new FHIRException("Unable to fetch resource " + Utilities.pathURL(getAddress(), type, id));
246    }
247    org.hl7.fhir.r5.model.Resource r5 = convertResource("read.result", r4);
248    if (r5 == null) {
249      throw new FHIRException("Unable to convert resource " + Utilities.pathURL(getAddress(), type, id) + " to R5 (internal representation)");
250    }
251    if (!(r5 instanceof CanonicalResource)) {
252      throw new FHIRException("Unable to convert resource " + Utilities.pathURL(getAddress(), type, id) + " to R5 canonical resource (internal representation)");
253    }
254    return (CanonicalResource) r5;
255  }
256
257  @Override
258  public Iterable<HTTPHeader> getClientHeaders() {
259    return clientHeaders.headers();
260  }
261
262  @Override
263  public ITerminologyClient setClientHeaders(ClientHeaders clientHeaders) {
264    this.clientHeaders = clientHeaders;
265    if (this.clientHeaders != null) {
266      this.client.setClientHeaders(this.clientHeaders.headers());
267    }
268    this.client.setVersionInMimeTypes(true);
269    return this;
270  }
271
272  @Override
273  public ITerminologyClient setUserAgent(String userAgent) {
274    client.setUserAgent(userAgent);
275    return this;
276  }
277
278  @Override
279  public String getUserAgent() {
280    return client.getUserAgent();
281  }
282
283  @Override
284  public String getServerVersion() {
285    return client.getServerVersion();
286  }
287
288
289  @Override
290  public ITerminologyClient setAcceptLanguage(String lang) {
291    client.setAcceptLanguage(lang);
292    return this;
293  }
294  
295  @Override
296  public ITerminologyClient setContentLanguage(String lang) {
297    client.setContentLanguage(lang);
298    return this;
299  }
300  
301  @Override
302  public int getUseCount() {
303    return client.getUseCount();
304  }
305
306  @Override
307  public Bundle search(String type, String criteria) {    
308    org.hl7.fhir.r4.model.Bundle result = client.search(type, criteria);
309    return result == null ? null : (Bundle) convertResource("search.result", result);
310  }
311
312  @Override
313  public Parameters translate(Parameters params) throws FHIRException {  
314    return (Parameters) convertResource("translate.response", client.translate((org.hl7.fhir.r4.model.Parameters) convertResource("translate.request", params)));
315  }
316
317  private org.hl7.fhir.r4.model.Resource convertResource(String name, org.hl7.fhir.r5.model.Resource resource) {
318    if (logger != null) {
319      try {
320        logger.log(name, resource.fhirType(), "r5", new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(resource));
321      } catch (IOException e) {
322        throw new FHIRException(e);
323      }
324    }
325    org.hl7.fhir.r4.model.Resource res = VersionConvertorFactory_40_50.convertResource(resource);
326    if (logger != null) {
327      try {
328        logger.log(name, resource.fhirType(), "r4", new org.hl7.fhir.r4.formats.JsonParser().setOutputStyle(org.hl7.fhir.r4.formats.IParser.OutputStyle.PRETTY).composeBytes(res));
329      } catch (IOException e) {
330        throw new FHIRException(e);
331      }
332    }
333    return res;
334  }
335
336  private org.hl7.fhir.r5.model.Resource convertResource(String name, org.hl7.fhir.r4.model.Resource resource) {
337    if (logger != null && name != null) {
338      try {
339        logger.log(name, resource.fhirType(), "r4", new org.hl7.fhir.r4.formats.JsonParser().setOutputStyle(org.hl7.fhir.r4.formats.IParser.OutputStyle.PRETTY).composeBytes(resource));
340      } catch (IOException e) {
341        throw new FHIRException(e);
342      }
343    }
344    org.hl7.fhir.r5.model.Resource res = VersionConvertorFactory_40_50.convertResource(resource);
345    if (logger != null && name != null) {
346      try {
347        logger.log(name, resource.fhirType(), "r5", new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(res));
348      } catch (IOException e) {
349        throw new FHIRException(e);
350      }
351    }
352    return res;
353  }
354
355  @Override
356  public void setConversionLogger(ITerminologyConversionLogger logger) {
357    this.logger = logger;    
358  }
359  
360}