View Javadoc
1   package ca.uhn.fhir.jpa.dao.r4;
2   
3   /*
4    * #%L
5    * HAPI FHIR JPA Server
6    * %%
7    * Copyright (C) 2014 - 2018 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   * http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import ca.uhn.fhir.context.RuntimeResourceDefinition;
24  import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
25  import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSubscription;
26  import ca.uhn.fhir.jpa.dao.data.ISubscriptionTableDao;
27  import ca.uhn.fhir.jpa.entity.ResourceTable;
28  import ca.uhn.fhir.jpa.entity.SubscriptionTable;
29  import ca.uhn.fhir.parser.DataFormatException;
30  import ca.uhn.fhir.rest.api.EncodingEnum;
31  import ca.uhn.fhir.rest.api.server.RequestDetails;
32  import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
33  import org.apache.commons.lang3.ObjectUtils;
34  import org.hl7.fhir.instance.model.api.IBaseResource;
35  import org.hl7.fhir.instance.model.api.IIdType;
36  import org.hl7.fhir.r4.model.Subscription;
37  import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
38  import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
39  import org.springframework.beans.factory.annotation.Autowired;
40  
41  import javax.annotation.Nullable;
42  import java.util.Date;
43  
44  import static org.apache.commons.lang3.StringUtils.isBlank;
45  
46  public class FhirResourceDaoSubscriptionR4 extends FhirResourceDaoR4<Subscription> implements IFhirResourceDaoSubscription<Subscription> {
47  
48  	@Autowired
49  	private ISubscriptionTableDao mySubscriptionTableDao;
50  
51  	private void createSubscriptionTable(ResourceTable theEntity, Subscription theSubscription) {
52  		SubscriptionTable subscriptionEntity = new SubscriptionTable();
53  		subscriptionEntity.setCreated(new Date());
54  		subscriptionEntity.setSubscriptionResource(theEntity);
55  		myEntityManager.persist(subscriptionEntity);
56  	}
57  
58  	@Override
59  	public Long getSubscriptionTablePidForSubscriptionResource(IIdType theId) {
60  		ResourceTable entity = readEntityLatestVersion(theId);
61  		SubscriptionTable table = mySubscriptionTableDao.findOneByResourcePid(entity.getId());
62  		if (table == null) {
63  			return null;
64  		}
65  		return table.getId();
66  	}
67  
68  
69  	@Override
70  	protected void postPersist(ResourceTable theEntity, Subscription theSubscription) {
71  		super.postPersist(theEntity, theSubscription);
72  
73  		createSubscriptionTable(theEntity, theSubscription);
74  	}
75  
76  
77  	@Override
78  	protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
79  													 Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
80  		ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
81  
82  		if (theDeletedTimestampOrNull != null) {
83  			Long subscriptionId = getSubscriptionTablePidForSubscriptionResource(theEntity.getIdDt());
84  			if (subscriptionId != null) {
85  				mySubscriptionTableDao.deleteAllForSubscription(retVal);
86  			}
87  		}
88  
89  		return retVal;
90  	}
91  
92  	protected void validateChannelEndpoint(Subscription theResource) {
93  		if (isBlank(theResource.getChannel().getEndpoint())) {
94  			throw new UnprocessableEntityException("Rest-hook subscriptions must have Subscription.channel.endpoint defined");
95  		}
96  	}
97  
98  	protected void validateChannelPayload(Subscription theResource) {
99  		if (!isBlank(theResource.getChannel().getPayload()) && EncodingEnum.forContentType(theResource.getChannel().getPayload()) == null) {
100 			throw new UnprocessableEntityException("Invalid value for Subscription.channel.payload: " + theResource.getChannel().getPayload());
101 		}
102 	}
103 
104 	@Nullable
105 	public RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(Subscription theResource) {
106 		switch (ObjectUtils.defaultIfNull(theResource.getStatus(), Subscription.SubscriptionStatus.OFF)) {
107 			case REQUESTED:
108 			case ACTIVE:
109 				break;
110 			case ERROR:
111 			case OFF:
112 			case NULL:
113 				return null;
114 		}
115 
116 		String query = theResource.getCriteria();
117 		if (isBlank(query)) {
118 			throw new UnprocessableEntityException("Subscription.criteria must be populated");
119 		}
120 
121 		int sep = query.indexOf('?');
122 		if (sep <= 1) {
123 			throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
124 		}
125 
126 		String resType = query.substring(0, sep);
127 		if (resType.contains("/")) {
128 			throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
129 		}
130 
131 		if (theResource.getChannel().getType() == null) {
132 			throw new UnprocessableEntityException("Subscription.channel.type must be populated");
133 		} else if (theResource.getChannel().getType() == SubscriptionChannelType.RESTHOOK) {
134 			validateChannelPayload(theResource);
135 			validateChannelEndpoint(theResource);
136 		}
137 
138 		RuntimeResourceDefinition resDef;
139 		try {
140 			resDef = getContext().getResourceDefinition(resType);
141 		} catch (DataFormatException e) {
142 			throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resType);
143 		}
144 		return resDef;
145 	}
146 
147 	@Override
148 	protected void validateResourceForStorage(Subscription theResource, ResourceTable theEntityToSave) {
149 		super.validateResourceForStorage(theResource, theEntityToSave);
150 
151 		RuntimeResourceDefinition resDef = validateCriteriaAndReturnResourceDefinition(theResource);
152 		if (resDef == null) {
153 			return;
154 		}
155 
156 		IFhirResourceDao<? extends IBaseResource> dao = getDao(resDef.getImplementingClass());
157 		if (dao == null) {
158 			throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resDef);
159 		}
160 
161 		if (theResource.getChannel().getType() == null) {
162 			throw new UnprocessableEntityException("Subscription.channel.type must be populated on this server");
163 		}
164 
165 		SubscriptionStatus status = theResource.getStatus();
166 		if (status == null) {
167 			throw new UnprocessableEntityException("Subscription.status must be populated on this server");
168 		}
169 
170 	}
171 
172 }