Merge pull request #1830 from hapifhir/2024-11-gg-tx-server-error

Handle secondary terminology server errors properly
This commit is contained in:
Grahame Grieve 2024-11-25 21:27:15 +11:00 committed by GitHub
commit 8869698a48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 188 additions and 26 deletions

View File

@ -1,6 +1,7 @@
## Validator Changes
* fix NPE in validator around Extension context
* Handle secondary terminology server errors properly
## Other code changes

View File

@ -439,7 +439,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
try {
feed = utils.issueGetFeedRequest(new URI(url), getPreferredResourceFormat());
} catch (Exception e) {
handleException("An error has occurred while trying to retrieve history since last update", e);
handleException("An error has occurred while trying to read a bundle", e);
}
return feed;
}

View File

@ -714,7 +714,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
try {
feed = utils.issueGetFeedRequest(new URI(url), getPreferredResourceFormat());
} catch (Exception e) {
handleException("An error has occurred while trying to retrieve history since last update", e);
handleException("An error has occurred while trying to read a bundle", e);
}
return feed;
}

View File

@ -413,7 +413,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
try {
feed = client.issueGetFeedRequest(new URI(url), getPreferredResourceFormat());
} catch (Exception e) {
handleException("An error has occurred while trying to retrieve history since last update", e);
handleException("An error has occurred while trying to read a bundle", e);
}
return feed;
}

View File

@ -383,7 +383,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
try {
feed = client.issueGetFeedRequest(new URI(url), withVer(getPreferredResourceFormat(), "4.0"), timeoutLong);
} catch (Exception e) {
handleException(0, "An error has occurred while trying to retrieve history since last update", e);
handleException(0, "An error has occurred while trying to read a bundle", e);
}
return feed;
}

View File

