4 * Communicates with the Comlink User provisioning api.
7 * @link http://www.open-emr.org
8 * @author Stephen Nielson <snielson@discoverandchange.com>
9 * @copyright Copyright (c) 2022 Comlink Inc <https://comlinkinc.com/>
10 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
13 namespace Comlink\OpenEMR\Modules\TeleHealthModule\Controller
;
15 use Comlink\OpenEMR\Modules\TeleHealthModule\Models\UserVideoRegistrationRequest
;
16 use Comlink\OpenEMR\Modules\TeleHealthModule\Repository\TeleHealthPersonSettingsRepository
;
17 use Comlink\OpenEMR\Modules\TeleHealthModule\Repository\TeleHealthProviderRepository
;
18 use Comlink\OpenEMR\Modules\TeleHealthModule\Repository\TeleHealthUserRepository
;
19 use Comlink\OpenEMR\Modules\TeleHealthModule\Services\TelehealthRegistrationCodeService
;
20 use Comlink\OpenEMR\Modules\TeleHealthModule\Services\TeleHealthRemoteRegistrationService
;
21 use GuzzleHttp\Client
;
22 use GuzzleHttp\Exception\GuzzleException
;
23 use OpenEMR\Common\Database\SqlQueryException
;
24 use OpenEMR\Common\Logging\SystemLogger
;
25 use OpenEMR\Common\Uuid\UuidRegistry
;
26 use OpenEMR\Events\Patient\PatientCreatedEvent
;
27 use OpenEMR\Events\Patient\PatientUpdatedEvent
;
28 use OpenEMR\Events\User\UserCreatedEvent
;
29 use OpenEMR\Events\User\UserUpdatedEvent
;
30 use OpenEMR\Services\PatientService
;
31 use OpenEMR\Services\UserService
;
32 use Symfony\Component\EventDispatcher\EventDispatcher
;
35 class TeleHealthVideoRegistrationController
38 * Repository for saving / retrieving telehealth user settings.
39 * @var TeleHealthUserRepository
41 private $userRepository;
50 * @var TeleHealthProviderRepository
52 private $providerRepository;
56 * @var TeleHealthRemoteRegistrationService
58 private $remoteService;
60 public function __construct(TeleHealthRemoteRegistrationService
$remoteService, TeleHealthProviderRepository
$repo)
62 $this->userRepository
= new TeleHealthUserRepository();
63 $this->remoteService
= $remoteService;
64 $this->providerRepository
= $repo;
65 $this->logger
= new SystemLogger();
68 public function subscribeToEvents(EventDispatcher
$eventDispatcher)
70 $eventDispatcher->addListener(PatientCreatedEvent
::EVENT_HANDLE
, [$this, 'onPatientCreatedEvent']);
71 $eventDispatcher->addListener(PatientUpdatedEvent
::EVENT_HANDLE
, [$this, 'onPatientUpdatedEvent']);
72 $eventDispatcher->addListener(UserCreatedEvent
::EVENT_HANDLE
, [$this, 'onUserCreatedEvent']);
73 $eventDispatcher->addListener(UserUpdatedEvent
::EVENT_HANDLE
, [$this, 'onUserUpdatedEvent']);
76 public function onPatientCreatedEvent(PatientCreatedEvent
$event)
78 $patient = $event->getPatientData();
80 self
::class . "->onPatientCreatedEvent received for patient ",
81 ['uuid' => $patient['uuid'] ??
null, 'patient' => $patient]
84 $patient['uuid'] = UuidRegistry
::uuidToString($patient['uuid']); // convert uuid to a string value
85 $this->createPatientRegistration($patient);
86 } catch (Exception
$exception) {
87 $this->logger
->errorLogCaller("Failed to create patient registration. Error: "
88 . $exception->getMessage(), ['trace' => $exception->getTraceAsString(), 'patient' => $patient['uuid']]);
92 public function onPatientUpdatedEvent(PatientUpdatedEvent
$event)
95 $patient = $event->getNewPatientData();
96 $oldPatient = $event->getDataBeforeUpdate();
97 // we need the patient uuid so we are going to grab it from the pid
98 $patientService = new PatientService();
100 $patient['uuid'] = UuidRegistry
::uuidToString($oldPatient['uuid']); // convert uuid to a string value
101 $this->logger
->debug(
102 self
::class . "->onPatientUpdatedEvent received for patient ",
103 ['uuid' => $patient['uuid'] ??
null, 'patient' => $patient]
105 // let's grab the patient data and create the patient if its not registered
106 $apiUser = $this->userRepository
->getUser($patient['uuid']);
107 if (empty($apiUser)) {
108 $this->createPatientRegistration($patient);
110 } catch (Exception
$exception) {
111 $this->logger
->errorLogCaller("Failed to create patient registration. Error: "
112 . $exception->getMessage(), ['trace' => $exception->getTraceAsString(), 'patient' => $patient['uuid']]);
116 public function onUserCreatedEvent(UserCreatedEvent
$event)
119 $user = $event->getUserData();
120 $userService = new UserService();
121 // our event doesn't have the uuid which is what we need
122 $userWithUuid = $userService->getUserByUsername($event->getUsername());
123 if (empty($userWithUuid)) {
124 throw new \
InvalidArgumentException("Could not find user with username " . $event->getUsername());
127 // we need to find out if we
128 $providerRepo = $this->providerRepository
;
129 // find out if the provider is enabled, if so we create the registration
130 $this->logger
->debug(
131 self
::class . "->onUserCreatedEvent received for user ",
132 ['username' => $event->getUsername(), 'userWithUuid' => $userWithUuid, 'uuid' => $userWithUuid['uuid'] ??
null]
134 if ($providerRepo->isEnabledProvider($userWithUuid['id'])) {
135 $this->createUserRegistration($userWithUuid);
137 $this->logger
->debug(
138 self
::class . "->onUserCreatedEvent skipping registration as user is not enrolled",
139 ['username' => $event->getUsername(), 'userWithUuid' => $userWithUuid, 'uuid' => $userWithUuid['uuid'] ??
null]
142 } catch (Exception
$exception) {
143 $this->logger
->errorLogCaller("Failed to create user registration. Error: "
144 . $exception->getMessage(), ['trace' => $exception->getTraceAsString(), 'user' => $user['uuid']]);
148 public function onUserUpdatedEvent(UserUpdatedEvent
$event)
151 $user = $event->getNewUserData();
152 $userService = new UserService();
153 // our event doesn't have the uuid which is what we need
154 $userWithUuid = $userService->getUser($event->getUserId());
155 if (empty($userWithUuid)) {
156 throw new \
InvalidArgumentException("Could not find user with username " . $event->getUsername());
158 $this->logger
->debug(self
::class . "->onUserUpdatedEvent received for user ", ['uuid' => $userWithUuid['uuid'] ??
null]);
160 $providerRepo = $this->providerRepository
;
162 // create the registration
163 $apiUser = $this->userRepository
->getUser($userWithUuid['uuid']);
165 if ($providerRepo->isEnabledProvider($userWithUuid['id'])) {
166 // create our registration if there is one
167 if (empty($apiUser)) {
168 $this->logger
->debug(self
::class . "->onUserUpdatedEvent registering user with comlink", ['uuid' => $userWithUuid['uuid'] ??
null]);
169 $this->createUserRegistration($userWithUuid);
171 if (!$apiUser->getIsActive()) {
172 $this->logger
->debug(
173 self
::class . "->onUserUpdatedEvent user auth record is suspended, activating",
174 ['uuid' => $userWithUuid['uuid'] ??
null]
176 // we need to activate the user
177 $this->resumeUser($apiUser->getUsername(), $apiUser->getAuthToken());
179 $this->logger
->debug(
180 self
::class . "->onUserUpdatedEvent user auth record is already active",
181 ['uuid' => $userWithUuid['uuid'] ??
null]
183 // TODO: if we ever want to update the password registration here we can do that here
184 // since we don't change the username and its a randomly generated password, there's no need to change
189 // we need to find out if a registration exists... if it does we need to deactivate it
190 if (empty($apiUser)) {
191 $this->logger
->debug(
192 self
::class . "->onUserUpdatedEvent telehealth disabled and no auth record exists",
193 ['uuid' => $userWithUuid['uuid'] ??
null]
195 // we do nothing here if the provider is not enabled and there's no auth we just ignore this
196 } else if ($apiUser->getIsActive()) {
197 $this->logger
->debug(
198 self
::class . "->onUserUpdatedEvent telehealth is disabled but registration is active. suspending user",
199 ['uuid' => $userWithUuid['uuid'] ??
null]
201 $this->suspendUser($apiUser->getUsername(), $apiUser->getAuthToken());
204 } catch (Exception
$exception) {
205 $this->logger
->errorLogCaller("Failed to create user registration. Error: "
206 . $exception->getMessage(), ['trace' => $exception->getTraceAsString(), 'user' => $user]);
210 public function createPatientRegistration($patient)
212 return $this->remoteService
->createPatientRegistration($patient);
215 public function createUserRegistration($user)
217 return $this->remoteService
->createUserRegistration($user);
221 * Allows the user repository to be set for testing or extension purposes
222 * @param TeleHealthUserRepository $userRepository
224 public function setTelehealthUserRepository(TeleHealthUserRepository
$userRepository)
226 // TODO: @adunsulag refactor unit tests so we don't have this layer of indirections since this is only used in unit tests right now
227 $this->remoteService
->setTelehealthUserRepository($userRepository);
231 * Returns if a registration should be created for the given provider id. This does not answer whether a registration
232 * exists, but whether the user passes the criteria for creating a registration record regardless of whether it exists or not.
236 public function shouldCreateRegistrationForProvider($providerId)
238 return $this->providerRepository
->isEnabledProvider($providerId);
242 * Provisions a new user with the Comlink video api system
243 * @param UserVideoRegistrationRequest $request
244 * @return false|int returns false if the user fails to add, otherwise returns the integer id of the provisioned user
246 public function addNewUser(UserVideoRegistrationRequest
$request)
248 return $this->remoteService
->addNewUser($request);
252 * Updates an existing provisioned user with the Comlink video api system. Everything but username can be changed
253 * @param UserVideoRegistrationRequest $request
254 * @return false|int returns false if the user fails to update, otherwise returns the integer id of the updated user
256 public function updateUser(UserVideoRegistrationRequest
$request)
258 return $this->remoteService
->updateUserFromRequest($request);
261 public function suspendUser(string $username, string $password): bool
263 return $this->remoteService
->suspendUser($username, $password);
266 public function resumeUser(string $username, string $password): bool
268 return $this->remoteService
->suspendUser($username, $password);
271 public function deactivateUser(string $username, string $password)
273 return $this->remoteService
->deactivateUser($username, $password);