View Javadoc
1   /*
2    * Copyright 2009 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.openehealth.ipf.commons.ihe.xds.core.validate.requests;
17  
18  import org.openehealth.ipf.commons.core.modules.api.Validator;
19  import org.openehealth.ipf.commons.ihe.core.InteractionId;
20  import org.openehealth.ipf.commons.ihe.xds.core.ebxml.EbXMLAdhocQueryRequest;
21  import org.openehealth.ipf.commons.ihe.xds.core.requests.query.QueryReturnType;
22  import org.openehealth.ipf.commons.ihe.xds.core.requests.query.QueryType;
23  import org.openehealth.ipf.commons.ihe.xds.core.transform.requests.QueryParameter;
24  import org.openehealth.ipf.commons.ihe.xds.core.validate.*;
25  import org.openehealth.ipf.commons.ihe.xds.core.validate.query.*;
26  
27  import java.util.*;
28  import java.util.stream.Collectors;
29  
30  import static org.apache.commons.lang3.Validate.notNull;
31  import static org.openehealth.ipf.commons.ihe.xds.XCA.Interactions.ITI_38;
32  import static org.openehealth.ipf.commons.ihe.xds.XCF.Interactions.ITI_63;
33  import static org.openehealth.ipf.commons.ihe.xds.XDS.Interactions.ITI_18;
34  import static org.openehealth.ipf.commons.ihe.xds.XDS.Interactions.ITI_51;
35  import static org.openehealth.ipf.commons.ihe.xds.core.requests.query.QueryType.*;
36  import static org.openehealth.ipf.commons.ihe.xds.core.transform.requests.QueryParameter.*;
37  import static org.openehealth.ipf.commons.ihe.xds.core.validate.ValidationMessage.*;
38  import static org.openehealth.ipf.commons.ihe.xds.core.validate.ValidatorAssertions.metaDataAssert;
39  
40  /**
41   * Validates an {@link EbXMLAdhocQueryRequest}.
42   *
43   * @author Jens Riemschneider
44   */
45  public class AdhocQueryRequestValidator implements Validator<EbXMLAdhocQueryRequest, ValidationProfile> {
46      private static final CXValidator cxValidator = new CXValidator(true);
47      private static final TimeValidator timeValidator = new TimeValidator();
48      private static final NopValidator nopValidator = new NopValidator();
49  
50  
51      private static void addAllowedMultipleSlots(QueryType queryType, QueryParameter... parameters) {
52          Set<String> slotNames = Arrays.stream(parameters)
53                  .map(QueryParameter::getSlotName)
54                  .collect(Collectors.toSet());
55          ALLOWED_MULTIPLE_SLOTS.put(queryType, slotNames);
56      }
57  
58  
59      private static final Map<QueryType, Set<String>> ALLOWED_MULTIPLE_SLOTS;
60  
61      static {
62          ALLOWED_MULTIPLE_SLOTS = new EnumMap<>(QueryType.class);
63  
64          addAllowedMultipleSlots(FIND_DOCUMENTS,
65                  DOC_ENTRY_EVENT_CODE,
66                  DOC_ENTRY_CONFIDENTIALITY_CODE);
67  
68          addAllowedMultipleSlots(FIND_DOCUMENTS_BY_REFERENCE_ID,
69                  DOC_ENTRY_REFERENCE_IDS,
70                  DOC_ENTRY_EVENT_CODE,
71                  DOC_ENTRY_CONFIDENTIALITY_CODE);
72  
73          addAllowedMultipleSlots(FIND_DOCUMENTS_MPQ,
74                  DOC_ENTRY_EVENT_CODE,
75                  DOC_ENTRY_CONFIDENTIALITY_CODE);
76  
77          addAllowedMultipleSlots(FIND_FOLDERS,
78                  FOLDER_CODES);
79  
80          addAllowedMultipleSlots(FIND_FOLDERS_MPQ,
81                  FOLDER_CODES);
82  
83          addAllowedMultipleSlots(GET_ALL,
84                  DOC_ENTRY_CONFIDENTIALITY_CODE);
85  
86          addAllowedMultipleSlots(GET_SUBMISSION_SET_AND_CONTENTS,
87                  DOC_ENTRY_CONFIDENTIALITY_CODE);
88  
89          addAllowedMultipleSlots(GET_FOLDER_AND_CONTENTS,
90                  DOC_ENTRY_CONFIDENTIALITY_CODE);
91  
92          addAllowedMultipleSlots(FETCH,
93                  DOC_ENTRY_EVENT_CODE,
94                  DOC_ENTRY_CONFIDENTIALITY_CODE);
95      }
96  
97  
98      private static final Map<InteractionId, Set<QueryType>> ALLOWED_QUERY_TYPES;
99  
100     static {
101         Set<QueryType> storedQueryTypes = EnumSet.of(
102                 FIND_DOCUMENTS,
103                 FIND_DOCUMENTS_BY_REFERENCE_ID,
104                 FIND_SUBMISSION_SETS,
105                 FIND_FOLDERS,
106                 GET_ALL,
107                 GET_DOCUMENTS,
108                 GET_FOLDERS,
109                 GET_ASSOCIATIONS,
110                 GET_DOCUMENTS_AND_ASSOCIATIONS,
111                 GET_SUBMISSION_SETS,
112                 GET_SUBMISSION_SET_AND_CONTENTS,
113                 GET_FOLDER_AND_CONTENTS,
114                 GET_FOLDERS_FOR_DOCUMENT,
115                 GET_RELATED_DOCUMENTS);
116 
117         ALLOWED_QUERY_TYPES = new HashMap<>(5);
118         ALLOWED_QUERY_TYPES.put(ITI_18, storedQueryTypes);
119         ALLOWED_QUERY_TYPES.put(ITI_38, storedQueryTypes);
120         ALLOWED_QUERY_TYPES.put(ITI_51, EnumSet.of(FIND_DOCUMENTS_MPQ, FIND_FOLDERS_MPQ));
121         ALLOWED_QUERY_TYPES.put(ITI_63, EnumSet.of(FETCH));
122     }
123 
124 
125     private QueryParameterValidation[] getValidators(QueryType queryType, ValidationProfile profile) {
126         boolean requireHomeCommunityId = profile.getInteractionProfile().requiresHomeCommunityId();
127 
128         switch (queryType) {
129             case FETCH:
130                 return new QueryParameterValidation[]{
131                         new StringValidation(DOC_ENTRY_PATIENT_ID, cxValidator, false),
132                         new CodeValidation(DOC_ENTRY_CLASS_CODE, false),
133                         new CodeValidation(DOC_ENTRY_TYPE_CODE),
134                         new CodeValidation(DOC_ENTRY_PRACTICE_SETTING_CODE),
135                         new CodeValidation(DOC_ENTRY_HEALTHCARE_FACILITY_TYPE_CODE),
136                         new CodeValidation(DOC_ENTRY_FORMAT_CODE),
137                         new NumberValidation(DOC_ENTRY_CREATION_TIME_FROM, timeValidator),
138                         new NumberValidation(DOC_ENTRY_CREATION_TIME_TO, timeValidator),
139                         new NumberValidation(DOC_ENTRY_SERVICE_START_TIME_FROM, timeValidator),
140                         new NumberValidation(DOC_ENTRY_SERVICE_START_TIME_TO, timeValidator),
141                         new NumberValidation(DOC_ENTRY_SERVICE_STOP_TIME_FROM, timeValidator),
142                         new NumberValidation(DOC_ENTRY_SERVICE_STOP_TIME_TO, timeValidator),
143                         new QueryListCodeValidation(DOC_ENTRY_EVENT_CODE, DOC_ENTRY_EVENT_CODE_SCHEME),
144                         new QueryListCodeValidation(DOC_ENTRY_CONFIDENTIALITY_CODE, DOC_ENTRY_CONFIDENTIALITY_CODE_SCHEME),
145                         new StringListValidation(DOC_ENTRY_AUTHOR_PERSON, nopValidator),
146                         new HomeCommunityIdValidation(true),
147                 };
148 
149             case FIND_DOCUMENTS:
150             case FIND_DOCUMENTS_MPQ:
151                 return new QueryParameterValidation[]{
152                         // PatientId MUST BE supplied in single patient query.
153                         // PatientId (list) MAY BE supplied in multi patient query.
154                         // The validators for the two cases are otherwise identical.
155                         queryType.equals(FIND_DOCUMENTS)
156                                 ? new StringValidation(DOC_ENTRY_PATIENT_ID, cxValidator, false)
157                                 : new StringListValidation(DOC_ENTRY_PATIENT_ID, cxValidator),
158                         new CodeValidation(DOC_ENTRY_CLASS_CODE),
159                         new CodeValidation(DOC_ENTRY_TYPE_CODE),
160                         new CodeValidation(DOC_ENTRY_PRACTICE_SETTING_CODE),
161                         new CodeValidation(DOC_ENTRY_HEALTHCARE_FACILITY_TYPE_CODE),
162                         new CodeValidation(DOC_ENTRY_FORMAT_CODE),
163                         new NumberValidation(DOC_ENTRY_CREATION_TIME_FROM, timeValidator),
164                         new NumberValidation(DOC_ENTRY_CREATION_TIME_TO, timeValidator),
165                         new NumberValidation(DOC_ENTRY_SERVICE_START_TIME_FROM, timeValidator),
166                         new NumberValidation(DOC_ENTRY_SERVICE_START_TIME_TO, timeValidator),
167                         new NumberValidation(DOC_ENTRY_SERVICE_STOP_TIME_FROM, timeValidator),
168                         new NumberValidation(DOC_ENTRY_SERVICE_STOP_TIME_TO, timeValidator),
169                         new QueryListCodeValidation(DOC_ENTRY_EVENT_CODE, DOC_ENTRY_EVENT_CODE_SCHEME),
170                         new QueryListCodeValidation(DOC_ENTRY_CONFIDENTIALITY_CODE, DOC_ENTRY_CONFIDENTIALITY_CODE_SCHEME),
171                         new StringListValidation(DOC_ENTRY_AUTHOR_PERSON, nopValidator),
172                         new StatusValidation(DOC_ENTRY_STATUS),
173                         new DocumentEntryTypeValidation(),
174                 };
175 
176             case FIND_DOCUMENTS_BY_REFERENCE_ID:
177                 return new QueryParameterValidation[]{
178                         new StringValidation(DOC_ENTRY_PATIENT_ID, cxValidator, false),
179                         new CodeValidation(DOC_ENTRY_CLASS_CODE),
180                         new CodeValidation(DOC_ENTRY_TYPE_CODE),
181                         new CodeValidation(DOC_ENTRY_PRACTICE_SETTING_CODE),
182                         new CodeValidation(DOC_ENTRY_HEALTHCARE_FACILITY_TYPE_CODE),
183                         new CodeValidation(DOC_ENTRY_FORMAT_CODE),
184                         new NumberValidation(DOC_ENTRY_CREATION_TIME_FROM, timeValidator),
185                         new NumberValidation(DOC_ENTRY_CREATION_TIME_TO, timeValidator),
186                         new NumberValidation(DOC_ENTRY_SERVICE_START_TIME_FROM, timeValidator),
187                         new NumberValidation(DOC_ENTRY_SERVICE_START_TIME_TO, timeValidator),
188                         new NumberValidation(DOC_ENTRY_SERVICE_STOP_TIME_FROM, timeValidator),
189                         new NumberValidation(DOC_ENTRY_SERVICE_STOP_TIME_TO, timeValidator),
190                         new QueryListCodeValidation(DOC_ENTRY_EVENT_CODE, DOC_ENTRY_EVENT_CODE_SCHEME),
191                         new QueryListCodeValidation(DOC_ENTRY_CONFIDENTIALITY_CODE, DOC_ENTRY_CONFIDENTIALITY_CODE_SCHEME),
192                         new StringListValidation(DOC_ENTRY_AUTHOR_PERSON, nopValidator),
193                         new StatusValidation(DOC_ENTRY_STATUS),
194                         new DocumentEntryTypeValidation(),
195                         new StringListValidation(DOC_ENTRY_REFERENCE_IDS, nopValidator),
196                 };
197 
198             case FIND_SUBMISSION_SETS:
199                 return new QueryParameterValidation[]{
200                         new StringValidation(SUBMISSION_SET_PATIENT_ID, cxValidator, false),
201                         // Excluded to avoid validation errors for xdstest requests
202                         // new StringListValidation(SUBMISSION_SET_SOURCE_ID, oidValidator),
203                         new NumberValidation(SUBMISSION_SET_SUBMISSION_TIME_FROM, timeValidator),
204                         new NumberValidation(SUBMISSION_SET_SUBMISSION_TIME_TO, timeValidator),
205                         new StringValidation(SUBMISSION_SET_AUTHOR_PERSON, nopValidator, true),
206                         new CodeValidation(SUBMISSION_SET_CONTENT_TYPE_CODE),
207                         new StatusValidation(SUBMISSION_SET_STATUS),
208                 };
209 
210             case FIND_FOLDERS:
211             case FIND_FOLDERS_MPQ:
212                 return new QueryParameterValidation[]{
213                         // PatientId MUST BE supplied in  single patient query.
214                         // PatientId (list) MAY BE supplied in multi patient query.
215                         // The validators for the two cases are otherwise identical.
216                         queryType.equals(FIND_FOLDERS) ? new StringValidation(FOLDER_PATIENT_ID, cxValidator, false) : new StringListValidation(FOLDER_PATIENT_ID, cxValidator),
217                         new NumberValidation(FOLDER_LAST_UPDATE_TIME_FROM, timeValidator),
218                         new NumberValidation(FOLDER_LAST_UPDATE_TIME_TO, timeValidator),
219                         new QueryListCodeValidation(FOLDER_CODES, FOLDER_CODES_SCHEME),
220                         new StatusValidation(FOLDER_STATUS),
221                 };
222 
223             case GET_ALL:
224                 return new QueryParameterValidation[]{
225                         new StringValidation(PATIENT_ID, cxValidator, false),
226                         new StatusValidation(DOC_ENTRY_STATUS),
227                         new StatusValidation(SUBMISSION_SET_STATUS),
228                         new StatusValidation(FOLDER_STATUS),
229                         new QueryListCodeValidation(DOC_ENTRY_FORMAT_CODE, DOC_ENTRY_FORMAT_CODE_SCHEME),
230                         new DocumentEntryTypeValidation(),
231                 };
232 
233             case GET_DOCUMENTS:
234                 return new QueryParameterValidation[]{
235                         new HomeCommunityIdValidation(requireHomeCommunityId),
236                         new ChoiceValidation(DOC_ENTRY_UUID, DOC_ENTRY_UNIQUE_ID, DOC_ENTRY_LOGICAL_ID),
237                         new StringListValidation(DOC_ENTRY_UUID, nopValidator),
238                         new StringListValidation(DOC_ENTRY_UNIQUE_ID, nopValidator),
239                 };
240 
241             case GET_DOCUMENTS_AND_ASSOCIATIONS:
242                 return new QueryParameterValidation[]{
243                         new HomeCommunityIdValidation(requireHomeCommunityId),
244                         new ChoiceValidation(DOC_ENTRY_UUID, DOC_ENTRY_UNIQUE_ID),
245                         new StringListValidation(DOC_ENTRY_UUID, nopValidator),
246                         new StringListValidation(DOC_ENTRY_UNIQUE_ID, nopValidator),
247                 };
248 
249             case GET_FOLDERS_FOR_DOCUMENT:
250                 return new QueryParameterValidation[]{
251                         new HomeCommunityIdValidation(requireHomeCommunityId),
252                         new ChoiceValidation(DOC_ENTRY_UUID, DOC_ENTRY_UNIQUE_ID),
253                         new StringValidation(DOC_ENTRY_UUID, nopValidator, true),
254                         new StringValidation(DOC_ENTRY_UNIQUE_ID, nopValidator, true),
255                 };
256 
257             case GET_FOLDERS:
258                 return new QueryParameterValidation[]{
259                         new HomeCommunityIdValidation(requireHomeCommunityId),
260                         new ChoiceValidation(FOLDER_UUID, FOLDER_UNIQUE_ID, FOLDER_LOGICAL_ID),
261                         new StringListValidation(FOLDER_UUID, nopValidator),
262                         new StringListValidation(FOLDER_UNIQUE_ID, nopValidator),
263                 };
264 
265             case GET_ASSOCIATIONS:
266             case GET_SUBMISSION_SETS:
267                 return new QueryParameterValidation[]{
268                         new HomeCommunityIdValidation(requireHomeCommunityId),
269                         new StringListValidation(UUID, nopValidator),
270                 };
271 
272             case GET_SUBMISSION_SET_AND_CONTENTS:
273                 return new QueryParameterValidation[]{
274                         new HomeCommunityIdValidation(requireHomeCommunityId),
275                         new ChoiceValidation(SUBMISSION_SET_UUID, SUBMISSION_SET_UNIQUE_ID),
276                         new StringValidation(SUBMISSION_SET_UUID, nopValidator, true),
277                         new StringValidation(SUBMISSION_SET_UNIQUE_ID, nopValidator, true),
278                         new QueryListCodeValidation(DOC_ENTRY_CONFIDENTIALITY_CODE, DOC_ENTRY_CONFIDENTIALITY_CODE_SCHEME),
279                         new QueryListCodeValidation(DOC_ENTRY_FORMAT_CODE, DOC_ENTRY_FORMAT_CODE_SCHEME),
280                         new DocumentEntryTypeValidation(),
281                 };
282 
283             case GET_FOLDER_AND_CONTENTS:
284                 return new QueryParameterValidation[]{
285                         new HomeCommunityIdValidation(requireHomeCommunityId),
286                         new ChoiceValidation(FOLDER_UUID, FOLDER_UNIQUE_ID),
287                         new StringValidation(FOLDER_UUID, nopValidator, true),
288                         new StringValidation(FOLDER_UNIQUE_ID, nopValidator, true),
289                         new QueryListCodeValidation(DOC_ENTRY_CONFIDENTIALITY_CODE, DOC_ENTRY_CONFIDENTIALITY_CODE_SCHEME),
290                         new QueryListCodeValidation(DOC_ENTRY_FORMAT_CODE, DOC_ENTRY_FORMAT_CODE_SCHEME),
291                         new DocumentEntryTypeValidation(),
292                 };
293 
294             case GET_RELATED_DOCUMENTS:
295                 return new QueryParameterValidation[]{
296                         new HomeCommunityIdValidation(requireHomeCommunityId),
297                         new ChoiceValidation(DOC_ENTRY_UUID, DOC_ENTRY_UNIQUE_ID),
298                         new StringValidation(DOC_ENTRY_UUID, nopValidator, true),
299                         new StringValidation(DOC_ENTRY_UNIQUE_ID, nopValidator, true),
300                         new AssociationValidation(ASSOCIATION_TYPE),
301                         new DocumentEntryTypeValidation(),
302                 };
303         }
304 
305         return null;    // should not occur
306     }
307 
308     @Override
309     public void validate(EbXMLAdhocQueryRequest request, ValidationProfile profile) {
310         notNull(request, "request cannot be null");
311 
312         if (profile == ITI_63) {
313             metaDataAssert(QueryReturnType.LEAF_CLASS_WITH_REPOSITORY_ITEM.getCode().equals(request.getReturnType()),
314                     UNKNOWN_RETURN_TYPE, request.getReturnType());
315         } else {
316             metaDataAssert(QueryReturnType.LEAF_CLASS.getCode().equals(request.getReturnType())
317                             || QueryReturnType.OBJECT_REF.getCode().equals(request.getReturnType()),
318                     UNKNOWN_RETURN_TYPE, request.getReturnType());
319         }
320 
321         QueryType queryType = QueryType.valueOfId(request.getId());
322         metaDataAssert(queryType != null, UNKNOWN_QUERY_TYPE, request.getId());
323 
324         Set<QueryType> allowedQueryTypes = ALLOWED_QUERY_TYPES.getOrDefault(profile.getInteractionId(), Collections.emptySet());
325         metaDataAssert(allowedQueryTypes.contains(queryType), UNSUPPORTED_QUERY_TYPE, queryType);
326 
327         new SlotLengthAndNameUniquenessValidator().validateQuerySlots(
328                 request.getSlots(),
329                 ALLOWED_MULTIPLE_SLOTS.getOrDefault(queryType, Collections.emptySet()));
330         QueryParameterValidation[] validations = getValidators(queryType, profile);
331         if (validations != null) {
332             for (QueryParameterValidation validation : validations) {
333                 validation.validate(request);
334             }
335         }
336 
337         if (queryType == FIND_DOCUMENTS_MPQ) {
338             metaDataAssert(
339                     (!request.getSlotValues(DOC_ENTRY_CLASS_CODE.getSlotName()).isEmpty()) ||
340                             (!request.getSlotValues(DOC_ENTRY_EVENT_CODE.getSlotName()).isEmpty()) ||
341                             (!request.getSlotValues(DOC_ENTRY_HEALTHCARE_FACILITY_TYPE_CODE.getSlotName()).isEmpty()),
342                     ValidationMessage.MISSING_REQUIRED_QUERY_PARAMETER,
343                     "at least one of $XDSDocumentEntryClassCode, $XDSDocumentEntryEventCodeList, $XDSDocumentEntryHealthcareFacilityTypeCode");
344         }
345 
346     }
347 }