@ -20,6 +20,7 @@ import org.hl7.fhir.r4.utils.client.EFhirClientException;
import org.hl7.fhir.r4.utils.client.ResourceFormat;
import org.hl7.fhir.utilities.MimeType;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.http.*;
import org.hl7.fhir.utilities.xhtml.XhtmlUtils;
@ -179,7 +180,11 @@ public class FhirRequestBuilder {
boolean ok = code >= 200 && code < 300;
if (response.getContent() == null) {
if (!ok) {
throw new EFhirClientException(response.getMessage());
if (Utilities.noString(response.getMessage())) {
throw new EFhirClientException(response.getMessagefromCode());
} else {
throw new EFhirClientException(response.getMessage());
}
} else {
return null;
}

View File

@ -399,7 +399,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient{
try {
feed = client.issueGetFeedRequest(new URI(url), getPreferredResourceFormat());
} catch (Exception e) {
handleException("An error has occurred while trying to retrieve history since last update", e);
handleException("An error has occurred while trying to read a bundle", e);
}
return feed;
}

View File

@ -18,6 +18,7 @@ import org.hl7.fhir.r4b.utils.ResourceUtilities;
import org.hl7.fhir.r4b.utils.client.EFhirClientException;
import org.hl7.fhir.r4b.utils.client.ResourceFormat;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.http.*;
import org.hl7.fhir.utilities.xhtml.XhtmlUtils;
@ -174,7 +175,11 @@ public class FhirRequestBuilder {
boolean ok = code >= 200 && code < 300;
if (response.getContent() == null) {
if (!ok) {
throw new EFhirClientException(response.getMessage());
if (Utilities.noString(response.getMessage())) {
throw new EFhirClientException(response.getMessagefromCode());
} else {
throw new EFhirClientException(response.getMessage());
}
} else {
return null;
}

View File

@ -794,6 +794,19 @@ public class ProfileUtilities {
}
}
for (int i = 0; i < derived.getSnapshot().getElement().size(); i++) {
ElementDefinition ed = derived.getSnapshot().getElement().get(i);
if (ed.getType().size() > 1) {
List<TypeRefComponent> toRemove = new ArrayList<ElementDefinition.TypeRefComponent>();
for (TypeRefComponent tr : ed.getType()) {
ElementDefinition typeSlice = findTypeSlice(derived.getSnapshot().getElement(), i, ed.getPath(), tr.getWorkingCode());
if (typeSlice != null && typeSlice.prohibited()) {
toRemove.add(tr);
}
}
ed.getType().removeAll(toRemove);
}
}
if (derived.getKind() != StructureDefinitionKind.LOGICAL && !derived.getSnapshot().getElementFirstRep().getType().isEmpty())
throw new Error(context.formatMessage(I18nConstants.TYPE_ON_FIRST_SNAPSHOT_ELEMENT_FOR__IN__FROM_, derived.getSnapshot().getElementFirstRep().getPath(), derived.getUrl(), base.getUrl()));
mappingDetails.update();
@ -992,6 +1005,34 @@ public class ProfileUtilities {
}
private ElementDefinition findTypeSlice(List<ElementDefinition> list, int i, String path, String typeCode) {
for (int j = i+1; j < list.size(); j++) {
ElementDefinition ed = list.get(j);
if (pathMatches(path, ed) && typeMatches(ed, typeCode)) {
return ed;
}
}
return null;
}
private boolean pathMatches(String path, ElementDefinition ed) {
String p = ed.getPath();
if (path.equals(p)) {
return true;
}
if (path.endsWith("[x]")) { // it should
path = path.substring(0, path.length()-3);
if (p.startsWith(path) && p.length() > path.length() && !p.substring(path.length()).contains(".")) {
return true;
}
}
return false;
}
private boolean typeMatches(ElementDefinition ed, String typeCode) {
return ed.getType().size() == 1 && typeCode.equals(ed.getTypeFirstRep().getWorkingCode());
}
private void checkTypeParameters(StructureDefinition base, StructureDefinition derived) {
String bt = ToolingExtensions.readStringExtension(base, ToolingExtensions.EXT_TYPE_PARAMETER);
if (!derived.hasExtension(ToolingExtensions.EXT_TYPE_PARAMETER)) {

View File

@ -237,7 +237,6 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private Object lock = new Object(); // used as a lock for the data that follows
protected String version; // although the internal resources are all R5, the version of FHIR they describe may not be
protected final TerminologyClientManager terminologyClientManager = new TerminologyClientManager(new TerminologyClientR5.TerminologyClientR5Factory(), UUID.randomUUID().toString());
private boolean minimalMemory = false;
private Map<String, Map<String, ResourceProxy>> allResourcesById = new HashMap<String, Map<String, ResourceProxy>>();
@ -279,6 +278,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
protected boolean noTerminologyServer;
private int expandCodesLimit = 1000;
protected org.hl7.fhir.r5.context.ILoggingService logger = new SystemOutLoggingService();
protected final TerminologyClientManager terminologyClientManager = new TerminologyClientManager(new TerminologyClientR5.TerminologyClientR5Factory(), UUID.randomUUID().toString(), logger);
protected Parameters expParameters;
private Map<String, PackageInformation> packages = new HashMap<>();
@ -2031,6 +2031,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
public void setLogger(@Nonnull org.hl7.fhir.r5.context.ILoggingService logger) {
this.logger = logger;
getTxClientManager().setLogger(logger);
}
public Parameters getExpansionParameters() {

View File

@ -15,6 +15,7 @@ import java.util.Set;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.context.CanonicalResourceManager;
import org.hl7.fhir.r5.context.ILoggingService;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.CodeSystem;
@ -27,6 +28,7 @@ import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext.TerminologyClientContextUseType;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ServerOptionList;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientR5.TerminologyClientR5Factory;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.SourcedCodeSystem;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.SourcedValueSet;
@ -86,11 +88,14 @@ public class TerminologyClientManager {
}
public class InternalLogEvent {
private boolean error;
private String message;
private String server;
private String vs;
private String systems;
private String choices;
private String context;
private String request;
protected InternalLogEvent(String message, String server, String vs, String systems, String choices) {
super();
this.message = message;
@ -99,9 +104,12 @@ public class TerminologyClientManager {
this.systems = systems;
this.choices = choices;
}
protected InternalLogEvent(String message) {
protected InternalLogEvent(String message, String ctxt, String request) {
super();
error = true;
this.message = message;
this.context = ctxt;
this.request = request;
}
public String getMessage() {
return message;
@ -118,7 +126,15 @@ public class TerminologyClientManager {
public String getServer() {
return server;
}
public boolean isError() {
return error;
}
public String getContext() {
return context;
}
public String getRequest() {
return request;
}
}
public static final String UNRESOLVED_VALUESET = "--unknown--";
@ -142,10 +158,13 @@ public class TerminologyClientManager {
private boolean useEcosystem;
public TerminologyClientManager(ITerminologyClientFactory factory, String cacheId) {
private ILoggingService logger;
public TerminologyClientManager(ITerminologyClientFactory factory, String cacheId, ILoggingService logger) {
super();
this.factory = factory;
this.cacheId = cacheId;
this.logger = logger;
}
public String getCacheId() {
@ -346,11 +365,11 @@ public class TerminologyClientManager {
}
return ret;
} catch (Exception e) {
String msg = "Error resolving system "+url+": "+e.getMessage()+" ("+request+")";
String msg = "Error resolving system "+url+": "+e.getMessage();
if (!hasMessage(msg)) {
internalLog.add(new InternalLogEvent(msg));
internalLog.add(new InternalLogEvent(msg, url, request));
}
if (!monitorServiceURL.contains("tx.fhir.org")) {
if (logger.isDebugLogging()) {
e.printStackTrace();
}
}
@ -571,12 +590,13 @@ public class TerminologyClientManager {
ValueSet vs = (ValueSet) client.getClient().read("ValueSet", rid);
return new SourcedValueSet(server, vs);
} catch (Exception e) {
e.printStackTrace();
String msg = "Error resolving valueSet "+canonical+": "+e.getMessage()+" ("+request+")";
String msg = "Error resolving valueSet "+canonical+": "+e.getMessage();
if (!hasMessage(msg)) {
internalLog.add(new InternalLogEvent(msg));
internalLog.add(new InternalLogEvent(msg, canonical, request));
}
if (logger.isDebugLogging()) {
e.printStackTrace();
}
e.printStackTrace();
return null;
}
}
@ -651,12 +671,13 @@ public class TerminologyClientManager {
CodeSystem vs = (CodeSystem) client.getClient().read("CodeSystem", rid);
return new SourcedCodeSystem(server, vs);
} catch (Exception e) {
e.printStackTrace();
String msg = "Error resolving valueSet "+canonical+": "+e.getMessage()+" ("+request+")";
String msg = "Error resolving CodeSystem "+canonical+": "+e.getMessage();
if (!hasMessage(msg)) {
internalLog.add(new InternalLogEvent(msg));
internalLog.add(new InternalLogEvent(msg, canonical, request));
}
if (logger.isDebugLogging()) {
e.printStackTrace();
}
e.printStackTrace();
return null;
}
}
@ -673,4 +694,14 @@ public class TerminologyClientManager {
public List<InternalLogEvent> getInternalLog() {
return internalLog;
}
public ILoggingService getLogger() {
return logger;
}
public void setLogger(ILoggingService logger) {
this.logger = logger;
}
}

View File

@ -112,6 +112,7 @@ public class CompareUtilities extends BaseTestingUtilities {
case "$url$": return "\"A URL\"";
case "$token$": return "\"A Token\"";
case "$version$": return variables.containsKey("version") ? variables.get("version") : "(anything)";
case "$semver$": return "A semver";
default: return "Unhandled template: "+expected;
}
}
@ -558,6 +559,7 @@ public class CompareUtilities extends BaseTestingUtilities {
case "$id$": return actualJsonString.matches("[A-Za-z0-9\\-\\.]{1,64}");
case "$url$": return actualJsonString.matches("(https?://|www\\.)[-a-zA-Z0-9+&@#/%?=~_|!:.;]*[-a-zA-Z0-9+&@#/%=~_|]");
case "$token$": return actualJsonString.matches("[0-9a-zA-Z_][0-9a-zA-Z_\\.\\-]*");
case "$semver$": return actualJsonString.matches("^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$");
case "$version$": return matchesVariable(actualJsonString, "version");
default:
throw new Error("Unhandled template: "+expectedJsonString);

View File

@ -441,7 +441,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
try {
feed = client.issueGetFeedRequest(new URI(url), getPreferredResourceFormat());
} catch (Exception e) {
handleException(0, "An error has occurred while trying to retrieve history since last update", e);
handleException(0, "An error has occurred while trying to read a bundle", e);
}
return feed;
}

View File

@ -19,6 +19,7 @@ import org.hl7.fhir.r5.utils.client.EFhirClientException;
import org.hl7.fhir.r5.utils.client.ResourceFormat;
import org.hl7.fhir.utilities.MimeType;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.http.*;
import org.hl7.fhir.utilities.xhtml.XhtmlUtils;
@ -162,7 +163,11 @@ public class FhirRequestBuilder {
boolean ok = code >= 200 && code < 300;
if (response.getContent() == null) {
if (!ok) {
throw new EFhirClientException(code, response.getMessage());
if (Utilities.noString(response.getMessage())) {
throw new EFhirClientException(code, response.getMessagefromCode());
} else {
throw new EFhirClientException(code, response.getMessage());
}
} else {
return null;
}

View File

@ -56,4 +56,74 @@ public class HTTPResult {
public String getContentAsString() {
return new String(content, StandardCharsets.UTF_8);
}
public String getMessagefromCode() {
switch (getCode()) {
case 100: return ""+getCode()+" Continue";
case 101: return ""+getCode()+" Switching Protocols";
case 102: return ""+getCode()+" Processing Deprecated";
case 103: return ""+getCode()+" Early Hints";
case 200: return ""+getCode()+" OK";
case 201: return ""+getCode()+" Created";
case 202: return ""+getCode()+" Accepted";
case 203: return ""+getCode()+" Non-Authoritative Information";
case 204: return ""+getCode()+" No Content";
case 205: return ""+getCode()+" Reset Content";
case 206: return ""+getCode()+" Partial Content";
case 207: return ""+getCode()+" Multi-Status";
case 208: return ""+getCode()+" Already Reported";
case 226: return ""+getCode()+" IM Used";
case 300: return ""+getCode()+" Multiple Choices";
case 301: return ""+getCode()+" Moved Permanently";
case 302: return ""+getCode()+" Found";
case 303: return ""+getCode()+" See Other";
case 304: return ""+getCode()+" Not Modified";
case 305: return ""+getCode()+" Use Proxy Deprecated";
case 306: return ""+getCode()+" unused";
case 307: return ""+getCode()+" Temporary Redirect";
case 308: return ""+getCode()+" Permanent Redirect";
case 400: return ""+getCode()+" Bad Request";
case 401: return ""+getCode()+" Unauthorized";
case 402: return ""+getCode()+" Payment Required";
case 403: return ""+getCode()+" Forbidden";
case 404: return ""+getCode()+" Not Found";
case 405: return ""+getCode()+" Method Not Allowed";
case 406: return ""+getCode()+" Not Acceptable";
case 407: return ""+getCode()+" Proxy Authentication Required";
case 408: return ""+getCode()+" Request Timeout";
case 409: return ""+getCode()+" Conflict";
case 410: return ""+getCode()+" Gone";
case 411: return ""+getCode()+" Length Required";
case 412: return ""+getCode()+" Precondition Failed";
case 413: return ""+getCode()+" Content Too Large";
case 414: return ""+getCode()+" URI Too Long";
case 415: return ""+getCode()+" Unsupported Media Type";
case 416: return ""+getCode()+" Range Not Satisfiable";
case 417: return ""+getCode()+" Expectation Failed";
case 418: return ""+getCode()+" I'm a teapot";
case 421: return ""+getCode()+" Misdirected Request";
case 422: return ""+getCode()+" Unprocessable Content";
case 423: return ""+getCode()+" Locked";
case 424: return ""+getCode()+" Failed Dependency";
case 425: return ""+getCode()+" Too Early Experimental";
case 426: return ""+getCode()+" Upgrade Required";
case 428: return ""+getCode()+" Precondition Required";
case 429: return ""+getCode()+" Too Many Requests";
case 431: return ""+getCode()+" Request Header Fields Too Large";
case 451: return ""+getCode()+" Unavailable For Legal Reasons";
case 500: return ""+getCode()+" Internal Server Error";
case 501: return ""+getCode()+" Not Implemented";
case 502: return ""+getCode()+" Bad Gateway";
case 503: return ""+getCode()+" Service Unavailable";
case 504: return ""+getCode()+" Gateway Timeout";
case 505: return ""+getCode()+" HTTP Version Not Supported";
case 506: return ""+getCode()+" Variant Also Negotiates";
case 507: return ""+getCode()+" Insufficient Storage";
case 508: return ""+getCode()+" Loop Detected";
case 510: return ""+getCode()+" Not Extended";
case 511: return ""+getCode()+" Network Authentication Required";
default: return "HTTP code "+code;
}
}
}

View File

@ -149,7 +149,7 @@ public class FilesystemPackageCacheManagerLocks {
channel.close();
throw new IOException("Lock file exists, but is not locked by a process: " + lockFile.getName());
}
System.out.println("File is locked.");
System.out.println("File is locked ('"+lockFile.getAbsolutePath()+"').");
}
}
try {

View File

@ -561,6 +561,7 @@ public class SnapShotGenerationXTests {
t1.setText(null);
StructureDefinition t2 = test.output.copy();
t2.setText(null);
t1.setIdBase(t2.getIdBase());
Assertions.assertTrue(t1.equalsDeep(t2), "Output does not match expected");
}
}

View File

@ -23,7 +23,7 @@
<commons_io_version>2.17.0</commons_io_version>
<guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.6.8</validator_test_case_version>
<validator_test_case_version>1.6.9-SNAPSHOT</validator_test_case_version>
<jackson_version>2.17.0</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>