Multiple account support

This commit is contained in:
Eugene Zadyra
2018-06-07 12:40:49 +02:00
parent 550d52881a
commit f935f71d0d
28 changed files with 665 additions and 368 deletions

View File

@@ -102,10 +102,14 @@ class BootConfig {
def settingsActorFactory: ByUserIdActorFactory = def settingsActorFactory: ByUserIdActorFactory =
userId => actorSystem.actorOf(Settings.props(userId, bot, dataService, localization)) userId => actorSystem.actorOf(Settings.props(userId, bot, dataService, localization))
@Bean
def accountActorFactory: ByUserIdActorFactory =
userId => actorSystem.actorOf(Account.props(userId, bot, dataService, localization, router))
@Bean @Bean
def chatActorFactory: ByUserIdActorFactory = def chatActorFactory: ByUserIdActorFactory =
userId => actorSystem.actorOf(Chat.props(userId, dataService, monitoringService, bookingActorFactory, helpActorFactory, userId => actorSystem.actorOf(Chat.props(userId, dataService, monitoringService, bookingActorFactory, helpActorFactory,
monitoringsActorFactory, historyActorFactory, visitsActorFactory, settingsActorFactory, bugActorFactory)) monitoringsActorFactory, historyActorFactory, visitsActorFactory, settingsActorFactory, bugActorFactory, accountActorFactory))
@Bean @Bean
def datePickerFactory: ByUserIdWithOriginatorActorFactory = (userId, originator) => def datePickerFactory: ByUserIdWithOriginatorActorFactory = (userId, originator) =>

View File

@@ -0,0 +1,103 @@
/**
* MIT License
*
* Copyright (c) 2018 Yevhen Zadyra
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.lbs.server.actor
import akka.actor.{ActorRef, Props}
import com.lbs.bot.model.{Button, Command}
import com.lbs.bot.{Bot, _}
import com.lbs.server.actor.Account._
import com.lbs.server.actor.Chat.Init
import com.lbs.server.actor.Login._
import com.lbs.server.lang.{Localizable, Localization}
import com.lbs.server.service.DataService
class Account(val userId: UserId, bot: Bot, dataService: DataService, val localization: Localization, router: ActorRef) extends SafeFSM[FSMState, FSMData] with Localizable {
startWith(AskAction, null)
whenSafe(AskAction) {
case Event(Next, _) =>
val credentials = dataService.getUserCredentials(userId.userId)
val buttons = Seq(Button(lang.addAccount, -1L), Button(lang.deleteAccount, -2L)) ++ credentials.map(c => Button(s"🔐️ ${c.username}", c.accountId))
bot.sendMessage(userId.source, lang.pleaseChooseAccount, inlineKeyboard = createInlineKeyboard(buttons, columns = 1))
goto(AwaitAction)
}
whenSafe(AwaitAction) {
case Event(cmd@Command(_, _, Some(actionStr)), _) =>
val action = actionStr.toLong
action match {
case -1L =>
router ! cmd.copy(message = cmd.message.copy(text = Some("/login")))
goto(AskAction) using null
case -2L =>
bot.sendMessage(userId.source, "Not implemented yet")
goto(AskAction) using null
case accountId =>
val accountMaybe = dataService.findUserCredentialsByAccountId(userId.userId, accountId)
accountMaybe match {
case Some(account) => //account was found
val userMaybe = dataService.findUser(userId.userId)
userMaybe.foreach { user =>
user.activeAccountId = accountId
dataService.saveUser(user)
router ! SwitchUser(UserId(account.userId, account.accountId, userId.source))
bot.sendMessage(userId.source, lang.accountSwitched(account.username))
}
goto(AskAction) using null
case None =>
LOG.error(s"This is not user [#${userId.userId}] account [#$accountId]")
goto(AskAction) using null
}
}
}
whenUnhandledSafe {
case Event(Init, _) =>
invokeNext()
goto(AskAction) using null
case e: Event =>
LOG.error(s"Unhandled event in state:$stateName. Event: $e")
stay()
}
initialize()
}
object Account {
def props(userId: UserId, bot: Bot, dataService: DataService, localization: Localization, router: ActorRef): Props =
Props(new Account(userId, bot, dataService, localization, router))
object AskAction extends FSMState
object AwaitAction extends FSMState
object AddAccount extends FSMState
object RemoveAccount extends FSMState
case class SwitchUser(userId: UserId)
}

View File

@@ -54,22 +54,22 @@ class Auth(val source: MessageSource, dataService: DataService, unauthorizedHelp
case cmd: Command if userId.nonEmpty => case cmd: Command if userId.nonEmpty =>
chatActor = getChatActor(userId.get) chatActor = getChatActor(userId.get)
chatActor ! cmd chatActor ! cmd
case LoggedIn(forwardCommand, id) => case LoggedIn(forwardCommand, uId, aId) =>
val uId = UserId(id, source) val id = UserId(uId, aId, source)
val cmd = forwardCommand.cmd val cmd = forwardCommand.cmd
userId = Some(uId) userId = Some(id)
chatActor = getChatActor(uId, reinit = true) chatActor = getChatActor(id, reInit = true)
if (!cmd.message.text.contains("/login")) if (!cmd.message.text.contains("/login"))
chatActor ! cmd chatActor ! cmd
case cmd: Command => case cmd: Command =>
chatActor ! cmd chatActor ! cmd
} }
private def getChatActor(userId: UserId, reinit: Boolean = false): ActorRef = { private def getChatActor(userId: UserId, reInit: Boolean = false): ActorRef = {
if (chatActor == null) { if (chatActor == null) {
chatActorFactory(userId) chatActorFactory(userId)
} else { } else {
if (reinit) { if (reInit) {
chatActor ! PoisonPill chatActor ! PoisonPill
chatActorFactory(userId) chatActorFactory(userId)
} else chatActor } else chatActor
@@ -77,8 +77,8 @@ class Auth(val source: MessageSource, dataService: DataService, unauthorizedHelp
} }
def getUserId: Option[UserId] = { def getUserId: Option[UserId] = {
val userIdMaybe = dataService.findUserIdBySource(source) val userIdMaybe = dataService.findUserAndAccountIdBySource(source)
userIdMaybe.map(id => UserId(id, source)) userIdMaybe.map { case (uId, aId) => UserId(uId, aId, source) }
} }
override def postStop(): Unit = { override def postStop(): Unit = {

View File

@@ -53,29 +53,29 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
requestStaticData(RequestCity, AwaitCity, cityConfig) { bd: BookingData => requestStaticData(RequestCity, AwaitCity, cityConfig) { bd: BookingData =>
withFunctions( withFunctions(
latestOptions = dataService.getLatestCities(userId.userId), latestOptions = dataService.getLatestCities(userId.accountId),
staticOptions = apiService.getAllCities(userId.userId), staticOptions = apiService.getAllCities(userId.accountId),
applyId = id => bd.copy(cityId = id)) applyId = id => bd.copy(cityId = id))
}(requestNext = RequestClinic) }(requestNext = RequestClinic)
requestStaticData(RequestClinic, AwaitClinic, clinicConfig) { bd: BookingData => requestStaticData(RequestClinic, AwaitClinic, clinicConfig) { bd: BookingData =>
withFunctions( withFunctions(
latestOptions = dataService.getLatestClinicsByCityId(userId.userId, bd.cityId.id), latestOptions = dataService.getLatestClinicsByCityId(userId.accountId, bd.cityId.id),
staticOptions = apiService.getAllClinics(userId.userId, bd.cityId.id), staticOptions = apiService.getAllClinics(userId.accountId, bd.cityId.id),
applyId = id => bd.copy(clinicId = id)) applyId = id => bd.copy(clinicId = id))
}(requestNext = RequestService) }(requestNext = RequestService)
requestStaticData(RequestService, AwaitService, serviceConfig) { bd: BookingData => requestStaticData(RequestService, AwaitService, serviceConfig) { bd: BookingData =>
withFunctions( withFunctions(
latestOptions = dataService.getLatestServicesByCityIdAndClinicId(userId.userId, bd.cityId.id, bd.clinicId.optionalId), latestOptions = dataService.getLatestServicesByCityIdAndClinicId(userId.accountId, bd.cityId.id, bd.clinicId.optionalId),
staticOptions = apiService.getAllServices(userId.userId, bd.cityId.id, bd.clinicId.optionalId), staticOptions = apiService.getAllServices(userId.accountId, bd.cityId.id, bd.clinicId.optionalId),
applyId = id => bd.copy(serviceId = id)) applyId = id => bd.copy(serviceId = id))
}(requestNext = RequestDoctor) }(requestNext = RequestDoctor)
requestStaticData(RequestDoctor, AwaitDoctor, doctorConfig) { bd: BookingData => requestStaticData(RequestDoctor, AwaitDoctor, doctorConfig) { bd: BookingData =>
withFunctions( withFunctions(
latestOptions = dataService.getLatestDoctorsByCityIdAndClinicIdAndServiceId(userId.userId, bd.cityId.id, bd.clinicId.optionalId, bd.serviceId.id), latestOptions = dataService.getLatestDoctorsByCityIdAndClinicIdAndServiceId(userId.accountId, bd.cityId.id, bd.clinicId.optionalId, bd.serviceId.id),
staticOptions = apiService.getAllDoctors(userId.userId, bd.cityId.id, bd.clinicId.optionalId, bd.serviceId.id), staticOptions = apiService.getAllDoctors(userId.accountId, bd.cityId.id, bd.clinicId.optionalId, bd.serviceId.id),
applyId = id => bd.copy(doctorId = id)) applyId = id => bd.copy(doctorId = id))
}(requestNext = RequestDateFrom) }(requestNext = RequestDateFrom)
@@ -128,7 +128,7 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
whenSafe(RequestAction) { whenSafe(RequestAction) {
case Event(Next, bookingData: BookingData) => case Event(Next, bookingData: BookingData) =>
dataService.storeAppointment(userId.userId, bookingData) dataService.storeAppointment(userId.accountId, bookingData)
bot.sendMessage(userId.source, bot.sendMessage(userId.source,
lang.bookingSummary(bookingData), lang.bookingSummary(bookingData),
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.findTerms, Tags.FindTerms), Button(lang.modifyDate, Tags.ModifyDate)))) inlineKeyboard = createInlineKeyboard(Seq(Button(lang.findTerms, Tags.FindTerms), Button(lang.modifyDate, Tags.ModifyDate))))
@@ -146,7 +146,7 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
whenSafe(RequestTerm) { whenSafe(RequestTerm) {
case Event(Next, bookingData: BookingData) => case Event(Next, bookingData: BookingData) =>
val availableTerms = apiService.getAvailableTerms(userId.userId, bookingData.cityId.id, val availableTerms = apiService.getAvailableTerms(userId.accountId, bookingData.cityId.id,
bookingData.clinicId.optionalId, bookingData.serviceId.id, bookingData.doctorId.optionalId, bookingData.clinicId.optionalId, bookingData.serviceId.id, bookingData.doctorId.optionalId,
bookingData.dateFrom, Some(bookingData.dateTo), timeOfDay = bookingData.timeOfDay) bookingData.dateFrom, Some(bookingData.dateTo), timeOfDay = bookingData.timeOfDay)
termsPager ! availableTerms termsPager ! availableTerms
@@ -174,7 +174,7 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
whenSafe(RequestReservation) { whenSafe(RequestReservation) {
case Event(term: AvailableVisitsTermPresentation, bookingData: BookingData) => case Event(term: AvailableVisitsTermPresentation, bookingData: BookingData) =>
val response = apiService.temporaryReservation(userId.userId, term.mapTo[TemporaryReservationRequest], term.mapTo[ValuationsRequest]) val response = apiService.temporaryReservation(userId.accountId, term.mapTo[TemporaryReservationRequest], term.mapTo[ValuationsRequest])
response match { response match {
case Left(ex) => case Left(ex) =>
bot.sendMessage(userId.source, ex.getMessage) bot.sendMessage(userId.source, ex.getMessage)
@@ -189,7 +189,7 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
whenSafe(AwaitReservation) { whenSafe(AwaitReservation) {
case Event(Command(_, _, Some(Tags.Cancel)), bookingData: BookingData) => case Event(Command(_, _, Some(Tags.Cancel)), bookingData: BookingData) =>
apiService.deleteTemporaryReservation(userId.userId, bookingData.temporaryReservationId.get) apiService.deleteTemporaryReservation(userId.accountId, bookingData.temporaryReservationId.get)
stay() stay()
case Event(Command(_, _, Some(Tags.Book)), bookingData: BookingData) => case Event(Command(_, _, Some(Tags.Book)), bookingData: BookingData) =>
val reservationRequestMaybe = for { val reservationRequestMaybe = for {
@@ -201,7 +201,7 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
reservationRequestMaybe match { reservationRequestMaybe match {
case Some(reservationRequest) => case Some(reservationRequest) =>
apiService.reservation(userId.userId, reservationRequest) match { apiService.reservation(userId.accountId, reservationRequest) match {
case Left(ex) => case Left(ex) =>
bot.sendMessage(userId.source, ex.getMessage) bot.sendMessage(userId.source, ex.getMessage)
invokeNext() invokeNext()

View File

@@ -36,7 +36,7 @@ import scala.util.matching.Regex
class Chat(val userId: UserId, dataService: DataService, monitoringService: MonitoringService, bookingActorFactory: ByUserIdActorFactory, helpActorFactory: ByUserIdActorFactory, class Chat(val userId: UserId, dataService: DataService, monitoringService: MonitoringService, bookingActorFactory: ByUserIdActorFactory, helpActorFactory: ByUserIdActorFactory,
monitoringsActorFactory: ByUserIdActorFactory, historyActorFactory: ByUserIdActorFactory, monitoringsActorFactory: ByUserIdActorFactory, historyActorFactory: ByUserIdActorFactory,
visitsActorFactory: ByUserIdActorFactory, settingsActorFactory: ByUserIdActorFactory, visitsActorFactory: ByUserIdActorFactory, settingsActorFactory: ByUserIdActorFactory,
bugActorFactory: ByUserIdActorFactory) extends SafeFSM[FSMState, FSMData] with Logger { bugActorFactory: ByUserIdActorFactory, accountActorFactory: ByUserIdActorFactory) extends SafeFSM[FSMState, FSMData] with Logger {
private val bookingActor = bookingActorFactory(userId) private val bookingActor = bookingActorFactory(userId)
private val helpActor = helpActorFactory(userId) private val helpActor = helpActorFactory(userId)
@@ -45,6 +45,7 @@ class Chat(val userId: UserId, dataService: DataService, monitoringService: Moni
private val visitsActor = visitsActorFactory(userId) private val visitsActor = visitsActorFactory(userId)
private val settingsActor = settingsActorFactory(userId) private val settingsActor = settingsActorFactory(userId)
private val bugActor = bugActorFactory(userId) private val bugActor = bugActorFactory(userId)
private val accountActor = accountActorFactory(userId)
startWith(HelpChat, null) startWith(HelpChat, null)
@@ -93,6 +94,12 @@ class Chat(val userId: UserId, dataService: DataService, monitoringService: Moni
stay() stay()
} }
when(AccountChat, accountActor) {
case Event(Command(_, Text("/accounts"), _), _) =>
accountActor ! Init
stay()
}
private def when(state: FSMState, actor: ActorRef)(mainStateFunction: StateFunction): Unit = { private def when(state: FSMState, actor: ActorRef)(mainStateFunction: StateFunction): Unit = {
whenSafe(state) { whenSafe(state) {
case event: Event => case event: Event =>
@@ -130,11 +137,14 @@ class Chat(val userId: UserId, dataService: DataService, monitoringService: Moni
case Event(cmd@Command(_, Text("/settings"), _), _) => case Event(cmd@Command(_, Text("/settings"), _), _) =>
self ! cmd self ! cmd
goto(SettingsChat) goto(SettingsChat)
case Event(cmd@Command(_, Text("/accounts"), _), _) =>
self ! cmd
goto(AccountChat)
case Event(cmd@Command(_, Text(MonitoringId(monitoringIdStr, scheduleIdStr, timeStr)), _), _) => case Event(cmd@Command(_, Text(MonitoringId(monitoringIdStr, scheduleIdStr, timeStr)), _), _) =>
val monitoringId = monitoringIdStr.toLong val monitoringId = monitoringIdStr.toLong
val scheduleId = scheduleIdStr.toLong val scheduleId = scheduleIdStr.toLong
val time = timeStr.toLong val time = timeStr.toLong
monitoringService.bookAppointmentByScheduleId(userId.userId, monitoringId, scheduleId, time) monitoringService.bookAppointmentByScheduleId(userId.accountId, monitoringId, scheduleId, time)
stay() stay()
case Event(cmd: Command, _) => case Event(cmd: Command, _) =>
actor ! cmd actor ! cmd
@@ -157,6 +167,7 @@ class Chat(val userId: UserId, dataService: DataService, monitoringService: Moni
visitsActor ! PoisonPill visitsActor ! PoisonPill
settingsActor ! PoisonPill settingsActor ! PoisonPill
bugActor ! PoisonPill bugActor ! PoisonPill
accountActor ! PoisonPill
super.postStop() super.postStop()
} }
} }
@@ -164,9 +175,10 @@ class Chat(val userId: UserId, dataService: DataService, monitoringService: Moni
object Chat { object Chat {
def props(userId: UserId, dataService: DataService, monitoringService: MonitoringService, bookingActorFactory: ByUserIdActorFactory, helpActorFactory: ByUserIdActorFactory, def props(userId: UserId, dataService: DataService, monitoringService: MonitoringService, bookingActorFactory: ByUserIdActorFactory, helpActorFactory: ByUserIdActorFactory,
monitoringsActorFactory: ByUserIdActorFactory, historyActorFactory: ByUserIdActorFactory, monitoringsActorFactory: ByUserIdActorFactory, historyActorFactory: ByUserIdActorFactory,
visitsActorFactory: ByUserIdActorFactory, settingsActorFactory: ByUserIdActorFactory, bugActorFactory: ByUserIdActorFactory): Props = visitsActorFactory: ByUserIdActorFactory, settingsActorFactory: ByUserIdActorFactory, bugActorFactory: ByUserIdActorFactory,
accountActorFactory: ByUserIdActorFactory): Props =
Props(new Chat(userId, dataService, monitoringService, bookingActorFactory, helpActorFactory, monitoringsActorFactory, Props(new Chat(userId, dataService, monitoringService, bookingActorFactory, helpActorFactory, monitoringsActorFactory,
historyActorFactory, visitsActorFactory, settingsActorFactory, bugActorFactory)) historyActorFactory, visitsActorFactory, settingsActorFactory, bugActorFactory, accountActorFactory))
object HelpChat extends FSMState object HelpChat extends FSMState
@@ -182,6 +194,8 @@ object Chat {
object BugChat extends FSMState object BugChat extends FSMState
object AccountChat extends FSMState
object Init object Init
val MonitoringId: Regex = s"/reserve_(\\d+)_(\\d+)_(\\d+)".r val MonitoringId: Regex = s"/reserve_(\\d+)_(\\d+)_(\\d+)".r

View File

@@ -41,7 +41,7 @@ class History(val userId: UserId, bot: Bot, apiService: ApiService, val localiza
whenSafe(RequestData) { whenSafe(RequestData) {
case Event(Next, _) => case Event(Next, _) =>
val visits = apiService.visitsHistory(userId.userId) val visits = apiService.visitsHistory(userId.accountId)
historyPager ! visits historyPager ! visits
goto(AwaitPage) goto(AwaitPage)
} }

View File

@@ -26,7 +26,6 @@ package com.lbs.server.actor
import akka.actor.{ActorRef, Props} import akka.actor.{ActorRef, Props}
import com.lbs.bot.Bot import com.lbs.bot.Bot
import com.lbs.bot.model.{Command, MessageSource} import com.lbs.bot.model.{Command, MessageSource}
import com.lbs.bot.telegram.TelegramBot
import com.lbs.server.actor.Chat.Init import com.lbs.server.actor.Chat.Init
import com.lbs.server.actor.Login._ import com.lbs.server.actor.Login._
import com.lbs.server.lang.{Localizable, Localization} import com.lbs.server.lang.{Localizable, Localization}
@@ -56,10 +55,10 @@ class Login(source: MessageSource, bot: Bot, dataService: DataService, apiServic
goto(RequestUsername) using LoginData() goto(RequestUsername) using LoginData()
case Right(loggedIn) => case Right(loggedIn) =>
val credentials = dataService.saveCredentials(source, username, password) val credentials = dataService.saveCredentials(source, username, password)
userId = UserId(credentials.userId, source) userId = UserId(credentials.userId, credentials.accountId, source)
apiService.addSession(credentials.userId, loggedIn.accessToken, loggedIn.tokenType) apiService.addSession(credentials.accountId, loggedIn.accessToken, loggedIn.tokenType)
bot.sendMessage(source, lang.loginAndPasswordAreOk) bot.sendMessage(source, lang.loginAndPasswordAreOk)
originator ! LoggedIn(forwardCommand, credentials.userId) originator ! LoggedIn(forwardCommand, credentials.userId, credentials.accountId)
stay() using null stay() using null
} }
} }
@@ -119,8 +118,8 @@ object Login {
case class ForwardCommand(cmd: Command) case class ForwardCommand(cmd: Command)
case class UserId(userId: Long, source: MessageSource) case class UserId(userId: Long, accountId: Long, source: MessageSource)
case class LoggedIn(forwardCommand: ForwardCommand, userId: Long) case class LoggedIn(forwardCommand: ForwardCommand, userId: Long, accountId: Long)
} }

View File

@@ -41,7 +41,7 @@ class Monitorings(val userId: UserId, bot: Bot, monitoringService: MonitoringSer
whenSafe(RequestData) { whenSafe(RequestData) {
case Event(Next, _) => case Event(Next, _) =>
val monitorings = monitoringService.getActiveMonitorings(userId.userId) val monitorings = monitoringService.getActiveMonitorings(userId.accountId)
monitoringsPager ! Right[Throwable, Seq[Monitoring]](monitorings) monitoringsPager ! Right[Throwable, Seq[Monitoring]](monitorings)
goto(AwaitPage) goto(AwaitPage)
} }

View File

@@ -26,6 +26,7 @@ package com.lbs.server.actor
import akka.actor.{Actor, ActorRef, Cancellable, PoisonPill, Props} import akka.actor.{Actor, ActorRef, Cancellable, PoisonPill, Props}
import com.lbs.bot.model.{Command, MessageSource} import com.lbs.bot.model.{Command, MessageSource}
import com.lbs.common.Logger import com.lbs.common.Logger
import com.lbs.server.actor.Account.SwitchUser
import com.lbs.server.actor.Router.DestroyChat import com.lbs.server.actor.Router.DestroyChat
import scala.collection.mutable import scala.collection.mutable
@@ -47,20 +48,34 @@ class Router(authActorFactory: ByMessageSourceActorFactory) extends Actor with L
scheduleIdleChatDestroyer(source) scheduleIdleChatDestroyer(source)
val chat = chats.get(source) match { val chat = chats.get(source) match {
case Some(actor) => actor case Some(actor) => actor
case None => case None => addNewChatActor(source)
val actor = authActorFactory(source)
chats += source -> actor
actor
} }
chat ! cmd chat ! cmd
case DestroyChat(source) => case DestroyChat(source) =>
destroyChat(source) destroyChat(source)
case SwitchUser(userId) =>
switchUser(userId)
case what => LOG.info(s"Unknown message: $what") case what => LOG.info(s"Unknown message: $what")
} }
private def addNewChatActor(source: MessageSource): ActorRef = {
val actor = authActorFactory(source)
chats += source -> actor
actor
}
private def destroyChat(source: MessageSource): Unit = { private def destroyChat(source: MessageSource): Unit = {
LOG.info(s"Destroying chat for $source due to $idleTimeout inactivity") LOG.info(s"Destroying chat for $source due to $idleTimeout inactivity")
timers.remove(source) timers.remove(source)
removeChat(source)
}
private def switchUser(userId: Login.UserId): Unit = {
removeChat(userId.source)
addNewChatActor(userId.source)
}
private def removeChat(source: MessageSource): Unit = {
chats.remove(source).foreach(_ ! PoisonPill) chats.remove(source).foreach(_ ! PoisonPill)
} }

View File

@@ -42,7 +42,7 @@ class Visits(val userId: UserId, bot: Bot, apiService: ApiService, val localizat
whenSafe(RequestData) { whenSafe(RequestData) {
case Event(Next, _) => case Event(Next, _) =>
val visits = apiService.reservedVisits(userId.userId) val visits = apiService.reservedVisits(userId.accountId)
reservedVisitsPager ! visits reservedVisitsPager ! visits
goto(AwaitPage) goto(AwaitPage)
} }
@@ -65,7 +65,7 @@ class Visits(val userId: UserId, bot: Bot, apiService: ApiService, val localizat
bot.sendMessage(userId.source, lang.appointmentWasNotCancelled) bot.sendMessage(userId.source, lang.appointmentWasNotCancelled)
goto(RequestData) goto(RequestData)
case Event(Command(_, _, Some(Tags.Yes)), visit: ReservedVisit) => case Event(Command(_, _, Some(Tags.Yes)), visit: ReservedVisit) =>
apiService.deleteReservation(userId.userId, visit.reservationId) match { apiService.deleteReservation(userId.accountId, visit.reservationId) match {
case Left(ex) => bot.sendMessage(userId.source, lang.unableToCancelUpcomingVisit(ex.getMessage)) case Left(ex) => bot.sendMessage(userId.source, lang.unableToCancelUpcomingVisit(ex.getMessage))
case Right(r) => bot.sendMessage(userId.source, lang.appointmentHasBeenCancelled) case Right(r) => bot.sendMessage(userId.source, lang.appointmentHasBeenCancelled)
} }

View File

@@ -1,26 +1,26 @@
/** /**
* MIT License * MIT License
* *
* Copyright (c) 2018 Yevhen Zadyra * Copyright (c) 2018 Yevhen Zadyra
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.lbs.server.lang package com.lbs.server.lang
import java.time.ZonedDateTime import java.time.ZonedDateTime
@@ -353,4 +353,13 @@ object En extends Lang {
override def allDay: String = "All day" override def allDay: String = "All day"
override def preferredTimeIs(time: Int): String = s"⏱ Preferred time is ${timeOfDay(time)}" override def preferredTimeIs(time: Int): String = s"⏱ Preferred time is ${timeOfDay(time)}"
override def deleteAccount: String = " Delete account"
override def addAccount: String = " Add account"
override def accountSwitched(username: String): String =
s"✅ Account has been switched to <b>$username</b>"
override def pleaseChooseAccount: String = "<b>➡</b> Please choose an <b>action</b> or select <b>account</b>"
} }

View File

@@ -1,26 +1,26 @@
/** /**
* MIT License * MIT License
* *
* Copyright (c) 2018 Yevhen Zadyra * Copyright (c) 2018 Yevhen Zadyra
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.lbs.server.lang package com.lbs.server.lang
import java.time.ZonedDateTime import java.time.ZonedDateTime
@@ -233,4 +233,12 @@ trait Lang {
def allDay: String def allDay: String
def preferredTimeIs(time: Int): String def preferredTimeIs(time: Int): String
def deleteAccount: String
def addAccount: String
def pleaseChooseAccount: String
def accountSwitched(username: String): String
} }

View File

@@ -1,26 +1,26 @@
/** /**
* MIT License * MIT License
* *
* Copyright (c) 2018 Yevhen Zadyra * Copyright (c) 2018 Yevhen Zadyra
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.lbs.server.lang package com.lbs.server.lang
import java.time.ZonedDateTime import java.time.ZonedDateTime
@@ -352,4 +352,13 @@ object Ua extends Lang {
override def allDay: String = "Весь день" override def allDay: String = "Весь день"
override def preferredTimeIs(time: Int): String = s"⏱ Бажаний час ${timeOfDay(time)}" override def preferredTimeIs(time: Int): String = s"⏱ Бажаний час ${timeOfDay(time)}"
override def deleteAccount: String = " Видалити акаунт"
override def addAccount: String = " Додати акаунт"
override def accountSwitched(username: String): String =
s"✅ Аккаунт було переключено на <b>$username</b>"
override def pleaseChooseAccount: String = "<b>➡</b> Будь ласка, оберіть <b>дію</b> або виберіть <b>акаунт</b>"
} }

View File

@@ -1,31 +1,31 @@
/** /**
* MIT License * MIT License
* *
* Copyright (c) 2018 Yevhen Zadyra * Copyright (c) 2018 Yevhen Zadyra
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.lbs.server.repository package com.lbs.server.repository
import java.time.ZonedDateTime import java.time.ZonedDateTime
import com.lbs.server.repository.model.{Bug, CityHistory, ClinicHistory, Credentials, DoctorHistory, JLong, Monitoring, ServiceHistory, Settings, Source} import com.lbs.server.repository.model.{Bug, CityHistory, ClinicHistory, Credentials, DoctorHistory, JLong, Monitoring, ServiceHistory, Settings, Source, SystemUser}
import javax.persistence.EntityManager import javax.persistence.EntityManager
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@@ -37,48 +37,48 @@ class DataRepository(@Autowired em: EntityManager) {
private val maxHistory = 2 private val maxHistory = 2
def getCityHistory(userId: Long): Seq[CityHistory] = { def getCityHistory(accountId: Long): Seq[CityHistory] = {
em.createQuery( em.createQuery(
"""select city from CityHistory city where city.recordId in """select city from CityHistory city where city.recordId in
| (select max(c.recordId) from CityHistory c where c.userId = :userId group by c.name order by MAX(c.time) desc) | (select max(c.recordId) from CityHistory c where c.accountId = :accountId group by c.name order by MAX(c.time) desc)
| order by city.time desc""".stripMargin, classOf[CityHistory]) | order by city.time desc""".stripMargin, classOf[CityHistory])
.setParameter("userId", userId) .setParameter("accountId", accountId)
.setMaxResults(maxHistory) .setMaxResults(maxHistory)
.getResultList.asScala .getResultList.asScala
} }
def getClinicHistory(userId: Long, cityId: Long): Seq[ClinicHistory] = { def getClinicHistory(accountId: Long, cityId: Long): Seq[ClinicHistory] = {
em.createQuery( em.createQuery(
"""select clinic from ClinicHistory clinic where clinic.recordId in """select clinic from ClinicHistory clinic where clinic.recordId in
| (select max(c.recordId) from ClinicHistory c where c.userId = :userId and c.cityId = :cityId group by c.name order by MAX(c.time) desc) | (select max(c.recordId) from ClinicHistory c where c.accountId = :accountId and c.cityId = :cityId group by c.name order by MAX(c.time) desc)
| order by clinic.time desc""".stripMargin, classOf[ClinicHistory]) | order by clinic.time desc""".stripMargin, classOf[ClinicHistory])
.setParameter("userId", userId) .setParameter("accountId", accountId)
.setParameter("cityId", cityId) .setParameter("cityId", cityId)
.setMaxResults(maxHistory) .setMaxResults(maxHistory)
.getResultList.asScala .getResultList.asScala
} }
def getServiceHistory(userId: Long, cityId: Long, clinicId: Option[Long]): Seq[ServiceHistory] = { def getServiceHistory(accountId: Long, cityId: Long, clinicId: Option[Long]): Seq[ServiceHistory] = {
val query = em.createQuery( val query = em.createQuery(
s"""select service from ServiceHistory service where service.recordId in s"""select service from ServiceHistory service where service.recordId in
| (select max(s.recordId) from ServiceHistory s where s.userId = :userId and s.cityId = :cityId | (select max(s.recordId) from ServiceHistory s where s.accountId = :accountId and s.cityId = :cityId
| and s.clinicId ${clinicId.map(_ => "= :clinicId").getOrElse("IS NULL")} group by s.name order by MAX(s.time) desc) | and s.clinicId ${clinicId.map(_ => "= :clinicId").getOrElse("IS NULL")} group by s.name order by MAX(s.time) desc)
| order by service.time desc""".stripMargin, classOf[ServiceHistory]) | order by service.time desc""".stripMargin, classOf[ServiceHistory])
.setParameter("userId", userId) .setParameter("accountId", accountId)
.setParameter("cityId", cityId) .setParameter("cityId", cityId)
.setMaxResults(maxHistory) .setMaxResults(maxHistory)
clinicId.map(id => query.setParameter("clinicId", id)).getOrElse(query).getResultList.asScala clinicId.map(id => query.setParameter("clinicId", id)).getOrElse(query).getResultList.asScala
} }
def getDoctorHistory(userId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long): Seq[DoctorHistory] = { def getDoctorHistory(accountId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long): Seq[DoctorHistory] = {
val query = em.createQuery( val query = em.createQuery(
s"""select doctor from DoctorHistory doctor where doctor.recordId in s"""select doctor from DoctorHistory doctor where doctor.recordId in
| (select max(d.recordId) from DoctorHistory d where d.userId = :userId | (select max(d.recordId) from DoctorHistory d where d.accountId = :accountId
| and d.cityId = :cityId and d.clinicId ${clinicId.map(_ => "= :clinicId").getOrElse("IS NULL")} | and d.cityId = :cityId and d.clinicId ${clinicId.map(_ => "= :clinicId").getOrElse("IS NULL")}
| and d.serviceId = :serviceId group by d.name order by MAX(d.time) desc) | and d.serviceId = :serviceId group by d.name order by MAX(d.time) desc)
| order by doctor.time desc""".stripMargin, classOf[DoctorHistory]) | order by doctor.time desc""".stripMargin, classOf[DoctorHistory])
.setParameter("userId", userId) .setParameter("accountId", accountId)
.setParameter("cityId", cityId) .setParameter("cityId", cityId)
.setParameter("serviceId", serviceId) .setParameter("serviceId", serviceId)
.setMaxResults(maxHistory) .setMaxResults(maxHistory)
@@ -86,10 +86,10 @@ class DataRepository(@Autowired em: EntityManager) {
clinicId.map(id => query.setParameter("clinicId", id)).getOrElse(query).getResultList.asScala clinicId.map(id => query.setParameter("clinicId", id)).getOrElse(query).getResultList.asScala
} }
def findCredentials(userId: Long): Option[Credentials] = { def findCredentials(accountId: Long): Option[Credentials] = {
em.createQuery( em.createQuery(
"select credentials from Credentials credentials where credentials.userId = :userId", classOf[Credentials]) "select credentials from Credentials credentials where credentials.accountId = :accountId", classOf[Credentials])
.setParameter("userId", userId) .setParameter("accountId", accountId)
.getResultList.asScala.headOption .getResultList.asScala.headOption
} }
@@ -107,29 +107,29 @@ class DataRepository(@Autowired em: EntityManager) {
.getResultList.asScala .getResultList.asScala
} }
def getActiveMonitoringsCount(userId: Long): JLong = { def getActiveMonitoringsCount(accountId: Long): JLong = {
em.createQuery( em.createQuery(
"""select count(monitoring) from Monitoring monitoring where monitoring.active = true """select count(monitoring) from Monitoring monitoring where monitoring.active = true
| and monitoring.userId = :userId""".stripMargin, classOf[JLong]) | and monitoring.accountId = :accountId""".stripMargin, classOf[JLong])
.setParameter("userId", userId) .setParameter("accountId", accountId)
.getSingleResult .getSingleResult
} }
def getActiveMonitorings(userId: Long): Seq[Monitoring] = { def getActiveMonitorings(accountId: Long): Seq[Monitoring] = {
em.createQuery( em.createQuery(
"""select monitoring from Monitoring monitoring where monitoring.active = true """select monitoring from Monitoring monitoring where monitoring.active = true
| and monitoring.userId = :userId order by monitoring.dateTo asc""".stripMargin, classOf[Monitoring]) | and monitoring.accountId = :accountId order by monitoring.dateTo asc""".stripMargin, classOf[Monitoring])
.setParameter("userId", userId) .setParameter("accountId", accountId)
.getResultList.asScala .getResultList.asScala
} }
def findActiveMonitoring(userId: Long, cityId: Long, serviceId: Long): Option[Monitoring] = { def findActiveMonitoring(accountId: Long, cityId: Long, serviceId: Long): Option[Monitoring] = {
em.createQuery( em.createQuery(
"""select monitoring from Monitoring monitoring where monitoring.active = true """select monitoring from Monitoring monitoring where monitoring.active = true
| and monitoring.userId = :userId | and monitoring.accountId = :accountId
| and monitoring.cityId = :cityId | and monitoring.cityId = :cityId
| and monitoring.serviceId = :serviceId""".stripMargin, classOf[Monitoring]) | and monitoring.serviceId = :serviceId""".stripMargin, classOf[Monitoring])
.setParameter("userId", userId) .setParameter("accountId", accountId)
.setParameter("cityId", cityId) .setParameter("cityId", cityId)
.setParameter("serviceId", serviceId) .setParameter("serviceId", serviceId)
.getResultList.asScala.headOption .getResultList.asScala.headOption
@@ -143,11 +143,11 @@ class DataRepository(@Autowired em: EntityManager) {
.getResultList.asScala .getResultList.asScala
} }
def findMonitoring(userId: Long, monitoringId: Long): Option[Monitoring] = { def findMonitoring(accountId: Long, monitoringId: Long): Option[Monitoring] = {
em.createQuery( em.createQuery(
"""select monitoring from Monitoring monitoring where monitoring.userId = :userId """select monitoring from Monitoring monitoring where monitoring.accountId = :accountId
| and monitoring.recordId = :monitoringId""".stripMargin, classOf[Monitoring]) | and monitoring.recordId = :monitoringId""".stripMargin, classOf[Monitoring])
.setParameter("userId", userId) .setParameter("accountId", accountId)
.setParameter("monitoringId", monitoringId) .setParameter("monitoringId", monitoringId)
.getResultList.asScala.headOption .getResultList.asScala.headOption
} }
@@ -186,6 +186,46 @@ class DataRepository(@Autowired em: EntityManager) {
.getResultList.asScala.headOption .getResultList.asScala.headOption
} }
def findUserIdBySource(chatId: String, sourceSystemId: Long): Option[JLong] = {
em.createQuery(
"select source.userId from Source source where source.chatId = :chatId" +
" and source.sourceSystemId = :sourceSystemId", classOf[JLong])
.setParameter("chatId", chatId)
.setParameter("sourceSystemId", sourceSystemId)
.getResultList.asScala.headOption
}
def findAccountId(userId: Long): Option[JLong] = {
em.createQuery(
"select systemUser.activeAccountId from SystemUser systemUser where systemUser.recordId = :recordId", classOf[JLong])
.setParameter("recordId", userId)
.getResultList.asScala.headOption
}
def findUser(userId: Long): Option[SystemUser] = {
em.createQuery(
"select systemUser from SystemUser systemUser where systemUser.recordId = :recordId", classOf[SystemUser])
.setParameter("recordId", userId)
.getResultList.asScala.headOption
}
def getUserCredentials(userId: Long): Seq[Credentials] = {
em.createQuery(
"select credentials from Credentials credentials where credentials.userId = :userId", classOf[Credentials])
.setParameter("userId", userId)
.getResultList.asScala
}
def findUserCredentialsByUserIdAndAccountId(userId: Long, accountId: Long): Option[Credentials] = {
em.createQuery(
"""select credentials from Credentials credentials where credentials.userId = :userId
| and credentials.accountId = :accountId
""".stripMargin, classOf[Credentials])
.setParameter("userId", userId)
.setParameter("accountId", accountId)
.getResultList.asScala.headOption
}
def saveEntity[T](entity: T): T = { def saveEntity[T](entity: T): T = {
em.merge(entity) em.merge(entity)
} }

View File

@@ -0,0 +1,31 @@
/**
* MIT License
*
* Copyright (c) 2018 Yevhen Zadyra
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.lbs.server.repository.model
import javax.persistence._
@Entity
@Access(AccessType.FIELD)
//just a sequence generator
class Account extends RecordId

View File

@@ -41,8 +41,8 @@ class CityHistory extends History with RecordId {
var name: String = _ var name: String = _
@BeanProperty @BeanProperty
@Column(name = "user_id", nullable = false) @Column(name = "account_id", nullable = false)
var userId: JLong = _ var accountId: JLong = _
@BeanProperty @BeanProperty
@Column(nullable = false) @Column(nullable = false)
@@ -50,9 +50,9 @@ class CityHistory extends History with RecordId {
} }
object CityHistory { object CityHistory {
def apply(userId: Long, id: Long, name: String, time: ZonedDateTime): CityHistory = { def apply(accountId: Long, id: Long, name: String, time: ZonedDateTime): CityHistory = {
val city = new CityHistory val city = new CityHistory
city.userId = userId city.accountId = accountId
city.id = id city.id = id
city.name = name city.name = name
city.time = time city.time = time

View File

@@ -41,8 +41,8 @@ class ClinicHistory extends History with RecordId {
var name: String = _ var name: String = _
@BeanProperty @BeanProperty
@Column(name = "user_id", nullable = false) @Column(name = "account_id", nullable = false)
var userId: JLong = _ var accountId: JLong = _
@BeanProperty @BeanProperty
@Column(name = "city_id", nullable = false) @Column(name = "city_id", nullable = false)
@@ -54,9 +54,9 @@ class ClinicHistory extends History with RecordId {
} }
object ClinicHistory { object ClinicHistory {
def apply(userId: Long, id: Long, name: String, cityId: Long, time: ZonedDateTime): ClinicHistory = { def apply(accountId: Long, id: Long, name: String, cityId: Long, time: ZonedDateTime): ClinicHistory = {
val clinic = new ClinicHistory val clinic = new ClinicHistory
clinic.userId = userId clinic.accountId = accountId
clinic.id = id clinic.id = id
clinic.name = name clinic.name = name
clinic.time = time clinic.time = time

View File

@@ -1,26 +1,26 @@
/** /**
* MIT License * MIT License
* *
* Copyright (c) 2018 Yevhen Zadyra * Copyright (c) 2018 Yevhen Zadyra
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.lbs.server.repository.model package com.lbs.server.repository.model
import javax.persistence._ import javax.persistence._
@@ -29,12 +29,15 @@ import scala.beans.BeanProperty
@Entity @Entity
@Access(AccessType.FIELD) @Access(AccessType.FIELD)
class Credentials { class Credentials extends RecordId {
@Id
@BeanProperty @BeanProperty
@Column(name = "user_id", nullable = false) @Column(name = "user_id", nullable = false)
var userId: JLong = _ var userId: JLong = _
@BeanProperty
@Column(name = "account_id", nullable = false)
var accountId: JLong = _
@BeanProperty @BeanProperty
@Column(nullable = false) @Column(nullable = false)
var username: String = _ var username: String = _
@@ -45,9 +48,10 @@ class Credentials {
} }
object Credentials { object Credentials {
def apply(userId: Long, username: String, password: String): Credentials = { def apply(userId: JLong, accountId: JLong, username: String, password: String): Credentials = {
val credentials = new Credentials val credentials = new Credentials
credentials.userId = userId credentials.userId = userId
credentials.accountId = accountId
credentials.username = username credentials.username = username
credentials.password = password credentials.password = password
credentials credentials

View File

@@ -41,8 +41,8 @@ class DoctorHistory extends History with RecordId {
var name: String = _ var name: String = _
@BeanProperty @BeanProperty
@Column(name = "user_id", nullable = false) @Column(name = "account_id", nullable = false)
var userId: JLong = _ var accountId: JLong = _
@BeanProperty @BeanProperty
@Column(name = "city_id", nullable = false) @Column(name = "city_id", nullable = false)
@@ -62,9 +62,9 @@ class DoctorHistory extends History with RecordId {
} }
object DoctorHistory { object DoctorHistory {
def apply(userId: Long, id: Long, name: String, cityId: Long, clinicId: Option[Long], serviceId: Long, time: ZonedDateTime): DoctorHistory = { def apply(accountId: Long, id: Long, name: String, cityId: Long, clinicId: Option[Long], serviceId: Long, time: ZonedDateTime): DoctorHistory = {
val doctor = new DoctorHistory val doctor = new DoctorHistory
doctor.userId = userId doctor.accountId = accountId
doctor.id = id doctor.id = id
doctor.name = name doctor.name = name
doctor.time = time doctor.time = time

View File

@@ -1,26 +1,26 @@
/** /**
* MIT License * MIT License
* *
* Copyright (c) 2018 Yevhen Zadyra * Copyright (c) 2018 Yevhen Zadyra
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.lbs.server.repository.model package com.lbs.server.repository.model
import java.time.ZonedDateTime import java.time.ZonedDateTime
@@ -36,6 +36,9 @@ class Monitoring extends RecordId {
@Column(name = "user_id", nullable = false) @Column(name = "user_id", nullable = false)
var userId: JLong = _ var userId: JLong = _
@BeanProperty
@Column(name = "account_id", nullable = false)
var accountId: JLong = _
@BeanProperty @BeanProperty
@Column(name = "chat_id", nullable = false) @Column(name = "chat_id", nullable = false)
@@ -103,12 +106,13 @@ class Monitoring extends RecordId {
} }
object Monitoring { object Monitoring {
def apply(userId: Long, chatId: String, sourceSystemId: Long, cityId: Long, cityName: String, clinicId: Option[Long], clinicName: String, def apply(userId: Long, accountId: Long, chatId: String, sourceSystemId: Long, cityId: Long, cityName: String, clinicId: Option[Long], clinicName: String,
serviceId: Long, serviceName: String, doctorId: Option[Long], doctorName: String, dateFrom: ZonedDateTime, serviceId: Long, serviceName: String, doctorId: Option[Long], doctorName: String, dateFrom: ZonedDateTime,
dateTo: ZonedDateTime, autobook: Boolean = false, created: ZonedDateTime = ZonedDateTime.now(), timeOfDay: Int, dateTo: ZonedDateTime, autobook: Boolean = false, created: ZonedDateTime = ZonedDateTime.now(), timeOfDay: Int,
active: Boolean = true): Monitoring = { active: Boolean = true): Monitoring = {
val monitoring = new Monitoring val monitoring = new Monitoring
monitoring.userId = userId monitoring.userId = userId
monitoring.accountId = accountId
monitoring.chatId = chatId monitoring.chatId = chatId
monitoring.sourceSystemId = sourceSystemId monitoring.sourceSystemId = sourceSystemId
monitoring.cityId = cityId monitoring.cityId = cityId

View File

@@ -41,8 +41,8 @@ class ServiceHistory extends History with RecordId {
var name: String = _ var name: String = _
@BeanProperty @BeanProperty
@Column(name = "userId_id", nullable = false) @Column(name = "account_id", nullable = false)
var userId: JLong = _ var accountId: JLong = _
@BeanProperty @BeanProperty
@Column(name = "city_id", nullable = false) @Column(name = "city_id", nullable = false)
@@ -58,9 +58,9 @@ class ServiceHistory extends History with RecordId {
} }
object ServiceHistory { object ServiceHistory {
def apply(userId: Long, id: Long, name: String, cityId: Long, clinicId: Option[Long], time: ZonedDateTime): ServiceHistory = { def apply(accountId: Long, id: Long, name: String, cityId: Long, clinicId: Option[Long], time: ZonedDateTime): ServiceHistory = {
val service = new ServiceHistory val service = new ServiceHistory
service.userId = userId service.accountId = accountId
service.id = id service.id = id
service.name = name service.name = name
service.time = time service.time = time

View File

@@ -1,34 +1,44 @@
/** /**
* MIT License * MIT License
* *
* Copyright (c) 2018 Yevhen Zadyra * Copyright (c) 2018 Yevhen Zadyra
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.lbs.server.repository.model package com.lbs.server.repository.model
import javax.persistence._ import javax.persistence._
import scala.beans.BeanProperty
@Entity @Entity
@Access(AccessType.FIELD) @Access(AccessType.FIELD)
class SystemUser extends RecordId class SystemUser extends RecordId {
@BeanProperty
@Column(name = "active_account_id", nullable = false)
var activeAccountId: JLong = _
}
object SystemUser {
def apply(activeAccountId: Long): SystemUser = {
val user = new SystemUser
user.activeAccountId = activeAccountId
user
}
}

View File

@@ -40,53 +40,53 @@ class ApiService extends SessionSupport {
@Autowired @Autowired
private var textEncryptor: TextEncryptor = _ private var textEncryptor: TextEncryptor = _
def getAllCities(userId: Long): Either[Throwable, List[IdName]] = def getAllCities(accountId: Long): Either[Throwable, List[IdName]] =
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.reservationFilter(session.accessToken, session.tokenType).map(_.cities) LuxmedApi.reservationFilter(session.accessToken, session.tokenType).map(_.cities)
} }
def getAllClinics(userId: Long, cityId: Long): Either[Throwable, List[IdName]] = def getAllClinics(accountId: Long, cityId: Long): Either[Throwable, List[IdName]] =
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.reservationFilter(session.accessToken, LuxmedApi.reservationFilter(session.accessToken,
session.tokenType, cityId = Some(cityId)).map(_.clinics) session.tokenType, cityId = Some(cityId)).map(_.clinics)
} }
def getAllServices(userId: Long, cityId: Long, clinicId: Option[Long]): Either[Throwable, List[IdName]] = def getAllServices(accountId: Long, cityId: Long, clinicId: Option[Long]): Either[Throwable, List[IdName]] =
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.reservationFilter(session.accessToken, LuxmedApi.reservationFilter(session.accessToken,
session.tokenType, cityId = Some(cityId), session.tokenType, cityId = Some(cityId),
clinicId = clinicId).map(_.services) clinicId = clinicId).map(_.services)
} }
def getAllDoctors(userId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long): Either[Throwable, List[IdName]] = def getAllDoctors(accountId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long): Either[Throwable, List[IdName]] =
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.reservationFilter(session.accessToken, LuxmedApi.reservationFilter(session.accessToken,
session.tokenType, cityId = Some(cityId), session.tokenType, cityId = Some(cityId),
clinicId = clinicId, serviceId = Some(serviceId)).map(_.doctors) clinicId = clinicId, serviceId = Some(serviceId)).map(_.doctors)
} }
def getDefaultPayer(userId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long): Either[Throwable, Option[IdName]] = def getDefaultPayer(accountId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long): Either[Throwable, Option[IdName]] =
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.reservationFilter(session.accessToken, LuxmedApi.reservationFilter(session.accessToken,
session.tokenType, cityId = Some(cityId), session.tokenType, cityId = Some(cityId),
clinicId = clinicId, serviceId = Some(serviceId)).map(_.defaultPayer) clinicId = clinicId, serviceId = Some(serviceId)).map(_.defaultPayer)
} }
def getAvailableTerms(userId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long, doctorId: Option[Long], def getAvailableTerms(accountId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long, doctorId: Option[Long],
fromDate: ZonedDateTime = ZonedDateTime.now(), toDate: Option[ZonedDateTime] = None, timeOfDay: Int = 0, fromDate: ZonedDateTime = ZonedDateTime.now(), toDate: Option[ZonedDateTime] = None, timeOfDay: Int = 0,
languageId: Long = 10, findFirstFreeTerm: Boolean = false): Either[Throwable, List[AvailableVisitsTermPresentation]] = languageId: Long = 10, findFirstFreeTerm: Boolean = false): Either[Throwable, List[AvailableVisitsTermPresentation]] =
getDefaultPayer(userId, cityId, clinicId, serviceId).flatMap { getDefaultPayer(accountId, cityId, clinicId, serviceId).flatMap {
case Some(payerId) => case Some(payerId) =>
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.availableTerms(session.accessToken, session.tokenType, payerId.id, cityId, clinicId, serviceId, doctorId, LuxmedApi.availableTerms(session.accessToken, session.tokenType, payerId.id, cityId, clinicId, serviceId, doctorId,
fromDate, toDate, timeOfDay, languageId, findFirstFreeTerm).map(_.availableVisitsTermPresentation) fromDate, toDate, timeOfDay, languageId, findFirstFreeTerm).map(_.availableVisitsTermPresentation)
} }
case None => sys.error(s"Can't determine payer id by user: $userId, city: $cityId, clinic: $clinicId, service: $serviceId") case None => sys.error(s"Can't determine payer id by user: $accountId, city: $cityId, clinic: $clinicId, service: $serviceId")
} }
def temporaryReservation(userId: Long, temporaryReservationRequest: TemporaryReservationRequest, valuationsRequest: ValuationsRequest): Either[Throwable, (TemporaryReservationResponse, ValuationsResponse)] = def temporaryReservation(accountId: Long, temporaryReservationRequest: TemporaryReservationRequest, valuationsRequest: ValuationsRequest): Either[Throwable, (TemporaryReservationResponse, ValuationsResponse)] =
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.temporaryReservation(session.accessToken, session.tokenType, temporaryReservationRequest) match { LuxmedApi.temporaryReservation(session.accessToken, session.tokenType, temporaryReservationRequest) match {
case Left(ex) => Left(ex) case Left(ex) => Left(ex)
case Right(temporaryReservation) => case Right(temporaryReservation) =>
@@ -97,30 +97,30 @@ class ApiService extends SessionSupport {
} }
} }
def deleteTemporaryReservation(userId: Long, temporaryReservationId: Long): Either[Throwable, HttpResponse[String]] = def deleteTemporaryReservation(accountId: Long, temporaryReservationId: Long): Either[Throwable, HttpResponse[String]] =
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.deleteTemporaryReservation(session.accessToken, session.tokenType, temporaryReservationId) LuxmedApi.deleteTemporaryReservation(session.accessToken, session.tokenType, temporaryReservationId)
} }
def reservation(userId: Long, reservationRequest: ReservationRequest): Either[Throwable, ReservationResponse] = def reservation(accountId: Long, reservationRequest: ReservationRequest): Either[Throwable, ReservationResponse] =
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.reservation(session.accessToken, session.tokenType, reservationRequest) LuxmedApi.reservation(session.accessToken, session.tokenType, reservationRequest)
} }
def visitsHistory(userId: Long, fromDate: ZonedDateTime = ZonedDateTime.now().minusYears(1), def visitsHistory(accountId: Long, fromDate: ZonedDateTime = ZonedDateTime.now().minusYears(1),
toDate: ZonedDateTime = ZonedDateTime.now(), page: Int = 1, pageSize: Int = 100): Either[Throwable, List[HistoricVisit]] = toDate: ZonedDateTime = ZonedDateTime.now(), page: Int = 1, pageSize: Int = 100): Either[Throwable, List[HistoricVisit]] =
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.visitsHistory(session.accessToken, session.tokenType, fromDate, toDate, page, pageSize).map(_.historicVisits) LuxmedApi.visitsHistory(session.accessToken, session.tokenType, fromDate, toDate, page, pageSize).map(_.historicVisits)
} }
def reservedVisits(userId: Long, fromDate: ZonedDateTime = ZonedDateTime.now(), def reservedVisits(accountId: Long, fromDate: ZonedDateTime = ZonedDateTime.now(),
toDate: ZonedDateTime = ZonedDateTime.now().plusMonths(3)): Either[Throwable, List[ReservedVisit]] = toDate: ZonedDateTime = ZonedDateTime.now().plusMonths(3)): Either[Throwable, List[ReservedVisit]] =
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.reservedVisits(session.accessToken, session.tokenType, fromDate, toDate).map(_.reservedVisits) LuxmedApi.reservedVisits(session.accessToken, session.tokenType, fromDate, toDate).map(_.reservedVisits)
} }
def deleteReservation(userId: Long, reservationId: Long): Either[Throwable, HttpResponse[String]] = def deleteReservation(accountId: Long, reservationId: Long): Either[Throwable, HttpResponse[String]] =
withSession(userId) { session => withSession(accountId) { session =>
LuxmedApi.deleteReservation(session.accessToken, session.tokenType, reservationId) LuxmedApi.deleteReservation(session.accessToken, session.tokenType, reservationId)
} }

View File

@@ -1,26 +1,26 @@
/** /**
* MIT License * MIT License
* *
* Copyright (c) 2018 Yevhen Zadyra * Copyright (c) 2018 Yevhen Zadyra
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.lbs.server.service package com.lbs.server.service
import java.time.ZonedDateTime import java.time.ZonedDateTime
@@ -39,10 +39,10 @@ import org.springframework.stereotype.Service
class DataService { class DataService {
@Autowired @Autowired
private var dataRepository: DataRepository = _ private[service] var dataRepository: DataRepository = _
def getLatestCities(userId: Long): Seq[IdName] = { def getLatestCities(accountId: Long): Seq[IdName] = {
dataRepository.getCityHistory(userId).mapTo[IdName] dataRepository.getCityHistory(accountId).mapTo[IdName]
} }
def getLatestClinicsByCityId(userId: Long, cityId: Long): Seq[IdName] = { def getLatestClinicsByCityId(userId: Long, cityId: Long): Seq[IdName] = {
@@ -57,8 +57,8 @@ class DataService {
dataRepository.getDoctorHistory(userId, cityId, clinicId, serviceId).mapTo[IdName] dataRepository.getDoctorHistory(userId, cityId, clinicId, serviceId).mapTo[IdName]
} }
def getCredentials(userId: Long): Option[Credentials] = { def getCredentials(accountId: Long): Option[Credentials] = {
dataRepository.findCredentials(userId) dataRepository.findCredentials(accountId)
} }
@Transactional @Transactional
@@ -79,38 +79,56 @@ class DataService {
dataRepository.getActiveMonitorings dataRepository.getActiveMonitorings
} }
def getActiveMonitoringsCount(userId: Long): Long = { def getActiveMonitoringsCount(accountId: Long): Long = {
dataRepository.getActiveMonitoringsCount(userId) dataRepository.getActiveMonitoringsCount(accountId)
} }
def getActiveMonitorings(userId: Long): Seq[Monitoring] = { def getActiveMonitorings(accountId: Long): Seq[Monitoring] = {
dataRepository.getActiveMonitorings(userId) dataRepository.getActiveMonitorings(accountId)
} }
def findActiveMonitoring(userId: Long, cityId: Long, serviceId: Long): Option[Monitoring] = { def findActiveMonitoring(accountId: Long, cityId: Long, serviceId: Long): Option[Monitoring] = {
dataRepository.findActiveMonitoring(userId, cityId, serviceId) dataRepository.findActiveMonitoring(accountId, cityId, serviceId)
} }
def getActiveMonitoringsSince(since: ZonedDateTime): Seq[Monitoring] = { def getActiveMonitoringsSince(since: ZonedDateTime): Seq[Monitoring] = {
dataRepository.getActiveMonitoringsSince(since) dataRepository.getActiveMonitoringsSince(since)
} }
def findMonitoring(userId: Long, monitoringId: Long): Option[Monitoring] = { def findMonitoring(accountId: Long, monitoringId: Long): Option[Monitoring] = {
dataRepository.findMonitoring(userId, monitoringId) dataRepository.findMonitoring(accountId, monitoringId)
} }
def findSettings(userId: Long): Option[Settings] = { def findSettings(userId: Long): Option[Settings] = {
dataRepository.findSettings(userId) dataRepository.findSettings(userId)
} }
def findUserIdBySource(source: MessageSource): Option[Long] = { def findUserAndAccountIdBySource(source: MessageSource): Option[(Long, Long)] = {
dataRepository.findUserId(source.chatId, source.sourceSystem.id).map(_.toLong) val userIdMaybe = dataRepository.findUserId(source.chatId, source.sourceSystem.id).map(_.toLong)
userIdMaybe.flatMap(userId => dataRepository.findAccountId(userId).map(_.toLong).map(accountId => userId -> accountId))
} }
def findCredentialsByUsername(username: String): Option[Credentials] = { def findCredentialsByUsername(username: String): Option[Credentials] = {
dataRepository.findCredentialsByUsername(username) dataRepository.findCredentialsByUsername(username)
} }
def getUserCredentials(userId: Long): Seq[Credentials] = {
dataRepository.getUserCredentials(userId)
}
def findUserCredentialsByAccountId(userId: Long, accountId: Long): Option[Credentials] = {
dataRepository.findUserCredentialsByUserIdAndAccountId(userId, accountId)
}
def findUser(userId: Long): Option[SystemUser] = {
dataRepository.findUser(userId)
}
@Transactional
def saveUser(user: SystemUser): SystemUser = {
dataRepository.saveEntity(user)
}
@Transactional @Transactional
def saveSettings(settings: Settings): Settings = { def saveSettings(settings: Settings): Settings = {
dataRepository.saveEntity(settings) dataRepository.saveEntity(settings)
@@ -128,36 +146,63 @@ class DataService {
val src = Source(source.chatId, source.sourceSystem.id, credentials.userId) val src = Source(source.chatId, source.sourceSystem.id, credentials.userId)
dataRepository.saveEntity(src) dataRepository.saveEntity(src)
} }
val userMaybe = dataRepository.findUser(credentials.userId)
userMaybe match {
case Some(user) =>
user.activeAccountId = credentials.accountId
dataRepository.saveEntity(user)
case None => sys.error(s"Strange, but user [#${credentials.userId}] not found")
}
credentials.username = username credentials.username = username
credentials.password = password credentials.password = password
dataRepository.saveEntity(credentials) dataRepository.saveEntity(credentials)
case None => //new user case None => //new user or new account?
val user = dataRepository.saveEntity(new SystemUser) val userMaybe = dataRepository.findUserIdBySource(source.chatId, source.sourceSystem.id).flatMap {
val src = Source(source.chatId, source.sourceSystem.id, user.recordId) userId => dataRepository.findUser(userId)
dataRepository.saveEntity(src) }
val credentials = Credentials(user.recordId, username, password) userMaybe match {
dataRepository.saveEntity(credentials) case Some(user) => //user already exists, this is just the new credentials
val account = dataRepository.saveEntity(new Account)
user.activeAccountId = account.recordId
dataRepository.saveEntity(user)
val sourceMaybe = dataRepository.findSource(source.chatId, source.sourceSystem.id, user.recordId)
sourceMaybe match {
case Some(_) => //source already exists. Just save credentials
case None => //add new source
val src = Source(source.chatId, source.sourceSystem.id, user.recordId)
dataRepository.saveEntity(src)
}
val credentials = Credentials(user.recordId, account.recordId, username, password)
dataRepository.saveEntity(credentials)
case None => //everything is new
val account = dataRepository.saveEntity(new Account)
val user = dataRepository.saveEntity(SystemUser(account.recordId))
val src = Source(source.chatId, source.sourceSystem.id, user.recordId)
dataRepository.saveEntity(src)
val credentials = Credentials(user.recordId, account.recordId, username, password)
dataRepository.saveEntity(credentials)
}
} }
} }
@Transactional @Transactional
def storeAppointment(userId: Long, bookingData: BookingData): Unit = { def storeAppointment(accountId: Long, bookingData: BookingData): Unit = {
val time = ZonedDateTime.now() val time = ZonedDateTime.now()
val cityId = bookingData.cityId val cityId = bookingData.cityId
val clinicId = bookingData.clinicId val clinicId = bookingData.clinicId
val serviceId = bookingData.serviceId val serviceId = bookingData.serviceId
val doctorId = bookingData.doctorId val doctorId = bookingData.doctorId
val city = CityHistory(userId, cityId.id, cityId.name, time) val city = CityHistory(accountId, cityId.id, cityId.name, time)
dataRepository.saveEntity(city) dataRepository.saveEntity(city)
val clinicMaybe = clinicId.optionalId.map(id => ClinicHistory(userId, id, clinicId.name, cityId.id, time)) val clinicMaybe = clinicId.optionalId.map(id => ClinicHistory(accountId, id, clinicId.name, cityId.id, time))
clinicMaybe.foreach(dataRepository.saveEntity) clinicMaybe.foreach(dataRepository.saveEntity)
val service = ServiceHistory(userId, serviceId.id, serviceId.name, cityId.id, clinicId.optionalId, time) val service = ServiceHistory(accountId, serviceId.id, serviceId.name, cityId.id, clinicId.optionalId, time)
dataRepository.saveEntity(service) dataRepository.saveEntity(service)
val doctorMaybe = doctorId.optionalId.map(id => DoctorHistory(userId, id, doctorId.name, cityId.id, clinicId.optionalId, serviceId.id, time)) val doctorMaybe = doctorId.optionalId.map(id => DoctorHistory(accountId, id, doctorId.name, cityId.id, clinicId.optionalId, serviceId.id, time))
doctorMaybe.foreach(dataRepository.saveEntity) doctorMaybe.foreach(dataRepository.saveEntity)
} }
} }

View File

@@ -90,7 +90,7 @@ class MonitoringService extends Logger {
private def monitor(monitoring: Monitoring): Unit = { private def monitor(monitoring: Monitoring): Unit = {
LOG.debug(s"Looking for available terms. Monitoring [#${monitoring.recordId}]") LOG.debug(s"Looking for available terms. Monitoring [#${monitoring.recordId}]")
val dateFrom = optimizeDateFrom(monitoring.dateFrom) val dateFrom = optimizeDateFrom(monitoring.dateFrom)
val termsEither = apiService.getAvailableTerms(monitoring.userId, monitoring.cityId, monitoring.clinicId, monitoring.serviceId, val termsEither = apiService.getAvailableTerms(monitoring.accountId, monitoring.cityId, monitoring.clinicId, monitoring.serviceId,
monitoring.doctorId, dateFrom, Some(monitoring.dateTo)) monitoring.doctorId, dateFrom, Some(monitoring.dateTo))
termsEither match { termsEither match {
case Right(terms) => case Right(terms) =>
@@ -108,7 +108,7 @@ class MonitoringService extends Logger {
case Left(ex: InvalidLoginOrPasswordException) => case Left(ex: InvalidLoginOrPasswordException) =>
LOG.error(s"User entered invalid name or password. Monitoring will be disabled", ex) LOG.error(s"User entered invalid name or password. Monitoring will be disabled", ex)
bot.sendMessage(monitoring.source, lang(monitoring.userId).invalidLoginOrPassword) bot.sendMessage(monitoring.source, lang(monitoring.userId).invalidLoginOrPassword)
val activeUserMonitorings = dataService.getActiveMonitorings(monitoring.userId) val activeUserMonitorings = dataService.getActiveMonitorings(monitoring.accountId)
activeUserMonitorings.foreach { m => activeUserMonitorings.foreach { m =>
deactivateMonitoring(m.recordId) deactivateMonitoring(m.recordId)
} }
@@ -172,12 +172,12 @@ class MonitoringService extends Logger {
val temporaryReservationRequest = term.mapTo[TemporaryReservationRequest] val temporaryReservationRequest = term.mapTo[TemporaryReservationRequest]
val valuationsRequest = term.mapTo[ValuationsRequest] val valuationsRequest = term.mapTo[ValuationsRequest]
val reservationMaybe = for { val reservationMaybe = for {
okResponse <- apiService.temporaryReservation(monitoring.userId, temporaryReservationRequest, valuationsRequest) okResponse <- apiService.temporaryReservation(monitoring.accountId, temporaryReservationRequest, valuationsRequest)
(temporaryReservation, valuations) = okResponse (temporaryReservation, valuations) = okResponse
temporaryReservationId = temporaryReservation.id temporaryReservationId = temporaryReservation.id
visitTermVariant = valuations.visitTermVariants.head visitTermVariant = valuations.visitTermVariants.head
reservationRequest = (temporaryReservationId, visitTermVariant, term).mapTo[ReservationRequest] reservationRequest = (temporaryReservationId, visitTermVariant, term).mapTo[ReservationRequest]
reservation <- apiService.reservation(monitoring.userId, reservationRequest) reservation <- apiService.reservation(monitoring.accountId, reservationRequest)
} yield reservation } yield reservation
reservationMaybe match { reservationMaybe match {
@@ -203,22 +203,22 @@ class MonitoringService extends Logger {
} }
def createMonitoring(monitoring: Monitoring): Monitoring = { def createMonitoring(monitoring: Monitoring): Monitoring = {
val userMonitoringsCount = dataService.getActiveMonitoringsCount(monitoring.userId) val userMonitoringsCount = dataService.getActiveMonitoringsCount(monitoring.accountId)
require(userMonitoringsCount + 1 <= 5, lang(monitoring.userId).maximumMonitoringsLimitExceeded) require(userMonitoringsCount + 1 <= 5, lang(monitoring.userId).maximumMonitoringsLimitExceeded)
val activeMonitoring = dataService.findActiveMonitoring(monitoring.userId, monitoring.cityId, monitoring.serviceId) val activeMonitoring = dataService.findActiveMonitoring(monitoring.accountId, monitoring.cityId, monitoring.serviceId)
require(activeMonitoring.isEmpty, lang(monitoring.userId).monitoringOfTheSameTypeExists) require(activeMonitoring.isEmpty, lang(monitoring.userId).monitoringOfTheSameTypeExists)
dataService.saveMonitoring(monitoring) dataService.saveMonitoring(monitoring)
} }
def getActiveMonitorings(userId: Long): Seq[Monitoring] = { def getActiveMonitorings(accountId: Long): Seq[Monitoring] = {
dataService.getActiveMonitorings(userId) dataService.getActiveMonitorings(accountId)
} }
def bookAppointmentByScheduleId(userId: Long, monitoringId: Long, scheduleId: Long, time: Long): Unit = { def bookAppointmentByScheduleId(accountId: Long, monitoringId: Long, scheduleId: Long, time: Long): Unit = {
val monitoringMaybe = dataService.findMonitoring(userId, monitoringId) val monitoringMaybe = dataService.findMonitoring(accountId, monitoringId)
monitoringMaybe match { monitoringMaybe match {
case Some(monitoring) => case Some(monitoring) =>
val termsEither = apiService.getAvailableTerms(monitoring.userId, monitoring.cityId, monitoring.clinicId, monitoring.serviceId, val termsEither = apiService.getAvailableTerms(monitoring.accountId, monitoring.cityId, monitoring.clinicId, monitoring.serviceId,
monitoring.doctorId, monitoring.dateFrom, Some(monitoring.dateTo)) monitoring.doctorId, monitoring.dateFrom, Some(monitoring.dateTo))
termsEither match { termsEither match {
case Right(terms) => case Right(terms) =>
@@ -254,6 +254,7 @@ class MonitoringService extends Logger {
val monitorings = dataService.getActiveMonitorings val monitorings = dataService.getActiveMonitorings
LOG.debug(s"Active monitorings found: ${monitorings.length}") LOG.debug(s"Active monitorings found: ${monitorings.length}")
initializeMonitorings(monitorings) initializeMonitorings(monitorings)
disableOutdated()
initializeDbChecker() initializeDbChecker()
} }
} }

View File

@@ -44,26 +44,26 @@ trait SessionSupport {
private val lock = new ParametrizedLock[Long] private val lock = new ParametrizedLock[Long]
protected def withSession[T](userId: Long)(fn: Session => Either[Throwable, T]): Either[Throwable, T] = protected def withSession[T](accountId: Long)(fn: Session => Either[Throwable, T]): Either[Throwable, T] =
lock.obtainLock(userId).synchronized { lock.obtainLock(accountId).synchronized {
def auth: Either[Throwable, Session] = { def auth: Either[Throwable, Session] = {
val credentialsMaybe = dataService.getCredentials(userId) val credentialsMaybe = dataService.getCredentials(accountId)
credentialsMaybe match { credentialsMaybe match {
case Some(credentials) => case Some(credentials) =>
val loginResponse = login(credentials.username, credentials.password) val loginResponse = login(credentials.username, credentials.password)
loginResponse.map(r => Session(r.accessToken, r.tokenType)) loginResponse.map(r => Session(r.accessToken, r.tokenType))
case None => Left(UserNotFoundException(userId)) case None => Left(UserNotFoundException(accountId))
} }
} }
def session: Either[Throwable, Session] = { def session: Either[Throwable, Session] = {
sessions.get(userId) match { sessions.get(accountId) match {
case Some(sess) => Right(sess) case Some(sess) => Right(sess)
case None => case None =>
auth match { auth match {
case Right(sess) => case Right(sess) =>
sessions.put(userId, sess) sessions.put(accountId, sess)
Right(sess) Right(sess)
case left => left case left => left
} }
@@ -74,8 +74,8 @@ trait SessionSupport {
case Right(s) => case Right(s) =>
fn(s) match { fn(s) match {
case Left(ex) if ex.getMessage.contains("session has expired") => case Left(ex) if ex.getMessage.contains("session has expired") =>
Log.debug(s"The session for user [#$userId] has expired. Try to relogin") Log.debug(s"The session for account [#$accountId] has expired. Try to relogin")
sessions.remove(userId) sessions.remove(accountId)
session.flatMap(fn) session.flatMap(fn)
case another => case another =>
Log.debug(s"Call to remote api function has completed with result:\n$another") Log.debug(s"Call to remote api function has completed with result:\n$another")
@@ -85,8 +85,8 @@ trait SessionSupport {
} }
} }
def addSession(userId: Long, accessToken: String, tokenType: String): Unit = def addSession(accountId: Long, accessToken: String, tokenType: String): Unit =
lock.obtainLock(userId).synchronized { lock.obtainLock(accountId).synchronized {
sessions.put(userId, Session(accessToken, tokenType)) sessions.put(accountId, Session(accessToken, tokenType))
} }
} }

View File

@@ -1,26 +1,26 @@
/** /**
* MIT License * MIT License
* *
* Copyright (c) 2018 Yevhen Zadyra * Copyright (c) 2018 Yevhen Zadyra
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.lbs.server package com.lbs.server
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@@ -49,6 +49,7 @@ package object util {
val (userId, bookingData) = data.asInstanceOf[(UserId, BookingData)] val (userId, bookingData) = data.asInstanceOf[(UserId, BookingData)]
Monitoring( Monitoring(
userId = userId.userId, userId = userId.userId,
accountId = userId.accountId,
chatId = userId.source.chatId, chatId = userId.source.chatId,
sourceSystemId = userId.source.sourceSystem.id, sourceSystemId = userId.source.sourceSystem.id,
cityId = bookingData.cityId.id, cityId = bookingData.cityId.id,

View File

@@ -13,7 +13,7 @@ class AuthSpec extends AkkaTestKit {
"An Auth actor " when { "An Auth actor " when {
val source = MessageSource(TelegramMessageSourceSystem, "1") val source = MessageSource(TelegramMessageSourceSystem, "1")
val userId = UserId(1L, source) val userId = UserId(1L, 1L, source)
"user is unauthorized" must { "user is unauthorized" must {
val unauthorizedHelpActor = TestProbe() val unauthorizedHelpActor = TestProbe()
@@ -23,7 +23,7 @@ class AuthSpec extends AkkaTestKit {
val loginActorFactory: ByMessageSourceWithOriginatorActorFactory = (_, _) => loginActor.ref val loginActorFactory: ByMessageSourceWithOriginatorActorFactory = (_, _) => loginActor.ref
val chatActorFactory: UserId => ActorRef = _ => chatActor.ref val chatActorFactory: UserId => ActorRef = _ => chatActor.ref
val dataService = mock(classOf[DataService]) val dataService = mock(classOf[DataService])
when(dataService.findUserIdBySource(source)).thenReturn(None) when(dataService.findUserAndAccountIdBySource(source)).thenReturn(None)
val auth = system.actorOf(Auth.props(source, dataService, unauthorizedHelpFactory, loginActorFactory, chatActorFactory)) val auth = system.actorOf(Auth.props(source, dataService, unauthorizedHelpFactory, loginActorFactory, chatActorFactory))
@@ -57,7 +57,7 @@ class AuthSpec extends AkkaTestKit {
"forward initial message to chat actor after the user has logged in" in { "forward initial message to chat actor after the user has logged in" in {
val cmd = Command(source, Message("1", Some("any"))) val cmd = Command(source, Message("1", Some("any")))
val msg = LoggedIn(ForwardCommand(cmd), 1L) val msg = LoggedIn(ForwardCommand(cmd), 1L, 1L)
auth ! msg auth ! msg
chatActor.expectMsg(cmd) chatActor.expectMsg(cmd)
} }
@@ -79,7 +79,7 @@ class AuthSpec extends AkkaTestKit {
val loginActorFactory: ByMessageSourceWithOriginatorActorFactory = (_, _) => loginActor.ref val loginActorFactory: ByMessageSourceWithOriginatorActorFactory = (_, _) => loginActor.ref
val chatActorFactory: UserId => ActorRef = _ => chatActor.ref val chatActorFactory: UserId => ActorRef = _ => chatActor.ref
val dataService = mock(classOf[DataService]) val dataService = mock(classOf[DataService])
when(dataService.findUserIdBySource(source)).thenReturn(Some(userId.userId)) when(dataService.findUserAndAccountIdBySource(source)).thenReturn(Some(userId.userId, userId.accountId))
val auth = system.actorOf(Auth.props(source, dataService, unauthorizedHelpFactory, loginActorFactory, chatActorFactory)) val auth = system.actorOf(Auth.props(source, dataService, unauthorizedHelpFactory, loginActorFactory, chatActorFactory))