View Javadoc
1   package ca.uhn.fhir.jpa.dao.dstu3;
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.dstu3.model.Subscription;
35  import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType;
36  import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus;
37  import org.hl7.fhir.instance.model.api.IBaseResource;
38  import org.hl7.fhir.instance.model.api.IIdType;
39  import org.springframework.beans.factory.annotation.Autowired;
40  import org.springframework.transaction.PlatformTransactionManager;
41  
42  import java.util.Date;
43  
44  import static org.apache.commons.lang3.StringUtils.isBlank;
45  
46  public class FhirResourceDaoSubscriptionDstu3 extends FhirResourceDaoDstu3<Subscription> implements IFhirResourceDaoSubscription<Subscription> {
47  
48  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSubscriptionDstu3.class);
49  
50  	@Autowired
51  	private ISubscriptionTableDao mySubscriptionTableDao;
52  
53  	@Autowired
54  	private PlatformTransactionManager myTxManager;
55  
56  	private void createSubscriptionTable(ResourceTable theEntity, Subscription theSubscription) {
57  		SubscriptionTable subscriptionEntity = new SubscriptionTable();
58  		subscriptionEntity.setCreated(new Date());
59  		subscriptionEntity.setSubscriptionResource(theEntity);
60  		myEntityManager.persist(subscriptionEntity);
61  	}
62  
63  	@Override
64  	public Long getSubscriptionTablePidForSubscriptionResource(IIdType theId) {
65  		ResourceTable entity = readEntityLatestVersion(theId);
66  		SubscriptionTable table = mySubscriptionTableDao.findOneByResourcePid(entity.getId());
67  		if (table == null) {
68  			return null;
69  		}
70  		return table.getId();
71  	}
72  
73  
74  
75  
76  	@Override
77  	protected void postPersist(ResourceTable theEntity, Subscription theSubscription) {
78  		super.postPersist(theEntity, theSubscription);
79  
80  		createSubscriptionTable(theEntity, theSubscription);
81  	}
82  
83  
84  
85  	@Override
86  	protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
87  													 Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
88  		ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
89  
90  		if (theDeletedTimestampOrNull != null) {
91  			mySubscriptionTableDao.deleteAllForSubscription(theEntity);
92  		}
93  		return retVal;
94  	}
95  
96  	protected void validateChannelEndpoint(Subscription theResource) {
97  		if (isBlank(theResource.getChannel().getEndpoint())) {
98  			throw new UnprocessableEntityException("Rest-hook subscriptions must have Subscription.channel.endpoint defined");
99  		}
100 	}
101 
102 	protected void validateChannelPayload(Subscription theResource) {
103 		if (!isBlank(theResource.getChannel().getPayload()) && EncodingEnum.forContentType(theResource.getChannel().getPayload()) == null) {
104 			throw new UnprocessableEntityException("Invalid value for Subscription.channel.payload: " + theResource.getChannel().getPayload());
105 		}
106 	}
107 
108 	public RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(Subscription theResource) {
109 		switch (ObjectUtils.defaultIfNull(theResource.getStatus(), SubscriptionStatus.OFF)) {
110 			case REQUESTED:
111 			case ACTIVE:
112 				break;
113 			case ERROR:
114 			case OFF:
115 			case NULL:
116 				return null;
117 		}
118 
119 		String query = theResource.getCriteria();
120 		if (isBlank(query)) {
121 			throw new UnprocessableEntityException("Subscription.criteria must be populated");
122 		}
123 
124 		int sep = query.indexOf('?');
125 		if (sep <= 1) {
126 			throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
127 		}
128 
129 		String resType = query.substring(0, sep);
130 		if (resType.contains("/")) {
131 			throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
132 		}
133 
134 		if (theResource.getChannel().getType() == null) {
135 			throw new UnprocessableEntityException("Subscription.channel.type must be populated");
136 		} else if (theResource.getChannel().getType() == SubscriptionChannelType.RESTHOOK) {
137 			validateChannelPayload(theResource);
138 			validateChannelEndpoint(theResource);
139 		}
140 
141 		RuntimeResourceDefinition resDef;
142 		try {
143 			resDef = getContext().getResourceDefinition(resType);
144 		} catch (DataFormatException e) {
145 			throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resType);
146 		}
147 		return resDef;
148 	}
149 
150 	@Override
151 	protected void validateResourceForStorage(Subscription theResource, ResourceTable theEntityToSave) {
152 		super.validateResourceForStorage(theResource, theEntityToSave);
153 
154 		RuntimeResourceDefinition resDef = validateCriteriaAndReturnResourceDefinition(theResource);
155 		if (resDef == null) {
156 			return;
157 		}
158 
159 		IFhirResourceDao<? extends IBaseResource> dao = getDao(resDef.getImplementingClass());
160 		if (dao == null) {
161 			throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resDef);
162 		}
163 
164 		if (theResource.getChannel().getType() == null) {
165 			throw new UnprocessableEntityException("Subscription.channel.type must be populated on this server");
166 		}
167 
168 		SubscriptionStatus status = theResource.getStatus();
169 		if (status == null) {
170 			throw new UnprocessableEntityException("Subscription.status must be populated on this server");
171 		}
172 
173 	}
174 
175 }