Merge pull request #838 from hapifhir/dotasek-capabilities-switch-preferred-resource-format

Attempt different preferredResourceFormat after failed initial attempt
This commit is contained in:
Grahame Grieve 2022-06-21 23:15:02 +03:00 committed by GitHub
commit 4fd6eb09e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 187 additions and 290 deletions

View File

@ -40,11 +40,16 @@ import org.hl7.fhir.r5.utils.client.network.Client;
import org.hl7.fhir.r5.utils.client.network.ResourceRequest;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Very Simple RESTful client. This is purely for use in the standalone
@ -73,6 +78,9 @@ import java.util.*;
*/
public class FHIRToolingClient {
private static final Logger logger = LoggerFactory.getLogger(FHIRToolingClient.class);
public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssK";
public static final String DATE_FORMAT = "yyyy-MM-dd";
public static final String hostKey = "http.proxyHost";
@ -97,7 +105,7 @@ public class FHIRToolingClient {
//Pass endpoint for client - URI
public FHIRToolingClient(String baseServiceUrl, String userAgent) throws URISyntaxException {
preferredResourceFormat = ResourceFormat.RESOURCE_XML;
preferredResourceFormat = ResourceFormat.RESOURCE_JSON;
this.userAgent = userAgent;
initialize(baseServiceUrl);
}
@ -132,45 +140,61 @@ public class FHIRToolingClient {
this.maxResultSetSize = maxResultSetSize;
}
private List<ResourceFormat> getResourceFormatsWithPreferredFirst() {
return Stream.concat(
Arrays.stream(new ResourceFormat[]{preferredResourceFormat}),
Arrays.stream(ResourceFormat.values()).filter(a -> a != preferredResourceFormat)
).collect(Collectors.toList());
}
private <T extends Resource> T getCapabilities(URI resourceUri, String message, String exceptionMessage) throws FHIRException {
final List<ResourceFormat> resourceFormats = getResourceFormatsWithPreferredFirst();
for (ResourceFormat attemptedResourceFormat : resourceFormats) {
try {
T output = (T) client.issueGetResourceRequest(resourceUri,
preferredResourceFormat.getHeader(),
generateHeaders(),
message,
TIMEOUT_NORMAL).getReference();
if (attemptedResourceFormat != preferredResourceFormat) {
setPreferredResourceFormat(attemptedResourceFormat);
}
return output;
} catch (Exception e) {
logger.warn("Failed attempt to fetch " + resourceUri, e);
}
}
throw new FHIRException(exceptionMessage);
}
public TerminologyCapabilities getTerminologyCapabilities() {
TerminologyCapabilities capabilities = null;
try {
capabilities = (TerminologyCapabilities) client.issueGetResourceRequest(resourceAddress.resolveMetadataTxCaps(),
getPreferredResourceFormat(),
generateHeaders(),
capabilities = getCapabilities(resourceAddress.resolveMetadataTxCaps(),
"TerminologyCapabilities",
TIMEOUT_NORMAL).getReference();
} catch (Exception e) {
throw new FHIRException("Error fetching the server's terminology capabilities", e);
}
"Error fetching the server's terminology capabilities");
return capabilities;
}
public CapabilityStatement getCapabilitiesStatement() {
CapabilityStatement capabilityStatement = null;
try {
capabilityStatement = (CapabilityStatement) client.issueGetResourceRequest(resourceAddress.resolveMetadataUri(false),
getPreferredResourceFormat(),
generateHeaders(),
"CapabilitiesStatement",
TIMEOUT_NORMAL).getReference();
} catch (Exception e) {
throw new FHIRException("Error fetching the server's conformance statement", e);
}
capabilityStatement = getCapabilities(resourceAddress.resolveMetadataUri(false),
"CapabilitiesStatement", "Error fetching the server's conformance statement");
return capabilityStatement;
}
public CapabilityStatement getCapabilitiesStatementQuick() throws EFhirClientException {
if (capabilities != null) return capabilities;
try {
capabilities = (CapabilityStatement) client.issueGetResourceRequest(resourceAddress.resolveMetadataUri(true),
getPreferredResourceFormat(),
generateHeaders(),
capabilities = getCapabilities(resourceAddress.resolveMetadataUri(true),
"CapabilitiesStatement-Quick",
TIMEOUT_NORMAL).getReference();
} catch (Exception e) {
throw new FHIRException("Error fetching the server's capability statement: "+e.getMessage(), e);
}
"Error fetching the server's capability statement");
return capabilities;
}

View File

@ -255,7 +255,7 @@ public class FhirRequestBuilder {
byte[] body = response.body().bytes();
String contentType = response.header("Content-Type");
if (body != null) {
if (contentType.contains(ResourceFormat.RESOURCE_XML.getHeader()) || contentType.contains("text/xml+fhir")) {
if (contentType.contains(ResourceFormat.RESOURCE_XML.getHeader()) || contentType.contains(ResourceFormat.RESOURCE_JSON.getHeader()) || contentType.contains("text/xml+fhir")) {
Resource rf = getParser(format).parse(body);
if (rf instanceof Bundle)
feed = (Bundle) rf;

View File

@ -6,6 +6,8 @@ import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Enumerations;
@ -30,6 +32,10 @@ import okhttp3.Headers;
import okhttp3.Request;
import okhttp3.internal.http2.Header;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.times;
class FHIRToolingClientTest {
String TX_ADDR = "http://tx.fhir.org";
@ -50,15 +56,6 @@ class FHIRToolingClientTest {
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.anyString(), Mockito.anyLong()))
.thenReturn(resourceResourceRequest);
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("TerminologyCapabilities"), Mockito.anyLong()))
.thenReturn(new ResourceRequest<>(new TerminologyCapabilities(), 200, "location"));
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("CapabilitiesStatement"), Mockito.anyLong()))
.thenReturn(new ResourceRequest<>(new CapabilityStatement(), 200, "location"));
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("CapabilitiesStatement-Quick"), Mockito.anyLong()))
.thenReturn(new ResourceRequest<>(new CapabilityStatement(), 200, "location"));
//PUT
Mockito.when(mockClient.issuePutRequest(Mockito.any(URI.class), Mockito.any(byte[].class), Mockito.anyString(),
@ -151,6 +148,10 @@ class FHIRToolingClientTest {
@Test
void getTerminologyCapabilities() throws IOException {
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("TerminologyCapabilities"), Mockito.anyLong()))
.thenReturn(new ResourceRequest<>(new TerminologyCapabilities(), 200, "location"));
ArgumentCaptor<Headers> headersArgumentCaptor = ArgumentCaptor.forClass(Headers.class);
toolingClient.setClientHeaders(getHeaders());
toolingClient.getTerminologyCapabilities();
@ -162,19 +163,93 @@ class FHIRToolingClientTest {
}
@Test
void getCapabilitiesStatement() throws IOException {
void getTerminologyCapabilitiesFailsForJSON() throws IOException {
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("TerminologyCapabilities"), Mockito.anyLong()))
.thenThrow(new FHIRFormatError("dummy error"))
.thenReturn(new ResourceRequest<>(new TerminologyCapabilities(), 200, "location"));
ArgumentCaptor<Headers> headersArgumentCaptor = ArgumentCaptor.forClass(Headers.class);
toolingClient.setClientHeaders(getHeaders());
toolingClient.getCapabilitiesStatement();
Mockito.verify(mockClient).issueGetResourceRequest(ArgumentMatchers.any(URI.class), ArgumentMatchers.anyString(),
toolingClient.getTerminologyCapabilities();
Mockito.verify(mockClient, times(2)).issueGetResourceRequest(ArgumentMatchers.any(URI.class), ArgumentMatchers.anyString(),
headersArgumentCaptor.capture(), ArgumentMatchers.anyString(), ArgumentMatchers.anyLong());
Headers argumentCaptorValue = headersArgumentCaptor.getValue();
checkHeaders(argumentCaptorValue);
}
@Test
void getTerminologyCapabilitiesStatementFailsForJSONandXML() throws IOException {
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("TerminologyCapabilities"), Mockito.anyLong()))
.thenThrow(new FHIRFormatError("dummy error"))
.thenThrow(new FHIRFormatError("dummy error 2"));
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
toolingClient.setClientHeaders(getHeaders());
Exception exception = assertThrows(FHIRException.class, () -> { ArgumentCaptor<Headers> headersArgumentCaptor = ArgumentCaptor.forClass(Headers.class);
toolingClient.getTerminologyCapabilities(); });
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
}
@Test
void getCapabilitiesStatement() throws IOException {
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("CapabilitiesStatement"), Mockito.anyLong()))
.thenReturn(new ResourceRequest<>(new CapabilityStatement(), 200, "location"));
ArgumentCaptor<Headers> headersArgumentCaptor = ArgumentCaptor.forClass(Headers.class);
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
toolingClient.setClientHeaders(getHeaders());
toolingClient.getCapabilitiesStatement();
Mockito.verify(mockClient).issueGetResourceRequest(ArgumentMatchers.any(URI.class), ArgumentMatchers.anyString(),
headersArgumentCaptor.capture(), ArgumentMatchers.anyString(), ArgumentMatchers.anyLong());
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
Headers argumentCaptorValue = headersArgumentCaptor.getValue();
checkHeaders(argumentCaptorValue);
}
@Test
void getCapabilitiesStatementFailsForJSON() throws IOException {
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("CapabilitiesStatement"), Mockito.anyLong()))
.thenThrow(new FHIRFormatError("dummy error"))
.thenReturn(new ResourceRequest<>(new CapabilityStatement(), 200, "location"));
ArgumentCaptor<Headers> headersArgumentCaptor = ArgumentCaptor.forClass(Headers.class);
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
toolingClient.setClientHeaders(getHeaders());
toolingClient.getCapabilitiesStatement();
Mockito.verify(mockClient, times(2)).issueGetResourceRequest(ArgumentMatchers.any(URI.class), ArgumentMatchers.anyString(),
headersArgumentCaptor.capture(), ArgumentMatchers.anyString(), ArgumentMatchers.anyLong());
assertEquals(ResourceFormat.RESOURCE_XML.getHeader(), toolingClient.getPreferredResourceFormat());
Headers argumentCaptorValue = headersArgumentCaptor.getValue();
checkHeaders(argumentCaptorValue);
}
@Test
void getCapabilitiesStatementFailsForJSONandXML() throws IOException {
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("CapabilitiesStatement"), Mockito.anyLong()))
.thenThrow(new FHIRFormatError("dummy error"))
.thenThrow(new FHIRFormatError("dummy error 2"));
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
toolingClient.setClientHeaders(getHeaders());
Exception exception = assertThrows(FHIRException.class, () -> { ArgumentCaptor<Headers> headersArgumentCaptor = ArgumentCaptor.forClass(Headers.class);
toolingClient.getCapabilitiesStatement(); });
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
}
@Test
void getCapabilitiesStatementQuick() throws IOException {
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("CapabilitiesStatement-Quick"), Mockito.anyLong()))
.thenReturn(new ResourceRequest<>(new CapabilityStatement(), 200, "location"));
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
ArgumentCaptor<Headers> headersArgumentCaptor = ArgumentCaptor.forClass(Headers.class);
toolingClient.setClientHeaders(getHeaders());
toolingClient.getCapabilitiesStatementQuick();
@ -183,6 +258,46 @@ class FHIRToolingClientTest {
Headers argumentCaptorValue = headersArgumentCaptor.getValue();
checkHeaders(argumentCaptorValue);
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
}
@Test
void getCapabilitiesStatementQuickFailsForJSON() throws IOException {
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("CapabilitiesStatement-Quick"), Mockito.anyLong()))
.thenThrow(new FHIRFormatError("dummy error"))
.thenReturn(new ResourceRequest<>(new CapabilityStatement(), 200, "location"));
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
ArgumentCaptor<Headers> headersArgumentCaptor = ArgumentCaptor.forClass(Headers.class);
toolingClient.setClientHeaders(getHeaders());
toolingClient.getCapabilitiesStatementQuick();
Mockito.verify(mockClient, times(2)).issueGetResourceRequest(ArgumentMatchers.any(URI.class), ArgumentMatchers.anyString(),
headersArgumentCaptor.capture(), ArgumentMatchers.anyString(), ArgumentMatchers.anyLong());
Headers argumentCaptorValue = headersArgumentCaptor.getValue();
checkHeaders(argumentCaptorValue);
assertEquals(ResourceFormat.RESOURCE_XML.getHeader(), toolingClient.getPreferredResourceFormat());
}
@Test
void getCapabilitiesStatementQuickFailsForJSONandXML() throws IOException {
Mockito.when(mockClient.issueGetResourceRequest(Mockito.any(URI.class), Mockito.anyString(),
Mockito.any(Headers.class), Mockito.eq("CapabilitiesStatement-Quick"), Mockito.anyLong()))
.thenThrow(new FHIRFormatError("dummy error"))
.thenThrow(new FHIRFormatError("dummy error 2"));
ArgumentCaptor<Headers> headersArgumentCaptor = ArgumentCaptor.forClass(Headers.class);
toolingClient.setClientHeaders(getHeaders());
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
Exception exception = assertThrows(FHIRException.class, () -> {
toolingClient.getCapabilitiesStatementQuick();
});
assertEquals(ResourceFormat.RESOURCE_JSON.getHeader(), toolingClient.getPreferredResourceFormat());
}
@Test

