Sync with upstream/master
This commit is contained in:
commit
98f929997b
|
@ -53,8 +53,8 @@ public class FhirTerser {
|
|||
if (theChildDefinition == null)
|
||||
return null;
|
||||
if (theCurrentList == null || theCurrentList.isEmpty())
|
||||
return new ArrayList<String>(Arrays.asList(theChildDefinition.getElementName()));
|
||||
List<String> newList = new ArrayList<String>(theCurrentList);
|
||||
return new ArrayList<>(Arrays.asList(theChildDefinition.getElementName()));
|
||||
List<String> newList = new ArrayList<>(theCurrentList);
|
||||
newList.add(theChildDefinition.getElementName());
|
||||
return newList;
|
||||
}
|
||||
|
|
|
@ -174,20 +174,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
@Autowired
|
||||
private ISearchDao mySearchDao;
|
||||
@Autowired
|
||||
private ISearchParamExtractor mySearchParamExtractor;
|
||||
@Autowired
|
||||
private ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
||||
//@Autowired
|
||||
//private ISearchResultDao mySearchResultDao;
|
||||
@Autowired
|
||||
private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
|
||||
@Autowired
|
||||
private BeanFactory beanFactory;
|
||||
@Autowired
|
||||
private DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
private SearchParamExtractorService mySearchParamExtractorService;
|
||||
@Autowired
|
||||
private SearchParamWithInlineReferencesExtractor mySearchParamWithInlineReferencesExtractor;
|
||||
@Autowired
|
||||
private DatabaseSearchParamSynchronizer myDatabaseSearchParamSynchronizer;
|
||||
|
|
|
@ -58,4 +58,6 @@ public interface IResourceReindexingSvc {
|
|||
* to be used by unit tests.
|
||||
*/
|
||||
void cancelAndPurgeAllJobs();
|
||||
|
||||
int countReindexJobs();
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.search.reindex;
|
|||
* 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.
|
||||
|
@ -30,11 +30,11 @@ import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
|||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
||||
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceReindexJobEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
@ -98,6 +98,8 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
|||
private FhirContext myContext;
|
||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||
private EntityManager myEntityManager;
|
||||
@Autowired
|
||||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
|
||||
@VisibleForTesting
|
||||
void setReindexJobDaoForUnitTest(IResourceReindexJobDao theReindexJobDao) {
|
||||
|
@ -186,7 +188,6 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
|||
runReindexingPass();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(Transactional.TxType.NEVER)
|
||||
public Integer runReindexingPass() {
|
||||
|
@ -203,7 +204,7 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
|||
return null;
|
||||
}
|
||||
|
||||
private Integer doReindexingPassInsideLock() {
|
||||
private int doReindexingPassInsideLock() {
|
||||
expungeJobsMarkedAsDeleted();
|
||||
return runReindexJobs();
|
||||
}
|
||||
|
@ -233,13 +234,13 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
|||
}
|
||||
|
||||
private int runReindexJobs() {
|
||||
Collection<ResourceReindexJobEntity> jobs = myTxTemplate.execute(t -> myReindexJobDao.findAll(PageRequest.of(0, 10), false));
|
||||
assert jobs != null;
|
||||
Collection<ResourceReindexJobEntity> jobs = getResourceReindexJobEntities();
|
||||
|
||||
if (jobs.size() > 0) {
|
||||
ourLog.info("Running {} reindex jobs: {}", jobs.size(), jobs);
|
||||
} else {
|
||||
ourLog.debug("Running {} reindex jobs: {}", jobs.size(), jobs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
@ -255,6 +256,17 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
|||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countReindexJobs() {
|
||||
return getResourceReindexJobEntities().size();
|
||||
}
|
||||
|
||||
private Collection<ResourceReindexJobEntity> getResourceReindexJobEntities() {
|
||||
Collection<ResourceReindexJobEntity> jobs = myTxTemplate.execute(t -> myReindexJobDao.findAll(PageRequest.of(0, 10), false));
|
||||
assert jobs != null;
|
||||
return jobs;
|
||||
}
|
||||
|
||||
private void markJobAsDeleted(ResourceReindexJobEntity theJob) {
|
||||
ourLog.info("Marking reindexing job ID[{}] as deleted", theJob.getId());
|
||||
myTxTemplate.execute(t -> {
|
||||
|
@ -263,6 +275,11 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
|||
});
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSearchParamRegistryForUnitTest(ISearchParamRegistry theSearchParamRegistry) {
|
||||
mySearchParamRegistry = theSearchParamRegistry;
|
||||
}
|
||||
|
||||
private int runReindexJob(ResourceReindexJobEntity theJob) {
|
||||
if (theJob.getSuspendedUntil() != null) {
|
||||
if (theJob.getSuspendedUntil().getTime() > System.currentTimeMillis()) {
|
||||
|
@ -274,6 +291,15 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
|||
StopWatch sw = new StopWatch();
|
||||
AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
/*
|
||||
* On the first time we run a particular reindex job, let's make sure we
|
||||
* have the latest search parameters loaded. This is good since a common reason to
|
||||
* be reindexing is that the search parameters have changed in some way.
|
||||
*/
|
||||
if (theJob.getThresholdLow() == null) {
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
}
|
||||
|
||||
// Calculate range
|
||||
Date low = theJob.getThresholdLow() != null ? theJob.getThresholdLow() : BEGINNING_OF_TIME;
|
||||
Date high = theJob.getThresholdHigh();
|
||||
|
@ -461,7 +487,7 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
|||
|
||||
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(resourceTable.getResourceType());
|
||||
long expectedVersion = resourceTable.getVersion();
|
||||
IBaseResource resource = dao.read(resourceTable.getIdDt().toVersionless(), null,true);
|
||||
IBaseResource resource = dao.read(resourceTable.getIdDt().toVersionless(), null, true);
|
||||
if (resource == null) {
|
||||
throw new InternalErrorException("Could not find resource version " + resourceTable.getIdDt().toUnqualified().getValue() + " in database");
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
|||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
|
@ -17,15 +19,13 @@ import org.apache.http.client.methods.HttpPost;
|
|||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.r4.model.Bundle.BundleType;
|
||||
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
|
||||
import org.hl7.fhir.r4.model.Organization;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
@ -38,6 +38,7 @@ import java.io.IOException;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.in;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
|
@ -236,6 +237,32 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCreateReflexResourceTheHardWay() throws IOException, ServletException {
|
||||
ServerOperationInterceptorAdapter interceptor = new ReflexInterceptor();
|
||||
|
||||
ourRestServer.registerInterceptor(interceptor);
|
||||
try {
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setActive(true);
|
||||
IIdType pid = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
Bundle observations = ourClient
|
||||
.search()
|
||||
.forResource("Observation")
|
||||
.where(Observation.SUBJECT.hasId(pid))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
assertEquals(1, observations.getEntry().size());
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(observations));
|
||||
|
||||
} finally {
|
||||
ourRestServer.unregisterInterceptor(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateResourceWithVersionedReference() throws IOException, ServletException {
|
||||
String methodName = "testCreateResourceWithVersionedReference";
|
||||
|
||||
|
@ -353,6 +380,26 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
}
|
||||
}
|
||||
|
||||
public class ReflexInterceptor extends ServerOperationInterceptorAdapter {
|
||||
@Override
|
||||
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
|
||||
if (theResource instanceof Patient) {
|
||||
((ServletRequestDetails) theRequest).getServletRequest().setAttribute("CREATED_PATIENT", theResource);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processingCompletedNormally(ServletRequestDetails theRequestDetails) {
|
||||
Patient createdPatient = (Patient) theRequestDetails.getServletRequest().getAttribute("CREATED_PATIENT");
|
||||
if (createdPatient != null) {
|
||||
Observation observation = new Observation();
|
||||
observation.setSubject(new Reference(createdPatient.getId()));
|
||||
|
||||
ourClient.create().resource(observation).execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
|
|
|
@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao;
|
|||
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceReindexJobEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -63,6 +64,8 @@ public class ResourceReindexingSvcImplTest extends BaseJpaTest {
|
|||
@Captor
|
||||
private ArgumentCaptor<Date> myHighCaptor;
|
||||
private ResourceReindexJobEntity mySingleJob;
|
||||
@Mock
|
||||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
|
||||
@Override
|
||||
protected FhirContext getContext() {
|
||||
|
@ -87,6 +90,7 @@ public class ResourceReindexingSvcImplTest extends BaseJpaTest {
|
|||
mySvc.setReindexJobDaoForUnitTest(myReindexJobDao);
|
||||
mySvc.setResourceTableDaoForUnitTest(myResourceTableDao);
|
||||
mySvc.setTxManagerForUnitTest(myTxManager);
|
||||
mySvc.setSearchParamRegistryForUnitTest(mySearchParamRegistry);
|
||||
mySvc.start();
|
||||
}
|
||||
|
||||
|
@ -175,6 +179,8 @@ public class ResourceReindexingSvcImplTest extends BaseJpaTest {
|
|||
verify(myReindexJobDao, times(1)).getReindexCount(any());
|
||||
verify(myReindexJobDao, times(1)).setReindexCount(any(), anyInt());
|
||||
verifyNoMoreInteractions(myReindexJobDao);
|
||||
|
||||
verify(mySearchParamRegistry, times(1)).forceRefresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
|
||||
|
@ -23,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
@ -583,6 +585,50 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a custom ValueSet against a custom CodeSystem expands correctly
|
||||
*/
|
||||
@Test
|
||||
public void testCustomValueSetExpansion() {
|
||||
|
||||
CodeSystem cs= new CodeSystem();
|
||||
cs.setUrl("http://codesystems-r-us");
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
|
||||
IIdType csId = myCodeSystemDao.create(cs).getId().toUnqualifiedVersionless();
|
||||
|
||||
TermCodeSystemVersion version = new TermCodeSystemVersion();
|
||||
version.getConcepts().add(new TermConcept(version, "A"));
|
||||
version.getConcepts().add(new TermConcept(version, "B"));
|
||||
version.getConcepts().add(new TermConcept(version, "C"));
|
||||
version.getConcepts().add(new TermConcept(version, "D"));
|
||||
runInTransaction(()->{
|
||||
ResourceTable resTable = myEntityManager.find(ResourceTable.class, csId.getIdPartAsLong());
|
||||
version.setResource(resTable);
|
||||
myTermSvc.storeNewCodeSystemVersion(csId.getIdPartAsLong(), cs.getUrl(), "My System", version);
|
||||
});
|
||||
|
||||
org.hl7.fhir.dstu3.model.ValueSet vs = new org.hl7.fhir.dstu3.model.ValueSet();
|
||||
vs.setUrl("http://valuesets-r-us");
|
||||
vs.getCompose()
|
||||
.addInclude()
|
||||
.setSystem(cs.getUrl())
|
||||
.addConcept(new org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent().setCode("A"))
|
||||
.addConcept(new org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent().setCode("C"));
|
||||
myValueSetDao.create(vs);
|
||||
|
||||
org.hl7.fhir.dstu3.model.ValueSet expansion = myValueSetDao.expandByIdentifier(vs.getUrl(), null);
|
||||
List<String> expansionCodes = expansion
|
||||
.getExpansion()
|
||||
.getContains()
|
||||
.stream()
|
||||
.map(t -> t.getCode())
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
assertEquals(Lists.newArrayList("A","C"), expansionCodes);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static List<String> toCodesContains(List<ValueSet.ValueSetExpansionContainsComponent> theContains) {
|
||||
List<String> retVal = new ArrayList<>();
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.migrate;
|
|||
* 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.
|
||||
|
@ -53,7 +53,7 @@ public class JdbcUtils {
|
|||
DatabaseMetaData metadata;
|
||||
try {
|
||||
metadata = connection.getMetaData();
|
||||
ResultSet indexes = metadata.getIndexInfo(connection.getCatalog(), connection.getSchema(), theTableName, false, true);
|
||||
ResultSet indexes = metadata.getIndexInfo(connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theTableName), false, true);
|
||||
|
||||
Set<String> indexNames = new HashSet<>();
|
||||
while (indexes.next()) {
|
||||
|
@ -81,7 +81,7 @@ public class JdbcUtils {
|
|||
DatabaseMetaData metadata;
|
||||
try {
|
||||
metadata = connection.getMetaData();
|
||||
ResultSet indexes = metadata.getIndexInfo(connection.getCatalog(), connection.getSchema(), theTableName, false, false);
|
||||
ResultSet indexes = metadata.getIndexInfo(connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theTableName), false, false);
|
||||
|
||||
while (indexes.next()) {
|
||||
String indexName = indexes.getString("INDEX_NAME");
|
||||
|
@ -112,7 +112,7 @@ public class JdbcUtils {
|
|||
metadata = connection.getMetaData();
|
||||
String catalog = connection.getCatalog();
|
||||
String schema = connection.getSchema();
|
||||
ResultSet indexes = metadata.getColumns(catalog, schema, theTableName, null);
|
||||
ResultSet indexes = metadata.getColumns(catalog, schema, massageIdentifier(metadata, theTableName), null);
|
||||
|
||||
while (indexes.next()) {
|
||||
|
||||
|
@ -165,7 +165,7 @@ public class JdbcUtils {
|
|||
DatabaseMetaData metadata;
|
||||
try {
|
||||
metadata = connection.getMetaData();
|
||||
ResultSet indexes = metadata.getCrossReference(connection.getCatalog(), connection.getSchema(), theTableName, connection.getCatalog(), connection.getSchema(), theForeignTable);
|
||||
ResultSet indexes = metadata.getCrossReference(connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theTableName), connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theForeignTable));
|
||||
|
||||
Set<String> columnNames = new HashSet<>();
|
||||
while (indexes.next()) {
|
||||
|
@ -201,7 +201,7 @@ public class JdbcUtils {
|
|||
DatabaseMetaData metadata;
|
||||
try {
|
||||
metadata = connection.getMetaData();
|
||||
ResultSet indexes = metadata.getColumns(connection.getCatalog(), connection.getSchema(), theTableName, null);
|
||||
ResultSet indexes = metadata.getColumns(connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theTableName), null);
|
||||
|
||||
Set<String> columnNames = new HashSet<>();
|
||||
while (indexes.next()) {
|
||||
|
@ -253,7 +253,7 @@ public class JdbcUtils {
|
|||
}
|
||||
}
|
||||
return sequenceNames;
|
||||
} catch (SQLException e ) {
|
||||
} catch (SQLException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
});
|
||||
|
@ -298,7 +298,7 @@ public class JdbcUtils {
|
|||
DatabaseMetaData metadata;
|
||||
try {
|
||||
metadata = connection.getMetaData();
|
||||
ResultSet tables = metadata.getColumns(connection.getCatalog(), connection.getSchema(), theTableName, theColumnName);
|
||||
ResultSet tables = metadata.getColumns(connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theTableName), null);
|
||||
|
||||
while (tables.next()) {
|
||||
String tableName = toUpperCase(tables.getString("TABLE_NAME"), Locale.US);
|
||||
|
@ -325,4 +325,14 @@ public class JdbcUtils {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static String massageIdentifier(DatabaseMetaData theMetadata, String theCatalog) throws SQLException {
|
||||
String retVal = theCatalog;
|
||||
if (theMetadata.storesLowerCaseIdentifiers()) {
|
||||
retVal = retVal.toLowerCase();
|
||||
} else {
|
||||
retVal = retVal.toUpperCase();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,10 +57,9 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
|
|||
@Autowired
|
||||
private ModelConfig myModelConfig;
|
||||
private volatile long myLastRefresh;
|
||||
private ApplicationContext myApplicationContext;
|
||||
private ISearchParamProvider mySearchParamProvider;
|
||||
|
||||
public BaseSearchParamRegistry(ISearchParamProvider theSearchParamProvider) {
|
||||
BaseSearchParamRegistry(ISearchParamProvider theSearchParamProvider) {
|
||||
super();
|
||||
mySearchParamProvider = theSearchParamProvider;
|
||||
}
|
||||
|
@ -128,7 +127,7 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
|
|||
return Collections.unmodifiableList(retVal);
|
||||
}
|
||||
|
||||
public Map<String, Map<String, RuntimeSearchParam>> getBuiltInSearchParams() {
|
||||
private Map<String, Map<String, RuntimeSearchParam>> getBuiltInSearchParams() {
|
||||
return myBuiltInSearchParams;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
private Function<IWorkerContext, IEnableWhenEvaluator> enableWhenEvaluatorSupplier = ctx -> new DefaultEnableWhenEvaluator();
|
||||
|
||||
private boolean errorForUnknownProfiles;
|
||||
private List<String> extensionDomains = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -87,18 +88,52 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
private String determineResourceName(Document theDocument) {
|
||||
Element root = null;
|
||||
/**
|
||||
* Every element in a resource or data type includes an optional <it>extension</it> child element
|
||||
* which is identified by it's {@code url attribute}. There exists a number of predefined
|
||||
* extension urls or extension domains:<ul>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* </ul>
|
||||
* It is possible to extend this list of known extension by defining custom extensions:
|
||||
* Any url which starts which one of the elements in the list of custom extension domains is
|
||||
* considered as known.
|
||||
* <p>
|
||||
* Any unknown extension domain will result in an information message when validating a resource.
|
||||
* </p>
|
||||
*/
|
||||
public FhirInstanceValidator setCustomExtensionDomains(List<String> extensionDomains) {
|
||||
this.extensionDomains = extensionDomains;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every element in a resource or data type includes an optional <it>extension</it> child element
|
||||
* which is identified by it's {@code url attribute}. There exists a number of predefined
|
||||
* extension urls or extension domains:<ul>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* </ul>
|
||||
* It is possible to extend this list of known extension by defining custom extensions:
|
||||
* Any url which starts which one of the elements in the list of custom extension domains is
|
||||
* considered as known.
|
||||
* <p>
|
||||
* Any unknown extension domain will result in an information message when validating a resource.
|
||||
* </p>
|
||||
*/
|
||||
public FhirInstanceValidator setCustomExtensionDomains(String... extensionDomains) {
|
||||
this.extensionDomains = Arrays.asList(extensionDomains);
|
||||
return this;
|
||||
}
|
||||
|
||||
private String determineResourceName(Document theDocument) {
|
||||
NodeList list = theDocument.getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
if (list.item(i) instanceof Element) {
|
||||
root = (Element) list.item(i);
|
||||
break;
|
||||
return list.item(i).getLocalName();
|
||||
}
|
||||
}
|
||||
root = theDocument.getDocumentElement();
|
||||
return root.getLocalName();
|
||||
return theDocument.getDocumentElement().getLocalName();
|
||||
}
|
||||
|
||||
private ArrayList<String> determineIfProfilesSpecified(Document theDocument) {
|
||||
|
@ -144,7 +179,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
* guielines will be ignored.
|
||||
* </p>
|
||||
*
|
||||
* @see {@link #setBestPracticeWarningLevel(BestPracticeWarningLevel)}
|
||||
* @see #setBestPracticeWarningLevel(BestPracticeWarningLevel)
|
||||
*/
|
||||
public BestPracticeWarningLevel getBestPracticeWarningLevel() {
|
||||
return myBestPracticeWarningLevel;
|
||||
|
@ -260,6 +295,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
v.setNoTerminologyChecks(isNoTerminologyChecks());
|
||||
v.setMyEnableWhenEvaluator(enableWhenEvaluatorSupplier.apply(wrappedWorkerContext));
|
||||
v.setErrorForUnknownProfiles(isErrorForUnknownProfiles());
|
||||
v.addExtensionDomains(extensionDomains);
|
||||
|
||||
List<ValidationMessage> messages = new ArrayList<>();
|
||||
|
||||
|
@ -368,7 +404,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
private LoadingCache<ResourceKey, org.hl7.fhir.r4.model.Resource> myFetchResourceCache;
|
||||
private org.hl7.fhir.r4.model.Parameters myExpansionProfile;
|
||||
|
||||
public WorkerContextWrapper(HapiWorkerContext theWorkerContext) {
|
||||
WorkerContextWrapper(HapiWorkerContext theWorkerContext) {
|
||||
myWrap = theWorkerContext;
|
||||
myConverter = new VersionConvertor_30_40();
|
||||
|
||||
|
@ -449,7 +485,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
}
|
||||
|
||||
@Override
|
||||
public void cacheResource(org.hl7.fhir.r4.model.Resource res) throws FHIRException {
|
||||
public void cacheResource(org.hl7.fhir.r4.model.Resource res) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -471,7 +507,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
|
||||
@Override
|
||||
public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r4.model.ValueSet source, boolean cacheOk, boolean heiarchical) {
|
||||
ValueSet convertedSource = null;
|
||||
ValueSet convertedSource;
|
||||
try {
|
||||
convertedSource = VersionConvertor_30_40.convertValueSet(source);
|
||||
} catch (FHIRException e) {
|
||||
|
@ -495,7 +531,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent binding, boolean cacheOk, boolean heiarchical) throws FHIRException {
|
||||
public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent binding, boolean cacheOk, boolean heiarchical) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -662,7 +698,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
}
|
||||
|
||||
@Override
|
||||
public IResourceValidator newValidator() throws FHIRException {
|
||||
public IResourceValidator newValidator() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -687,7 +723,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSystem(String system) throws TerminologyServiceException {
|
||||
public boolean supportsSystem(String system) {
|
||||
return myWrap.supportsSystem(system);
|
||||
}
|
||||
|
||||
|
@ -704,6 +740,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
@Override
|
||||
public ValidationResult validateCode(String system, String code, String display) {
|
||||
org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult result = myWrap.validateCode(system, code, display);
|
||||
// TODO: converted code might be null -> NPE
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
||||
|
@ -754,6 +791,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
// TODO: converted code might be null -> NPE
|
||||
org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult result = myWrap.validateCode(convertedCode, convertedVs);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
@ -774,6 +812,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
// TODO: converted code might be null -> NPE
|
||||
org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult result = myWrap.validateCode(convertedCode, convertedVs);
|
||||
return convertValidationResult(result);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import javax.xml.parsers.DocumentBuilderFactory;
|
|||
import java.io.StringReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -45,6 +46,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
private DocumentBuilderFactory myDocBuilderFactory;
|
||||
private boolean myNoTerminologyChecks;
|
||||
private StructureDefinition myStructureDefintion;
|
||||
private List<String> extensionDomains = Collections.emptyList();
|
||||
|
||||
private IValidationSupport myValidationSupport;
|
||||
|
||||
|
@ -68,18 +70,52 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
private String determineResourceName(Document theDocument) {
|
||||
Element root = null;
|
||||
/**
|
||||
* Every element in a resource or data type includes an optional <it>extension</it> child element
|
||||
* which is identified by it's {@code url attribute}. There exists a number of predefined
|
||||
* extension urls or extension domains:<ul>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* </ul>
|
||||
* It is possible to extend this list of known extension by defining custom extensions:
|
||||
* Any url which starts which one of the elements in the list of custom extension domains is
|
||||
* considered as known.
|
||||
* <p>
|
||||
* Any unknown extension domain will result in an information message when validating a resource.
|
||||
* </p>
|
||||
*/
|
||||
public FhirInstanceValidator setCustomExtensionDomains(List<String> extensionDomains) {
|
||||
this.extensionDomains = extensionDomains;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every element in a resource or data type includes an optional <it>extension</it> child element
|
||||
* which is identified by it's {@code url attribute}. There exists a number of predefined
|
||||
* extension urls or extension domains:<ul>
|
||||
* <li>any url which contains {@code example.org}, {@code nema.org}, or {@code acme.com}.</li>
|
||||
* <li>any url which starts with {@code http://hl7.org/fhir/StructureDefinition/}.</li>
|
||||
* </ul>
|
||||
* It is possible to extend this list of known extension by defining custom extensions:
|
||||
* Any url which starts which one of the elements in the list of custom extension domains is
|
||||
* considered as known.
|
||||
* <p>
|
||||
* Any unknown extension domain will result in an information message when validating a resource.
|
||||
* </p>
|
||||
*/
|
||||
public FhirInstanceValidator setCustomExtensionDomains(String... extensionDomains) {
|
||||
this.extensionDomains = Arrays.asList(extensionDomains);
|
||||
return this;
|
||||
}
|
||||
|
||||
private String determineResourceName(Document theDocument) {
|
||||
NodeList list = theDocument.getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
if (list.item(i) instanceof Element) {
|
||||
root = (Element) list.item(i);
|
||||
break;
|
||||
return list.item(i).getLocalName();
|
||||
}
|
||||
}
|
||||
root = theDocument.getDocumentElement();
|
||||
return root.getLocalName();
|
||||
return theDocument.getDocumentElement().getLocalName();
|
||||
}
|
||||
|
||||
private ArrayList<String> determineIfProfilesSpecified(Document theDocument) {
|
||||
|
@ -120,8 +156,8 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
* reported at the ERROR level. If this setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
|
||||
* guielines will be ignored.
|
||||
* </p>
|
||||
*
|
||||
* @see {@link #setBestPracticeWarningLevel(BestPracticeWarningLevel)}
|
||||
*
|
||||
* @see #setBestPracticeWarningLevel(BestPracticeWarningLevel)
|
||||
*/
|
||||
public BestPracticeWarningLevel getBestPracticeWarningLevel() {
|
||||
return myBestPracticeWarningLevel;
|
||||
|
@ -211,8 +247,9 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
v.setAnyExtensionsAllowed(isAnyExtensionsAllowed());
|
||||
v.setResourceIdRule(IdStatus.OPTIONAL);
|
||||
v.setNoTerminologyChecks(isNoTerminologyChecks());
|
||||
v.addExtensionDomains(extensionDomains);
|
||||
|
||||
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
|
||||
List<ValidationMessage> messages = new ArrayList<>();
|
||||
|
||||
if (theEncoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
|
@ -312,7 +349,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
public static class NullEvaluationContext implements IEvaluationContext {
|
||||
|
||||
@Override
|
||||
public TypeDetails checkFunction(Object theAppContext, String theFunctionName, List<TypeDetails> theParameters) throws PathEngineException {
|
||||
public TypeDetails checkFunction(Object theAppContext, String theFunctionName, List<TypeDetails> theParameters) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -327,12 +364,12 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
}
|
||||
|
||||
@Override
|
||||
public Base resolveConstant(Object theAppContext, String theName) throws PathEngineException {
|
||||
public Base resolveConstant(Object theAppContext, String theName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeDetails resolveConstantType(Object theAppContext, String theName) throws PathEngineException {
|
||||
public TypeDetails resolveConstantType(Object theAppContext, String theName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -420,8 +420,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
|
||||
private boolean allowUnknownExtension(String url) {
|
||||
if (url.contains("example.org") || url.contains("acme.com") || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression"))
|
||||
// Added structuredefinition-expression explicitly because it wasn't defined in the version of the spec it needs to be used with
|
||||
if (isPredefinedExtension(url))
|
||||
return true;
|
||||
for (String s : extensionDomains)
|
||||
if (url.startsWith(s))
|
||||
|
@ -430,8 +429,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
|
||||
private boolean isKnownExtension(String url) {
|
||||
// Added structuredefinition-expression and following extensions explicitly because they weren't defined in the version of the spec they need to be used with
|
||||
if (url.contains("example.org") || url.contains("acme.com") || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression") || url.equals(VersionConvertorConstants.IG_DEPENDSON_PACKAGE_EXTENSION))
|
||||
if (isPredefinedExtension(url))
|
||||
return true;
|
||||
for (String s : extensionDomains)
|
||||
if (url.startsWith(s))
|
||||
|
@ -439,6 +437,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean isPredefinedExtension(String url) {
|
||||
return url.contains("example.org")
|
||||
|| url.contains("acme.com")
|
||||
|| url.contains("nema.org")
|
||||
|| url.startsWith("http://hl7.org/fhir/StructureDefinition/")
|
||||
|| url.equals(VersionConvertorConstants.IG_DEPENDSON_PACKAGE_EXTENSION);
|
||||
}
|
||||
|
||||
private void bpCheck(List<ValidationMessage> errors, IssueType invalid, int line, int col, String literalPath, boolean test, String message) {
|
||||
if (bpWarnings != null) {
|
||||
switch (bpWarnings) {
|
||||
|
@ -1954,6 +1960,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return extensionDomains;
|
||||
}
|
||||
|
||||
public InstanceValidator addExtensionDomains(List<String> extensionDomains) {
|
||||
this.extensionDomains.addAll(extensionDomains);
|
||||
return this;
|
||||
}
|
||||
|
||||
private Element getFromBundle(Element bundle, String ref, String fullUrl, List<ValidationMessage> errors, String path) {
|
||||
String targetUrl = null;
|
||||
String version = "";
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
package org.hl7.fhir.dstu3.hapi.validation;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.dstu3.model.CodeableConcept;
|
||||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
|
||||
import org.hl7.fhir.dstu3.model.Questionnaire;
|
||||
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class QuestionnaireValidatorDstu3Test {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(QuestionnaireValidatorDstu3Test.class);
|
||||
private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport();
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
private FhirInstanceValidator myInstanceVal;
|
||||
private FhirValidator myVal;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
IValidationSupport myValSupport = mock(IValidationSupport.class);
|
||||
|
||||
myVal = ourCtx.newValidator();
|
||||
myVal.setValidateAgainstStandardSchema(false);
|
||||
myVal.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myValSupport, myDefaultValidationSupport);
|
||||
myInstanceVal = new FhirInstanceValidator(validationSupport);
|
||||
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuestionnaireWithPredefinedExtensionDomainsForCoding() {
|
||||
String[] extensionDomainsToTest = new String[] {
|
||||
"http://example.org/questionnaire-color-control-1",
|
||||
"https://example.org/questionnaire-color-control-2",
|
||||
"http://acme.com/questionnaire-color-control-3",
|
||||
"https://acme.com/questionnaire-color-control-4",
|
||||
"http://nema.org/questionnaire-color-control-5",
|
||||
"https://nema.org/questionnaire-color-control-6",
|
||||
"http://hl7.org/fhir/StructureDefinition/questionnaire-scoreItem",
|
||||
"http://hl7.org/fhir/StructureDefinition/structuredefinition-expression",
|
||||
};
|
||||
for (String extensionDomainToTest : extensionDomainsToTest) {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.setStatus(PublicationStatus.ACTIVE)
|
||||
.addItem()
|
||||
.setLinkId("link0")
|
||||
.setType(QuestionnaireItemType.STRING)
|
||||
.addExtension()
|
||||
.setUrl(extensionDomainToTest)
|
||||
.setValue(new Coding(null, "text-box", null));
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(q);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.isSuccessful(), Matchers.is(true));
|
||||
assertThat(errors.getMessages(), Matchers.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuestionnaireWithPredefinedExtensionDomainsForCodeableConcept() {
|
||||
String[] extensionDomainsToTest = new String[] {
|
||||
"http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
|
||||
};
|
||||
for (String extensionDomainToTest : extensionDomainsToTest) {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.setStatus(PublicationStatus.ACTIVE)
|
||||
.addItem()
|
||||
.setLinkId("link0")
|
||||
.setType(QuestionnaireItemType.STRING)
|
||||
.addExtension()
|
||||
.setUrl(extensionDomainToTest)
|
||||
.setValue(new CodeableConcept().addCoding(new Coding(null, "text-box", null)));
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(q);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.isSuccessful(), Matchers.is(true));
|
||||
assertThat(errors.getMessages(), Matchers.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuestionnaireWithCustomExtensionDomain() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
String extensionUrl = "http://my.own.domain/StructureDefinition/";
|
||||
q.setStatus(PublicationStatus.ACTIVE)
|
||||
.addItem()
|
||||
.setLinkId("link0")
|
||||
.setType(QuestionnaireItemType.STRING)
|
||||
.addExtension()
|
||||
.setUrl(extensionUrl + "questionnaire-itemControl")
|
||||
.setValue(new Coding(null, "text-box", null));
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(q);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.isSuccessful(), Matchers.is(true));
|
||||
assertThat(errors.getMessages(), Matchers.hasSize(1));
|
||||
assertEquals(errors.getMessages().get(0).getSeverity(), ResultSeverityEnum.INFORMATION);
|
||||
assertThat(errors.getMessages().get(0).getMessage(), Matchers.startsWith("Unknown extension " + extensionUrl));
|
||||
|
||||
myInstanceVal.setCustomExtensionDomains(Collections.singletonList(extensionUrl));
|
||||
errors = myVal.validateWithResult(q);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.isSuccessful(), Matchers.is(true));
|
||||
assertThat(errors.getMessages(), Matchers.empty());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
myDefaultValidationSupport.flush();
|
||||
myDefaultValidationSupport = null;
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
package org.hl7.fhir.r4.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.r4.hapi.validation.ValidationSupportChain;
|
||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
|
||||
import org.hl7.fhir.r4.model.Narrative;
|
||||
import org.hl7.fhir.r4.model.Narrative.NarrativeStatus;
|
||||
import org.hl7.fhir.r4.model.Questionnaire;
|
||||
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class QuestionnaireValidatorR4Test {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(QuestionnaireValidatorR4Test.class);
|
||||
private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport();
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
private FhirInstanceValidator myInstanceVal;
|
||||
private FhirValidator myVal;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
IValidationSupport myValSupport = mock(IValidationSupport.class);
|
||||
|
||||
myVal = ourCtx.newValidator();
|
||||
myVal.setValidateAgainstStandardSchema(false);
|
||||
myVal.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(myValSupport, myDefaultValidationSupport);
|
||||
myInstanceVal = new FhirInstanceValidator(validationSupport);
|
||||
|
||||
myVal.registerValidatorModule(myInstanceVal);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuestionnaireWithPredefinedExtensionDomains() {
|
||||
String[] extensionDomainsToTest = new String[] {
|
||||
"http://example.org/questionnaire-color-control-1",
|
||||
"https://example.org/questionnaire-color-control-2",
|
||||
"http://acme.com/questionnaire-color-control-3",
|
||||
"https://acme.com/questionnaire-color-control-4",
|
||||
"http://nema.org/questionnaire-color-control-5",
|
||||
"https://nema.org/questionnaire-color-control-6",
|
||||
"http://hl7.org/fhir/StructureDefinition/questionnaire-scoreItem",
|
||||
"http://hl7.org/fhir/StructureDefinition/structuredefinition-expression",
|
||||
|
||||
};
|
||||
for (String extensionDomainToTest : extensionDomainsToTest) {
|
||||
Questionnaire q = minimalValidQuestionnaire();
|
||||
q.addItem()
|
||||
.setLinkId("link0")
|
||||
.setType(QuestionnaireItemType.STRING)
|
||||
.addExtension()
|
||||
.setUrl(extensionDomainToTest)
|
||||
.setValue(new Coding(null, "text-box", null));
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(q);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.isSuccessful(), Matchers.is(true));
|
||||
assertThat(errors.getMessages(), Matchers.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuestionnaireWithPredefinedExtensionDomainsForCodeableConcept() {
|
||||
String[] extensionDomainsToTest = new String[] {
|
||||
"http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
|
||||
};
|
||||
for (String extensionDomainToTest : extensionDomainsToTest) {
|
||||
Questionnaire q = minimalValidQuestionnaire();
|
||||
q.addItem()
|
||||
.setLinkId("link0")
|
||||
.setType(QuestionnaireItemType.STRING)
|
||||
.addExtension()
|
||||
.setUrl(extensionDomainToTest)
|
||||
.setValue(new CodeableConcept().addCoding(new Coding(null, "text-box", null)));
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(q);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.isSuccessful(), Matchers.is(true));
|
||||
assertThat(errors.getMessages(), Matchers.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuestionnaireWithCustomExtensionDomain() {
|
||||
String extensionUrl = "http://my.own.domain/StructureDefinition/";
|
||||
Questionnaire q = minimalValidQuestionnaire();
|
||||
q.addItem()
|
||||
.setLinkId("link0")
|
||||
.setType(QuestionnaireItemType.STRING)
|
||||
.addExtension()
|
||||
.setUrl(extensionUrl + "questionnaire-itemControl")
|
||||
.setValue(new Coding(null, "text-box", null));
|
||||
|
||||
ValidationResult errors = myVal.validateWithResult(q);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.isSuccessful(), Matchers.is(true));
|
||||
assertThat(errors.getMessages(), Matchers.hasSize(1));
|
||||
assertEquals(errors.getMessages().get(0).getSeverity(), ResultSeverityEnum.INFORMATION);
|
||||
assertThat(errors.getMessages().get(0).getMessage(), Matchers.startsWith("Unknown extension " + extensionUrl));
|
||||
|
||||
myInstanceVal.setCustomExtensionDomains(extensionUrl);
|
||||
errors = myVal.validateWithResult(q);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.isSuccessful(), Matchers.is(true));
|
||||
assertThat(errors.getMessages(), Matchers.empty());
|
||||
}
|
||||
|
||||
private Questionnaire minimalValidQuestionnaire() {
|
||||
Narrative n = new Narrative().setStatus(NarrativeStatus.GENERATED);
|
||||
n.setDivAsString("simple example");
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.setText(n);
|
||||
q.setName("SomeName");
|
||||
q.setStatus(PublicationStatus.ACTIVE);
|
||||
return q;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
myDefaultValidationSupport.flush();
|
||||
myDefaultValidationSupport = null;
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package ca.uhn.fhir.tinder;
|
||||
|
||||
import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugins.annotations.Component;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base class for mojo generatorss.
|
||||
*/
|
||||
public abstract class AbstractGeneratorMojo extends AbstractMojo {
|
||||
|
||||
protected final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Parameter(required = true, defaultValue = "${project.build.directory}/..")
|
||||
protected String baseDir;
|
||||
|
||||
@Parameter
|
||||
protected String packageBase = "";
|
||||
|
||||
@Parameter
|
||||
protected List<String> baseResourceNames;
|
||||
|
||||
@Parameter
|
||||
protected List<String> excludeResourceNames;
|
||||
|
||||
@Parameter
|
||||
protected String templateName;
|
||||
|
||||
@Parameter(required = true)
|
||||
protected String version;
|
||||
|
||||
@Component
|
||||
protected MavenProject myProject;
|
||||
|
||||
@Override
|
||||
public final void execute() throws MojoExecutionException, MojoFailureException {
|
||||
doExecute(new Configuration(this.version, baseDir, getTargetDirectory(), this.packageBase, this.baseResourceNames, this.excludeResourceNames));
|
||||
}
|
||||
|
||||
protected abstract void doExecute(Configuration mavenGeneratorConfiguration) throws MojoExecutionException, MojoFailureException;
|
||||
|
||||
protected abstract File getTargetDirectory();
|
||||
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package ca.uhn.fhir.tinder;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.tinder.parser.BaseStructureSpreadsheetParser;
|
||||
import org.apache.commons.lang.WordUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class Configuration {
|
||||
|
||||
private final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Configuration.class);
|
||||
|
||||
private String version;
|
||||
private File targetDirectory;
|
||||
private String packageSuffix;
|
||||
|
||||
private String packageBase;
|
||||
private FhirContext fhirContext;
|
||||
private File packageDirectoryBase;
|
||||
|
||||
private final List<String> resourceNames = new ArrayList<>();
|
||||
private String baseDir;
|
||||
|
||||
public Configuration(String version, String baseDir, File targetDirectory, String packageBase, List<String> baseResourceNames, List<String> excludeResourceNames) {
|
||||
this.targetDirectory = targetDirectory;
|
||||
this.packageBase = packageBase;
|
||||
this.packageDirectoryBase = new File(targetDirectory, packageBase.replace(".", File.separatorChar + ""));
|
||||
|
||||
switch (version) {
|
||||
case "dstu2":
|
||||
fhirContext = FhirContext.forDstu2();
|
||||
break;
|
||||
case "dstu3":
|
||||
fhirContext = FhirContext.forDstu3();
|
||||
packageSuffix = ".dstu3";
|
||||
break;
|
||||
case "r4":
|
||||
fhirContext = FhirContext.forR4();
|
||||
packageSuffix = ".r4";
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown version configured: " + version);
|
||||
}
|
||||
|
||||
this.version = version;
|
||||
if (baseResourceNames == null || baseResourceNames.isEmpty()) {
|
||||
ourLog.info("No resource names supplied, going to use all resources from version: {}", fhirContext.getVersion().getVersion());
|
||||
|
||||
Properties p = new Properties();
|
||||
try {
|
||||
p.load(fhirContext.getVersion().getFhirVersionPropertiesFile());
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("Failed to load version property file", e);
|
||||
}
|
||||
|
||||
ourLog.debug("Property file contains: {}", p);
|
||||
|
||||
TreeSet<String> keys = new TreeSet<String>();
|
||||
for (Object next : p.keySet()) {
|
||||
keys.add((String) next);
|
||||
}
|
||||
for (String next : keys) {
|
||||
if (next.startsWith("resource.")) {
|
||||
resourceNames.add(next.substring("resource.".length()).toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
if (fhirContext.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
|
||||
resourceNames.remove("conformance");
|
||||
}
|
||||
} else {
|
||||
for (String resourceName : baseResourceNames) {
|
||||
resourceNames.add(resourceName.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
if (excludeResourceNames != null) {
|
||||
for (String resourceName : excludeResourceNames) {
|
||||
resourceNames.remove(resourceName.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("Including the following resources: {}", resourceNames);
|
||||
}
|
||||
|
||||
public File getPackageDirectoryBase() {
|
||||
return packageDirectoryBase;
|
||||
}
|
||||
|
||||
public String getPackageSuffix() {
|
||||
return packageSuffix;
|
||||
}
|
||||
|
||||
public List<String> getResourceNames() {
|
||||
return resourceNames;
|
||||
}
|
||||
|
||||
public String getPackageBase() {
|
||||
return packageBase;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getResourcePackage() {
|
||||
if (BaseStructureSpreadsheetParser.determineVersionEnum(version).isRi()) {
|
||||
return "org.hl7.fhir." + version + ".model";
|
||||
}
|
||||
return "ca.uhn.fhir.model." + version + ".resource";
|
||||
}
|
||||
|
||||
public String getVersionCapitalized() {
|
||||
String capitalize = WordUtils.capitalize(version);
|
||||
if ("Dstu".equals(capitalize)) {
|
||||
return "Dstu1";
|
||||
}
|
||||
return capitalize;
|
||||
}
|
||||
|
||||
public File getTargetDirectory() {
|
||||
return targetDirectory;
|
||||
}
|
||||
|
||||
public String getBaseDir() {
|
||||
return baseDir;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package ca.uhn.fhir.tinder;
|
||||
|
||||
import ca.uhn.fhir.tinder.parser.ResourceGeneratorUsingModel;
|
||||
import ca.uhn.fhir.tinder.parser.ResourceGeneratorUsingSpreadsheet;
|
||||
import org.apache.maven.model.Resource;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
import org.apache.maven.plugins.annotations.Mojo;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.apache.velocity.VelocityContext;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.tools.generic.EscapeTool;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@Mojo(name = "generate-resource", defaultPhase = LifecyclePhase.GENERATE_RESOURCES)
|
||||
public class TinderResourceGeneratorMojo extends AbstractGeneratorMojo {
|
||||
|
||||
@Parameter(required = true, defaultValue = "${project.build.directory}/generated-resources/tinder")
|
||||
protected File targetDirectory;
|
||||
|
||||
@Parameter(required = true)
|
||||
protected String fileName = "";
|
||||
|
||||
@Override
|
||||
protected void doExecute(Configuration configuration) throws MojoExecutionException, MojoFailureException {
|
||||
File packageDirectoryBase = configuration.getPackageDirectoryBase();
|
||||
packageDirectoryBase.mkdirs();
|
||||
|
||||
ResourceGeneratorUsingModel gen = new ResourceGeneratorUsingModel(configuration.getVersion(), configuration.getBaseDir());
|
||||
gen.setBaseResourceNames(configuration.getResourceNames());
|
||||
|
||||
try {
|
||||
gen.parse();
|
||||
|
||||
VelocityContext ctx = new VelocityContext();
|
||||
ctx.put("resources", gen.getResources());
|
||||
ctx.put("packageBase", configuration.getPackageBase());
|
||||
ctx.put("version", configuration.getVersion());
|
||||
ctx.put("package_suffix", configuration.getPackageSuffix());
|
||||
ctx.put("esc", new EscapeTool());
|
||||
|
||||
ctx.put("resourcePackage", configuration.getResourcePackage());
|
||||
ctx.put("versionCapitalized", configuration.getVersionCapitalized());
|
||||
|
||||
VelocityEngine v = new VelocityEngine();
|
||||
v.setProperty("resource.loader", "cp");
|
||||
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
|
||||
v.setProperty("runtime.references.strict", Boolean.TRUE);
|
||||
|
||||
InputStream templateIs = ResourceGeneratorUsingSpreadsheet.class.getResourceAsStream(templateName);
|
||||
InputStreamReader templateReader = new InputStreamReader(templateIs);
|
||||
|
||||
File file = new File(packageDirectoryBase, fileName);
|
||||
OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(file, false), "UTF-8");
|
||||
v.evaluate(ctx, w, "", templateReader);
|
||||
w.close();
|
||||
|
||||
Resource resource = new Resource();
|
||||
resource.setDirectory(packageDirectoryBase.getAbsolutePath());
|
||||
//resource.setDirectory(targetDirectory.getAbsolutePath());
|
||||
//resource.addInclude(packageBase);
|
||||
myProject.addResource(resource);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new MojoFailureException("Failed to generate resources", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getTargetDirectory() {
|
||||
return targetDirectory;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package ca.uhn.fhir.tinder;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.maven.plugin.*;
|
||||
import org.apache.maven.plugins.annotations.*;
|
||||
import org.apache.maven.plugins.annotations.Mojo;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.tinder.parser.*;
|
||||
|
||||
@Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
|
||||
public class TinderSourcesGeneratorMojo extends AbstractGeneratorMojo {
|
||||
|
||||
@Parameter(required = true, defaultValue = "${project.build.directory}/generated-sources/tinder")
|
||||
protected File targetDirectory;
|
||||
|
||||
@Parameter
|
||||
private String filenameSuffix = "ResourceProvider";
|
||||
|
||||
@Parameter
|
||||
private String filenamePrefix = "";
|
||||
|
||||
@Override
|
||||
public void doExecute(Configuration configuration) throws MojoExecutionException, MojoFailureException {
|
||||
File packageDirectoryBase = configuration.getPackageDirectoryBase();
|
||||
packageDirectoryBase.mkdirs();
|
||||
|
||||
ResourceGeneratorUsingModel gen = new ResourceGeneratorUsingModel(configuration.getVersion(), configuration.getBaseDir());
|
||||
gen.setBaseResourceNames(configuration.getResourceNames());
|
||||
|
||||
try {
|
||||
gen.parse();
|
||||
|
||||
gen.setFilenameSuffix(filenameSuffix);
|
||||
gen.setFilenamePrefix(filenamePrefix);
|
||||
gen.setTemplate(templateName);
|
||||
gen.writeAll(packageDirectoryBase, null, configuration.getPackageBase());
|
||||
} catch (Exception e) {
|
||||
throw new MojoFailureException("Failed to generate server", e);
|
||||
}
|
||||
|
||||
myProject.addCompileSourceRoot(configuration.getTargetDirectory().getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getTargetDirectory() {
|
||||
return targetDirectory;
|
||||
}
|
||||
}
|
|
@ -711,7 +711,7 @@ public abstract class BaseStructureParser {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static FhirVersionEnum determineVersionEnum(String version) throws MojoFailureException {
|
||||
public static FhirVersionEnum determineVersionEnum(String version) {
|
||||
FhirVersionEnum versionEnum;
|
||||
if ("dstu2".equals(version)) {
|
||||
versionEnum = FhirVersionEnum.DSTU2;
|
||||
|
@ -720,7 +720,7 @@ public abstract class BaseStructureParser {
|
|||
} else if ("r4".equals(version)) {
|
||||
versionEnum = FhirVersionEnum.R4;
|
||||
} else {
|
||||
throw new MojoFailureException("Unknown version: " + version);
|
||||
throw new IllegalArgumentException("Unknown version: " + version);
|
||||
}
|
||||
return versionEnum;
|
||||
}
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -518,7 +518,7 @@
|
|||
<jaxb_api_version>2.3.0</jaxb_api_version>
|
||||
<jaxb_core_version>2.3.0</jaxb_core_version>
|
||||
<jersey_version>2.25.1</jersey_version>
|
||||
<jetty_version>9.4.12.v20180830</jetty_version>
|
||||
<jetty_version>9.4.14.v20181114</jetty_version>
|
||||
<jsr305_version>3.0.2</jsr305_version>
|
||||
<!--<hibernate_version>5.2.10.Final</hibernate_version>-->
|
||||
<hibernate_version>5.3.6.Final</hibernate_version>
|
||||
|
|
|
@ -98,9 +98,11 @@
|
|||
or authorizing the contents of the response.
|
||||
</action>
|
||||
<action type="add">
|
||||
JPA Migrator tool enhancements:
|
||||
An invalid SQL syntax issue has been fixed when running the CLI JPA Migrator tool against
|
||||
Oracle or SQL Server. In addition, when using the "Dry Run" option, all generated SQL
|
||||
statements will be logged at the end of the run.
|
||||
statements will be logged at the end of the run. Also, a case sensitivity issue when running against
|
||||
some Postgres databases has been corrected.
|
||||
</action>
|
||||
<action type="add">
|
||||
In the JPA server, when performing a chained reference search on a search parameter with
|
||||
|
@ -142,6 +144,18 @@
|
|||
causes Media resources to be served as raw content if the client explicitly requests
|
||||
the correct content type cia the Accept header.
|
||||
</action>
|
||||
<action type="add" issue="917">
|
||||
A new configuration item has been added to the FhirInstanceValidator that
|
||||
allows you to specify additional "known extension domains", meaning
|
||||
domains in which the validator will not complain about when it
|
||||
encounters new extensions. Thanks to Heinz-Dieter Conradi for the
|
||||
pull request!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Under some circumstances, when a custom search parameter was added to the JPA server
|
||||
resources could start reindexing before the new search parameter had been saved, meaning that
|
||||
it was not applied to all resources. This has been corrected.
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.6.0" date="2018-11-12" description="Food">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue