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}