View File

@ -16,7 +16,7 @@
"version" : "1.0.2-2.0.14",
"name" : "FHIR Reference Server Conformance Statement",
"status" : "active",
"date" : "2022-06-09T21:15:44.743Z",
"date" : "2022-06-17T22:07:09.897Z",
"contact" : [{
"telecom" : [{
"system" : "other",

View File

@ -16,7 +16,7 @@
"version" : "3.0.2-2.0.14",
"name" : "FHIR Reference Server Conformance Statement",
"status" : "active",
"date" : "2022-06-09T21:14:05.196Z",
"date" : "2022-06-15T16:31:15.631Z",
"contact" : [{
"telecom" : [{
"system" : "other",

View File

@ -16,7 +16,7 @@
"version" : "3.0.2-2.0.14",
"name" : "FHIR Reference Server Conformance Statement",
"status" : "active",
"date" : "2022-06-09T21:13:00.228Z",
"date" : "2022-06-15T16:31:15.631Z",
"contact" : [{
"telecom" : [{
"system" : "other",

View File

@ -12,7 +12,7 @@
"version" : "4.0.1-2.0.14",
"name" : "FHIR Reference Server Conformance Statement",
"status" : "active",
"date" : "2022-06-09T21:09:34.321Z",
"date" : "2022-06-17T22:02:53.771Z",
"contact" : [{
"telecom" : [{
"system" : "other",

View File

@ -5,7 +5,7 @@
"version" : "1.0.0",
"name" : "FHIR Reference Server Teminology Capability Statement",
"status" : "active",
"date" : "2022-06-09T11:12:53.805Z",
"date" : "2022-06-17T22:02:55.131Z",
"contact" : [{
"telecom" : [{
"system" : "other",

View File

@ -12,7 +12,7 @@
"version" : "4.0.1-2.0.14",
"name" : "FHIR Reference Server Conformance Statement",
"status" : "active",
"date" : "2022-06-09T21:16:00.478Z",
"date" : "2022-06-17T22:07:43.178Z",
"contact" : [{
"telecom" : [{
"system" : "other",

View File

@ -5,7 +5,7 @@
"version" : "1.0.0",
"name" : "FHIR Reference Server Teminology Capability Statement",
"status" : "active",
"date" : "2022-06-09T11:12:53.805Z",
"date" : "2022-06-17T22:07:43.413Z",
"contact" : [{
"telecom" : [{
"system" : "other",

View File

@ -12,7 +12,7 @@
"version" : "4.0.1-2.0.14",
"name" : "FHIR Reference Server Conformance Statement",
"status" : "active",
"date" : "2022-06-09T21:12:46.775Z",
"date" : "2022-06-17T22:03:23.787Z",
"contact" : [{
"telecom" : [{
"system" : "other",

View File

@ -5,7 +5,7 @@
"version" : "1.0.0",
"name" : "FHIR Reference Server Teminology Capability Statement",
"status" : "active",
"date" : "2022-06-09T11:12:53.805Z",
"date" : "2022-06-17T22:03:23.912Z",
"contact" : [{
"telecom" : [{
"system" : "other",

View File

@ -1,19 +0,0 @@
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"inactive" : true,
"include" : [{
"system" : "urn:iso:std:iso:4217"
}]
}
}}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------
{"hierarchical" : false, "url": "http://hl7.org/fhir/ValueSet/currencies", "version": "4.0.1"}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------

View File

@ -1,11 +0,0 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "urn:ietf:bcp:47",
"code" : "fr-CA"
}, "valueSet" :null, "lang":"en-US", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"true"}####
v: {
"severity" : "error",
"error" : "Attempt to use Terminology server when no Terminology server is available",
"class" : "SERVER_ERROR"
}
-------------------------------------------------------------------------------------

View File

@ -1,90 +0,0 @@
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"inactive" : true,
"include" : [{
"system" : "http://loinc.org",
"concept" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "A."
}],
"code" : "LA20752-4",
"display" : "Within 24 hours"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "B."
}],
"code" : "LA20753-2",
"display" : "After 24 hours but before 3 days"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "C."
}],
"code" : "LA20754-0",
"display" : "Three days or later"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "D."
}],
"code" : "LA4489-6",
"display" : "Unknown"
}]
}]
}
}}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"include" : [{
"system" : "http://loinc.org",
"concept" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "A."
}],
"code" : "LA20752-4",
"display" : "Within 24 hours"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "B."
}],
"code" : "LA20753-2",
"display" : "After 24 hours but before 3 days"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "C."
}],
"code" : "LA20754-0",
"display" : "Three days or later"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "D."
}],
"code" : "LA4489-6",
"display" : "Unknown"
}]
}]
}
}}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------

View File

@ -1,19 +0,0 @@
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"inactive" : true,
"include" : [{
"system" : "urn:ietf:bcp:13"
}]
}
}}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------
{"hierarchical" : false, "url": "http://hl7.org/fhir/ValueSet/mimetypes", "version": "4.0.1"}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------

View File

@ -1,103 +0,0 @@
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"inactive" : true,
"include" : [{
"system" : "http://unitsofmeasure.org",
"concept" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "second"
}],
"code" : "s",
"display" : "second",
"designation" : [{
"language" : "zh",
"value" : "秒"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "minute"
}],
"code" : "min",
"display" : "minute",
"designation" : [{
"language" : "zh",
"value" : "分钟"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "hour"
}],
"code" : "h",
"display" : "hour",
"designation" : [{
"language" : "zh",
"value" : "小时"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "day"
}],
"code" : "d",
"display" : "day",
"designation" : [{
"language" : "zh",
"value" : "天"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "week"
}],
"code" : "wk",
"display" : "week",
"designation" : [{
"language" : "zh",
"value" : "星期"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "month"
}],
"code" : "mo",
"display" : "month",
"designation" : [{
"language" : "zh",
"value" : "月"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "year"
}],
"code" : "a",
"display" : "year",
"designation" : [{
"language" : "zh",
"value" : "年"
}]
}]
}]
}
}}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------
{"hierarchical" : false, "url": "http://hl7.org/fhir/ValueSet/units-of-time", "version": "4.0.1"}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------