Merge remote-tracking branch 'origin/master'

This commit is contained in:
patrick-werner 2018-05-17 09:53:10 +02:00
commit ecbf72c4cb
23 changed files with 91910 additions and 366 deletions

View File

@ -109,15 +109,15 @@ public class SingleValidationMessage {
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
if (myLocationCol != null || myLocationLine != null) {
b.append("myLocationCol", myLocationCol);
b.append("myLocationRow", myLocationLine);
b.append("col", myLocationCol);
b.append("row", myLocationLine);
}
if (myLocationString != null) {
b.append("myLocationString", myLocationString);
b.append("locationString", myLocationString);
}
b.append("myMessage", myMessage);
b.append("message", myMessage);
if (mySeverity != null) {
b.append("mySeverity", mySeverity.getCode());
b.append("severity", mySeverity.getCode());
}
return b.toString();
}

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.cli;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -33,6 +33,7 @@ import org.fusesource.jansi.AnsiConsole;
import org.slf4j.LoggerFactory;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -52,16 +53,20 @@ public abstract class BaseApp {
loggingConfigOff();
// We don't use qualified names for loggers in CLI
ourLog = LoggerFactory.getLogger(App.class.getSimpleName());
ourLog = LoggerFactory.getLogger(App.class);
}
private MyShutdownHook myShutdownHook;
private boolean myShutdownHookHasNotRun;
private void logAppHeader() {
System.out.flush();
System.out.println("------------------------------------------------------------");
System.out.println("\ud83d\udd25 " + ansi().bold() + " " + provideProductName() + ansi().boldOff() + " " + provideProductVersion() + " - Command Line Tool");
System.out.println("------------------------------------------------------------");
System.out.println("Max configured JVM memory (Xmx): " + FileHelper.getFileSizeDisplay(Runtime.getRuntime().maxMemory(), 1));
System.out.println("Detected Java version: " + System.getProperty("java.version"));
System.out.println("Process ID : " + ManagementFactory.getRuntimeMXBean().getName());
System.out.println("Max configured JVM memory (Xmx) : " + FileHelper.getFileSizeDisplay(Runtime.getRuntime().maxMemory(), 1));
System.out.println("Detected Java version : " + System.getProperty("java.version"));
System.out.println("------------------------------------------------------------");
}
@ -197,6 +202,9 @@ public abstract class BaseApp {
return;
}
myShutdownHook = new MyShutdownHook(command);
Runtime.getRuntime().addShutdownHook(myShutdownHook);
Options options = command.getOptions();
DefaultParser parser = new DefaultParser();
CommandLine parsedOptions;
@ -215,6 +223,9 @@ public abstract class BaseApp {
// Actually execute the command
command.run(parsedOptions);
myShutdownHookHasNotRun = true;
runCleanupHookAndUnregister();
if (!"true".equals(System.getProperty("test"))) {
System.exit(0);
}
@ -225,9 +236,11 @@ public abstract class BaseApp {
System.err.println(" " + ansi().fg(Ansi.Color.RED).bold() + e.getMessage());
System.err.println("" + ansi().fg(Ansi.Color.WHITE).boldOff());
logCommandUsageNoHeader(command);
runCleanupHookAndUnregister();
System.exit(1);
} catch (CommandFailureException e) {
ourLog.error(e.getMessage());
runCleanupHookAndUnregister();
if ("true".equals(System.getProperty("test"))) {
throw e;
} else {
@ -235,12 +248,22 @@ public abstract class BaseApp {
}
} catch (Throwable t) {
ourLog.error("Error during execution: ", t);
runCleanupHookAndUnregister();
if ("true".equals(System.getProperty("test"))) {
throw new CommandFailureException("Error: " + t.toString(), t);
} else {
System.exit(1);
}
}
}
private void runCleanupHookAndUnregister() {
if (myShutdownHookHasNotRun) {
Runtime.getRuntime().removeShutdownHook(myShutdownHook);
myShutdownHook.run();
myShutdownHookHasNotRun = false;
}
}
private void validateJavaVersion() {
@ -275,4 +298,17 @@ public abstract class BaseApp {
}
private class MyShutdownHook extends Thread {
private final BaseCommand myFinalCommand;
public MyShutdownHook(BaseCommand theFinalCommand) {
myFinalCommand = theFinalCommand;
}
@Override
public void run() {
ourLog.info(provideProductName() + " is shutting down...");
myFinalCommand.cleanup();
}
}
}

View File

@ -152,6 +152,13 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
addOptionalOption(theOptions, VERBOSE_LOGGING_PARAM, VERBOSE_LOGGING_PARAM_LONGOPT, false, VERBOSE_LOGGING_PARAM_DESC);
}
/**
* Subclasses may override if they want, to do any cleanup they need to do.
*/
public void cleanup() {
// nothing
}
@Override
public int compareTo(BaseCommand theO) {
return getCommandName().compareTo(theO.getCommandName());

View File

@ -29,6 +29,7 @@ import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.web.context.ContextLoader;
@ -192,8 +193,21 @@ public class RunServerCommand extends BaseCommand {
ourLog.info("Server started on port {}", myPort);
ourLog.info("Web Testing UI : http://localhost:{}/", myPort);
ourLog.info("Server Base URL: http://localhost:{}{}", myPort, path);
// Never quit.. We'll let the user ctrl-C their way out.
loopForever();
}
@SuppressWarnings("InfiniteLoopStatement")
private void loopForever() {
while (true) {
try {
Thread.sleep(DateUtils.MILLIS_PER_MINUTE);
} catch (InterruptedException theE) {
// ignore
}
}
}
public static void main(String[] theArgs) {

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.cli;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.igpacks.parser.IgPackParserDstu2;
import ca.uhn.fhir.igpacks.parser.IgPackParserDstu3;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.LenientErrorHandler;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.SingleValidationMessage;
@ -103,7 +104,7 @@ public class ValidateCommand extends BaseCommand {
parseFhirContext(theCommandLine);
String fileName = theCommandLine.getOptionValue("n");
String contents = theCommandLine.getOptionValue("c");
String contents = theCommandLine.getOptionValue("d");
if (isNotBlank(fileName) && isNotBlank(contents)) {
throw new ParseException("Can not supply both a file (-n) and data (-d)");
}
@ -199,7 +200,12 @@ public class ValidateCommand extends BaseCommand {
val.setValidateAgainstStandardSchema(theCommandLine.hasOption("x"));
val.setValidateAgainstStandardSchematron(theCommandLine.hasOption("s"));
ValidationResult results = val.validateWithResult(contents);
ValidationResult results;
try {
results = val.validateWithResult(contents);
} catch (DataFormatException e) {
throw new CommandFailureException(e.getMessage());
}
StringBuilder b = new StringBuilder("Validation results:" + ansi().boldOff());
int count = 0;

View File

@ -53,7 +53,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class ValidationDataUploader extends BaseCommand {
// TODO: Don't use qualified names for loggers in HAPI CLI.
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidationDataUploader.class);
private ArrayList<IIdType> myExcludes = new ArrayList<>();
@ -354,9 +353,10 @@ public class ValidationDataUploader extends BaseCommand {
ourLog.info("Finished uploading ValueSets");
uploadDstu3Profiles(theCtx, client, "profiles-resources");
uploadDstu3Profiles(theCtx, client, "profiles-types");
uploadDstu3Profiles(theCtx, client, "profiles-others");
uploadDstu3Profiles(theCtx, client, "profile/profiles-resources");
uploadDstu3Profiles(theCtx, client, "profile/profiles-types");
uploadDstu3Profiles(theCtx, client, "profile/profiles-others");
uploadDstu3Profiles(theCtx, client, "extension/extension-definitions");
ourLog.info("Finished uploading ValueSets");
@ -446,9 +446,10 @@ public class ValidationDataUploader extends BaseCommand {
ourLog.info("Finished uploading ValueSets");
uploadR4Profiles(theCtx, client, "profiles-resources");
uploadR4Profiles(theCtx, client, "profiles-types");
uploadR4Profiles(theCtx, client, "profiles-others");
uploadR4Profiles(theCtx, client, "profile/profiles-resources");
uploadR4Profiles(theCtx, client, "profile/profiles-types");
uploadR4Profiles(theCtx, client, "profile/profiles-others");
uploadR4Profiles(theCtx, client, "extension/extension-definitions");
ourLog.info("Finished uploading ValueSets");
@ -457,14 +458,14 @@ public class ValidationDataUploader extends BaseCommand {
ourLog.info("Finished uploading definitions to server (took {} ms)", delay);
}
private void uploadDstu3Profiles(FhirContext ctx, IGenericClient client, String name) throws CommandFailureException {
private void uploadDstu3Profiles(FhirContext ctx, IGenericClient client, String theName) throws CommandFailureException {
int total;
int count;
org.hl7.fhir.dstu3.model.Bundle bundle;
ourLog.info("Uploading " + name);
ourLog.info("Uploading " + theName);
String vsContents;
try {
vsContents = IOUtils.toString(ValidationDataUploader.class.getResourceAsStream("/org/hl7/fhir/dstu3/model/profile/" + name + ".xml"), "UTF-8");
vsContents = IOUtils.toString(ValidationDataUploader.class.getResourceAsStream("/org/hl7/fhir/dstu3/model/" + theName + ".xml"), "UTF-8");
} catch (IOException e) {
throw new CommandFailureException(e.toString());
}
@ -498,26 +499,26 @@ public class ValidationDataUploader extends BaseCommand {
continue;
}
ourLog.info("Uploading {} StructureDefinition {}/{} : {}", new Object[] {name, count, total, next.getIdElement().getValue()});
ourLog.info("Uploading {} StructureDefinition {}/{} : {}", new Object[] {theName, count, total, next.getIdElement().getValue()});
client.update().resource(next).execute();
count++;
}
}
private void uploadR4Profiles(FhirContext ctx, IGenericClient client, String name) throws CommandFailureException {
private void uploadR4Profiles(FhirContext theContext, IGenericClient theClient, String theName) throws CommandFailureException {
int total;
int count;
org.hl7.fhir.r4.model.Bundle bundle;
ourLog.info("Uploading " + name);
ourLog.info("Uploading " + theName);
String vsContents;
try {
vsContents = IOUtils.toString(ValidationDataUploader.class.getResourceAsStream("/org/hl7/fhir/r4/model/profile/" + name + ".xml"), "UTF-8");
vsContents = IOUtils.toString(ValidationDataUploader.class.getResourceAsStream("/org/hl7/fhir/r4/model/" + theName + ".xml"), "UTF-8");
} catch (IOException e) {
throw new CommandFailureException(e.toString());
}
bundle = ctx.newXmlParser().parseResource(org.hl7.fhir.r4.model.Bundle.class, vsContents);
bundle = theContext.newXmlParser().parseResource(org.hl7.fhir.r4.model.Bundle.class, vsContents);
filterBundle(bundle);
total = bundle.getEntry().size();
count = 1;
@ -546,9 +547,9 @@ public class ValidationDataUploader extends BaseCommand {
continue;
}
ourLog.info("Uploading {} StructureDefinition {}/{} : {}", new Object[] {name, count, total, next.getIdElement().getValue()});
ourLog.info("Uploading {} StructureDefinition {}/{} : {}", new Object[] {theName, count, total, next.getIdElement().getValue()});
try {
client.update().resource(next).execute();
theClient.update().resource(next).execute();
} catch (BaseServerResponseException e) {
ourLog.warn("Server responded HTTP " + e.getStatusCode() + ": " + e.toString());
}

View File

@ -511,6 +511,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
@SuppressWarnings("unchecked")
protected Set<String> extractResourceLinks(ResourceTable theEntity, IBaseResource theResource, Set<ResourceLink> theLinks, Date theUpdateTime) {
HashSet<String> retVal = new HashSet<>();
String resourceType = theEntity.getResourceType();
/*
* For now we don't try to load any of the links in a bundle if it's the actual bundle we're storing..
@ -580,6 +581,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
}
} else if (myContext.getElementDefinition((Class<? extends IBase>) nextObject.getClass()).getName().equals("uri")) {
continue;
} else if (resourceType.equals("Consent") && nextPathAndRef.getPath().equals("Consent.source")) {
// Consent#source-identifier has a path that isn't typed - This is a one-off to deal with that
continue;
} else {
if (!multiType) {
if (nextSpDef.getName().equals("sourceuri")) {

View File

@ -105,7 +105,8 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
*/
@Override
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<ResourceIndexedSearchParamDate>();
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<>();
String resourceType = theEntity.getResourceType();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
@ -164,6 +165,9 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
} else if (nextObject instanceof StringType) {
// CarePlan.activitydate can be a string
continue;
} else if (resourceType.equals("Consent") && nextPath.equals("Consent.source")) {
// Consent#source-identifier has a path that isn't typed - This is a one-off to deal with that
continue;
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao.r4;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -20,12 +20,28 @@ package ca.uhn.fhir.jpa.dao.r4;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.*;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
@ -33,20 +49,10 @@ import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.*;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.validation.*;
import java.util.ArrayList;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoR4<T extends IAnyResource> extends BaseHapiFhirResourceDao<T> {
@ -107,7 +113,7 @@ public class FhirResourceDaoR4<T extends IAnyResource> extends BaseHapiFhirResou
IFhirResourceDao<? extends IBaseResource> dao = getDao(type);
resourceToValidateById = dao.read(theId, theRequestDetails);
}
ValidationResult result;
if (theResource == null) {
if (resourceToValidateById != null) {

View File

@ -468,6 +468,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
continue;
}
String resourceType = theEntity.getResourceType();
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
continue;
@ -478,8 +479,8 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
multiType = true;
}
List<String> systems = new ArrayList<String>();
List<String> codes = new ArrayList<String>();
List<String> systems = new ArrayList<>();
List<String> codes = new ArrayList<>();
for (Object nextObject : extractValues(nextPath, theResource)) {
@ -555,9 +556,15 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
} else if (nextObject instanceof LocationPositionComponent) {
ourLog.warn("Position search not currently supported, not indexing location");
continue;
} else if (nextObject instanceof StructureDefinition.StructureDefinitionContextComponent) {
ourLog.warn("StructureDefinition context indexing not currently supported"); // TODO: implement this
continue;
} else if (resourceType.equals("Consent") && nextPath.equals("Consent.source")) {
// Consent#source-identifier has a path that isn't typed - This is a one-off to deal with that
continue;
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
throw new ConfigurationException("Search param " + nextSpDef.getName() + " with path " + nextPath + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}

View File

@ -160,6 +160,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
@Qualifier("myOrganizationDaoDstu3")
protected IFhirResourceDao<Organization> myOrganizationDao;
@Autowired
@Qualifier("myConsentDaoDstu3")
protected IFhirResourceDao<Consent> myConsentDao;
@Autowired
protected DatabaseBackedPagingProvider myPagingProvider;
@Autowired
@Qualifier("myPatientDaoDstu3")

View File

@ -1,49 +1,53 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import java.util.*;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.exceptions.*;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.TestUtil;
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.hamcrest.Matchers;
import org.hamcrest.core.StringContains;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Bundle.*;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
import org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu3.model.OperationOutcome.IssueType;
import org.hl7.fhir.dstu3.model.Quantity.QuantityComparator;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.google.common.collect.Lists;
import java.util.*;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.exceptions.*;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.TestUtil;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
@SuppressWarnings({ "unchecked", "deprecation" })
@SuppressWarnings({"unchecked", "deprecation"})
public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3Test.class);
@ -78,7 +82,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
}
@Before
public void beforeDisableResultReuse() {
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
@ -117,7 +121,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
}
private void sortCodings(List<Coding> theSecLabels) {
Collections.sort(theSecLabels, new Comparator<Coding>() {
@Override
@ -126,7 +129,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
});
}
private List<UriType> sortIds(List<UriType> theProfiles) {
ArrayList<UriType> retVal = new ArrayList<UriType>(theProfiles);
Collections.sort(retVal, new Comparator<UriType>() {
@ -137,7 +140,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
});
return retVal;
}
@Test
public void testCantSearchForDeletedResourceByLanguageOrTag() {
String methodName = "testCantSearchForDeletedResourceByLanguageOrTag";
@ -478,8 +481,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
myBundleDao.create(bundle, mySrd);
}
@Test
public void testCreateDifferentTypesWithSameForcedId() {
String idName = "forcedId";
@ -500,12 +502,11 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
obs = myObservationDao.read(obsId.toUnqualifiedVersionless(), mySrd);
}
@Test
public void testCreateDuplicateTagsDoesNotCauseDuplicates() {
Patient p = new Patient();
p.setActive(true);
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
@ -513,9 +514,9 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
myPatientDao.create(p);
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
@ -523,24 +524,24 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
assertThat(myTagDefinitionDao.findAll(), hasSize(1));
}
});
}
@Test
public void testCreateEmptyTagsIsIgnored() {
Patient p = new Patient();
p.setActive(true);
// Add an empty tag
p.getMeta().addTag();
// Add another empty tag
p.getMeta().addTag().setSystem("");
p.getMeta().addTag().setCode("");
p.getMeta().addTag().setDisplay("");
myPatientDao.create(p);
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
@ -548,29 +549,29 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
assertThat(myTagDefinitionDao.findAll(), empty());
}
});
}
@Test
public void testCreateLongString() {
//@formatter:off
String input = "<NamingSystem>\n" +
" <name value=\"NDF-RT (National Drug File Reference Terminology)\"/>\n" +
" <status value=\"draft\"/>\n" +
" <kind value=\"codesystem\"/>\n" +
" <publisher value=\"HL7, Inc\"/>\n" +
" <date value=\"2015-08-21\"/>\n" +
" <uniqueId>\n" +
" <type value=\"uri\"/>\n" +
" <value value=\"http://hl7.org/fhir/ndfrt\"/>\n" +
" <preferred value=\"true\"/>\n" +
" </uniqueId>\n" +
" <uniqueId>\n" +
" <type value=\"oid\"/>\n" +
" <value value=\"2.16.840.1.113883.6.209\"/>\n" +
" <preferred value=\"false\"/>\n" +
" </uniqueId>\n" +
" </NamingSystem>";
String input = "<NamingSystem>\n" +
" <name value=\"NDF-RT (National Drug File Reference Terminology)\"/>\n" +
" <status value=\"draft\"/>\n" +
" <kind value=\"codesystem\"/>\n" +
" <publisher value=\"HL7, Inc\"/>\n" +
" <date value=\"2015-08-21\"/>\n" +
" <uniqueId>\n" +
" <type value=\"uri\"/>\n" +
" <value value=\"http://hl7.org/fhir/ndfrt\"/>\n" +
" <preferred value=\"true\"/>\n" +
" </uniqueId>\n" +
" <uniqueId>\n" +
" <type value=\"oid\"/>\n" +
" <value value=\"2.16.840.1.113883.6.209\"/>\n" +
" <preferred value=\"false\"/>\n" +
" </uniqueId>\n" +
" </NamingSystem>";
//@formatter:on
NamingSystem res = myFhirCtx.newXmlParser().parseResource(NamingSystem.class, input);
@ -633,9 +634,9 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
Organization org = new Organization();
org.setActive(true);
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
myOrganizationDao.delete(orgId);
Patient p = new Patient();
p.getManagingOrganization().setReferenceElement(orgId);
try {
@ -809,7 +810,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
}
@Test
public void testCreateWithInvalidReferenceFailsGracefully() {
Patient patient = new Patient();
@ -1088,7 +1088,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
patient.addIdentifier().setSystem("ZZZZZZZ").setValue("ZZZZZZZZZ");
id2b = myPatientDao.update(patient, mySrd).getId();
}
ourLog.info("ID1:{} ID2:{} ID2b:{}", new Object[] { id1, id2, id2b });
ourLog.info("ID1:{} ID2:{} ID2b:{}", new Object[] {id1, id2, id2b});
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronous(true);
@ -1697,7 +1697,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
preDates.add(new Date());
Thread.sleep(100);
patient.setId(id);
patient.getName().get(0).getFamilyElement().setValue(methodName + "_i"+i);
patient.getName().get(0).getFamilyElement().setValue(methodName + "_i" + i);
ids.add(myPatientDao.update(patient, mySrd).getId().toUnqualified().getValue());
}
@ -2056,27 +2056,27 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
public void testOrganizationName() {
//@formatter:off
String inputStr =
"{" +
" \"resourceType\":\"Organization\",\n" +
" \"extension\":[\n" +
" {\n" +
" \"url\":\"http://fhir.connectinggta.ca/Profile/organization#providerIdPool\",\n" +
" \"valueUri\":\"urn:oid:2.16.840.1.113883.3.239.23.21.1\"\n" +
" }\n" +
" ],\n" +
" \"text\":{\n" +
" \"status\":\"empty\",\n" +
" \"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">No narrative template available for resource profile: http://fhir.connectinggta.ca/Profile/organization</div>\"\n" +
" },\n" +
" \"identifier\":[\n" +
" {\n" +
" \"use\":\"official\",\n" +
" \"system\":\"urn:cgta:hsp_ids\",\n" +
" \"value\":\"urn:oid:2.16.840.1.113883.3.239.23.21\"\n" +
" }\n" +
" ],\n" +
" \"name\":\"Peterborough Regional Health Centre\"\n" +
String inputStr =
"{" +
" \"resourceType\":\"Organization\",\n" +
" \"extension\":[\n" +
" {\n" +
" \"url\":\"http://fhir.connectinggta.ca/Profile/organization#providerIdPool\",\n" +
" \"valueUri\":\"urn:oid:2.16.840.1.113883.3.239.23.21.1\"\n" +
" }\n" +
" ],\n" +
" \"text\":{\n" +
" \"status\":\"empty\",\n" +
" \"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">No narrative template available for resource profile: http://fhir.connectinggta.ca/Profile/organization</div>\"\n" +
" },\n" +
" \"identifier\":[\n" +
" {\n" +
" \"use\":\"official\",\n" +
" \"system\":\"urn:cgta:hsp_ids\",\n" +
" \"value\":\"urn:oid:2.16.840.1.113883.3.239.23.21\"\n" +
" }\n" +
" ],\n" +
" \"name\":\"Peterborough Regional Health Centre\"\n" +
"}\n";
//@formatter:on
@ -2131,7 +2131,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
dr01.setSubject(new Reference(patientId01));
IIdType drId01 = myDiagnosticReportDao.create(dr01, mySrd).getId();
ourLog.info("P1[{}] P2[{}] O1[{}] O2[{}] D1[{}]", new Object[] { patientId01, patientId02, obsId01, obsId02, drId01 });
ourLog.info("P1[{}] P2[{}] O1[{}] O2[{}] D1[{}]", new Object[] {patientId01, patientId02, obsId01, obsId02, drId01});
List<Observation> result = toList(myObservationDao.search(new SearchParameterMap(Observation.SP_SUBJECT, new ReferenceParam(patientId01.getIdPart())).setLoadSynchronous(true)));
assertEquals(1, result.size());
@ -2141,7 +2141,8 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
assertEquals(1, result.size());
assertEquals(obsId02.getIdPart(), result.get(0).getIdElement().getIdPart());
result = toList(myObservationDao.search(new SearchParameterMap(Observation.SP_SUBJECT, new ReferenceParam("999999999999")).setLoadSynchronous(true)));;
result = toList(myObservationDao.search(new SearchParameterMap(Observation.SP_SUBJECT, new ReferenceParam("999999999999")).setLoadSynchronous(true)));
;
assertEquals(0, result.size());
}
@ -2224,7 +2225,8 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
long id = outcome.getId().getIdPartAsLong();
TokenParam value = new TokenParam("urn:system", "001testPersistSearchParams");
List<Patient> found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_IDENTIFIER, value).setLoadSynchronous(true)));;
List<Patient> found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_IDENTIFIER, value).setLoadSynchronous(true)));
;
assertEquals(1, found.size());
assertEquals(id, found.get(0).getIdElement().getIdPartAsLong().longValue());
@ -2797,7 +2799,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE).setOrder(SortOrderEnum.DESC));
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
assertEquals(4, actual.size());
// The first would be better, but JPA doesn't do NULLS LAST
// The first would be better, but JPA doesn't do NULLS LAST
// assertThat(actual, contains(id3, id2, id1, id4));
assertThat(actual, contains(id4, id3, id2, id1));
@ -3081,7 +3083,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
pm.setSort(new SortSpec(Patient.SP_FAMILY).setOrder(SortOrderEnum.DESC));
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
assertEquals(4, actual.size());
// The first would be better, but JPA doesn't do NULLS LAST
// The first would be better, but JPA doesn't do NULLS LAST
// assertThat(actual, contains(id3, id2, id1, id4));
assertThat(actual, contains(id4, id3, id2, id1));
}
@ -3535,6 +3537,16 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
/**
* Make sure this can upload successfully (indexer failed at one point)
*/
@Test
public void testUploadConsentWithSourceAttachment() {
Consent consent = new Consent();
consent.setSource(new Attachment().setUrl("http://foo"));
myConsentDao.create(consent);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
@ -3542,7 +3554,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
public static void assertConflictException(ResourceVersionConflictException e) {
assertThat(e.getMessage(), matchesPattern(
"Unable to delete [a-zA-Z]+/[0-9]+ because at least one resource has a reference to this resource. First reference found was resource [a-zA-Z]+/[0-9]+ in path [a-zA-Z]+.[a-zA-Z]+"));
"Unable to delete [a-zA-Z]+/[0-9]+ because at least one resource has a reference to this resource. First reference found was resource [a-zA-Z]+/[0-9]+ in path [a-zA-Z]+.[a-zA-Z]+"));
}
private static List<String> toStringList(List<UriType> theUriType) {

View File

@ -212,6 +212,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
@Qualifier("myStructureDefinitionDaoR4")
protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
@Autowired
@Qualifier("myConsentDaoR4")
protected IFhirResourceDao<Consent> myConsentDao;
@Autowired
@Qualifier("mySubscriptionDaoR4")
protected IFhirResourceDaoSubscription<Subscription> mySubscriptionDao;
@Autowired

View File

@ -21,6 +21,7 @@ import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hamcrest.Matchers;
import org.hamcrest.core.StringContains;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -2966,7 +2967,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE).setOrder(SortOrderEnum.DESC));
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
assertEquals(4, actual.size());
// The first would be better, but JPA doesn't do NULLS LAST
// The first would be better, but JPA doesn't do NULLS LAST
// assertThat(actual, contains(id3, id2, id1, id4));
assertThat(actual, contains(id4, id3, id2, id1));
@ -3284,7 +3285,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
pm.setSort(new SortSpec(Patient.SP_FAMILY).setOrder(SortOrderEnum.DESC));
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
assertEquals(4, actual.size());
// The first would be better, but JPA doesn't do NULLS LAST
// The first would be better, but JPA doesn't do NULLS LAST
// assertThat(actual, contains(id3, id2, id1, id4));
assertThat(actual, contains(id4, id3, id2, id1));
}
@ -3738,6 +3739,26 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
}
/**
* Make sure this can upload successfully (indexer failed at one point)
*/
@Test
public void testUploadConsentWithSourceAttachment() {
Consent consent = new Consent();
consent.setSource(new Attachment().setUrl("http://foo"));
myConsentDao.create(consent);
}
/**
* Make sure this can upload successfully (indexer failed at one point)
*/
@Test
public void testUploadExtensionStructureDefinition() {
StructureDefinition ext = myValidationSupport.fetchStructureDefinition(myFhirCtx, "http://hl7.org/fhir/StructureDefinition/familymemberhistory-type");
Validate.notNull(ext);
myStructureDefinitionDao.update(ext);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -86,7 +86,7 @@ public class FhirTesterConfig {
.withName("Health Intersections (DSTU2 FHIR)")
.addServer()
.withId("spark2")
.withFhirVersion(FhirVersionEnum.DSTU2)
.withFhirVersion(FhirVersionEnum.DSTU3)
.withBaseUrl("http://vonk.furore.com/")
.withName("Vonk - Furore (STU3 FHIR)");

View File

@ -29,7 +29,7 @@
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
<level>INFO</level>
</filter>
<file>${fhir.logdir}/fhirtest.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
@ -106,4 +106,4 @@
<appender-ref ref="FILE"/>
</root>
</configuration>
</configuration>

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -19,42 +19,62 @@ package ca.uhn.fhir.rest.server;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.jar.Manifest;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.*;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.ProvidedResourceScanner;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.api.AddProfileTagEnum;
import ca.uhn.fhir.context.api.BundleInclusionRule;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.Destroy;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Initialize;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.server.IFhirVersionServer;
import ca.uhn.fhir.rest.api.server.IRestfulServer;
import ca.uhn.fhir.rest.api.server.ParseAction;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding;
import ca.uhn.fhir.rest.server.exceptions.*;
import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.ConformanceMethodBinding;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.rest.server.tenant.ITenantIdentificationStrategy;
import ca.uhn.fhir.util.*;
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.context.api.AddProfileTagEnum;
import ca.uhn.fhir.context.api.BundleInclusionRule;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.server.*;
import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding;
import ca.uhn.fhir.rest.server.exceptions.*;
import ca.uhn.fhir.rest.server.interceptor.*;
import ca.uhn.fhir.rest.server.method.*;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.*;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.jar.Manifest;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@SuppressWarnings("WeakerAccess")
public class RestfulServer extends HttpServlet implements IRestfulServer<ServletRequestDetails> {
@ -96,7 +116,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
private BaseMethodBinding<?> myServerConformanceMethod;
private Object myServerConformanceProvider;
private String myServerName = "HAPI FHIR Server";
/** This is configurable but by default we just use HAPI version */
/**
* This is configurable but by default we just use HAPI version
*/
private String myServerVersion = VersionUtil.getVersion();
private boolean myStarted;
private Map<String, IResourceProvider> myTypeToProvider = new HashMap<>();
@ -121,10 +143,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
myFhirContext = theCtx;
}
private static boolean partIsOperation(String nextString) {
return nextString.length() > 0 && (nextString.charAt(0) == '_' || nextString.charAt(0) == '$' || nextString.equals(Constants.URL_TOKEN_METADATA));
}
private void addContentLocationHeaders(RequestDetails theRequest, HttpServletResponse servletResponse, MethodOutcome response, String resourceName) {
if (response != null && response.getId() != null) {
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_LOCATION, resourceName);
@ -140,15 +158,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* </p>
*/
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
StringBuilder b = new StringBuilder();
b.append("HAPI FHIR ");
b.append(VersionUtil.getVersion());
b.append(" REST Server (FHIR Server; FHIR ");
b.append(myFhirContext.getVersion().getVersion().getFhirVersionString());
b.append('/');
b.append(myFhirContext.getVersion().getVersion().name());
b.append(")");
theHttpResponse.addHeader("X-Powered-By", b.toString());
String b = createPoweredByHeader();
theHttpResponse.addHeader("X-Powered-By", b);
}
private void addLocationHeader(RequestDetails theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation, String resourceName) {
@ -198,6 +209,33 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return result;
}
protected List<String> createPoweredByAttributes() {
return Lists.newArrayList("FHIR Server", "FHIR " + myFhirContext.getVersion().getVersion().getFhirVersionString() + "/" + myFhirContext.getVersion().getVersion().name());
}
protected String createPoweredByHeader() {
StringBuilder b = new StringBuilder();
b.append(createPoweredByHeaderProductName());
b.append(" ");
b.append(VersionUtil.getVersion());
b.append(" REST Server (");
List<String> poweredByAttributes = createPoweredByAttributes();
for (ListIterator<String> iter = poweredByAttributes.listIterator(); iter.hasNext(); ) {
if (iter.nextIndex() > 0) {
b.append("; ");
}
b.append(iter.next());
}
b.append(")");
return b.toString();
}
protected String createPoweredByHeaderProductName() {
return "HAPI FHIR";
}
@Override
public void destroy() {
if (getResourceProviders() != null) {
@ -253,6 +291,31 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return resourceMethod;
}
@Override
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handleRequest(RequestTypeEnum.DELETE, request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handleRequest(RequestTypeEnum.GET, request, response);
}
@Override
protected void doOptions(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
handleRequest(RequestTypeEnum.OPTIONS, theReq, theResp);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handleRequest(RequestTypeEnum.POST, request, response);
}
@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handleRequest(RequestTypeEnum.PUT, request, response);
}
/**
* Count length of URL string, but treating unescaped sequences (e.g. ' ') as their unescaped equivalent (%20)
*/
@ -356,7 +419,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/**
* @deprecated As of HAPI FHIR 1.5, this property has been moved to
* {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
* {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
*/
@Override
@Deprecated
@ -369,10 +432,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* (which is the default), the server will automatically add a profile tag based on
* the class of the resource(s) being returned.
*
* @param theAddProfileTag
* The behaviour enum (must not be null)
* @param theAddProfileTag The behaviour enum (must not be null)
* @deprecated As of HAPI FHIR 1.5, this property has been moved to
* {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
* {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
*/
@Deprecated
@CoverageIgnore
@ -389,8 +451,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/**
* Set how bundle factory should decide whether referenced resources should be included in bundles
*
* @param theBundleInclusionRule
* - inclusion rule (@see BundleInclusionRule for behaviors)
* @param theBundleInclusionRule - inclusion rule (@see BundleInclusionRule for behaviors)
*/
public void setBundleInclusionRule(BundleInclusionRule theBundleInclusionRule) {
myBundleInclusionRule = theBundleInclusionRule;
@ -429,8 +490,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* Sets (enables/disables) the server support for ETags. Must not be <code>null</code>. Default is
* {@link #DEFAULT_ETAG_SUPPORT}
*
* @param theETagSupport
* The ETag support mode
* @param theETagSupport The ETag support mode
*/
public void setETagSupport(ETagSupportEnum theETagSupport) {
if (theETagSupport == null) {
@ -477,8 +537,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/**
* Sets (or clears) the list of interceptors
*
* @param theList
* The list of interceptors (may be null)
* @param theList The list of interceptors (may be null)
*/
public void setInterceptors(IServerInterceptor... theList) {
myInterceptors.clear();
@ -487,19 +546,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
}
}
/**
* Sets (or clears) the list of interceptors
*
* @param theList
* The list of interceptors (may be null)
*/
public void setInterceptors(List<IServerInterceptor> theList) {
myInterceptors.clear();
if (theList != null) {
myInterceptors.addAll(theList);
}
}
@Override
public IPagingProvider getPagingProvider() {
return myPagingProvider;
@ -533,25 +579,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
}
}
/**
* Sets the non-resource specific providers which implement method calls on this server.
*
* @see #setResourceProviders(Collection)
*/
public void setPlainProviders(Object... theProv) {
setPlainProviders(Arrays.asList(theProv));
}
/**
* Allows users of RestfulServer to override the getRequestPath method to let them build their custom request path
* implementation
*
* @param requestFullPath
* the full request path
* @param servletContextPath
* the servelet context path
* @param servletPath
* the servelet path
* @param requestFullPath the full request path
* @param servletContextPath the servelet context path
* @param servletPath the servelet path
* @return created resource path
*/
protected String getRequestPath(String requestFullPath, String servletContextPath, String servletPath) {
@ -579,16 +613,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
}
}
/**
* Sets the resource providers for this server
*/
public void setResourceProviders(IResourceProvider... theResourceProviders) {
myResourceProviders.clear();
if (theResourceProviders != null) {
myResourceProviders.addAll(Arrays.asList(theResourceProviders));
}
}
/**
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this
* server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
@ -608,6 +632,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/**
* Returns the server base URL (with no trailing '/') for a given request
*
* @param theRequest
*/
public String getServerBaseForRequest(ServletRequestDetails theRequest) {
@ -656,9 +681,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* </p>
* Note that this method can only be called before the server is initialized.
*
* @throws IllegalStateException
* Note that this method can only be called prior to {@link #init() initialization} and will throw an
* {@link IllegalStateException} if called after that.
* @throws IllegalStateException Note that this method can only be called prior to {@link #init() initialization} and will throw an
* {@link IllegalStateException} if called after that.
*/
public void setServerConformanceProvider(Object theServerConformanceProvider) {
if (myStarted) {
@ -670,9 +694,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
// passing the server into the constructor. Having that sort
// of cross linkage causes reference cycles in Spring wiring
try {
Method setRestfulServer = theServerConformanceProvider.getClass().getMethod("setRestfulServer", new Class[] { RestfulServer.class });
Method setRestfulServer = theServerConformanceProvider.getClass().getMethod("setRestfulServer", new Class[] {RestfulServer.class});
if (setRestfulServer != null) {
setRestfulServer.invoke(theServerConformanceProvider, new Object[] { this });
setRestfulServer.invoke(theServerConformanceProvider, new Object[] {this});
}
} catch (Exception e) {
ourLog.warn("Error calling IServerConformanceProvider.setRestfulServer", e);
@ -680,15 +704,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
myServerConformanceProvider = theServerConformanceProvider;
}
/**
* If provided (default is <code>null</code>), the tenant identification
* strategy provides a mechanism for a multitenant server to identify which tenant
* a given request corresponds to.
*/
public void setTenantIdentificationStrategy(ITenantIdentificationStrategy theTenantIdentificationStrategy) {
myTenantIdentificationStrategy = theTenantIdentificationStrategy;
}
/**
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only,
* but can be helpful to set with something appropriate.
@ -1029,10 +1044,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the
* server being used.
*
* @throws ServletException
* If the initialization failed. Note that you should consider throwing {@link UnavailableException}
* (which extends {@link ServletException}), as this is a flag to the servlet container
* that the servlet is not usable.
* @throws ServletException If the initialization failed. Note that you should consider throwing {@link UnavailableException}
* (which extends {@link ServletException}), as this is a flag to the servlet container
* that the servlet is not usable.
*/
protected void initialize() throws ServletException {
// nothing by default
@ -1056,6 +1070,24 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
}
}
private void invokeInitialize(Object theProvider) {
invokeInitialize(theProvider, theProvider.getClass());
}
private void invokeInitialize(Object theProvider, Class<?> clazz) {
for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) {
Initialize initialize = m.getAnnotation(Initialize.class);
if (initialize != null) {
invokeInitializeOrDestroyMethod(theProvider, m, "initialize");
}
}
Class<?> supertype = clazz.getSuperclass();
if (!Object.class.equals(supertype)) {
invokeInitialize(theProvider, supertype);
}
}
private void invokeInitializeOrDestroyMethod(Object theProvider, Method m, String theMethodDescription) {
Class<?>[] paramTypes = m.getParameterTypes();
@ -1078,24 +1110,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
}
}
private void invokeInitialize(Object theProvider) {
invokeInitialize(theProvider, theProvider.getClass());
}
private void invokeInitialize(Object theProvider, Class<?> clazz) {
for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) {
Initialize initialize = m.getAnnotation(Initialize.class);
if (initialize != null) {
invokeInitializeOrDestroyMethod(theProvider, m, "initialize");
}
}
Class<?> supertype = clazz.getSuperclass();
if (!Object.class.equals(supertype)) {
invokeInitialize(theProvider, supertype);
}
}
/**
* Should the server "pretty print" responses by default (requesting clients can always override this default by
* supplying an <code>Accept</code> header in the request, or a <code>_pretty</code>
@ -1119,8 +1133,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* The default is <code>false</code>
* </p>
*
* @param theDefaultPrettyPrint
* The default pretty print setting
* @param theDefaultPrettyPrint The default pretty print setting
*/
public void setDefaultPrettyPrint(boolean theDefaultPrettyPrint) {
myDefaultPrettyPrint = theDefaultPrettyPrint;
@ -1174,8 +1187,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/**
* @deprecated This feature did not work well, and will be removed. Use {@link ResponseHighlighterInterceptor}
* instead as an interceptor on your server and it will provide more useful syntax
* highlighting. Deprocated in 1.4
* instead as an interceptor on your server and it will provide more useful syntax
* highlighting. Deprocated in 1.4
*/
@Deprecated
@Override
@ -1185,8 +1198,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/**
* @deprecated This feature did not work well, and will be removed. Use {@link ResponseHighlighterInterceptor}
* instead as an interceptor on your server and it will provide more useful syntax
* highlighting. Deprocated in 1.4
* instead as an interceptor on your server and it will provide more useful syntax
* highlighting. Deprocated in 1.4
*/
@Deprecated
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
@ -1285,8 +1298,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/**
* Register a group of providers. These could be Resource Providers, "plain" providers or a mixture of the two.
*
* @param providers
* a {@code Collection} of providers. The parameter could be null or an empty {@code Collection}
* @param providers a {@code Collection} of providers. The parameter could be null or an empty {@code Collection}
*/
public void registerProviders(Collection<? extends Object> providers) {
myProviderRegistrationMutex.lock();
@ -1327,7 +1339,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
String resourceName = getFhirContext().getResourceDefinition(resourceType).getName();
if (myTypeToProvider.containsKey(resourceName)) {
throw new ConfigurationException("Multiple resource providers return resource type[" + resourceName + "]: First[" + myTypeToProvider.get(resourceName).getClass().getCanonicalName()
+ "] and Second[" + rsrcProvider.getClass().getCanonicalName() + "]");
+ "] and Second[" + rsrcProvider.getClass().getCanonicalName() + "]");
}
if (!inInit) {
myResourceProviders.add(rsrcProvider);
@ -1376,7 +1388,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/*
* Remove registered RESTful methods for a Provider (and all superclasses) when it is being unregistered
*/
private void removeResourceMethods(Object theProvider) throws Exception {
private void removeResourceMethods(Object theProvider) {
ourLog.info("Removing RESTful methods for: {}", theProvider.getClass());
Class<?> clazz = theProvider.getClass();
Class<?> supertype = clazz.getSuperclass();
@ -1447,50 +1459,46 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
}
switch (method) {
case DELETE:
doDelete(theReq, theResp);
break;
case GET:
doGet(theReq, theResp);
break;
case OPTIONS:
doOptions(theReq, theResp);
break;
case POST:
doPost(theReq, theResp);
break;
case PUT:
doPut(theReq, theResp);
break;
default:
handleRequest(method, theReq, theResp);
break;
case DELETE:
doDelete(theReq, theResp);
break;
case GET:
doGet(theReq, theResp);
break;
case OPTIONS:
doOptions(theReq, theResp);
break;
case POST:
doPost(theReq, theResp);
break;
case PUT:
doPut(theReq, theResp);
break;
default:
handleRequest(method, theReq, theResp);
break;
}
}
@Override
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handleRequest(RequestTypeEnum.DELETE, request, response);
/**
* Sets (or clears) the list of interceptors
*
* @param theList The list of interceptors (may be null)
*/
public void setInterceptors(List<IServerInterceptor> theList) {
myInterceptors.clear();
if (theList != null) {
myInterceptors.addAll(theList);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handleRequest(RequestTypeEnum.GET, request, response);
}
@Override
protected void doOptions(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
handleRequest(RequestTypeEnum.OPTIONS, theReq, theResp);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handleRequest(RequestTypeEnum.POST, request, response);
}
@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handleRequest(RequestTypeEnum.PUT, request, response);
/**
* Sets the non-resource specific providers which implement method calls on this server.
*
* @see #setResourceProviders(Collection)
*/
public void setPlainProviders(Object... theProv) {
setPlainProviders(Arrays.asList(theProv));
}
/**
@ -1505,6 +1513,25 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
}
}
/**
* Sets the resource providers for this server
*/
public void setResourceProviders(IResourceProvider... theResourceProviders) {
myResourceProviders.clear();
if (theResourceProviders != null) {
myResourceProviders.addAll(Arrays.asList(theResourceProviders));
}
}
/**
* If provided (default is <code>null</code>), the tenant identification
* strategy provides a mechanism for a multitenant server to identify which tenant
* a given request corresponds to.
*/
public void setTenantIdentificationStrategy(ITenantIdentificationStrategy theTenantIdentificationStrategy) {
myTenantIdentificationStrategy = theTenantIdentificationStrategy;
}
public void unregisterInterceptor(IServerInterceptor theInterceptor) {
Validate.notNull(theInterceptor, "Interceptor can not be null");
myInterceptors.remove(theInterceptor);
@ -1567,6 +1594,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
theResponse.getWriter().write(theException.getMessage());
}
private static boolean partIsOperation(String nextString) {
return nextString.length() > 0 && (nextString.charAt(0) == '_' || nextString.charAt(0) == '$' || nextString.equals(Constants.URL_TOKEN_METADATA));
}
// /**
// * Returns the read method binding for the given resource type, or
// * returns <code>null</code> if not

View File

@ -228,6 +228,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/r4/model/profile/profiles-resources.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/r4/model/profile/profiles-types.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/r4/model/profile/profiles-others.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/r4/model/extension/extension-definitions.xml");
myStructureDefinitions = structureDefinitions;
}

View File

@ -38,6 +38,7 @@ import org.mockito.stubbing.Answer;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.GZIPInputStream;
import static org.hamcrest.Matchers.*;
@ -411,11 +412,8 @@ public class FhirInstanceValidatorR4Test {
ourLog.info("Took {} ms -- {}ms / pass", delay, per);
}
/**
* // TODO: reenable
*/
@Test
@Ignore
@Ignore
public void testValidateBuiltInProfiles() throws Exception {
org.hl7.fhir.r4.model.Bundle bundle;
String name = "profiles-resources";
@ -442,16 +440,23 @@ public class FhirInstanceValidatorR4Test {
ourLog.trace(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(next));
ValidationResult output = myVal.validateWithResult(next);
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
List<SingleValidationMessage> results = logResultsAndReturnAll(output);
// This isn't a validator problem but a definition problem.. it should get fixed at some point and
// we can remove this
if (next.getId().equalsIgnoreCase("http://hl7.org/fhir/OperationDefinition/StructureDefinition-generate")) {
assertEquals(1, errors.size());
assertEquals("A search type can only be specified for parameters of type string [searchType implies type = 'string']", errors.get(0).getMessage());
// we can remove this. Tracker #17207 was filed about this
// https://gforge.hl7.org/gf/project/fhir/tracker/?action=TrackerItemEdit&tracker_item_id=17207
if (next.getId().equalsIgnoreCase("http://hl7.org/fhir/OperationDefinition/StructureDefinition-snapshot")) {
assertEquals(1, results.size());
assertEquals("A search type can only be specified for parameters of type string [searchType.exists() implies type = 'string']", results.get(0).getMessage());
continue;
}
List<SingleValidationMessage> errors = results
.stream()
.filter(t -> t.getSeverity() != ResultSeverityEnum.INFORMATION)
.collect(Collectors.toList());
assertThat("Failed to validate " + i.getFullUrl() + " - " + errors, errors, empty());
}

View File

@ -185,34 +185,52 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.karaf.tooling</groupId>
<artifactId>karaf-maven-plugin</artifactId>
<version>${apache_karaf_version}</version>
<configuration>
<descriptors>
<descriptor>file://${project.build.directory}/classes/${features.file}</descriptor>
<descriptor>mvn:org.apache.karaf.features/enterprise/${apache_karaf_version}/xml/features</descriptor>
</descriptors>
<distribution>org.apache.karaf.features:framework</distribution>
<javase>1.8</javase>
<framework>
<feature>framework</feature>
</framework>
<features>
<feature>hapi-fhir*</feature>
</features>
</configuration>
<executions>
<execution>
<id>validate</id>
<phase>process-resources</phase>
<goals>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<!--
The karaf verification step doesn't happen by default
because it doesn't seem to work on Windows.
See https://github.com/jamesagnew/hapi-fhir/issues/921
-->
<profile>
<id>DIST</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.karaf.tooling</groupId>
<artifactId>karaf-maven-plugin</artifactId>
<version>${apache_karaf_version}</version>
<configuration>
<descriptors>
<descriptor>file://${project.build.directory}/classes/${features.file}</descriptor>
<descriptor>mvn:org.apache.karaf.features/enterprise/${apache_karaf_version}/xml/features</descriptor>
</descriptors>
<distribution>org.apache.karaf.features:framework</distribution>
<javase>1.8</javase>
<framework>
<feature>framework</feature>
</framework>
<features>
<feature>hapi-fhir*</feature>
</features>
</configuration>
<executions>
<execution>
<id>validate</id>
<phase>process-resources</phase>
<goals>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -172,6 +172,18 @@
invocation at the type level as being at the instance level if the method
indicated that the IdParam parameter was optional. This has been fixed.
</action>
<action type="add">
StructureDefinitions for the FHIR standard extensions have been added to the
hapi-fhir-validation-resources-XXXX modules. Thanks to Patrick Werner for the
pull request! These have also been added to the list of definitions uploaded
by the CLI "upload-definitions" command.
</action>
<action type="fix">
A workaround for an invalid search parameter path in the R4 consent
resource has been implemented. This path was preventing some Consent
resources from successfully being uploaded to the JPA server. Thanks to
Anthony Sute for identifying this.
</action>
</release>
<release version="3.3.0" date="2018-03-29">
<action type="add">

View File

@ -60,3 +60,4 @@ cp ~/workspace/fhir/trunk/build/publish/profiles-*.xml hapi-fhir-validatio
cp ~/workspace/fhir/trunk/build/publish/v2-tables.xml hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/valueset/
cp ~/workspace/fhir/trunk/build/publish/v3-codesystems.xml hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/valueset/
cp ~/workspace/fhir/trunk/build/publish/valuesets.xml hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/valueset/
cp ~/workspace/fhir/trunk/build/publish/extension-definitions.xml hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/extension/