View Javadoc
1   package ca.uhn.fhir.jpa.dao.r4;
2   
3   import ca.uhn.fhir.context.FhirContext;
4   import ca.uhn.fhir.context.FhirVersionEnum;
5   import ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor;
6   import ca.uhn.fhir.jpa.dao.DaoConfig;
7   import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter;
8   import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
9   import ca.uhn.fhir.jpa.entity.ResourceTable;
10  import ca.uhn.fhir.parser.DataFormatException;
11  import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
12  import ca.uhn.fhir.util.ElementUtil;
13  import org.hl7.fhir.instance.model.api.IBase;
14  import org.hl7.fhir.instance.model.api.IBaseResource;
15  import org.hl7.fhir.instance.model.api.IPrimitiveType;
16  import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
17  import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
18  import org.hl7.fhir.r4.model.*;
19  import org.hl7.fhir.r4.utils.FHIRLexer;
20  import org.hl7.fhir.r4.utils.FHIRPathEngine;
21  import org.springframework.beans.factory.annotation.Autowired;
22  
23  import java.util.List;
24  
25  import static org.apache.commons.lang3.StringUtils.isBlank;
26  
27  /*
28   * #%L
29   * HAPI FHIR JPA Server
30   * %%
31   * Copyright (C) 2014 - 2018 University Health Network
32   * %%
33   * Licensed under the Apache License, Version 2.0 (the "License");
34   * you may not use this file except in compliance with the License.
35   * You may obtain a copy of the License at
36   * 
37   * http://www.apache.org/licenses/LICENSE-2.0
38   * 
39   * Unless required by applicable law or agreed to in writing, software
40   * distributed under the License is distributed on an "AS IS" BASIS,
41   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42   * See the License for the specific language governing permissions and
43   * limitations under the License.
44   * #L%
45   */
46  
47  public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
48  
49  	public static final DefaultProfileValidationSupport VALIDATION_SUPPORT = new DefaultProfileValidationSupport();
50  	@Autowired
51  	private IFhirSystemDao<Bundle, Meta> mySystemDao;
52  
53  	protected void markAffectedResources(SearchParameter theResource) {
54  		Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
55  		String expression = theResource != null ? theResource.getExpression() : null;
56  		markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression);
57  	}
58  
59  
60  	@Override
61  	protected void postPersist(ResourceTable theEntity, SearchParameter theResource) {
62  		super.postPersist(theEntity, theResource);
63  		markAffectedResources(theResource);
64  	}
65  
66  	@Override
67  	protected void postUpdate(ResourceTable theEntity, SearchParameter theResource) {
68  		super.postUpdate(theEntity, theResource);
69  		markAffectedResources(theResource);
70  	}
71  
72  	@Override
73  	protected void preDelete(SearchParameter theResourceToDelete, ResourceTable theEntityToDelete) {
74  		super.preDelete(theResourceToDelete, theEntityToDelete);
75  		markAffectedResources(theResourceToDelete);
76  	}
77  
78  	@Override
79  	protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
80  		super.validateResourceForStorage(theResource, theEntityToSave);
81  
82  		Enum<?> status = theResource.getStatus();
83  		List<CodeType> base = theResource.getBase();
84  		String expression = theResource.getExpression();
85  		FhirContext context = getContext();
86  		Enum<?> type = theResource.getType();
87  
88  		FhirResourceDaoSearchParameterR4.validateSearchParam(type, status, base, expression, context, getConfig());
89  	}
90  
91  	public static void validateSearchParam(Enum<?> theType, Enum<?> theStatus, List<? extends IPrimitiveType> theBase, String theExpression, FhirContext theContext, DaoConfig theDaoConfig) {
92  		if (theStatus == null) {
93  			throw new UnprocessableEntityException("SearchParameter.status is missing or invalid");
94  		}
95  
96  		if (ElementUtil.isEmpty(theBase) && (theType == null || !Enumerations.SearchParamType.COMPOSITE.name().equals(theType.name()))) {
97  			throw new UnprocessableEntityException("SearchParameter.base is missing");
98  		}
99  
100 		if (theType != null && theType.name().equals(Enumerations.SearchParamType.COMPOSITE.name()) && isBlank(theExpression)) {
101 
102 			// this is ok
103 
104 		} else if (isBlank(theExpression)) {
105 
106 			throw new UnprocessableEntityException("SearchParameter.expression is missing");
107 
108 		} else {
109 
110 			theExpression = theExpression.trim();
111 
112 			if (!theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
113 				String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(theExpression);
114 				for (String nextPath : expressionSplit) {
115 					nextPath = nextPath.trim();
116 
117 					int dotIdx = nextPath.indexOf('.');
118 					if (dotIdx == -1) {
119 						throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". Must start with a resource name");
120 					}
121 
122 					String resourceName = nextPath.substring(0, dotIdx);
123 					try {
124 						theContext.getResourceDefinition(resourceName);
125 					} catch (DataFormatException e) {
126 						throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
127 					}
128 
129 					if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
130 						if (theDaoConfig.isValidateSearchParameterExpressionsOnSave()) {
131 							IBaseResource temporaryInstance = theContext.getResourceDefinition(resourceName).newInstance();
132 							try {
133 								theContext.newFluentPath().evaluate(temporaryInstance, nextPath, IBase.class);
134 							} catch (Exception e) {
135 								String msg = theContext.getLocalizer().getMessage(FhirResourceDaoSearchParameterR4.class, "invalidSearchParamExpression", nextPath, e.getMessage());
136 								throw new UnprocessableEntityException(msg, e);
137 							}
138 						}
139 					}
140 				}
141 
142 			} else {
143 
144 				FHIRPathEngine fhirPathEngine = new FHIRPathEngine(new HapiWorkerContext(theContext, VALIDATION_SUPPORT));
145 				try {
146 					fhirPathEngine.parse(theExpression);
147 				} catch (FHIRLexer.FHIRLexerException e) {
148 					throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + theExpression + "\": " + e.getMessage());
149 				}
150 
151 			}
152 		} // if have expression
153 	}
154 
155 }