diff --git a/api/src/main/scala/com/lbs/api/LuxmedApiAsync.scala b/api/src/main/scala/com/lbs/api/LuxmedApiAsync.scala
deleted file mode 100644
index 902a554..0000000
--- a/api/src/main/scala/com/lbs/api/LuxmedApiAsync.scala
+++ /dev/null
@@ -1,73 +0,0 @@
-
-package com.lbs.api
-
-import java.time.ZonedDateTime
-
-import com.lbs.api.json.model._
-import scalaj.http.HttpResponse
-
-import scala.concurrent.{ExecutionContext, Future}
-import scala.language.implicitConversions
-
-
-object LuxmedApiAsync {
-
- private val syncApi = LuxmedApi
-
- def login(username: String, password: String, clientId: String = "iPhone")(implicit ec: ExecutionContext): Future[LoginResponse] = {
- async(syncApi.login(username, password, clientId))
- }
-
- def refreshToken(refreshToken: String, clientId: String = "iPhone")(implicit ec: ExecutionContext): Future[LoginResponse] = {
- async(syncApi.refreshToken(refreshToken, clientId))
- }
-
- def reservedVisits(accessToken: String, tokenType: String, fromDate: ZonedDateTime = ZonedDateTime.now(),
- toDate: ZonedDateTime = ZonedDateTime.now().plusMonths(3))(implicit ec: ExecutionContext): Future[ReservedVisitsResponse] = {
- async(syncApi.reservedVisits(accessToken, tokenType, fromDate, toDate))
- }
-
- def visitsHistory(accessToken: String, tokenType: String, fromDate: ZonedDateTime = ZonedDateTime.now().minusYears(1),
- toDate: ZonedDateTime, page: Int = 1, pageSize: Int = 100)(implicit ec: ExecutionContext): Future[VisitsHistoryResponse] = {
- async(syncApi.visitsHistory(accessToken, tokenType, fromDate, toDate, page, pageSize))
- }
-
- def reservationFilter(accessToken: String, tokenType: String, fromDate: ZonedDateTime = ZonedDateTime.now(),
- toDate: Option[ZonedDateTime] = None, cityId: Option[Long] = None,
- serviceId: Option[Long] = None)(implicit ec: ExecutionContext): Future[ReservationFilterResponse] = {
- async(syncApi.reservationFilter(accessToken, tokenType, fromDate, toDate, cityId, serviceId))
- }
-
- def availableTerms(accessToken: String, tokenType: String, payerId: Long, cityId: Long, clinicId: Option[Long], serviceId: Long, doctorId: Option[Long],
- fromDate: ZonedDateTime = ZonedDateTime.now(), toDate: Option[ZonedDateTime] = None, timeOfDay: Int = 0,
- languageId: Long = 10, findFirstFreeTerm: Boolean = true)(implicit ec: ExecutionContext): Future[AvailableTermsResponse] = {
- async(syncApi.availableTerms(accessToken, tokenType, cityId, payerId, clinicId, serviceId, doctorId, fromDate, toDate, timeOfDay, languageId, findFirstFreeTerm))
- }
-
- def temporaryReservation(accessToken: String, tokenType: String, temporaryReservationRequest: TemporaryReservationRequest)(implicit ec: ExecutionContext): Future[TemporaryReservationResponse] = {
- async(syncApi.temporaryReservation(accessToken, tokenType, temporaryReservationRequest))
- }
-
- def deleteTemporaryReservation(accessToken: String, tokenType: String, temporaryReservationId: Long)(implicit ec: ExecutionContext): Future[HttpResponse[String]] = {
- async(syncApi.deleteTemporaryReservation(accessToken, tokenType, temporaryReservationId))
- }
-
- def valuations(accessToken: String, tokenType: String, valuationsRequest: ValuationsRequest)(implicit ec: ExecutionContext): Future[ValuationsResponse] = {
- async(syncApi.valuations(accessToken, tokenType, valuationsRequest))
- }
-
- def reservation(accessToken: String, tokenType: String, reservationRequest: ReservationRequest)(implicit ec: ExecutionContext): Future[ReservationResponse] = {
- async(syncApi.reservation(accessToken, tokenType, reservationRequest))
- }
-
- def deleteReservation(accessToken: String, tokenType: String, reservationId: Long)(implicit ec: ExecutionContext): Future[HttpResponse[String]] = {
- async(syncApi.deleteReservation(accessToken, tokenType, reservationId))
- }
-
- private def async[T](f: => Either[Throwable, T])(implicit ec: ExecutionContext) = {
- Future(f).flatMap {
- case Right(r) => Future.successful(r)
- case Left(ex) => Future.failed(ex)
- }
- }
-}
diff --git a/api/src/main/scala/com/lbs/api/exception/GenericException.scala b/api/src/main/scala/com/lbs/api/exception/GenericException.scala
index 170369e..9038211 100644
--- a/api/src/main/scala/com/lbs/api/exception/GenericException.scala
+++ b/api/src/main/scala/com/lbs/api/exception/GenericException.scala
@@ -1,6 +1,6 @@
package com.lbs.api.exception
-class GenericException(code: Int, status: String, message: String) extends ApiException(message) {
+class GenericException(val code: Int, val status: String, val message: String) extends ApiException(message) {
override def toString: String = s"Code: $code, status: $status, message: $message"
}
diff --git a/api/src/main/scala/com/lbs/api/exception/ServiceIsAlreadyBookedException.scala b/api/src/main/scala/com/lbs/api/exception/ServiceIsAlreadyBookedException.scala
index 1e3b12b..57d3fb3 100644
--- a/api/src/main/scala/com/lbs/api/exception/ServiceIsAlreadyBookedException.scala
+++ b/api/src/main/scala/com/lbs/api/exception/ServiceIsAlreadyBookedException.scala
@@ -1,4 +1,4 @@
package com.lbs.api.exception
-class ServiceIsAlreadyBookedException extends ApiException("Service is already booked")
+class ServiceIsAlreadyBookedException extends ApiException("You have already booked this service")
diff --git a/api/src/main/scala/com/lbs/api/http/package.scala b/api/src/main/scala/com/lbs/api/http/package.scala
index 701e130..a321be6 100644
--- a/api/src/main/scala/com/lbs/api/http/package.scala
+++ b/api/src/main/scala/com/lbs/api/http/package.scala
@@ -56,18 +56,19 @@ package object http extends Logger {
}
private def luxmedErrorToApiException[T <: LuxmedBaseError](ler: HttpResponse[T]): ApiException = {
- ler.body match {
+ val genericException = ler.body match {
case e: LuxmedCompositeError =>
new GenericException(ler.code, ler.statusLine, e.errors.map(_.message).mkString("; "))
case e: LuxmedError =>
- val errorMessage = e.message.toLowerCase
- if (errorMessage.contains("invalid login or password"))
- new InvalidLoginOrPasswordException
- else if (errorMessage.contains("have already booked this service"))
- new ServiceIsAlreadyBookedException
- else
- new GenericException(ler.code, ler.statusLine, e.message)
+ new GenericException(ler.code, ler.statusLine, e.message)
}
+
+ val errorMessage = genericException.message.toLowerCase
+ if (errorMessage.contains("invalid login or password"))
+ new InvalidLoginOrPasswordException
+ else if (errorMessage.contains("already booked this service"))
+ new ServiceIsAlreadyBookedException
+ else genericException
}
private def extractLuxmedError(httpResponse: Try[HttpResponse[String]]) = {
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 8295719..29394a5 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -1,7 +1,7 @@
version: '3.4'
services:
luxmedbookingservice:
- image: eugenezadyra/luxmed-bot:1.0.0
+ image: eugenezadyra/luxmed-bot:1.0.1
environment:
DB_HOST: "database"
volumes:
diff --git a/server/src/main/scala/com/lbs/server/conversation/Book.scala b/server/src/main/scala/com/lbs/server/conversation/Book.scala
index b51c78c..a78c392 100644
--- a/server/src/main/scala/com/lbs/server/conversation/Book.scala
+++ b/server/src/main/scala/com/lbs/server/conversation/Book.scala
@@ -16,7 +16,7 @@ import com.lbs.server.conversation.base.Conversation
import com.lbs.server.lang.{Localizable, Localization}
import com.lbs.server.repository.model.Monitoring
import com.lbs.server.service.{ApiService, DataService, MonitoringService}
-import com.lbs.server.util.MessageExtractors.CallbackCommand
+import com.lbs.server.util.MessageExtractors.{BooleanString, CallbackCommand}
import com.lbs.server.util.ServerModelConverters._
class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: DataService, monitoringService: MonitoringService,
@@ -165,7 +165,7 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
goto(requestDateFrom) using bookingData.copy(dateFrom = ZonedDateTime.now(),
dateTo = ZonedDateTime.now().plusDays(1L))
case Msg(CallbackCommand(Tags.CreateMonitoring), _) =>
- goto(askMonitoringOptions)
+ goto(askMonitoringAutobookOption)
}
private def awaitReservation: Step =
@@ -200,14 +200,24 @@ class Book(val userId: UserId, bot: Bot, apiService: ApiService, dataService: Da
}
}
- private def askMonitoringOptions: Step =
+ private def askMonitoringAutobookOption: Step =
ask { _ =>
bot.sendMessage(userId.source, lang.chooseTypeOfMonitoring,
inlineKeyboard = createInlineKeyboard(Seq(Button(lang.bookByApplication, Tags.BookByApplication), Button(lang.bookManually, Tags.BookManually)), columns = 1))
} onReply {
- case Msg(CallbackCommand(autobookStr), bookingData: BookingData) =>
- val autobook = autobookStr.toBoolean
- goto(createMonitoring) using bookingData.copy(autobook = autobook)
+ case Msg(CallbackCommand(BooleanString(autobook)), bookingData: BookingData) =>
+ val data = bookingData.copy(autobook = autobook)
+ if(autobook) goto(askMonitoringRebookOption) using data
+ else goto(createMonitoring) using data
+ }
+
+ private def askMonitoringRebookOption: Step =
+ ask { _ =>
+ bot.sendMessage(userId.source, lang.rebookIfExists,
+ inlineKeyboard = createInlineKeyboard(Seq(Button(lang.yes, Tags.RebookYes), Button(lang.no, Tags.RebookNo)), columns = 1))
+ } onReply {
+ case Msg(CallbackCommand(BooleanString(rebookIfExists)), bookingData: BookingData) =>
+ goto(createMonitoring) using bookingData.copy(rebookIfExists = rebookIfExists)
}
private def createMonitoring: Step =
@@ -244,8 +254,10 @@ object Book {
case class BookingData(cityId: IdName = null, clinicId: IdName = null,
serviceId: IdName = null, doctorId: IdName = null, dateFrom: ZonedDateTime = ZonedDateTime.now(),
- dateTo: ZonedDateTime = ZonedDateTime.now().plusDays(1L), timeFrom: LocalTime = LocalTime.of(7, 0), timeTo: LocalTime = LocalTime.of(21, 0), autobook: Boolean = false, term: Option[AvailableVisitsTermPresentation] = None,
- temporaryReservationId: Option[Long] = None, valuations: Option[ValuationsResponse] = None)
+ dateTo: ZonedDateTime = ZonedDateTime.now().plusDays(1L), timeFrom: LocalTime = LocalTime.of(7, 0),
+ timeTo: LocalTime = LocalTime.of(21, 0), autobook: Boolean = false, rebookIfExists: Boolean = false,
+ term: Option[AvailableVisitsTermPresentation] = None, temporaryReservationId: Option[Long] = None,
+ valuations: Option[ValuationsResponse] = None)
object Tags {
val Cancel = "cancel"
@@ -255,6 +267,8 @@ object Book {
val CreateMonitoring = "create_monitoring"
val BookManually = "false"
val BookByApplication = "true"
+ val RebookYes = "true"
+ val RebookNo = "false"
}
}
diff --git a/server/src/main/scala/com/lbs/server/lang/En.scala b/server/src/main/scala/com/lbs/server/lang/En.scala
index 04e4f1f..cec8b61 100644
--- a/server/src/main/scala/com/lbs/server/lang/En.scala
+++ b/server/src/main/scala/com/lbs/server/lang/En.scala
@@ -95,6 +95,8 @@ object En extends Lang {
override def bookManually: String = "👤 Book manually"
+ override def rebookIfExists: String = "➡ Do you want to update term if reservation already exists?"
+
override def city: String = "city"
override def clinic: String = "clinic"
diff --git a/server/src/main/scala/com/lbs/server/lang/Lang.scala b/server/src/main/scala/com/lbs/server/lang/Lang.scala
index 7ef800a..0ce376a 100644
--- a/server/src/main/scala/com/lbs/server/lang/Lang.scala
+++ b/server/src/main/scala/com/lbs/server/lang/Lang.scala
@@ -85,6 +85,8 @@ trait Lang {
def bookManually: String
+ def rebookIfExists: String
+
def city: String
def clinic: String
diff --git a/server/src/main/scala/com/lbs/server/lang/Ua.scala b/server/src/main/scala/com/lbs/server/lang/Ua.scala
index fe99f8a..4f7307c 100644
--- a/server/src/main/scala/com/lbs/server/lang/Ua.scala
+++ b/server/src/main/scala/com/lbs/server/lang/Ua.scala
@@ -95,6 +95,8 @@ object Ua extends Lang {
override def bookManually: String = "👤 Ручна резервація"
+ override def rebookIfExists: String = "➡ Чи хотіли би ви змінити термін в разі, якщо резервація вже існує?"
+
override def city: String = "місто"
override def clinic: String = "клініка"
diff --git a/server/src/main/scala/com/lbs/server/repository/model/Monitoring.scala b/server/src/main/scala/com/lbs/server/repository/model/Monitoring.scala
index 4f5f521..20478ab 100644
--- a/server/src/main/scala/com/lbs/server/repository/model/Monitoring.scala
+++ b/server/src/main/scala/com/lbs/server/repository/model/Monitoring.scala
@@ -78,6 +78,10 @@ class Monitoring extends RecordId {
@Column(nullable = false)
var autobook: Boolean = false
+ @BeanProperty
+ @Column(name = "rebook_if_exists", nullable = false)
+ var rebookIfExists: Boolean = false
+
@BeanProperty
@Column(nullable = false)
var created: ZonedDateTime = _
@@ -90,7 +94,7 @@ class Monitoring extends RecordId {
object Monitoring {
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,
- dateTo: ZonedDateTime, autobook: Boolean = false, created: ZonedDateTime = ZonedDateTime.now(), timeFrom: LocalTime, timeTo: LocalTime,
+ dateTo: ZonedDateTime, autobook: Boolean = false, rebookIfExists: Boolean = false, created: ZonedDateTime = ZonedDateTime.now(), timeFrom: LocalTime, timeTo: LocalTime,
active: Boolean = true): Monitoring = {
val monitoring = new Monitoring
monitoring.userId = userId
@@ -110,6 +114,7 @@ object Monitoring {
monitoring.timeFrom = timeFrom
monitoring.timeTo = timeTo
monitoring.autobook = autobook
+ monitoring.rebookIfExists = rebookIfExists
monitoring.created = created
monitoring.active = active
monitoring
diff --git a/server/src/main/scala/com/lbs/server/service/ApiService.scala b/server/src/main/scala/com/lbs/server/service/ApiService.scala
index 51236fc..5a16fbc 100644
--- a/server/src/main/scala/com/lbs/server/service/ApiService.scala
+++ b/server/src/main/scala/com/lbs/server/service/ApiService.scala
@@ -5,6 +5,7 @@ import java.time.{LocalTime, ZonedDateTime}
import com.lbs.api.LuxmedApi
import com.lbs.api.json.model._
+import com.lbs.server.util.ServerModelConverters._
import org.jasypt.util.text.TextEncryptor
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
@@ -91,6 +92,68 @@ class ApiService extends SessionSupport {
LuxmedApi.reservation(session.accessToken, session.tokenType, reservationRequest)
}
+ def reserveVisit(accountId: Long, term: AvailableVisitsTermPresentation): Either[Throwable, ReservationResponse] = {
+ val temporaryReservationRequest = term.mapTo[TemporaryReservationRequest]
+ val valuationsRequest = term.mapTo[ValuationsRequest]
+ for {
+ okResponse <- temporaryReservation(accountId, temporaryReservationRequest, valuationsRequest)
+ (temporaryReservation, valuations) = okResponse
+ temporaryReservationId = temporaryReservation.id
+ visitTermVariant = valuations.visitTermVariants.head
+ reservationRequest = (temporaryReservationId, visitTermVariant, term).mapTo[ReservationRequest]
+ reservation <- reservation(accountId, reservationRequest)
+ } yield reservation
+ }
+
+ def canTermBeChanged(accountId: Long, reservationId: Long): Either[Throwable, HttpResponse[String]] =
+ withSession(accountId) { session =>
+ LuxmedApi.canTermBeChanged(session.accessToken, session.tokenType, reservationId)
+ }
+
+
+ def detailToChangeTerm(accountId: Long, reservationId: Long): Either[Throwable, ChangeTermDetailsResponse] =
+ withSession(accountId) { session =>
+ LuxmedApi.detailToChangeTerm(session.accessToken, session.tokenType, reservationId)
+ }
+
+ def temporaryReservationToChangeTerm(accountId: Long, reservationId: Long, temporaryReservationRequest: TemporaryReservationRequest, valuationsRequest: ValuationsRequest): Either[Throwable, (TemporaryReservationResponse, ValuationsResponse)] =
+ withSession(accountId) { session =>
+ LuxmedApi.temporaryReservationToChangeTerm(session.accessToken, session.tokenType, reservationId, temporaryReservationRequest) match {
+ case Left(ex) => Left(ex)
+ case Right(temporaryReservation) =>
+ LuxmedApi.valuationToChangeTerm(session.accessToken, session.tokenType, reservationId, valuationsRequest) match {
+ case Left(ex) => Left(ex)
+ case Right(valuationsResponse) => Right(temporaryReservation -> valuationsResponse)
+ }
+ }
+ }
+
+ def valuationToChangeTerm(accountId: Long, reservationId: Long, valuationsRequest: ValuationsRequest): Either[Throwable, ValuationsResponse] =
+ withSession(accountId) { session =>
+ LuxmedApi.valuationToChangeTerm(session.accessToken, session.tokenType, reservationId, valuationsRequest)
+ }
+
+ def changeTerm(accountId: Long, reservationId: Long, reservationRequest: ReservationRequest): Either[Throwable, ChangeTermResponse] =
+ withSession(accountId) { session =>
+ LuxmedApi.changeTerm(session.accessToken, session.tokenType, reservationId, reservationRequest)
+ }
+
+ def updateTerm(accountId: Long, reservationId: Long, term: AvailableVisitsTermPresentation): Either[Throwable, ChangeTermResponse] = {
+ val temporaryReservationRequest = term.mapTo[TemporaryReservationRequest]
+ val valuationsRequest = term.mapTo[ValuationsRequest]
+ val canTermBeChangedResponse = canTermBeChanged(accountId, reservationId)
+ if (canTermBeChangedResponse.exists(_.code == 204)) {
+ for {
+ okResponse <- temporaryReservationToChangeTerm(accountId, reservationId, temporaryReservationRequest, valuationsRequest)
+ (temporaryReservation, valuations) = okResponse
+ temporaryReservationId = temporaryReservation.id
+ visitTermVariant = valuations.visitTermVariants.head
+ reservationRequest = (temporaryReservationId, visitTermVariant, term).mapTo[ReservationRequest]
+ reservation <- changeTerm(accountId, reservationId, reservationRequest)
+ } yield reservation
+ } else Left(new RuntimeException(s"Term for reservation [$reservationId] can't be changed"))
+ }
+
def visitsHistory(accountId: Long, fromDate: ZonedDateTime = ZonedDateTime.now().minusYears(1),
toDate: ZonedDateTime = ZonedDateTime.now(), page: Int = 1, pageSize: Int = 100): Either[Throwable, List[HistoricVisit]] =
withSession(accountId) { session =>
diff --git a/server/src/main/scala/com/lbs/server/service/MonitoringService.scala b/server/src/main/scala/com/lbs/server/service/MonitoringService.scala
index a7f6594..814b2b4 100644
--- a/server/src/main/scala/com/lbs/server/service/MonitoringService.scala
+++ b/server/src/main/scala/com/lbs/server/service/MonitoringService.scala
@@ -4,22 +4,21 @@ package com.lbs.server.service
import java.time.ZonedDateTime
import java.util.concurrent.ScheduledFuture
-import com.lbs.api.exception.InvalidLoginOrPasswordException
-import com.lbs.api.json.model.{AvailableVisitsTermPresentation, ReservationRequest, TemporaryReservationRequest, ValuationsRequest}
+import com.lbs.api.exception.{InvalidLoginOrPasswordException, ServiceIsAlreadyBookedException}
+import com.lbs.api.json.model.AvailableVisitsTermPresentation
import com.lbs.bot.Bot
import com.lbs.bot.model.{MessageSource, MessageSourceSystem}
import com.lbs.common.{Logger, Scheduler}
import com.lbs.server.lang.Localization
import com.lbs.server.repository.model._
import com.lbs.server.util.DateTimeUtil._
-import com.lbs.server.util.ServerModelConverters._
import javax.annotation.PostConstruct
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import scala.collection.mutable
import scala.concurrent.duration._
-import scala.util.Random
+import scala.util.{Failure, Random}
@Service
class MonitoringService extends Logger {
@@ -147,25 +146,22 @@ class MonitoringService extends Logger {
}
private def bookAppointment(term: AvailableVisitsTermPresentation, monitoring: Monitoring): Unit = {
- val temporaryReservationRequest = term.mapTo[TemporaryReservationRequest]
- val valuationsRequest = term.mapTo[ValuationsRequest]
- val reservationMaybe = for {
- okResponse <- apiService.temporaryReservation(monitoring.accountId, temporaryReservationRequest, valuationsRequest)
- (temporaryReservation, valuations) = okResponse
- temporaryReservationId = temporaryReservation.id
- visitTermVariant = valuations.visitTermVariants.head
- reservationRequest = (temporaryReservationId, visitTermVariant, term).mapTo[ReservationRequest]
- reservation <- apiService.reservation(monitoring.accountId, reservationRequest)
- } yield reservation
-
- reservationMaybe match {
+ apiService.reserveVisit(monitoring.accountId, term).toTry.recoverWith {
+ case _: ServiceIsAlreadyBookedException if monitoring.rebookIfExists =>
+ info(s"Service [${monitoring.serviceName}] is already booked. Trying to update term")
+ val reservation = apiService.reservedVisits(monitoring.accountId, toDate = ZonedDateTime.now().plusMonths(6)).map(_.head)
+ reservation.toTry.flatMap { r =>
+ val reservationId = r.reservationId
+ apiService.updateTerm(monitoring.accountId, reservationId, term).toTry
+ }
+ case ex => Failure(ex)
+ }.toEither match {
case Right(_) =>
bot.sendMessage(monitoring.source, lang(monitoring.userId).appointmentIsBooked(term, monitoring))
deactivateMonitoring(monitoring.recordId)
case Left(ex) =>
error(s"Unable to book appointment by monitoring [${monitoring.recordId}]", ex)
}
-
}
def deactivateMonitoring(monitoringId: JLong): Unit = {
diff --git a/server/src/main/scala/com/lbs/server/util/package.scala b/server/src/main/scala/com/lbs/server/util/package.scala
index f15f8e6..ba73555 100644
--- a/server/src/main/scala/com/lbs/server/util/package.scala
+++ b/server/src/main/scala/com/lbs/server/util/package.scala
@@ -14,6 +14,7 @@ import com.lbs.server.repository.model.{History, Monitoring}
import scala.collection.generic.CanBuildFrom
import scala.language.{higherKinds, implicitConversions}
+import scala.util.Try
package object util {
@@ -42,7 +43,8 @@ package object util {
dateTo = bookingData.dateTo,
timeFrom = bookingData.timeFrom,
timeTo = bookingData.timeTo,
- autobook = bookingData.autobook
+ autobook = bookingData.autobook,
+ rebookIfExists = bookingData.rebookIfExists
)
}
}
@@ -117,6 +119,10 @@ package object util {
def unapply(cmd: Command): Option[String] = cmd.callbackData
}
+ object BooleanString {
+ def unapply(string: String): Option[Boolean] = Try(string.toBoolean).toOption
+ }
+
}
object DateTimeUtil {
@@ -154,4 +160,11 @@ package object util {
}
}
+ implicit class RichEither[T](either: Either[Throwable, T]) {
+ def toTry: Try[T] = either match {
+ case Left(ex) => throw ex
+ case Right(v) => Try(v)
+ }
+ }
+
}