Add support for $snapshot generation and validation based on snapshots
This commit is contained in:
parent
120b93f204
commit
7831e8a0ed
|
@ -263,7 +263,13 @@ public class ValidatorExamples {
|
|||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* ValidationSupportChain strings multiple instances of IValidationSupport together. The
|
||||
|
|
|
@ -75,4 +75,16 @@ public class ValidateUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static void exactlyOneNotNullOrThrowInvalidRequestException(Object[] theObjects, String theMessage) {
|
||||
int count = 0;
|
||||
for (Object next : theObjects) {
|
||||
if (next != null) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count != 1) {
|
||||
throw new InvalidRequestException(theMessage);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -95,6 +95,11 @@ public class LoadingValidationSupportDstu3 implements IValidationSupport {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
|
||||
return Collections.emptyList();
|
||||
|
|
|
@ -92,6 +92,11 @@ public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IVal
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return null;
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 University Health Network
|
||||
* %%
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.model.dstu2.resource.StructureDefinition;
|
||||
|
||||
public class FhirResourceDaoStructureDefinitionDstu2 extends FhirResourceDaoDstu2<StructureDefinition> implements IFhirResourceDaoStructureDefinition<StructureDefinition> {
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
// FIXME: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 University Health Network
|
||||
* %%
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
public interface IFhirResourceDaoStructureDefinition<T extends IBaseResource> extends IFhirResourceDao<T> {
|
||||
|
||||
T generateSnapshot(T theInput, String theUrl, String theName);
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoStructureDefinition;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 University Health Network
|
||||
* %%
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
public class FhirResourceDaoStructureDefinitionDstu3 extends FhirResourceDaoDstu3<StructureDefinition> implements IFhirResourceDaoStructureDefinition<StructureDefinition> {
|
||||
|
||||
@Autowired
|
||||
private IValidationSupport myValidationSupport;
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
StructureDefinition output = myValidationSupport.generateSnapshot(theInput, theUrl, theName);
|
||||
Validate.notNull(output);
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
|
@ -128,7 +128,11 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3, Ap
|
|||
}
|
||||
} else if ("StructureDefinition".equals(resourceName)) {
|
||||
if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
|
||||
return null;
|
||||
// Don't allow the core FHIR definitions to be overwritten
|
||||
String typeName = theUri.substring("http://hl7.org/fhir/StructureDefinition/".length());
|
||||
if (myDstu3Ctx.getElementDefinition(typeName) != null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronousUpTo(1);
|
||||
|
@ -189,4 +193,9 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3, Ap
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoStructureDefinition;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 University Health Network
|
||||
* %%
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
public class FhirResourceDaoStructureDefinitionR4 extends FhirResourceDaoR4<StructureDefinition> implements IFhirResourceDaoStructureDefinition<StructureDefinition> {
|
||||
|
||||
@Autowired
|
||||
private IValidationSupport myValidationSupport;
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
StructureDefinition output = myValidationSupport.generateSnapshot(theInput, theUrl, theName);
|
||||
Validate.notNull(output);
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -23,5 +24,5 @@ import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
|||
*/
|
||||
|
||||
public interface IJpaValidationSupportR4 extends IValidationSupport {
|
||||
|
||||
// nothing yet
|
||||
}
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.conformance.ProfileUtilities;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -19,6 +26,7 @@ import org.springframework.context.ApplicationContextAware;
|
|||
import javax.annotation.PostConstruct;
|
||||
import javax.transaction.Transactional;
|
||||
import javax.transaction.Transactional.TxType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -121,7 +129,9 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat
|
|||
search = myValueSetDao.search(params);
|
||||
}
|
||||
} else if ("StructureDefinition".equals(resourceName)) {
|
||||
if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
|
||||
// Don't allow the core FHIR definitions to be overwritten
|
||||
String typeName = theUri.substring("http://hl7.org/fhir/StructureDefinition/".length());
|
||||
if (myR4Ctx.getElementDefinition(typeName) != null) {
|
||||
return null;
|
||||
}
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
|
@ -183,4 +193,9 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package ca.uhn.fhir.jpa.util;
|
||||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.util;
|
|||
* 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
|
||||
* 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,
|
||||
|
@ -20,10 +20,10 @@ package ca.uhn.fhir.jpa.util;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link ca.uhn.fhir.util.StopWatch} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class StopWatch extends ca.uhn.fhir.util.StopWatch {
|
||||
// this just exists since existing code may depend on it
|
||||
import ca.uhn.fhir.model.dstu2.resource.StructureDefinition;
|
||||
|
||||
public class BaseJpaResourceProviderStructureDefinitionDstu2 extends JpaResourceProviderDstu2<StructureDefinition> {
|
||||
|
||||
// nothing yet
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package ca.uhn.fhir.jpa.provider.dstu3;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoStructureDefinition;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.util.ValidateUtil;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 University Health Network
|
||||
* %%
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
public class BaseJpaResourceProviderStructureDefinitionDstu3 extends JpaResourceProviderDstu3<StructureDefinition> {
|
||||
|
||||
/**
|
||||
* <code>$snapshot</code> operation
|
||||
*/
|
||||
@Operation(name=JpaConstants.OPERATION_SNAPSHOT, idempotent = true)
|
||||
public StructureDefinition snapshot(
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "definition") StructureDefinition theStructureDefinition,
|
||||
@OperationParam(name = "url") StringType theUrl,
|
||||
RequestDetails theRequestDetails) {
|
||||
|
||||
ValidateUtil.exactlyOneNotNullOrThrowInvalidRequestException(
|
||||
new Object[]{ theId, theStructureDefinition, theUrl },
|
||||
"Must supply either an ID or a StructureDefinition or a URL (but not more than one of these things)"
|
||||
);
|
||||
|
||||
StructureDefinition sd;
|
||||
if (theId == null && theStructureDefinition != null && theUrl == null) {
|
||||
sd = theStructureDefinition;
|
||||
} else if (theId != null && theStructureDefinition == null) {
|
||||
sd = getDao().read(theId, theRequestDetails);
|
||||
} else {
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setLoadSynchronousUpTo(2);
|
||||
map.add(StructureDefinition.SP_URL, new UriParam(theUrl.getValue()));
|
||||
IBundleProvider outcome = getDao().search(map, theRequestDetails);
|
||||
if (outcome.size() == 0) {
|
||||
throw new ResourceNotFoundException("No StructureDefiniton found with url = '" + theUrl.getValue() + "'");
|
||||
}
|
||||
sd = (StructureDefinition) outcome.getResources(0, 1).get(0);
|
||||
}
|
||||
|
||||
return getDao().generateSnapshot(sd, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFhirResourceDaoStructureDefinition<StructureDefinition> getDao() {
|
||||
return (IFhirResourceDaoStructureDefinition<StructureDefinition>) super.getDao();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoStructureDefinition;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.util.ValidateUtil;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 University Health Network
|
||||
* %%
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
public class BaseJpaResourceProviderStructureDefinitionR4 extends JpaResourceProviderR4<StructureDefinition> {
|
||||
|
||||
/**
|
||||
* <code>$snapshot</code> operation
|
||||
*/
|
||||
@Operation(name=JpaConstants.OPERATION_SNAPSHOT, idempotent = true)
|
||||
public StructureDefinition snapshot(
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "definition") StructureDefinition theStructureDefinition,
|
||||
@OperationParam(name = "url") StringType theUrl,
|
||||
RequestDetails theRequestDetails) {
|
||||
|
||||
ValidateUtil.exactlyOneNotNullOrThrowInvalidRequestException(
|
||||
new Object[]{ theId, theStructureDefinition, theUrl },
|
||||
"Must supply either an ID or a StructureDefinition or a URL (but not more than one of these things)"
|
||||
);
|
||||
|
||||
StructureDefinition sd;
|
||||
if (theId == null && theStructureDefinition != null && theUrl == null) {
|
||||
sd = theStructureDefinition;
|
||||
} else if (theId != null && theStructureDefinition == null) {
|
||||
sd = getDao().read(theId, theRequestDetails);
|
||||
} else {
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setLoadSynchronousUpTo(2);
|
||||
map.add(StructureDefinition.SP_URL, new UriParam(theUrl.getValue()));
|
||||
IBundleProvider outcome = getDao().search(map, theRequestDetails);
|
||||
if (outcome.size() == 0) {
|
||||
throw new ResourceNotFoundException("No StructureDefiniton found with url = '" + theUrl.getValue() + "'");
|
||||
}
|
||||
sd = (StructureDefinition) outcome.getResources(0, 1).get(0);
|
||||
}
|
||||
|
||||
return getDao().generateSnapshot(sd, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFhirResourceDaoStructureDefinition<StructureDefinition> getDao() {
|
||||
return (IFhirResourceDaoStructureDefinition<StructureDefinition>) super.getDao();
|
||||
}
|
||||
|
||||
}
|
|
@ -297,5 +297,10 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
|
|||
return new CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -225,6 +225,11 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
|
|||
return myTerminologySvc.supportsSystem(theSystem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@CoverageIgnore
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
|
|
|
@ -142,4 +142,9 @@ public class JpaConstants {
|
|||
* Operation name for the "$subsumes" operation
|
||||
*/
|
||||
public static final String OPERATION_SUBSUMES = "$subsumes";
|
||||
|
||||
/**
|
||||
* Operation name for the "$snapshot" operation
|
||||
*/
|
||||
public static final String OPERATION_SNAPSHOT = "$snapshot";
|
||||
}
|
||||
|
|
|
@ -20,31 +20,52 @@ package ca.uhn.fhir.jpa.validation;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvcDstu3;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.SnapshotGeneratingValidationSupport;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvcDstu3;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
public class JpaValidationSupportChainDstu3 extends ValidationSupportChain {
|
||||
|
||||
private DefaultProfileValidationSupport myDefaultProfileValidationSupport = new DefaultProfileValidationSupport();
|
||||
|
||||
@Autowired
|
||||
@Qualifier("myJpaValidationSupportDstu3")
|
||||
public ca.uhn.fhir.jpa.dao.dstu3.IJpaValidationSupportDstu3 myJpaValidationSupportDstu3;
|
||||
|
||||
private DefaultProfileValidationSupport myDefaultProfileValidationSupport = new DefaultProfileValidationSupport();
|
||||
@Autowired
|
||||
private IHapiTerminologySvcDstu3 myTerminologyService;
|
||||
|
||||
@Autowired
|
||||
private FhirContext myFhirContext;
|
||||
|
||||
public JpaValidationSupportChainDstu3() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
if (theClass.equals(StructureDefinition.class)) {
|
||||
return (T) fetchStructureDefinition(theContext, theUri);
|
||||
}
|
||||
return super.fetchResource(theContext, theClass, theUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
|
||||
StructureDefinition retVal = super.fetchStructureDefinition(theCtx, theUrl);
|
||||
if (retVal != null && !retVal.hasSnapshot()) {
|
||||
retVal = generateSnapshot(retVal, theUrl, null);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
myDefaultProfileValidationSupport.flush();
|
||||
}
|
||||
|
@ -54,12 +75,13 @@ public class JpaValidationSupportChainDstu3 extends ValidationSupportChain {
|
|||
addValidationSupport(myDefaultProfileValidationSupport);
|
||||
addValidationSupport(myJpaValidationSupportDstu3);
|
||||
addValidationSupport(myTerminologyService);
|
||||
addValidationSupport(new SnapshotGeneratingValidationSupport(myFhirContext, this));
|
||||
}
|
||||
|
||||
|
||||
@PreDestroy
|
||||
public void preDestroy() {
|
||||
flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -23,8 +23,12 @@ package ca.uhn.fhir.jpa.validation;
|
|||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.r4.hapi.validation.SnapshotGeneratingValidationSupport;
|
||||
import org.hl7.fhir.r4.hapi.validation.ValidationSupportChain;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
|
@ -33,7 +37,10 @@ import ca.uhn.fhir.jpa.term.IHapiTerminologySvcR4;
|
|||
public class JpaValidationSupportChainR4 extends ValidationSupportChain {
|
||||
|
||||
private DefaultProfileValidationSupport myDefaultProfileValidationSupport = new DefaultProfileValidationSupport();
|
||||
|
||||
|
||||
@Autowired
|
||||
private FhirContext myFhirContext;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("myJpaValidationSupportR4")
|
||||
public ca.uhn.fhir.jpa.dao.r4.IJpaValidationSupportR4 myJpaValidationSupportR4;
|
||||
|
@ -49,11 +56,31 @@ public class JpaValidationSupportChainR4 extends ValidationSupportChain {
|
|||
myDefaultProfileValidationSupport.flush();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
if (theClass.equals(StructureDefinition.class)) {
|
||||
return (T) fetchStructureDefinition(theContext, theUri);
|
||||
}
|
||||
return super.fetchResource(theContext, theClass, theUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
|
||||
StructureDefinition retVal = super.fetchStructureDefinition(theCtx, theUrl);
|
||||
if (retVal != null && !retVal.hasSnapshot()) {
|
||||
retVal = generateSnapshot(retVal, theUrl, null);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
addValidationSupport(myDefaultProfileValidationSupport);
|
||||
addValidationSupport(myJpaValidationSupportR4);
|
||||
addValidationSupport(myTerminologyService);
|
||||
addValidationSupport(new SnapshotGeneratingValidationSupport(myFhirContext, this));
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
|
|
|
@ -221,7 +221,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
protected IStaleSearchDeletingSvc myStaleSearchDeletingSvc;
|
||||
@Autowired
|
||||
@Qualifier("myStructureDefinitionDaoDstu3")
|
||||
protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
|
||||
protected IFhirResourceDaoStructureDefinition<StructureDefinition> myStructureDefinitionDao;
|
||||
@Autowired
|
||||
@Qualifier("mySubscriptionDaoDstu3")
|
||||
protected IFhirResourceDaoSubscription<Subscription> mySubscriptionDao;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@SuppressWarnings({"unchecked", "deprecation"})
|
||||
public class FhirResourceDaoDstu3StructureDefinitionTest extends BaseJpaDstu3Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3StructureDefinitionTest.class);
|
||||
|
||||
@After
|
||||
public final void after() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateSnapshot() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/dstu3/profile-differential-patient-dstu3.json");
|
||||
assertEquals(0, sd.getSnapshot().getElement().size());
|
||||
|
||||
StructureDefinition output = myStructureDefinitionDao.generateSnapshot(sd, "http://foo", "THE BEST PROFILE");
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(54, output.getSnapshot().getElement().size());
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
}
|
|
@ -426,6 +426,23 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateUsingDifferentialProfile() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/dstu3/profile-differential-patient-dstu3.json");
|
||||
myStructureDefinitionDao.create(sd);
|
||||
|
||||
Patient p = new Patient();
|
||||
p.getMeta().addProfile("http://hl7.org/fhir/StructureDefinition/MyPatient421");
|
||||
p.setActive(true);
|
||||
|
||||
String raw = myFhirCtx.newJsonParser().encodeResourceToString(p);
|
||||
MethodOutcome outcome = myPatientDao.validate(p, null, raw, EncodingEnum.JSON, null, null, mySrd);
|
||||
|
||||
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome());
|
||||
ourLog.info("OO: {}", encoded);
|
||||
assertThat(encoded, containsString("No issues detected"));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
|
|
|
@ -255,7 +255,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
|||
protected IStaleSearchDeletingSvc myStaleSearchDeletingSvc;
|
||||
@Autowired
|
||||
@Qualifier("myStructureDefinitionDaoR4")
|
||||
protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
|
||||
protected IFhirResourceDaoStructureDefinition<StructureDefinition> myStructureDefinitionDao;
|
||||
@Autowired
|
||||
@Qualifier("myConsentDaoR4")
|
||||
protected IFhirResourceDao<Consent> myConsentDao;
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@SuppressWarnings({"unchecked", "deprecation"})
|
||||
public class FhirResourceDaoR4StructureDefinitionTest extends BaseJpaR4Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4StructureDefinitionTest.class);
|
||||
|
||||
@After
|
||||
public final void after() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateSnapshot() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/r4/profile-differential-patient-r4.json");
|
||||
assertEquals(0, sd.getSnapshot().getElement().size());
|
||||
|
||||
StructureDefinition output = myStructureDefinitionDao.generateSnapshot(sd, "http://foo", "THE BEST PROFILE");
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(51, output.getSnapshot().getElement().size());
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
}
|
|
@ -446,6 +446,26 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateUsingDifferentialProfile() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/r4/profile-differential-patient-r4.json");
|
||||
myStructureDefinitionDao.create(sd);
|
||||
|
||||
Patient p = new Patient();
|
||||
p.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
p.getText().getDiv().setValue("<div>hello</div>");
|
||||
p.getMeta().addProfile("http://example.com/fhir/StructureDefinition/patient-1a-extensions");
|
||||
p.setActive(true);
|
||||
|
||||
String raw = myFhirCtx.newJsonParser().encodeResourceToString(p);
|
||||
MethodOutcome outcome = myPatientDao.validate(p, null, raw, EncodingEnum.JSON, null, null, mySrd);
|
||||
|
||||
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome());
|
||||
ourLog.info("OO: {}", encoded);
|
||||
assertThat(encoded, containsString("No issues detected"));
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package ca.uhn.fhir.jpa.provider.dstu3;
|
||||
|
||||
import ca.uhn.fhir.jpa.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ResourceProviderDstu3StructureDefinitionTest extends BaseResourceProviderDstu3Test {
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapshotWithResourceParameter() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/dstu3/profile-differential-patient-dstu3.json");
|
||||
|
||||
StructureDefinition response = ourClient
|
||||
.operation()
|
||||
.onType(StructureDefinition.class)
|
||||
.named(JpaConstants.OPERATION_SNAPSHOT)
|
||||
.withParameter(Parameters.class, "definition", sd)
|
||||
.returnResourceType(StructureDefinition.class)
|
||||
.execute();
|
||||
assertEquals(54, response.getSnapshot().getElement().size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSnapshotWithId() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/dstu3/profile-differential-patient-dstu3.json");
|
||||
IIdType id = ourClient.create().resource(sd).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
StructureDefinition response = ourClient
|
||||
.operation()
|
||||
.onInstance(id)
|
||||
.named(JpaConstants.OPERATION_SNAPSHOT)
|
||||
.withNoParameters(Parameters.class)
|
||||
.returnResourceType(StructureDefinition.class)
|
||||
.execute();
|
||||
assertEquals(54, response.getSnapshot().getElement().size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSnapshotWithUrl() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/dstu3/profile-differential-patient-dstu3.json");
|
||||
IIdType id = ourClient.create().resource(sd).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
StructureDefinition response = ourClient
|
||||
.operation()
|
||||
.onType(StructureDefinition.class)
|
||||
.named(JpaConstants.OPERATION_SNAPSHOT)
|
||||
.withParameter(Parameters.class, "url", new StringType("http://hl7.org/fhir/StructureDefinition/MyPatient421"))
|
||||
.returnResourceType(StructureDefinition.class)
|
||||
.execute();
|
||||
assertEquals(54, response.getSnapshot().getElement().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapshotWithUrlAndId() {
|
||||
try {
|
||||
ourClient
|
||||
.operation()
|
||||
.onInstance(new IdType("StructureDefinition/123"))
|
||||
.named(JpaConstants.OPERATION_SNAPSHOT)
|
||||
.withParameter(Parameters.class, "url", new StringType("http://hl7.org/fhir/StructureDefinition/MyPatient421"))
|
||||
.returnResourceType(StructureDefinition.class)
|
||||
.execute();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: Must supply either an ID or a StructureDefinition or a URL (but not more than one of these things)", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapshotWithInvalidUrl() {
|
||||
try {
|
||||
ourClient
|
||||
.operation()
|
||||
.onType(StructureDefinition.class)
|
||||
.named(JpaConstants.OPERATION_SNAPSHOT)
|
||||
.withParameter(Parameters.class, "url", new StringType("http://hl7.org/fhir/StructureDefinition/FOO"))
|
||||
.returnResourceType(StructureDefinition.class)
|
||||
.execute();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertEquals("HTTP 404 Not Found: No StructureDefiniton found with url = 'http://hl7.org/fhir/StructureDefinition/FOO'", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ResourceProviderR4StructureDefinitionTest extends BaseResourceProviderR4Test {
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapshotWithResourceParameter() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/r4/profile-differential-patient-r4.json");
|
||||
|
||||
StructureDefinition response = ourClient
|
||||
.operation()
|
||||
.onType(StructureDefinition.class)
|
||||
.named(JpaConstants.OPERATION_SNAPSHOT)
|
||||
.withParameter(Parameters.class, "definition", sd)
|
||||
.returnResourceType(StructureDefinition.class)
|
||||
.execute();
|
||||
assertEquals(51, response.getSnapshot().getElement().size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSnapshotWithId() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/r4/profile-differential-patient-r4.json");
|
||||
IIdType id = ourClient.create().resource(sd).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
StructureDefinition response = ourClient
|
||||
.operation()
|
||||
.onInstance(id)
|
||||
.named(JpaConstants.OPERATION_SNAPSHOT)
|
||||
.withNoParameters(Parameters.class)
|
||||
.returnResourceType(StructureDefinition.class)
|
||||
.execute();
|
||||
assertEquals(51, response.getSnapshot().getElement().size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSnapshotWithUrl() throws IOException {
|
||||
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/r4/profile-differential-patient-r4.json");
|
||||
IIdType id = ourClient.create().resource(sd).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
StructureDefinition response = ourClient
|
||||
.operation()
|
||||
.onType(StructureDefinition.class)
|
||||
.named(JpaConstants.OPERATION_SNAPSHOT)
|
||||
.withParameter(Parameters.class, "url", new StringType("http://example.com/fhir/StructureDefinition/patient-1a-extensions"))
|
||||
.returnResourceType(StructureDefinition.class)
|
||||
.execute();
|
||||
assertEquals(51, response.getSnapshot().getElement().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapshotWithUrlAndId() {
|
||||
try {
|
||||
ourClient
|
||||
.operation()
|
||||
.onInstance(new IdType("StructureDefinition/123"))
|
||||
.named(JpaConstants.OPERATION_SNAPSHOT)
|
||||
.withParameter(Parameters.class, "url", new StringType("http://example.com/fhir/StructureDefinition/patient-1a-extensions"))
|
||||
.returnResourceType(StructureDefinition.class)
|
||||
.execute();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: Must supply either an ID or a StructureDefinition or a URL (but not more than one of these things)", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapshotWithInvalidUrl() {
|
||||
try {
|
||||
ourClient
|
||||
.operation()
|
||||
.onType(StructureDefinition.class)
|
||||
.named(JpaConstants.OPERATION_SNAPSHOT)
|
||||
.withParameter(Parameters.class, "url", new StringType("http://hl7.org/fhir/StructureDefinition/FOO"))
|
||||
.returnResourceType(StructureDefinition.class)
|
||||
.execute();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertEquals("HTTP 404 Not Found: No StructureDefiniton found with url = 'http://hl7.org/fhir/StructureDefinition/FOO'", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"resourceType":"StructureDefinition",
|
||||
"meta":{
|
||||
"lastUpdated":"2017-08-26T18:27:24.959+01:00"
|
||||
},
|
||||
"url":"http://hl7.org/fhir/StructureDefinition/MyPatient421",
|
||||
"name":"MyPatient",
|
||||
"status":"draft",
|
||||
"date":"2017-08-25T14:34:21.08+01:00",
|
||||
"fhirVersion":"3.0.1",
|
||||
"kind":"resource",
|
||||
"abstract":false,
|
||||
"type":"Patient",
|
||||
"baseDefinition":"http://hl7.org/fhir/StructureDefinition/Patient",
|
||||
"derivation":"constraint",
|
||||
"differential":{
|
||||
"element": [
|
||||
{
|
||||
"id":"Patient.id",
|
||||
"path":"Patient.id",
|
||||
"definition":"The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes."
|
||||
},
|
||||
{
|
||||
"id":"Patient.extension",
|
||||
"path":"Patient.extension",
|
||||
"slicing":{
|
||||
"discriminator": [
|
||||
{
|
||||
"type":"value",
|
||||
"path":"url"
|
||||
}
|
||||
],
|
||||
"rules":"open"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":"Patient.extension:us-core-race",
|
||||
"path":"Patient.extension",
|
||||
"sliceName":"us-core-race",
|
||||
"type": [
|
||||
{
|
||||
"code":"Extension",
|
||||
"profile":"http://hl7.org/fhir/StructureDefinition/us-core-race"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":"Patient.extension:us-core-religion",
|
||||
"path":"Patient.extension",
|
||||
"sliceName":"us-core-religion",
|
||||
"short":"Optional Extensions Element",
|
||||
"definition":"Optional Extension Element - found in all resources.",
|
||||
"min":"0",
|
||||
"max":"*",
|
||||
"type": [
|
||||
{
|
||||
"code":"Extension",
|
||||
"profile":"http://hl7.org/fhir/StructureDefinition/us-core-religion"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"resourceType":"StructureDefinition",
|
||||
"url":"http://example.com/fhir/StructureDefinition/patient-1a-extensions",
|
||||
"version":"0.1",
|
||||
"name":"PatientWithExtensions",
|
||||
"title":"Patient Profile 1 - 1a Extensions",
|
||||
"status":"active",
|
||||
"experimental":false,
|
||||
"date":"2019",
|
||||
"publisher":"Chris Grenz",
|
||||
"contact": [
|
||||
{
|
||||
"telecom": [
|
||||
{
|
||||
"system":"email",
|
||||
"value":"chris.grenz@thoughtworks.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"description":"Profile of Patient with extensions",
|
||||
"copyright":"(c)2019 Chris Grenz into public domain",
|
||||
"fhirVersion":"4.0.0",
|
||||
"kind":"resource",
|
||||
"abstract":false,
|
||||
"type":"Patient",
|
||||
"baseDefinition":"http://hl7.org/fhir/StructureDefinition/Patient",
|
||||
"derivation":"constraint",
|
||||
"differential":{
|
||||
"element": [
|
||||
{
|
||||
"id":"Patient",
|
||||
"path":"Patient"
|
||||
},
|
||||
{
|
||||
"id":"Patient.extension",
|
||||
"path":"Patient.extension"
|
||||
},
|
||||
{
|
||||
"id":"Patient.extension:doNotCall",
|
||||
"path":"Patient.extension",
|
||||
"sliceName":"doNotCall",
|
||||
"type": [
|
||||
{
|
||||
"code":"Extension",
|
||||
"profile": [
|
||||
"http://example.com/fhir/StructureDefinition/patient-donotcall"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":"Patient.extension:legalCase",
|
||||
"path":"Patient.extension",
|
||||
"sliceName":"legalCase",
|
||||
"type": [
|
||||
{
|
||||
"code":"Extension",
|
||||
"profile": [
|
||||
"http://example.com/fhir/StructureDefinition/patient-legalcase"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id":"Patient.extension:legalCase.value[x]:valueBoolean.extension:leadCounsel",
|
||||
"path":"Patient.extension.valueBoolean.extension",
|
||||
"sliceName":"leadCounsel",
|
||||
"type": [
|
||||
{
|
||||
"code":"Extension",
|
||||
"profile": [
|
||||
"http://example.com/fhir/StructureDefinition/patient-legalcase-leadcounsel"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -293,4 +293,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -99,7 +99,16 @@ public interface IValidationSupport
|
|||
@Override
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
|
||||
|
||||
class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, IssueSeverity> {
|
||||
/**
|
||||
* Generate a snapshot from the given differential profile.
|
||||
*
|
||||
* @param theInput
|
||||
* @param theUrl
|
||||
* @return Returns null if this module does not know how to handle this request
|
||||
*/
|
||||
StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName);
|
||||
|
||||
class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, IssueSeverity> {
|
||||
|
||||
public CodeValidationResult(ConceptDefinitionComponent theNext) {
|
||||
super(theNext);
|
||||
|
|
|
@ -174,6 +174,11 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
return cs != null && cs.getContent() != CodeSystemContentMode.NOTPRESENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void loadCodeSystems(FhirContext theContext, Map<String, CodeSystem> theCodeSystems, Map<String, ValueSet> theValueSets, String theClasspath) {
|
||||
ourLog.info("Loading CodeSystem/ValueSet from classpath: {}", theClasspath);
|
||||
InputStream inputStream = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
|
||||
|
|
|
@ -14,104 +14,104 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
|||
import java.util.List;
|
||||
|
||||
public interface IValidationSupport
|
||||
extends ca.uhn.fhir.context.support.IContextValidationSupport<ConceptSetComponent, ValueSetExpander.ValueSetExpansionOutcome, StructureDefinition, CodeSystem, ConceptDefinitionComponent, IssueSeverity> {
|
||||
extends ca.uhn.fhir.context.support.IContextValidationSupport<ConceptSetComponent, ValueSetExpander.ValueSetExpansionOutcome, StructureDefinition, CodeSystem, ConceptDefinitionComponent, IssueSeverity> {
|
||||
|
||||
/**
|
||||
* Expands the given portion of a ValueSet
|
||||
*
|
||||
* @param theInclude
|
||||
* The portion to include
|
||||
* @return The expansion
|
||||
*/
|
||||
@Override
|
||||
ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude);
|
||||
/**
|
||||
* Expands the given portion of a ValueSet
|
||||
*
|
||||
* @param theInclude The portion to include
|
||||
* @return The expansion
|
||||
*/
|
||||
@Override
|
||||
ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude);
|
||||
|
||||
/**
|
||||
* Load and return all possible structure definitions
|
||||
*/
|
||||
@Override
|
||||
List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext);
|
||||
/**
|
||||
* Load and return all possible structure definitions
|
||||
*/
|
||||
@Override
|
||||
List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext);
|
||||
|
||||
/**
|
||||
* Fetch a code system by Uri
|
||||
*
|
||||
* @param uri
|
||||
* Canonical Uri of the code system
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
@Override
|
||||
CodeSystem fetchCodeSystem(FhirContext theContext, String uri);
|
||||
/**
|
||||
* Fetch a code system by Uri
|
||||
*
|
||||
* @param uri Canonical Uri of the code system
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
@Override
|
||||
CodeSystem fetchCodeSystem(FhirContext theContext, String uri);
|
||||
|
||||
/**
|
||||
* Fetch a valueset by Uri
|
||||
*
|
||||
* @param uri
|
||||
* Canonical Uri of the ValueSet
|
||||
* @param uri Canonical Uri of the ValueSet
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
ValueSet fetchValueSet(FhirContext theContext, String uri);
|
||||
|
||||
/**
|
||||
* Loads a resource needed by the validation (a StructureDefinition, or a
|
||||
* ValueSet)
|
||||
*
|
||||
* @param theContext
|
||||
* The HAPI FHIR Context object current in use by the validator
|
||||
* @param theClass
|
||||
* The type of the resource to load
|
||||
* @param theUri
|
||||
* The resource URI
|
||||
* @return Returns the resource, or <code>null</code> if no resource with the
|
||||
* given URI can be found
|
||||
*/
|
||||
@Override
|
||||
<T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri);
|
||||
/**
|
||||
* Loads a resource needed by the validation (a StructureDefinition, or a
|
||||
* ValueSet)
|
||||
*
|
||||
* @param theContext The HAPI FHIR Context object current in use by the validator
|
||||
* @param theClass The type of the resource to load
|
||||
* @param theUri The resource URI
|
||||
* @return Returns the resource, or <code>null</code> if no resource with the
|
||||
* given URI can be found
|
||||
*/
|
||||
@Override
|
||||
<T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri);
|
||||
|
||||
@Override
|
||||
StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl);
|
||||
@Override
|
||||
StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl);
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if codes in the given code system can be expanded
|
||||
* or validated
|
||||
*
|
||||
* @param theSystem
|
||||
* The URI for the code system, e.g. <code>"http://loinc.org"</code>
|
||||
* @return Returns <code>true</code> if codes in the given code system can be
|
||||
* validated
|
||||
*/
|
||||
@Override
|
||||
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
|
||||
/**
|
||||
* Returns <code>true</code> if codes in the given code system can be expanded
|
||||
* or validated
|
||||
*
|
||||
* @param theSystem The URI for the code system, e.g. <code>"http://loinc.org"</code>
|
||||
* @return Returns <code>true</code> if codes in the given code system can be
|
||||
* validated
|
||||
*/
|
||||
@Override
|
||||
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
|
||||
|
||||
/**
|
||||
* Validates that the given code exists and if possible returns a display
|
||||
* name. This method is called to check codes which are found in "example"
|
||||
* binding fields (e.g. <code>Observation.code</code> in the default profile.
|
||||
*
|
||||
* @param theCodeSystem
|
||||
* The code system, e.g. "<code>http://loinc.org</code>"
|
||||
* @param theCode
|
||||
* The code, e.g. "<code>1234-5</code>"
|
||||
* @param theDisplay
|
||||
* The display name, if it should also be validated
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
@Override
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
|
||||
/**
|
||||
* Generate a snapshot from the given differential profile.
|
||||
*
|
||||
* @param theInput
|
||||
* @param theUrl
|
||||
* @param theProfileName
|
||||
* @return Returns null if this module does not know how to handle this request
|
||||
*/
|
||||
StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName);
|
||||
|
||||
class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, IssueSeverity> {
|
||||
/**
|
||||
* Validates that the given code exists and if possible returns a display
|
||||
* name. This method is called to check codes which are found in "example"
|
||||
* binding fields (e.g. <code>Observation.code</code> in the default profile.
|
||||
*
|
||||
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
|
||||
* @param theCode The code, e.g. "<code>1234-5</code>"
|
||||
* @param theDisplay The display name, if it should also be validated
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
@Override
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
|
||||
|
||||
public CodeValidationResult(ConceptDefinitionComponent theNext) {
|
||||
super(theNext);
|
||||
}
|
||||
class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, IssueSeverity> {
|
||||
|
||||
public CodeValidationResult(IssueSeverity theSeverity, String theMessage) {
|
||||
super(theSeverity, theMessage);
|
||||
}
|
||||
public CodeValidationResult(ConceptDefinitionComponent theNext) {
|
||||
super(theNext);
|
||||
}
|
||||
|
||||
public CodeValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) {
|
||||
super(severity, message, definition);
|
||||
}
|
||||
public CodeValidationResult(IssueSeverity theSeverity, String theMessage) {
|
||||
super(theSeverity, theMessage);
|
||||
}
|
||||
|
||||
}
|
||||
public CodeValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) {
|
||||
super(severity, message, definition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -69,4 +69,9 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
return myWrap.generateSnapshot(theInput, theUrl, theName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
|
|||
|
||||
@Override
|
||||
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
|
||||
return new ArrayList<StructureDefinition>(myStructureDefinitions.values());
|
||||
return new ArrayList<>(myStructureDefinitions.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -185,4 +185,9 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
package org.hl7.fhir.dstu3.hapi.validation;
|
||||
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
|
||||
import org.hl7.fhir.dstu3.context.IWorkerContext;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.ElementDefinition;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Simple validation support module that handles profile snapshot generation. This is
|
||||
* separate from other funcrtions since it needs a link to a validation support
|
||||
* module itself, and it is useful to be able to pass a chain in.
|
||||
*/
|
||||
public class SnapshotGeneratingValidationSupport implements IValidationSupport {
|
||||
private final FhirContext myCtx;
|
||||
private final IValidationSupport myValidationSupport;
|
||||
|
||||
public SnapshotGeneratingValidationSupport(FhirContext theCtx, IValidationSupport theValidationSupport) {
|
||||
Validate.notNull(theCtx);
|
||||
Validate.notNull(theValidationSupport);
|
||||
myCtx = theCtx;
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet.ValueSetExpansionComponent expandValueSet(FhirContext theContext, ValueSet.ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeSystem fetchCodeSystem(FhirContext theContext, String uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchValueSet(FhirContext theContext, String uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
|
||||
IWorkerContext context = new HapiWorkerContext(myCtx, myValidationSupport);
|
||||
ProfileUtilities.ProfileKnowledgeProvider profileKnowledgeProvider = new MyProfileKnowledgeWorker();
|
||||
ArrayList<ValidationMessage> messages = new ArrayList<>();
|
||||
|
||||
StructureDefinition base = myValidationSupport.fetchStructureDefinition(myCtx, theInput.getBaseDefinition());
|
||||
if (base == null) {
|
||||
throw new PreconditionFailedException("Unknown base definition: " + theInput.getBaseDefinition());
|
||||
}
|
||||
|
||||
new ProfileUtilities(context, messages, profileKnowledgeProvider).generateSnapshot(base, theInput, theUrl, theProfileName);
|
||||
|
||||
return theInput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private class MyProfileKnowledgeWorker implements ProfileUtilities.ProfileKnowledgeProvider {
|
||||
@Override
|
||||
public boolean isDatatype(String typeSimple) {
|
||||
BaseRuntimeElementDefinition<?> def = myCtx.getElementDefinition(typeSimple);
|
||||
Validate.notNull(typeSimple);
|
||||
return (def instanceof RuntimePrimitiveDatatypeDefinition) || (def instanceof RuntimeCompositeDatatypeDefinition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResource(String typeSimple) {
|
||||
BaseRuntimeElementDefinition<?> def = myCtx.getElementDefinition(typeSimple);
|
||||
Validate.notNull(typeSimple);
|
||||
return def instanceof RuntimeResourceDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLinkFor(String typeSimple) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLinkFor(String corePath, String typeSimple) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingResolution resolveBinding(StructureDefinition def, ElementDefinition.ElementDefinitionBindingComponent binding, String path) throws FHIRException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLinkForProfile(StructureDefinition profile, String url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prependLinks() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -81,11 +81,14 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
@Override
|
||||
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
|
||||
ArrayList<StructureDefinition> retVal = new ArrayList<StructureDefinition>();
|
||||
Set<String> urls = new HashSet<String>();
|
||||
Set<String> urls = new HashSet<>();
|
||||
for (IValidationSupport nextSupport : myChain) {
|
||||
for (StructureDefinition next : nextSupport.fetchAllStructureDefinitions(theContext)) {
|
||||
if (isBlank(next.getUrl()) || urls.add(next.getUrl())) {
|
||||
retVal.add(next);
|
||||
List<StructureDefinition> list = nextSupport.fetchAllStructureDefinitions(theContext);
|
||||
if (list != null) {
|
||||
for (StructureDefinition next : list) {
|
||||
if (isBlank(next.getUrl()) || urls.add(next.getUrl())) {
|
||||
retVal.add(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,5 +170,16 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
|
||||
StructureDefinition outcome = null;
|
||||
for (org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport next : myChain) {
|
||||
outcome = next.generateSnapshot(theInput, theUrl, theProfileName);
|
||||
if (outcome != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return outcome;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,446 +0,0 @@
|
|||
package org.hl7.fhir.dstu3.validation;
|
||||
|
||||
/*
|
||||
Copyright (c) 2011+, HL7, Inc
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of HL7 nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||
|
||||
public class BaseValidator {
|
||||
|
||||
protected Source source;
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean fail(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg) {
|
||||
if (!thePass) {
|
||||
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.FATAL));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) {
|
||||
if (!thePass) {
|
||||
String path = toPath(pathParts);
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.FATAL));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
String path = toPath(pathParts);
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, formatMessage(theMessage, theMessageArguments), IssueSeverity.FATAL));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#FATAL} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean fail(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
|
||||
if (!thePass) {
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.FATAL));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
|
||||
private String formatMessage(String theMessage, Object... theMessageArguments) {
|
||||
String message;
|
||||
if (theMessageArguments != null && theMessageArguments.length > 0) {
|
||||
message = MessageFormat.format(theMessage, theMessageArguments);
|
||||
} else {
|
||||
message = theMessage;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
protected boolean grammarWord(String w) {
|
||||
return w.equals("and") || w.equals("or") || w.equals("a") || w.equals("the") || w.equals("for") || w.equals("this") || w.equals("that") || w.equals("of");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean hint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg) {
|
||||
if (!thePass) {
|
||||
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.INFORMATION));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean hint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
String message = formatMessage(theMessage, theMessageArguments);
|
||||
errors.add(new ValidationMessage(source, type, line, col, path, message, IssueSeverity.INFORMATION));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean hint(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
String path = toPath(pathParts);
|
||||
String message = formatMessage(theMessage, theMessageArguments);
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.INFORMATION));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#INFORMATION} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean hint(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
|
||||
if (!thePass) {
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.INFORMATION));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean rule(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
String message = formatMessage(theMessage, theMessageArguments);
|
||||
errors.add(new ValidationMessage(source, type, line, col, path, message, IssueSeverity.ERROR));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean rule(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) {
|
||||
if (!thePass) {
|
||||
String path = toPath(pathParts);
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean rule(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
String path = toPath(pathParts);
|
||||
String message = formatMessage(theMessage, theMessageArguments);
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.ERROR));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
|
||||
if (!thePass) {
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
static public boolean rule(List<ValidationMessage> errors, Source source, IssueType type, String path, boolean thePass, String msg) {
|
||||
if (!thePass) {
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.ERROR));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#ERROR} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
|
||||
if (!thePass) {
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.ERROR));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
protected String splitByCamelCase(String s) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (Character.isUpperCase(c) && !(i == 0 || Character.isUpperCase(s.charAt(i-1))))
|
||||
b.append(' ');
|
||||
b.append(c);
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
protected String stripPunctuation(String s, boolean numbers) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (char c : s.toCharArray()) {
|
||||
int t = Character.getType(c);
|
||||
if (t == Character.UPPERCASE_LETTER || t == Character.LOWERCASE_LETTER || t == Character.TITLECASE_LETTER || t == Character.MODIFIER_LETTER || t == Character.OTHER_LETTER || (t == Character.DECIMAL_DIGIT_NUMBER && numbers) || (t == Character.LETTER_NUMBER && numbers) || c == ' ')
|
||||
b.append(c);
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private String toPath(List<String> pathParts) {
|
||||
if (pathParts == null || pathParts.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return "//" + StringUtils.join(pathParts, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean warning(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
msg = formatMessage(msg, theMessageArguments);
|
||||
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.WARNING));
|
||||
}
|
||||
return thePass;
|
||||
|
||||
}
|
||||
|
||||
protected boolean warningOrError(boolean isError, List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
msg = formatMessage(msg, theMessageArguments);
|
||||
errors.add(new ValidationMessage(source, type, line, col, path, msg, isError ? IssueSeverity.ERROR : IssueSeverity.WARNING));
|
||||
}
|
||||
return thePass;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean warning(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
String path = toPath(pathParts);
|
||||
String message = formatMessage(theMessage, theMessageArguments);
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.WARNING));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
|
||||
if (!thePass) {
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.WARNING));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
|
||||
if (!thePass) {
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
msg = formatMessage(msg, theMessageArguments);
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.WARNING));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
//---------
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
msg = formatMessage(msg, theMessageArguments);
|
||||
errors.add(new ValidationMessage(source, type, line, col, path, msg, IssueSeverity.INFORMATION));
|
||||
}
|
||||
return thePass;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String theMessage, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
String path = toPath(pathParts);
|
||||
String message = formatMessage(theMessage, theMessageArguments);
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, message, IssueSeverity.INFORMATION));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
|
||||
if (!thePass) {
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, IssueSeverity.INFORMATION));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
|
||||
if (!thePass) {
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.INFORMATION));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
|
||||
*
|
||||
* @param thePass
|
||||
* Set this parameter to <code>false</code> if the validation does not pass
|
||||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
protected boolean suppressedwarning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
|
||||
if (!thePass) {
|
||||
msg = formatMessage(msg, theMessageArguments);
|
||||
errors.add(new ValidationMessage(source, type, -1, -1, path, msg, html, IssueSeverity.INFORMATION));
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package org.hl7.fhir.dstu3.validation;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.exceptions.DefinitionException;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This interface is used to provide extension location services for the validator
|
||||
*
|
||||
* when it encounters an extension, it asks this server to locate it, or tell it
|
||||
* whether to ignore the extension, or mark it as invalid
|
||||
*
|
||||
* @author Grahame
|
||||
*
|
||||
*/
|
||||
public interface ExtensionLocatorService {
|
||||
|
||||
public enum Status {
|
||||
Located, NotAllowed, Unknown
|
||||
}
|
||||
|
||||
public class ExtensionLocationResponse {
|
||||
private Status status;
|
||||
private StructureDefinition definition;
|
||||
private String message;
|
||||
private String url;
|
||||
|
||||
public ExtensionLocationResponse(String url, Status status, StructureDefinition definition, String message) {
|
||||
super();
|
||||
this.url = url;
|
||||
this.status = status;
|
||||
this.definition = definition;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public StructureDefinition getDefinition() {
|
||||
return definition;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine is used when walking into a complex extension.
|
||||
* the non-tail part of the relative URL matches the end of the
|
||||
* exiting URL
|
||||
* @param url - the relative URL
|
||||
* @return
|
||||
* @throws DefinitionException
|
||||
* @
|
||||
*/
|
||||
public ExtensionLocationResponse clone(String url) throws DefinitionException {
|
||||
if (!this.url.endsWith(url.substring(0, url.lastIndexOf("."))))
|
||||
throw new DefinitionException("the relative URL "+url+" cannot be used in the context "+this.url);
|
||||
|
||||
return new ExtensionLocationResponse(this.url+"."+url.substring(url.lastIndexOf(".")+1), status, definition, message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ExtensionLocationResponse locateExtension(String uri);
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package org.hl7.fhir.dstu3.validation;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GraphicalValidator {
|
||||
|
||||
public ValidatorFrame frame;
|
||||
|
||||
/**
|
||||
* Launch the application.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
GraphicalValidator window = new GraphicalValidator();
|
||||
window.frame.setVisible(true);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the application.
|
||||
* @throws IOException
|
||||
*/
|
||||
public GraphicalValidator() throws IOException {
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the contents of the frame.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void initialize() throws IOException {
|
||||
frame = new ValidatorFrame();
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,72 +0,0 @@
|
|||
package org.hl7.fhir.dstu3.validation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.dstu3.context.IWorkerContext;
|
||||
import org.hl7.fhir.dstu3.model.ElementDefinition;
|
||||
import org.hl7.fhir.dstu3.model.ElementDefinition.ElementDefinitionConstraintComponent;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.dstu3.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
|
||||
public class ProfileValidator extends BaseValidator {
|
||||
|
||||
IWorkerContext context;
|
||||
|
||||
public void setContext(IWorkerContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
protected boolean rule(List<ValidationMessage> errors, IssueType type, String path, boolean b, String msg) {
|
||||
String rn = path.contains(".") ? path.substring(0, path.indexOf(".")) : path;
|
||||
return super.rule(errors, type, path, b, msg, "<a href=\""+(rn.toLowerCase())+".html\">"+rn+"</a>: "+Utilities.escapeXml(msg));
|
||||
}
|
||||
|
||||
public List<ValidationMessage> validate(StructureDefinition profile, boolean forBuild) {
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
|
||||
// first check: extensions must exist
|
||||
for (ElementDefinition ec : profile.getDifferential().getElement())
|
||||
checkExtensions(profile, errors, "differential", ec);
|
||||
rule(errors, IssueType.STRUCTURE, profile.getId(), profile.hasSnapshot(), "missing Snapshot at "+profile.getName()+"."+profile.getName());
|
||||
for (ElementDefinition ec : profile.getSnapshot().getElement())
|
||||
checkExtensions(profile, errors, "snapshot", ec);
|
||||
|
||||
if (rule(errors, IssueType.STRUCTURE, profile.getId(), profile.hasSnapshot(), "A snapshot is required")) {
|
||||
for (ElementDefinition ed : profile.getSnapshot().getElement()) {
|
||||
checkExtensions(profile, errors, "snapshot", ed);
|
||||
for (ElementDefinitionConstraintComponent inv : ed.getConstraint()) {
|
||||
if (forBuild) {
|
||||
if (!inExemptList(inv.getKey())) {
|
||||
if (rule(errors, IssueType.BUSINESSRULE, profile.getId()+"::"+ed.getPath()+"::"+inv.getKey(), inv.hasExpression(), "The invariant has no FHIR Path expression ("+inv.getXpath()+")")) {
|
||||
try {
|
||||
new FHIRPathEngine(context).check(null, profile.getType(), ed.getPath(), inv.getExpression()); // , inv.hasXpath() && inv.getXpath().startsWith("@value")
|
||||
} catch (Exception e) {
|
||||
// rule(errors, IssueType.STRUCTURE, profile.getId()+"::"+ed.getPath()+"::"+inv.getId(), exprExt != null, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
// these are special cases
|
||||
private boolean inExemptList(String key) {
|
||||
return key.startsWith("txt-");
|
||||
}
|
||||
|
||||
private void checkExtensions(StructureDefinition profile, List<ValidationMessage> errors, String kind, ElementDefinition ec) {
|
||||
if (!ec.getType().isEmpty() && "Extension".equals(ec.getType().get(0).getCode()) && ec.getType().get(0).hasProfile()) {
|
||||
String url = ec.getType().get(0).getProfile();
|
||||
StructureDefinition defn = context.fetchResource(StructureDefinition.class, url);
|
||||
rule(errors, IssueType.BUSINESSRULE, profile.getId(), defn != null, "Unable to find Extension '"+url+"' referenced at "+profile.getUrl()+" "+kind+" "+ec.getPath()+" ("+ec.getSliceName()+")");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,393 +0,0 @@
|
|||
package org.hl7.fhir.dstu3.validation;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.context.IWorkerContext;
|
||||
import org.hl7.fhir.dstu3.model.Attachment;
|
||||
import org.hl7.fhir.dstu3.model.BooleanType;
|
||||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.DateTimeType;
|
||||
import org.hl7.fhir.dstu3.model.DateType;
|
||||
import org.hl7.fhir.dstu3.model.DecimalType;
|
||||
import org.hl7.fhir.dstu3.model.InstantType;
|
||||
import org.hl7.fhir.dstu3.model.IntegerType;
|
||||
import org.hl7.fhir.dstu3.model.Quantity;
|
||||
import org.hl7.fhir.dstu3.model.Questionnaire;
|
||||
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent;
|
||||
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType;
|
||||
import org.hl7.fhir.dstu3.model.QuestionnaireResponse;
|
||||
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent;
|
||||
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
|
||||
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus;
|
||||
import org.hl7.fhir.dstu3.model.Reference;
|
||||
import org.hl7.fhir.dstu3.model.Resource;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.dstu3.model.TimeType;
|
||||
import org.hl7.fhir.dstu3.model.Type;
|
||||
import org.hl7.fhir.dstu3.model.UriType;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
|
||||
/**
|
||||
* Validates that an instance of {@link QuestionnaireResponse} is valid against the {@link Questionnaire} that it claims
|
||||
* to conform to.
|
||||
*
|
||||
* @author James Agnew
|
||||
*/
|
||||
public class QuestionnaireResponseValidator extends BaseValidator {
|
||||
|
||||
/*
|
||||
* ***************************************************************** Note to anyone working on this class -
|
||||
*
|
||||
* This class has unit tests which run within the HAPI project build. Please sync any changes here to HAPI and ensure
|
||||
* that unit tests are run. ****************************************************************
|
||||
*/
|
||||
|
||||
private IWorkerContext myWorkerCtx;
|
||||
|
||||
public QuestionnaireResponseValidator(IWorkerContext theWorkerCtx) {
|
||||
this.myWorkerCtx = theWorkerCtx;
|
||||
}
|
||||
|
||||
private Set<Class<? extends Type>> allowedTypes(Class<? extends Type> theClass0) {
|
||||
return allowedTypes(theClass0, null);
|
||||
}
|
||||
|
||||
private Set<Class<? extends Type>> allowedTypes(Class<? extends Type> theClass0, Class<? extends Type> theClass1) {
|
||||
HashSet<Class<? extends Type>> retVal = new HashSet<Class<? extends Type>>();
|
||||
retVal.add(theClass0);
|
||||
if (theClass1 != null) {
|
||||
retVal.add(theClass1);
|
||||
}
|
||||
return Collections.unmodifiableSet(retVal);
|
||||
}
|
||||
|
||||
private List<QuestionnaireResponseItemComponent> findResponsesByLinkId(List<QuestionnaireResponseItemComponent> theItem, String theLinkId) {
|
||||
Validate.notBlank(theLinkId, "theLinkId must not be blank");
|
||||
|
||||
ArrayList<QuestionnaireResponseItemComponent> retVal = new ArrayList<QuestionnaireResponse.QuestionnaireResponseItemComponent>();
|
||||
for (QuestionnaireResponseItemComponent next : theItem) {
|
||||
if (theLinkId.equals(next.getLinkId())) {
|
||||
retVal.add(next);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public void validate(List<ValidationMessage> theErrors, QuestionnaireResponse theAnswers) {
|
||||
LinkedList<String> pathStack = new LinkedList<String>();
|
||||
pathStack.add("QuestionnaireResponse");
|
||||
pathStack.add(QuestionnaireResponse.SP_QUESTIONNAIRE);
|
||||
|
||||
if (!fail(theErrors, IssueType.INVALID, pathStack, theAnswers.hasQuestionnaire(), "QuestionnaireResponse does not specity which questionnaire it is providing answers to")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference questionnaireRef = theAnswers.getQuestionnaire();
|
||||
Questionnaire questionnaire = getQuestionnaire(theAnswers, questionnaireRef);
|
||||
if (!fail(theErrors, IssueType.INVALID, pathStack, questionnaire != null, "Questionnaire {0} is not found in the WorkerContext", theAnswers.getQuestionnaire().getReference())) {
|
||||
return;
|
||||
}
|
||||
|
||||
QuestionnaireResponseStatus status = theAnswers.getStatus();
|
||||
boolean validateRequired = false;
|
||||
if (status == QuestionnaireResponseStatus.COMPLETED || status == QuestionnaireResponseStatus.AMENDED) {
|
||||
validateRequired = true;
|
||||
}
|
||||
|
||||
pathStack.removeLast();
|
||||
// pathStack.add("group(0)");
|
||||
validateItems(theErrors, questionnaire.getItem(), theAnswers.getItem(), pathStack, theAnswers, validateRequired);
|
||||
}
|
||||
|
||||
private Questionnaire getQuestionnaire(QuestionnaireResponse theAnswers, Reference theQuestionnaireRef) {
|
||||
Questionnaire retVal;
|
||||
String value = theQuestionnaireRef.getReferenceElement().getValue();
|
||||
if (theQuestionnaireRef.getReferenceElement().isLocal()) {
|
||||
retVal = (Questionnaire) theQuestionnaireRef.getResource();
|
||||
if (retVal == null) {
|
||||
for (Resource next : theAnswers.getContained()) {
|
||||
if (value.equals(next.getId())) {
|
||||
retVal = (Questionnaire) next;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
retVal = myWorkerCtx.fetchResource(Questionnaire.class, value);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private ValueSet getValueSet(QuestionnaireResponse theResponse, Reference theQuestionnaireRef) {
|
||||
ValueSet retVal;
|
||||
if (theQuestionnaireRef.getReferenceElement().isLocal()) {
|
||||
retVal = (ValueSet) theQuestionnaireRef.getResource();
|
||||
if (retVal == null) {
|
||||
for (Resource next : theResponse.getContained()) {
|
||||
if (theQuestionnaireRef.getReferenceElement().getValue().equals(next.getId())) {
|
||||
retVal = (ValueSet) next;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
retVal = myWorkerCtx.fetchResource(ValueSet.class, theQuestionnaireRef.getReferenceElement().getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void validateGroup(List<ValidationMessage> theErrors, QuestionnaireItemComponent theQuestGroup, QuestionnaireResponseItemComponent theRespGroup, LinkedList<String> thePathStack, QuestionnaireResponse theResponse, boolean theValidateRequired) {
|
||||
validateItems(theErrors, theQuestGroup.getItem(), theRespGroup.getItem(), thePathStack, theResponse, theValidateRequired);
|
||||
}
|
||||
|
||||
private void validateQuestion(List<ValidationMessage> theErrors, QuestionnaireItemComponent theQuestion, QuestionnaireResponseItemComponent theRespGroup, LinkedList<String> thePathStack, QuestionnaireResponse theResponse, boolean theValidateRequired) {
|
||||
String linkId = theQuestion.getLinkId();
|
||||
if (!fail(theErrors, IssueType.INVALID, thePathStack, isNotBlank(linkId), "Questionnaire is invalid, question found with no link ID")) {
|
||||
return;
|
||||
}
|
||||
|
||||
QuestionnaireItemType type = theQuestion.getType();
|
||||
if (type == null) {
|
||||
rule(theErrors, IssueType.INVALID, thePathStack, false, "Questionnaire is invalid, no type specified for question with link ID[{0}]", linkId);
|
||||
return;
|
||||
}
|
||||
|
||||
// List<QuestionnaireResponseItemComponent> responses;
|
||||
// if (theRespGroup == null) {
|
||||
// responses = findResponsesByLinkId(theResponse.getItem(), linkId);
|
||||
// } else {
|
||||
// responses = findResponsesByLinkId(theRespGroup.getItem(), linkId);
|
||||
// }
|
||||
List<QuestionnaireResponseItemAnswerComponent> responses = theRespGroup.getAnswer();
|
||||
|
||||
if (responses.size() > 1) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !theQuestion.getRepeats(), "Multiple answers found with linkId[{0}]", linkId);
|
||||
}
|
||||
if (responses.size() == 0) {
|
||||
if (theValidateRequired) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !theQuestion.getRequired(), "Missing answer item for required item with linkId[{0}]", linkId);
|
||||
} else {
|
||||
hint(theErrors, IssueType.BUSINESSRULE, thePathStack, !theQuestion.getRequired(), "Missing answer item for required item with linkId[{0}]", linkId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// QuestionnaireResponseItemComponent responseItem = responses.get(0);
|
||||
try {
|
||||
// thePathStack.add("item(" + responses.indexOf(responseItem) + ")");
|
||||
validateQuestionAnswers(theErrors, theQuestion, thePathStack, type, theRespGroup, theResponse, theValidateRequired);
|
||||
} finally {
|
||||
// thePathStack.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
private void validateItems(List<ValidationMessage> theErrors, List<QuestionnaireItemComponent> theQuestionnaireItems, List<QuestionnaireResponseItemComponent> theResponseItems, LinkedList<String> thePathStack, QuestionnaireResponse theResponse, boolean theValidateRequired) {
|
||||
Set<String> allowedItems = new HashSet<String>();
|
||||
for (QuestionnaireItemComponent nextQuestionnaireItem : theQuestionnaireItems) {
|
||||
if (nextQuestionnaireItem.getType()== QuestionnaireItemType.NULL || nextQuestionnaireItem.getType() == null) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Questionnaire definition contains item with no type");
|
||||
String linkId = nextQuestionnaireItem.getLinkId();
|
||||
if (isNotBlank(linkId)) {
|
||||
// Just so that we don't also get a warning about the answer being present
|
||||
allowedItems.add(linkId);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!QuestionnaireItemType.DISPLAY.equals(nextQuestionnaireItem.getType())) {
|
||||
String itemType = QuestionnaireItemType.GROUP.equals(nextQuestionnaireItem.getType()) ? "group" : "question";
|
||||
String linkId = nextQuestionnaireItem.getLinkId();
|
||||
if (isBlank(linkId)) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Questionnaire definition contains {0} with no linkId", itemType);
|
||||
continue;
|
||||
}
|
||||
allowedItems.add(linkId);
|
||||
|
||||
List<QuestionnaireResponseItemComponent> responseItems = findResponsesByLinkId(theResponseItems, linkId);
|
||||
if (responseItems.isEmpty()) {
|
||||
if (nextQuestionnaireItem.getRequired()) {
|
||||
if (theValidateRequired) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Missing required {0} with linkId[{1}]", itemType, linkId);
|
||||
} else {
|
||||
hint(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Missing required {0} with linkId[{1}]", itemType, linkId);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (responseItems.size() > 1) {
|
||||
if (nextQuestionnaireItem.getRepeats() == false) {
|
||||
int index = theResponseItems.indexOf(responseItems.get(1));
|
||||
thePathStack.add("item(" + index + ")");
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Multiple repetitions of {0} with linkId[{1}] found at this position, but this item cannot repeat", itemType, linkId);
|
||||
thePathStack.removeLast();
|
||||
}
|
||||
}
|
||||
for (QuestionnaireResponseItemComponent nextResponseItem : responseItems) {
|
||||
int index = theResponseItems.indexOf(nextResponseItem);
|
||||
thePathStack.add("item(" + index + ")");
|
||||
if (nextQuestionnaireItem.getType() == QuestionnaireItemType.GROUP) {
|
||||
validateGroup(theErrors, nextQuestionnaireItem, nextResponseItem, thePathStack, theResponse, theValidateRequired);
|
||||
} else {
|
||||
validateQuestion(theErrors, nextQuestionnaireItem, nextResponseItem, thePathStack, theResponse, theValidateRequired);
|
||||
}
|
||||
thePathStack.removeLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure there are no items in response that aren't in the questionnaire
|
||||
int idx = -1;
|
||||
for (QuestionnaireResponseItemComponent next : theResponseItems) {
|
||||
idx++;
|
||||
if (!allowedItems.contains(next.getLinkId())) {
|
||||
thePathStack.add("item(" + idx + ")");
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Item with linkId[{0}] found at this position, but this item does not exist at this position in Questionnaire", next.getLinkId());
|
||||
thePathStack.removeLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateQuestionAnswers(List<ValidationMessage> theErrors, QuestionnaireItemComponent theQuestion, LinkedList<String> thePathStack, QuestionnaireItemType type, QuestionnaireResponseItemComponent responseQuestion, QuestionnaireResponse theResponse, boolean theValidateRequired) {
|
||||
|
||||
String linkId = theQuestion.getLinkId();
|
||||
Set<Class<? extends Type>> allowedAnswerTypes = determineAllowedAnswerTypes(type);
|
||||
if (allowedAnswerTypes.isEmpty()) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, responseQuestion.isEmpty(), "Question with linkId[{0}] has no answer type but an answer was provided", linkId);
|
||||
} else {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !(responseQuestion.getAnswer().size() > 1 && !theQuestion.getRepeats()), "Multiple answers to non repeating question with linkId[{0}]", linkId);
|
||||
if (theValidateRequired) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !(theQuestion.getRequired() && responseQuestion.getAnswer().isEmpty()), "Missing answer to required question with linkId[{0}]", linkId);
|
||||
} else {
|
||||
hint(theErrors, IssueType.BUSINESSRULE, thePathStack, !(theQuestion.getRequired() && responseQuestion.getAnswer().isEmpty()), "Missing answer to required question with linkId[{0}]", linkId);
|
||||
}
|
||||
}
|
||||
|
||||
int answerIdx = -1;
|
||||
for (QuestionnaireResponseItemAnswerComponent nextAnswer : responseQuestion.getAnswer()) {
|
||||
answerIdx++;
|
||||
try {
|
||||
thePathStack.add("answer(" + answerIdx + ")");
|
||||
Type nextValue = nextAnswer.getValue();
|
||||
if (!allowedAnswerTypes.contains(nextValue.getClass())) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Answer to question with linkId[{0}] found of type [{1}] but this is invalid for question of type [{2}]", linkId, nextValue.getClass().getSimpleName(), type.toCode());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate choice answers
|
||||
if (type == QuestionnaireItemType.CHOICE || type == QuestionnaireItemType.OPENCHOICE) {
|
||||
if (nextAnswer.getValue() instanceof StringType) {
|
||||
// n.b. we can only be here if it's an open-choice
|
||||
String value = ((StringType)nextAnswer.getValue()).getValue();
|
||||
if (isBlank(value)) {
|
||||
if (Boolean.TRUE.equals(theQuestion.getRequiredElement().getValue())) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Answer to question with linkId[{0}] has no value but this item is required", linkId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Coding coding = (Coding) nextAnswer.getValue();
|
||||
if (isBlank(coding.getCode()) && isBlank(coding.getDisplay()) && isBlank(coding.getSystem())) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Answer to question with linkId[{0}] is of type coding, but none of code, system, and display are populated", linkId);
|
||||
continue;
|
||||
} else if (isBlank(coding.getCode()) && isBlank(coding.getSystem())) {
|
||||
if (type != QuestionnaireItemType.OPENCHOICE) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Answer to question with linkId[{0}] is of type only has a display populated (no code or system) but question does not allow {1}", linkId, QuestionnaireItemType.OPENCHOICE.name());
|
||||
continue;
|
||||
}
|
||||
} else if (isBlank(coding.getCode()) || isBlank(coding.getSystem())) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Answer to question with linkId[{0}] has a coding, but this coding does not contain a code and system (both must be present, or neither as the question allows {1})", linkId, QuestionnaireItemType.OPENCHOICE.name());
|
||||
continue;
|
||||
}
|
||||
|
||||
String optionsRef = theQuestion.getOptions().getReference();
|
||||
if (isNotBlank(optionsRef)) {
|
||||
ValueSet valueSet = getValueSet(theResponse, theQuestion.getOptions());
|
||||
if (valueSet == null) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Question with linkId[{0}] has options ValueSet[{1}] but this ValueSet can not be found", linkId, optionsRef);
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean found = false;
|
||||
for (ValueSetExpansionContainsComponent next : valueSet.getExpansion().getContains()) {
|
||||
if (coding.getCode().equals(next.getCode()) && coding.getSystem().equals(next.getSystem())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, found, "Question with linkId[{0}] has answer with system[{1}] and code[{2}] but this is not a valid answer for ValueSet[{3}]", linkId, coding.getSystem(), coding.getCode(), optionsRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validateItems(theErrors, theQuestion.getItem(), nextAnswer.getItem(), thePathStack, theResponse, theValidateRequired);
|
||||
|
||||
} finally {
|
||||
thePathStack.removeLast();
|
||||
}
|
||||
|
||||
} // for answers
|
||||
}
|
||||
|
||||
private Set<Class<? extends Type>> determineAllowedAnswerTypes(QuestionnaireItemType type) {
|
||||
Set<Class<? extends Type>> allowedAnswerTypes;
|
||||
switch (type) {
|
||||
case ATTACHMENT:
|
||||
allowedAnswerTypes = allowedTypes(Attachment.class);
|
||||
break;
|
||||
case BOOLEAN:
|
||||
allowedAnswerTypes = allowedTypes(BooleanType.class);
|
||||
break;
|
||||
case CHOICE:
|
||||
allowedAnswerTypes = allowedTypes(Coding.class);
|
||||
break;
|
||||
case DATE:
|
||||
allowedAnswerTypes = allowedTypes(DateType.class);
|
||||
break;
|
||||
case DATETIME:
|
||||
allowedAnswerTypes = allowedTypes(DateTimeType.class);
|
||||
break;
|
||||
case DECIMAL:
|
||||
allowedAnswerTypes = allowedTypes(DecimalType.class);
|
||||
break;
|
||||
case INTEGER:
|
||||
allowedAnswerTypes = allowedTypes(IntegerType.class);
|
||||
break;
|
||||
case OPENCHOICE:
|
||||
allowedAnswerTypes = allowedTypes(Coding.class, StringType.class);
|
||||
break;
|
||||
case QUANTITY:
|
||||
allowedAnswerTypes = allowedTypes(Quantity.class);
|
||||
break;
|
||||
case REFERENCE:
|
||||
allowedAnswerTypes = allowedTypes(Reference.class);
|
||||
break;
|
||||
case STRING:
|
||||
allowedAnswerTypes = allowedTypes(StringType.class);
|
||||
break;
|
||||
case TEXT:
|
||||
allowedAnswerTypes = allowedTypes(StringType.class);
|
||||
break;
|
||||
case TIME:
|
||||
allowedAnswerTypes = allowedTypes(TimeType.class);
|
||||
break;
|
||||
case URL:
|
||||
allowedAnswerTypes = allowedTypes(UriType.class);
|
||||
break;
|
||||
case NULL:
|
||||
default:
|
||||
allowedAnswerTypes = Collections.emptySet();
|
||||
}
|
||||
return allowedAnswerTypes;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
package org.hl7.fhir.dstu3.validation;
|
||||
/*
|
||||
Copyright (c) 2011+, HL7, Inc
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of HL7 nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||
import org.xml.sax.ErrorHandler;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
public class ValidationErrorHandler implements ErrorHandler {
|
||||
|
||||
private List<ValidationMessage> outputs;
|
||||
private String path;
|
||||
|
||||
public ValidationErrorHandler(List<ValidationMessage> outputs, String path) {
|
||||
this.outputs = outputs;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(SAXParseException arg0) throws SAXException {
|
||||
outputs.add(new ValidationMessage(Source.Schema, IssueType.INVALID, arg0.getLineNumber(), arg0.getColumnNumber(), path, arg0.getMessage(), IssueSeverity.ERROR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fatalError(SAXParseException arg0) throws SAXException {
|
||||
outputs.add(new ValidationMessage(Source.Schema, IssueType.INVALID, arg0.getLineNumber(), arg0.getColumnNumber(), path, arg0.getMessage(), IssueSeverity.FATAL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(SAXParseException arg0) throws SAXException {
|
||||
outputs.add(new ValidationMessage(Source.Schema, IssueType.INVALID, arg0.getLineNumber(), arg0.getColumnNumber(), path, arg0.getMessage(), IssueSeverity.WARNING));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package org.hl7.fhir.dstu3.validation;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ValidatorFrame extends javax.swing.JFrame {
|
||||
|
||||
public ValidatorFrame() throws IOException {
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -66,6 +66,11 @@ public class CachingValidationSupport implements IValidationSupport {
|
|||
return myWrap.isCodeSystemSupported(theContext, theSystem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
|
||||
return myWrap.generateSnapshot(theInput, theUrl, theProfileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay);
|
||||
|
|
|
@ -182,7 +182,12 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
package org.hl7.fhir.r4.hapi.validation;
|
||||
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.conformance.ProfileUtilities;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
|
||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.ElementDefinition;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Simple validation support module that handles profile snapshot generation. This is
|
||||
* separate from other funcrtions since it needs a link to a validation support
|
||||
* module itself, and it is useful to be able to pass a chain in.
|
||||
*/
|
||||
public class SnapshotGeneratingValidationSupport implements IValidationSupport {
|
||||
private final FhirContext myCtx;
|
||||
private final IValidationSupport myValidationSupport;
|
||||
|
||||
public SnapshotGeneratingValidationSupport(FhirContext theCtx, IValidationSupport theValidationSupport) {
|
||||
Validate.notNull(theCtx);
|
||||
Validate.notNull(theValidationSupport);
|
||||
myCtx = theCtx;
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ValueSet.ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeSystem fetchCodeSystem(FhirContext theContext, String uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchValueSet(FhirContext theContext, String uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
|
||||
IWorkerContext context = new HapiWorkerContext(myCtx, myValidationSupport);
|
||||
ProfileUtilities.ProfileKnowledgeProvider profileKnowledgeProvider = new MyProfileKnowledgeWorker();
|
||||
ArrayList<ValidationMessage> messages = new ArrayList<>();
|
||||
|
||||
StructureDefinition base = myValidationSupport.fetchStructureDefinition(myCtx, theInput.getBaseDefinition());
|
||||
if (base == null) {
|
||||
throw new PreconditionFailedException("Unknown base definition: " + theInput.getBaseDefinition());
|
||||
}
|
||||
|
||||
new ProfileUtilities(context, messages, profileKnowledgeProvider).generateSnapshot(base, theInput, theUrl, theProfileName);
|
||||
|
||||
return theInput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private class MyProfileKnowledgeWorker implements ProfileUtilities.ProfileKnowledgeProvider {
|
||||
@Override
|
||||
public boolean isDatatype(String typeSimple) {
|
||||
BaseRuntimeElementDefinition<?> def = myCtx.getElementDefinition(typeSimple);
|
||||
Validate.notNull(typeSimple);
|
||||
return (def instanceof RuntimePrimitiveDatatypeDefinition) || (def instanceof RuntimeCompositeDatatypeDefinition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResource(String typeSimple) {
|
||||
BaseRuntimeElementDefinition<?> def = myCtx.getElementDefinition(typeSimple);
|
||||
Validate.notNull(typeSimple);
|
||||
return def instanceof RuntimeResourceDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLinkFor(String typeSimple) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLinkFor(String corePath, String typeSimple) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingResolution resolveBinding(org.hl7.fhir.r4.model.StructureDefinition def, ElementDefinition.ElementDefinitionBindingComponent binding, String path) throws FHIRException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLinkForProfile(org.hl7.fhir.r4.model.StructureDefinition profile, String url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prependLinks() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,6 @@ package org.hl7.fhir.r4.hapi.validation;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
|
@ -28,7 +27,7 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
* Constructor
|
||||
*/
|
||||
public ValidationSupportChain() {
|
||||
myChain = new ArrayList<IValidationSupport>();
|
||||
myChain = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,19 +60,19 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
throw new InvalidRequestException("unable to find code system " + theInclude.getSystem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
|
||||
List<IBaseResource> retVal = new ArrayList<>();
|
||||
for (IValidationSupport next : myChain) {
|
||||
List<IBaseResource> candidates = next.fetchAllConformanceResources(theContext);
|
||||
if (candidates != null) {
|
||||
retVal.addAll(candidates);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
@Override
|
||||
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
|
||||
List<IBaseResource> retVal = new ArrayList<>();
|
||||
for (IValidationSupport next : myChain) {
|
||||
List<IBaseResource> candidates = next.fetchAllConformanceResources(theContext);
|
||||
if (candidates != null) {
|
||||
retVal.addAll(candidates);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (next.isCodeSystemSupported(theCtx, theSystem)) {
|
||||
|
@ -129,11 +128,23 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
|
||||
StructureDefinition outcome = null;
|
||||
for (IValidationSupport next : myChain) {
|
||||
outcome = next.generateSnapshot(theInput, theUrl, theProfileName);
|
||||
if (outcome != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return outcome;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
|
||||
|
||||
ourLog.debug("Validating code {} in chain with {} items", theCode, myChain.size());
|
||||
|
||||
|
||||
for (IValidationSupport next : myChain) {
|
||||
if (next.isCodeSystemSupported(theCtx, theCodeSystem)) {
|
||||
CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay);
|
||||
|
@ -150,12 +161,15 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
|
||||
@Override
|
||||
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
|
||||
ArrayList<StructureDefinition> retVal = new ArrayList<StructureDefinition>();
|
||||
Set<String> urls= new HashSet<String>();
|
||||
ArrayList<StructureDefinition> retVal = new ArrayList<>();
|
||||
Set<String> urls = new HashSet<>();
|
||||
for (IValidationSupport nextSupport : myChain) {
|
||||
for (StructureDefinition next : nextSupport.fetchAllStructureDefinitions(theContext)) {
|
||||
if (isBlank(next.getUrl()) || urls.add(next.getUrl())) {
|
||||
retVal.add(next);
|
||||
List<StructureDefinition> list = nextSupport.fetchAllStructureDefinitions(theContext);
|
||||
if (list != null) {
|
||||
for (StructureDefinition next : list) {
|
||||
if (isBlank(next.getUrl()) || urls.add(next.getUrl())) {
|
||||
retVal.add(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
|
|||
public class ${className}ResourceProvider extends
|
||||
## We have specialized base classes for RPs that handle certain resource types. These
|
||||
## RPs implement type specific operations
|
||||
#if ( $version != 'dstu' && (${className} == 'Encounter' || ${className} == 'Patient' || ${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers' || ${className} == 'CodeSystem' || ($version != 'dstu2' && ${className} == 'ConceptMap') || ${className} == 'MessageHeader' || ${className} == 'Composition'))
|
||||
#if ( $version != 'dstu' && (${className} == 'Encounter' || ${className} == 'Patient' || ${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers' || ${className} == 'CodeSystem' || ($version != 'dstu2' && ${className} == 'ConceptMap') || ${className} == 'MessageHeader' || ${className} == 'Composition' || ${className} == 'StructureDefinition'))
|
||||
BaseJpaResourceProvider${className}${versionCapitalized}
|
||||
#else
|
||||
JpaResourceProvider${versionCapitalized}<${className}>
|
||||
|
|
|
@ -66,7 +66,7 @@ public abstract class BaseJavaConfig${versionCapitalized} extends ca.uhn.fhir.jp
|
|||
IFhirResourceDaoConceptMap<org.hl7.fhir.dstu3.model.ConceptMap>
|
||||
#elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'ConceptMap' )
|
||||
IFhirResourceDaoConceptMap<org.hl7.fhir.r4.model.ConceptMap>
|
||||
#elseif ( ${versionCapitalized} != 'Dstu1' && (${res.name} == 'Composition' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'SearchParameter' || ${res.name} == 'MessageHeader'))
|
||||
#elseif ( ${versionCapitalized} != 'Dstu1' && (${res.name} == 'Composition' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'SearchParameter' || ${res.name} == 'MessageHeader' || ${res.name} == 'StructureDefinition'))
|
||||
IFhirResourceDao${res.name}<${resourcePackage}.${res.declaringClassNameComplete}>
|
||||
#else
|
||||
IFhirResourceDao<${resourcePackage}.${res.declaringClassNameComplete}>
|
||||
|
@ -76,7 +76,7 @@ public abstract class BaseJavaConfig${versionCapitalized} extends ca.uhn.fhir.jp
|
|||
ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}();
|
||||
#elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'ConceptMap' )
|
||||
ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}();
|
||||
#elseif ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter' || ${res.name} == 'CodeSystem' || ${res.name} == 'MessageHeader' || ${res.name} == 'Composition')
|
||||
#elseif ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter' || ${res.name} == 'CodeSystem' || ${res.name} == 'MessageHeader' || ${res.name} == 'Composition' || ${res.name} == 'StructureDefinition')
|
||||
ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}();
|
||||
#else
|
||||
ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${versionCapitalized}<${resourcePackage}.${res.declaringClassNameComplete}> retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${versionCapitalized}<${resourcePackage}.${res.declaringClassNameComplete}>();
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -880,7 +880,7 @@
|
|||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.12</version>
|
||||
<version>8.0.16</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.json-lib</groupId>
|
||||
|
|
|
@ -279,6 +279,11 @@
|
|||
Added a new Pointcut STORAGE_PRESTORAGE_EXPUNGE_EVERYTHING that is called at the start of
|
||||
the expungeEverything operation.
|
||||
</action>
|
||||
<action type="add">
|
||||
The JPA server now has the ability to generate snapshot profiles from differential
|
||||
profiles via the $snapshot operation, and will automatically generate a snapshot when
|
||||
needed for validation.
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.8.0" date="2019-05-30" description="Hippo">
|
||||
<action type="fix">
|
||||
|
|
Loading…
Reference in New Issue