001package ca.uhn.fhir.test.utilities;
002
003/*-
004 * #%L
005 * HAPI FHIR Test Utilities
006 * %%
007 * Copyright (C) 2014 - 2023 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.context.FhirContext;
024import ca.uhn.fhir.context.FhirVersionEnum;
025import ca.uhn.fhir.i18n.Msg;
026import ca.uhn.fhir.interceptor.api.HookParams;
027import ca.uhn.fhir.rest.annotation.Transaction;
028import ca.uhn.fhir.rest.annotation.TransactionParam;
029import ca.uhn.fhir.rest.api.MethodOutcome;
030import ca.uhn.fhir.rest.api.server.RequestDetails;
031import ca.uhn.fhir.rest.server.IResourceProvider;
032import ca.uhn.fhir.rest.server.IServerAddressStrategy;
033import ca.uhn.fhir.rest.server.RestfulServer;
034import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
035import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
036import ca.uhn.test.concurrency.IPointcutLatch;
037import ca.uhn.test.concurrency.PointcutLatch;
038import org.hl7.fhir.dstu3.model.ConceptMap;
039import org.hl7.fhir.dstu3.model.Observation;
040import org.hl7.fhir.dstu3.model.Organization;
041import org.hl7.fhir.dstu3.model.Patient;
042import org.hl7.fhir.dstu3.model.Reference;
043import org.hl7.fhir.instance.model.api.IBaseBundle;
044import org.hl7.fhir.instance.model.api.IBaseResource;
045import org.hl7.fhir.instance.model.api.IIdType;
046import org.junit.jupiter.api.extension.AfterEachCallback;
047import org.junit.jupiter.api.extension.BeforeEachCallback;
048import org.junit.jupiter.api.extension.ExtensionContext;
049
050import javax.servlet.ServletException;
051import java.util.ArrayList;
052import java.util.Collections;
053import java.util.List;
054
055public class RestServerDstu3Helper extends BaseRestServerHelper implements IPointcutLatch, BeforeEachCallback, AfterEachCallback {
056        protected final MyRestfulServer myRestServer;
057
058        public RestServerDstu3Helper() {
059                this(false);
060        }
061
062        public RestServerDstu3Helper(boolean theInitialize) {
063                super(FhirContext.forDstu3());
064                myRestServer = new MyRestfulServer(myFhirContext);
065                if(theInitialize){
066                        try {
067                                myRestServer.initialize();
068                        } catch (ServletException e) {
069                                throw new RuntimeException(Msg.code(2112)+"Failed to initialize server", e);
070                        }
071                }
072        }
073
074        @Override
075        public void beforeEach(ExtensionContext context) throws Exception {
076                startServer(myRestServer);
077        }
078
079        @Override
080        public void afterEach(ExtensionContext context) throws Exception {
081                super.afterEach();
082                myRestServer.getInterceptorService().unregisterAllAnonymousInterceptors();
083                myRestServer.clearDataAndCounts();
084        }
085
086        @Override
087        public void clearDataAndCounts() {
088                myRestServer.clearDataAndCounts();
089        }
090
091        @Override
092        public void setFailNextPut(boolean theFailNextPut) {
093                myRestServer.setFailNextPut(theFailNextPut);
094        }
095
096        @Override
097        public List<Object> getInterceptors() {
098                return myRestServer.getInterceptorService().getAllRegisteredInterceptors();
099        }
100
101        @Override
102        public void unregisterInterceptor(Object theInterceptor) {
103                myRestServer.getInterceptorService().unregisterInterceptor(theInterceptor);
104        }
105
106        @Override
107        public void clearCounts() {
108                myRestServer.clearCounts();
109        }
110
111        @Override
112        public long getPatientCountSearch() {
113                return myRestServer.getPatientResourceProvider().getCountSearch();
114        }
115
116        @Override
117        public long getPatientCountDelete() {
118                return myRestServer.getPatientResourceProvider().getCountDelete();
119        }
120
121        @Override
122        public long getPatientCountUpdate() {
123                return myRestServer.getPatientResourceProvider().getCountUpdate();
124        }
125
126        @Override
127        public long getPatientCountRead() {
128                return myRestServer.getPatientResourceProvider().getCountRead();
129        }
130
131        @Override
132        public long getObservationCountSearch() {
133                return myRestServer.getObservationResourceProvider().getCountSearch();
134        }
135
136        @Override
137        public long getObservationCountDelete() {
138                return myRestServer.getObservationResourceProvider().getCountDelete();
139        }
140
141        @Override
142        public long getObservationCountUpdate() {
143                return myRestServer.getObservationResourceProvider().getCountUpdate();
144        }
145
146        @Override
147        public long getObservationCountRead() {
148                return myRestServer.getObservationResourceProvider().getCountRead();
149        }
150
151        @Override
152        public boolean registerInterceptor(Object theInterceptor) {
153                return myRestServer.getInterceptorService().registerInterceptor(theInterceptor);
154        }
155
156        @Override
157        public void clear() {
158                myRestServer.getPlainProvider().clear();
159        }
160
161        @Override
162        public void setExpectedCount(int theCount) {
163                myRestServer.getPlainProvider().setExpectedCount(theCount);
164        }
165
166        @Override
167        public List<HookParams> awaitExpected() throws InterruptedException {
168                return myRestServer.getPlainProvider().awaitExpected();
169        }
170
171        public void registerProvider(Object theProvider) {
172                myRestServer.registerProvider(theProvider);
173        }
174
175        public static class MyPlainProvider implements IPointcutLatch {
176                private final PointcutLatch myPointcutLatch = new PointcutLatch("Transaction Counting Provider");
177                private final List<IBaseBundle> myTransactions = Collections.synchronizedList(new ArrayList<>());
178
179                @Transaction
180                public synchronized IBaseBundle transaction(@TransactionParam IBaseBundle theBundle) {
181                        myPointcutLatch.call(theBundle);
182                        myTransactions.add(theBundle);
183                        return theBundle;
184                }
185
186                @Override
187                public void clear() {
188                        myPointcutLatch.clear();
189                }
190
191                @Override
192                public void setExpectedCount(int theCount) {
193                        myPointcutLatch.setExpectedCount(theCount);
194                }
195
196                @Override
197                public List<HookParams> awaitExpected() throws InterruptedException {
198                        return myPointcutLatch.awaitExpected();
199                }
200
201                public List<IBaseBundle> getTransactions() {
202                        return Collections.unmodifiableList(new ArrayList<>(myTransactions));
203                }
204        }
205
206        private static class MyRestfulServer extends RestfulServer {
207                private boolean myFailNextPut;
208                private HashMapResourceProvider<Patient> myPatientResourceProvider;
209                private HashMapResourceProvider<Observation> myObservationResourceProvider;
210                private HashMapResourceProvider<Organization> myOrganizationResourceProvider;
211                private HashMapResourceProvider<ConceptMap> myConceptMapResourceProvider;
212                private MyPlainProvider myPlainProvider;
213
214                public MyRestfulServer(FhirContext theFhirContext) {
215                        super(theFhirContext);
216                }
217
218                public MyPlainProvider getPlainProvider() {
219                        return myPlainProvider;
220                }
221
222                public void setFailNextPut(boolean theFailNextPut) {
223                        myFailNextPut = theFailNextPut;
224                }
225
226                public void clearCounts() {
227                        for (IResourceProvider next : getResourceProviders()) {
228                                if (next instanceof HashMapResourceProvider) {
229                                        HashMapResourceProvider provider = (HashMapResourceProvider) next;
230                                        provider.clearCounts();
231                                }
232                        }
233                        myPlainProvider.clear();
234                }
235
236                public void clearDataAndCounts() {
237                        for (IResourceProvider next : getResourceProviders()) {
238                                if (next instanceof HashMapResourceProvider) {
239                                        HashMapResourceProvider provider = (HashMapResourceProvider) next;
240                                        provider.clear();
241                                }
242                        }
243                        clearCounts();
244                }
245
246                public HashMapResourceProvider<Observation> getObservationResourceProvider() {
247                        return myObservationResourceProvider;
248                }
249
250                public HashMapResourceProvider<Organization> getOrganizationResourceProvider() {
251                        return myOrganizationResourceProvider;
252                }
253
254                public HashMapResourceProvider<ConceptMap> getConceptMapResourceProvider() {
255                        return myConceptMapResourceProvider;
256                }
257
258                public HashMapResourceProvider<Patient> getPatientResourceProvider() {
259                        return myPatientResourceProvider;
260                }
261
262                @Override
263                protected void initialize() throws ServletException {
264                        super.initialize();
265
266                        FhirContext fhirContext = getFhirContext();
267                        myPatientResourceProvider = new MyHashMapResourceProvider(fhirContext, Patient.class);
268                        registerProvider(myPatientResourceProvider);
269                        myObservationResourceProvider = new MyHashMapResourceProvider(fhirContext, Observation.class);
270                        registerProvider(myObservationResourceProvider);
271                        myOrganizationResourceProvider = new MyHashMapResourceProvider(fhirContext, Organization.class);
272                        registerProvider(myOrganizationResourceProvider);
273                        myConceptMapResourceProvider = new MyHashMapResourceProvider(fhirContext, ConceptMap.class);
274                        registerProvider(myConceptMapResourceProvider);
275
276                        myPlainProvider = new MyPlainProvider();
277                        registerProvider(myPlainProvider);
278                }
279
280                public void setConceptMapResourceProvider(HashMapResourceProvider<ConceptMap> theResourceProvider) {
281                        myConceptMapResourceProvider.getStoredResources().forEach(c -> theResourceProvider.store(c));
282
283                        unregisterProvider(myConceptMapResourceProvider);
284                        registerProvider(theResourceProvider);
285                        myConceptMapResourceProvider = theResourceProvider;
286                }
287
288
289                public class MyHashMapResourceProvider<T extends IBaseResource> extends HashMapResourceProvider<T> {
290                        public MyHashMapResourceProvider(FhirContext theContext, Class theType) {
291                                super(theContext, theType);
292                        }
293
294                        @Override
295                        public MethodOutcome update(T theResource, String theConditional, RequestDetails theRequestDetails) {
296                                if (myFailNextPut) {
297                                        throw new PreconditionFailedException(Msg.code(2113)+"Failed update operation");
298                                }
299                                return super.update(theResource, theConditional, theRequestDetails);
300                        }
301                }
302        }
303
304        @Override
305        public HashMapResourceProvider<Observation> getObservationResourceProvider() {
306                return myRestServer.getObservationResourceProvider();
307        }
308
309        @Override
310        public HashMapResourceProvider<Patient> getPatientResourceProvider() {
311                return myRestServer.getPatientResourceProvider();
312        }
313
314        @Override
315        public HashMapResourceProvider<ConceptMap> getConceptMapResourceProvider() {
316                return myRestServer.getConceptMapResourceProvider();
317        }
318
319        @Override
320        public IIdType createPatientWithId(String theId) {
321                Patient patient = new Patient();
322                patient.setId("Patient/" + theId);
323                patient.addIdentifier().setSystem("http://foo").setValue(theId);
324                return this.createPatient(patient);
325        }
326
327        @Override
328        public IIdType createPatient(IBaseResource theBaseResource) {
329                return myRestServer.getPatientResourceProvider().store((Patient) theBaseResource);
330
331        }
332
333        @Override
334        public IIdType createObservationForPatient(IIdType theFirstTargetPatientId) {
335                Observation observation = new Observation();
336                observation.setSubject(new Reference(theFirstTargetPatientId));
337                return this.createObservation(observation);
338
339        }
340
341        @Override
342        public IIdType createObservation(IBaseResource theBaseResource) {
343                return myRestServer.getObservationResourceProvider().store((Observation) theBaseResource);
344        }
345
346        @Override
347        protected void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy) {
348                myRestServer.setServerAddressStrategy(theServerAddressStrategy);
349        }
350
351        public void setConceptMapResourceProvider(HashMapResourceProvider<ConceptMap> theResourceProvider) {
352                myRestServer.setConceptMapResourceProvider(theResourceProvider);
353        }
354}