Throw correct HTTP status when validation fails because of missing ID
This commit is contained in:
parent
53f6effd56
commit
82ac69d86c
|
@ -14,7 +14,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
* 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,
|
||||
|
@ -48,7 +48,7 @@ abstract class BaseParam implements IQueryParameterType {
|
|||
public Boolean getMissing() {
|
||||
return myMissing;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final String getQueryParameterQualifier() {
|
||||
if (myMissing != null && myMissing.booleanValue()) {
|
||||
|
@ -73,8 +73,8 @@ abstract class BaseParam implements IQueryParameterType {
|
|||
}
|
||||
|
||||
/**
|
||||
* If set to non-null value, indicates that this parameter has been populated
|
||||
* with a "[name]:missing=true" or "[name]:missing=false" vale instead of a
|
||||
* If set to non-null value, indicates that this parameter has been populated
|
||||
* with a "[name]:missing=true" or "[name]:missing=false" vale instead of a
|
||||
* normal value
|
||||
*
|
||||
* @return Returns a reference to <code>this</code> for easier method chaining
|
||||
|
@ -97,10 +97,10 @@ abstract class BaseParam implements IQueryParameterType {
|
|||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
myMissing = null;
|
||||
doSetValueAsQueryToken(theContext, theParamName, theQualifier, theValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.param;
|
|||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
|
@ -32,17 +33,26 @@ import ca.uhn.fhir.rest.server.Constants;
|
|||
|
||||
public class StringParam extends BaseParam implements IQueryParameterType {
|
||||
|
||||
private boolean myContains;
|
||||
private boolean myExact;
|
||||
private String myValue;
|
||||
private boolean myContains;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public StringParam() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public StringParam(String theValue) {
|
||||
setValue(theValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public StringParam(String theValue, boolean theExact) {
|
||||
setValue(theValue);
|
||||
setExact(theExact);
|
||||
|
@ -79,6 +89,29 @@ public class StringParam extends BaseParam implements IQueryParameterType {
|
|||
myValue = ParameterUtil.unescape(theValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof StringParam)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StringParam other = (StringParam) obj;
|
||||
|
||||
EqualsBuilder eb = new EqualsBuilder();
|
||||
eb.append(myExact, other.myExact);
|
||||
eb.append(myContains, other.myContains);
|
||||
eb.append(myValue, other.myValue);
|
||||
eb.append(getMissing(), other.getMissing());
|
||||
|
||||
return eb.isEquals();
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
@ -91,6 +124,13 @@ public class StringParam extends BaseParam implements IQueryParameterType {
|
|||
return defaultString(myValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* String parameter modifier <code>:contains</code>
|
||||
*/
|
||||
public boolean isContains() {
|
||||
return myContains;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return StringUtils.isEmpty(myValue);
|
||||
}
|
||||
|
@ -99,6 +139,18 @@ public class StringParam extends BaseParam implements IQueryParameterType {
|
|||
return myExact;
|
||||
}
|
||||
|
||||
/**
|
||||
* String parameter modifier <code>:contains</code>
|
||||
*/
|
||||
public StringParam setContains(boolean theContains) {
|
||||
myContains = theContains;
|
||||
if (myContains) {
|
||||
setExact(false);
|
||||
setMissing(null);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public StringParam setExact(boolean theExact) {
|
||||
myExact = theExact;
|
||||
if (myExact) {
|
||||
|
@ -108,13 +160,6 @@ public class StringParam extends BaseParam implements IQueryParameterType {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* String parameter modifier <code>:contains</code>
|
||||
*/
|
||||
public boolean isContains() {
|
||||
return myContains;
|
||||
}
|
||||
|
||||
public StringParam setValue(String theValue) {
|
||||
myValue = theValue;
|
||||
return this;
|
||||
|
@ -136,16 +181,4 @@ public class StringParam extends BaseParam implements IQueryParameterType {
|
|||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* String parameter modifier <code>:contains</code>
|
||||
*/
|
||||
public StringParam setContains(boolean theContains) {
|
||||
myContains = theContains;
|
||||
if (myContains) {
|
||||
setExact(false);
|
||||
setMissing(null);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionEntryHasInvalidVerb=Transac
|
|||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionMissingUrl=Unable to perform {0}, no URL provided.
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionInvalidUrl=Unable to perform {0}, URL provided is invalid: {1}
|
||||
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.cantValidateWithNoResource=No resource supplied for $validate operation (resource is required unless mode is \"delete\")
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.failedToCreateWithInvalidId=Can not process entity with ID[{0}], this is not a valid FHIR ID
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.incorrectResourceType=Incorrect resource type detected for endpoint, found {0} but expected {1}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package ca.uhn.fhir.rest.param;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class StringParamTest {
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
StringParam input = new StringParam("foo", true);
|
||||
|
||||
assertTrue(input.equals(input));
|
||||
assertFalse(input.equals(null));
|
||||
assertFalse(input.equals(""));
|
||||
assertFalse(input.equals(new StringParam("foo", false)));
|
||||
}
|
||||
|
||||
}
|
|
@ -11,7 +11,7 @@ import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
|||
* 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,
|
||||
|
@ -335,7 +335,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Class<? extends IBaseResource> type = resourceDefinition.getImplementingClass();
|
||||
String id = nextId.getIdPart();
|
||||
if (StringUtils.isBlank(id)) {
|
||||
|
@ -677,18 +677,19 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
theRequestDetails.getUserData().put(PROCESSING_SUB_REQUEST, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void notifyInterceptors(RestOperationTypeEnum theOperationType, ActionRequestDetails theRequestDetails) {
|
||||
if (theRequestDetails.getId() != null && theRequestDetails.getId().hasResourceType() && isNotBlank(theRequestDetails.getResourceType())) {
|
||||
if (theRequestDetails.getId().getResourceType().equals(theRequestDetails.getResourceType()) == false) {
|
||||
throw new InternalErrorException("Inconsistent server state - Resource types don't match: " + theRequestDetails.getId().getResourceType() + " / " + theRequestDetails.getResourceType());
|
||||
throw new InternalErrorException(
|
||||
"Inconsistent server state - Resource types don't match: " + theRequestDetails.getId().getResourceType() + " / " + theRequestDetails.getResourceType());
|
||||
}
|
||||
}
|
||||
|
||||
if (theRequestDetails.getUserData().get(PROCESSING_SUB_REQUEST) == Boolean.TRUE) {
|
||||
theRequestDetails.notifyIncomingRequestPreHandled(theOperationType);
|
||||
}
|
||||
|
||||
|
||||
List<IServerInterceptor> interceptors = getConfig().getInterceptors();
|
||||
if (interceptors == null) {
|
||||
return;
|
||||
|
@ -779,7 +780,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
theEntity.setHasTags(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ArrayList<ResourceTag> existingTags = new ArrayList<ResourceTag>();
|
||||
if (theEntity.isHasTags()) {
|
||||
existingTags.addAll(theEntity.getTags());
|
||||
|
@ -937,9 +938,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
* Subclasses may override to provide behaviour. Called when a resource has been inserted into the database for the first time.
|
||||
*
|
||||
* @param theEntity
|
||||
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||
* @param theTag
|
||||
* The tag
|
||||
* The tag
|
||||
* @return Returns <code>true</code> if the tag should be removed
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -951,9 +952,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
* Subclasses may override to provide behaviour. Called when a pre-existing resource has been updated in the database
|
||||
*
|
||||
* @param theEntity
|
||||
* The resource
|
||||
* The resource
|
||||
* @param theResource
|
||||
* The resource being persisted
|
||||
* The resource being persisted
|
||||
*/
|
||||
protected void postUpdate(ResourceTable theEntity, T theResource) {
|
||||
// nothing
|
||||
|
@ -1009,9 +1010,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
* </p>
|
||||
*
|
||||
* @param theEntity
|
||||
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||
* @param theTag
|
||||
* The tag
|
||||
* The tag
|
||||
* @return Retturns <code>true</code> if the tag should be removed
|
||||
*/
|
||||
protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) {
|
||||
|
@ -1072,7 +1073,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IParser parser = theEntity.getEncoding().newParser(getContext(theEntity.getFhirVersion()));
|
||||
R retVal;
|
||||
try {
|
||||
|
@ -1241,7 +1242,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
/*
|
||||
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the matching
|
||||
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the
|
||||
* matching
|
||||
* resource.
|
||||
*/
|
||||
if (myConfig.isAllowInlineMatchUrlReferences()) {
|
||||
|
@ -1437,7 +1439,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
theEntity.setResourceLinks(links);
|
||||
|
||||
theEntity.toString();
|
||||
|
||||
|
||||
} // if thePerformIndexing
|
||||
|
||||
theEntity = myEntityManager.merge(theEntity);
|
||||
|
@ -1538,9 +1540,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
* "subsetted" tag and rejects resources which have it. Subclasses should call the superclass implementation to preserve this check.
|
||||
*
|
||||
* @param theResource
|
||||
* The resource that is about to be persisted
|
||||
* The resource that is about to be persisted
|
||||
* @param theEntityToSave
|
||||
* TODO
|
||||
* TODO
|
||||
*/
|
||||
protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) {
|
||||
Object tag = null;
|
||||
|
@ -1747,7 +1749,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
paramMap.add(nextParamName, param);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (Constants.PARAM_COUNT.equals(nextParamName)) {
|
||||
if (paramList.size() > 0 && paramList.get(0).size() > 0) {
|
||||
String intString = paramList.get(0).get(0);
|
||||
|
@ -1772,7 +1774,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
} else {
|
||||
RuntimeSearchParam paramDef = theCallingDao.getSearchParamByName(resourceDef, nextParamName);
|
||||
if (paramDef == null) {
|
||||
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
|
||||
throw new InvalidRequestException(
|
||||
"Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
|
||||
}
|
||||
|
||||
IQueryParameterAnd<?> param = MethodUtil.parseQueryParams(theContext, paramDef, nextParamName, paramList);
|
||||
|
|
|
@ -56,6 +56,7 @@ import ca.uhn.fhir.rest.method.RequestDetails;
|
|||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
|
@ -133,8 +134,11 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
|
|||
ValidationResult result;
|
||||
if (isNotBlank(theRawResource)) {
|
||||
result = validator.validateWithResult(theRawResource);
|
||||
} else {
|
||||
} else if (theResource != null) {
|
||||
result = validator.validateWithResult(theResource);
|
||||
} else {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
if (result.isSuccessful()) {
|
||||
|
@ -160,11 +164,11 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
|
|||
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
|
||||
if (myMode == ValidationModeEnum.CREATE) {
|
||||
if (hasId) {
|
||||
throw new InvalidRequestException("Resource has an ID - ID must not be populated for a FHIR create");
|
||||
throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create");
|
||||
}
|
||||
} else if (myMode == ValidationModeEnum.UPDATE) {
|
||||
if (hasId == false) {
|
||||
throw new InvalidRequestException("Resource has no ID - ID must be populated for a FHIR update");
|
||||
throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2017 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 org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords;
|
||||
|
||||
public interface IResourceIndexedSearchParamCoordsDao extends JpaRepository<ResourceIndexedSearchParamCoords, Long> {
|
||||
// nothing yet
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2017 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 org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
|
||||
|
||||
public interface IResourceIndexedSearchParamDateDao extends JpaRepository<ResourceIndexedSearchParamDate, Long> {
|
||||
// nothing yet
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2017 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 org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
|
||||
|
||||
public interface IResourceIndexedSearchParamNumberDao extends JpaRepository<ResourceIndexedSearchParamNumber, Long> {
|
||||
// nothing yet
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2017 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 org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity;
|
||||
|
||||
public interface IResourceIndexedSearchParamQuantityDao extends JpaRepository<ResourceIndexedSearchParamQuantity, Long> {
|
||||
// nothing yet
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2017 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 org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
|
||||
|
||||
public interface IResourceIndexedSearchParamStringDao extends JpaRepository<ResourceIndexedSearchParamString, Long> {
|
||||
// nothing yet
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2017 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 org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
|
||||
|
||||
public interface IResourceIndexedSearchParamTokenDao extends JpaRepository<ResourceIndexedSearchParamToken, Long> {
|
||||
// nothing yet
|
||||
}
|
|
@ -54,6 +54,7 @@ import ca.uhn.fhir.rest.method.RequestDetails;
|
|||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
|
@ -143,7 +144,8 @@ public class FhirResourceDaoDstu3<T extends IAnyResource> extends BaseHapiFhirRe
|
|||
if (resourceToValidateById != null) {
|
||||
result = validator.validateWithResult(resourceToValidateById);
|
||||
} else {
|
||||
throw new InvalidRequestException("No resource supplied for $validate operation (resource is required unless mode is \"delete\")");
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
} else if (isNotBlank(theRawResource)) {
|
||||
result = validator.validateWithResult(theRawResource);
|
||||
|
@ -180,11 +182,11 @@ public class FhirResourceDaoDstu3<T extends IAnyResource> extends BaseHapiFhirRe
|
|||
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
|
||||
if (myMode == ValidationModeEnum.CREATE) {
|
||||
if (hasId) {
|
||||
throw new InvalidRequestException("Resource has an ID - ID must not be populated for a FHIR create");
|
||||
throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create");
|
||||
}
|
||||
} else if (myMode == ValidationModeEnum.UPDATE) {
|
||||
if (hasId == false) {
|
||||
throw new InvalidRequestException("Resource has no ID - ID must be populated for a FHIR update");
|
||||
throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2556,4 +2556,61 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateOnNoId() throws Exception {
|
||||
HttpGet get = new HttpGet(ourServerBase + "/QuestionnaireResponse/$validate");
|
||||
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||
try {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response: {}", responseString);
|
||||
assertThat(responseString, containsString("No resource supplied for $validate operation"));
|
||||
assertEquals(400, response.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* From a Skype message from Brian Postlethwaite
|
||||
*/
|
||||
@Test
|
||||
public void testValidateQuestionnaireResponseWithNoIdForCreate() throws Exception {
|
||||
|
||||
String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"create\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"<div>!-- populated from the rendered HTML below --></div>\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\",\"group\":{\"question\":[{\"linkId\":\"d94b4f57-1ca0-4d65-acba-8bd9a3926c8c\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has a valid Medicare or DVA entitlement card\"},{\"linkId\":\"0cbe66db-ff12-473a-940e-4672fb82de44\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has received a MedsCheck, Diabetes MedsCheck, Home Medicines Review (HMR) otr Restidential Medication Management Review (RMMR) in the past 12 months\"},{\"linkId\":\"35790cfd-2d98-4721-963e-9663e1897a17\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient is living at home in a community setting\"},{\"linkId\":\"3ccc8304-76cd-41ff-9360-2c8755590bae\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has been recently diagnosed with type 3 diabetes (in the last 12 months) AND is unable to gain timely access to existing diabetes education or health services in the community OR \"},{\"linkId\":\"b05f6f09-49ec-40f9-a889-9a3fdff9e0da\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has type 2 diabetes , is less than ideally controlled AND is unable to gain timely access to existing diabetes education or health services in their community \"},{\"linkId\":\"4a777f56-800d-4e0b-a9c3-e929832adb5b\",\"answer\":[{\"valueBoolean\":false,\"group\":[{\"linkId\":\"95bbc904-149e-427f-88a4-7f6c8ab186fa\",\"question\":[{\"linkId\":\"f0acea9e-716c-4fce-b7a2-aad59de9d136\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Patient has had an Acute or Adverse Event\"},{\"linkId\":\"e1629159-6dea-4295-a93e-e7c2829ce180\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Exacerbation of a Chronic Disease or Condition\"},{\"linkId\":\"2ce526fa-edaa-44b3-8d5a-6e97f6379ce8\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"New Diagnosis\"},{\"linkId\":\"9d6ffa9f-0110-418c-9ed0-f04910fda2ed\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Recent hospital admission (<3 months)\"},{\"linkId\":\"d2803ff7-25f7-4c7b-ab92-356c49910478\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Major change to regular medication regime\"},{\"linkId\":\"b34af32d-c69d-4d44-889f-5b6d420a7d08\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Suspected non-adherence to the patient's medication regime \"},{\"linkId\":\"74bad553-c273-41e6-8647-22b860430bc2\",\"answer\":[],\"text\":\"Other\"}]}]}],\"text\":\"The patient has experienced one or more of the following recent significant medical events\"},{\"linkId\":\"ecbf4e5a-d4d1-43eb-9f43-0c0e35fc09c7\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The Pharmacist has obtained patient consent to take part in the MedsCheck Service or Diabetes MedsCheck Service and share information obtained during the services with other nominated members of the patients healthcare team (such as their GP, diabetes educator) if required\"},{\"linkId\":\"8ef66774-43b0-4190-873f-cfbb6e980aa9\",\"answer\":[],\"text\":\"Question\"}]}}}]}";
|
||||
HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true");
|
||||
post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
try {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response: {}", responseString);
|
||||
assertThat(responseString, containsString("No issues detected"));
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* From a Skype message from Brian Postlethwaite
|
||||
*/
|
||||
@Test
|
||||
public void testValidateQuestionnaireResponseWithNoIdForUpdate() throws Exception {
|
||||
|
||||
String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"update\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"<div>!-- populated from the rendered HTML below --></div>\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\",\"group\":{\"question\":[{\"linkId\":\"d94b4f57-1ca0-4d65-acba-8bd9a3926c8c\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has a valid Medicare or DVA entitlement card\"},{\"linkId\":\"0cbe66db-ff12-473a-940e-4672fb82de44\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has received a MedsCheck, Diabetes MedsCheck, Home Medicines Review (HMR) otr Restidential Medication Management Review (RMMR) in the past 12 months\"},{\"linkId\":\"35790cfd-2d98-4721-963e-9663e1897a17\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient is living at home in a community setting\"},{\"linkId\":\"3ccc8304-76cd-41ff-9360-2c8755590bae\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has been recently diagnosed with type 3 diabetes (in the last 12 months) AND is unable to gain timely access to existing diabetes education or health services in the community OR \"},{\"linkId\":\"b05f6f09-49ec-40f9-a889-9a3fdff9e0da\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has type 2 diabetes , is less than ideally controlled AND is unable to gain timely access to existing diabetes education or health services in their community \"},{\"linkId\":\"4a777f56-800d-4e0b-a9c3-e929832adb5b\",\"answer\":[{\"valueBoolean\":false,\"group\":[{\"linkId\":\"95bbc904-149e-427f-88a4-7f6c8ab186fa\",\"question\":[{\"linkId\":\"f0acea9e-716c-4fce-b7a2-aad59de9d136\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Patient has had an Acute or Adverse Event\"},{\"linkId\":\"e1629159-6dea-4295-a93e-e7c2829ce180\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Exacerbation of a Chronic Disease or Condition\"},{\"linkId\":\"2ce526fa-edaa-44b3-8d5a-6e97f6379ce8\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"New Diagnosis\"},{\"linkId\":\"9d6ffa9f-0110-418c-9ed0-f04910fda2ed\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Recent hospital admission (<3 months)\"},{\"linkId\":\"d2803ff7-25f7-4c7b-ab92-356c49910478\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Major change to regular medication regime\"},{\"linkId\":\"b34af32d-c69d-4d44-889f-5b6d420a7d08\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Suspected non-adherence to the patient's medication regime \"},{\"linkId\":\"74bad553-c273-41e6-8647-22b860430bc2\",\"answer\":[],\"text\":\"Other\"}]}]}],\"text\":\"The patient has experienced one or more of the following recent significant medical events\"},{\"linkId\":\"ecbf4e5a-d4d1-43eb-9f43-0c0e35fc09c7\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The Pharmacist has obtained patient consent to take part in the MedsCheck Service or Diabetes MedsCheck Service and share information obtained during the services with other nominated members of the patients healthcare team (such as their GP, diabetes educator) if required\"},{\"linkId\":\"8ef66774-43b0-4190-873f-cfbb6e980aa9\",\"answer\":[],\"text\":\"Question\"}]}}}]}";
|
||||
HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true");
|
||||
post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
try {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response: {}", responseString);
|
||||
assertThat(responseString, containsString("Resource has no ID"));
|
||||
assertEquals(422, response.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
|
@ -181,5 +183,63 @@ public class ResourceProviderQuestionnaireResponseDstu3Test extends BaseResource
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateOnNoId() throws Exception {
|
||||
HttpGet get = new HttpGet(ourServerBase + "/QuestionnaireResponse/$validate");
|
||||
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||
try {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response: {}", responseString);
|
||||
assertThat(responseString, containsString("No resource supplied for $validate operation"));
|
||||
assertEquals(400, response.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* From a Skype message from Brian Postlethwaite
|
||||
*/
|
||||
@Test
|
||||
public void testValidateQuestionnaireResponseWithNoIdForCreate() throws Exception {
|
||||
|
||||
String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"create\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">!-- populated from the rendered HTML below --></div>\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\"}}]}";
|
||||
HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true");
|
||||
post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
try {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response: {}", responseString);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* From a Skype message from Brian Postlethwaite
|
||||
*/
|
||||
@Test
|
||||
public void testValidateQuestionnaireResponseWithNoIdForUpdate() throws Exception {
|
||||
|
||||
String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"update\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">!-- populated from the rendered HTML below --></div>\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\"}}]}";
|
||||
HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true");
|
||||
post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
try {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response: {}", responseString);
|
||||
assertThat(responseString, containsString("Resource has no ID"));
|
||||
assertEquals(422, response.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
[*.java]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
|
@ -152,6 +152,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
|
|||
throw new ConfigurationException(e);
|
||||
}
|
||||
|
||||
v.setShouldCheckForIdPresence(false);
|
||||
v.setBestPracticeWarningLevel(myBestPracticeWarningLevel);
|
||||
v.setAnyExtensionsAllowed(true);
|
||||
v.setRequireResourceId(false);
|
||||
|
|
|
@ -80,7 +80,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
// used during the build process to keep the overall volume of messages down
|
||||
private boolean suppressLoincSnomedMessages;
|
||||
|
||||
public InstanceValidator(IWorkerContext theContext) throws Exception {
|
||||
private boolean shouldCheckForIdPresence = true;
|
||||
|
||||
public InstanceValidator(IWorkerContext theContext) throws Exception {
|
||||
super();
|
||||
this.context = theContext;
|
||||
source = Source.InstanceValidator;
|
||||
|
@ -117,6 +119,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
}
|
||||
|
||||
public void setShouldCheckForIdPresence(boolean theShouldCheckForIdPresence) {
|
||||
shouldCheckForIdPresence = theShouldCheckForIdPresence;
|
||||
}
|
||||
|
||||
private boolean check(String v1, String v2) {
|
||||
return v1 == null ? Utilities.noString(v1) : v1.equals(v2);
|
||||
}
|
||||
|
@ -1825,7 +1831,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (type.equals("Extension"))
|
||||
checkExtension(errors, ei.path, ei.element, ei.definition, profile, localStack);
|
||||
else if (type.equals("Resource"))
|
||||
validateContains(errors, ei.path, ei.definition, definition, ei.element, localStack, !isBundleEntry(ei.path) && !isParametersEntry(ei.path)); // if
|
||||
validateContains(errors, ei.path, ei.definition, definition, ei.element, localStack, !isBundleEntry(ei.path) && !isParametersEntry(ei.path) && shouldCheckForIdPresence); // if
|
||||
// (str.matches(".*([.,/])work\\1$"))
|
||||
else {
|
||||
StructureDefinition p = getProfileForType(type);
|
||||
|
|
Loading…
Reference in New Issue