Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/mobility-core/mobility-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,13 @@ library
Kernel.External.SOS.Interface
Kernel.External.SOS.Interface.ERSS
Kernel.External.SOS.Interface.GJ112
Kernel.External.SOS.Interface.Trinity
Kernel.External.SOS.Interface.Types
Kernel.External.SOS.Trinity.API
Kernel.External.SOS.Trinity.Auth
Kernel.External.SOS.Trinity.Config
Kernel.External.SOS.Trinity.Flow
Kernel.External.SOS.Trinity.Types
Kernel.External.SOS.Types
Kernel.External.Ticket.Interface
Kernel.External.Ticket.Interface.Kapture
Expand Down
5 changes: 5 additions & 0 deletions lib/mobility-core/src/Kernel/External/SOS/Interface.hs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ where
import Kernel.External.Encryption
import qualified Kernel.External.SOS.Interface.ERSS as ERSS
import qualified Kernel.External.SOS.Interface.GJ112 as GJ112
import qualified Kernel.External.SOS.Interface.Trinity as Trinity
import Kernel.External.SOS.Interface.Types as Reexport
import Kernel.Prelude
import qualified Kernel.Storage.Hedis as Redis
Expand All @@ -46,6 +47,7 @@ sendInitialSOS ::
sendInitialSOS config req = case config of
ERSSConfig erssCfg -> ERSS.sendInitialSOS erssCfg req
GJ112Config gj112Cfg -> GJ112.sendInitialSOS gj112Cfg req
TrinityConfig trCfg -> Trinity.sendInitialSOS trCfg req

-- | Send SOS Trace (location update) - dispatches to appropriate provider
sendSOSTrace ::
Expand All @@ -62,6 +64,7 @@ sendSOSTrace ::
sendSOSTrace config req = case config of
ERSSConfig erssCfg -> ERSS.sendSOSTrace erssCfg req
GJ112Config gj112Cfg -> GJ112.sendSOSTrace gj112Cfg req
TrinityConfig trCfg -> Trinity.sendSOSTrace trCfg req

-- | Update SOS Status - dispatches to appropriate provider
updateSOSStatus ::
Expand All @@ -78,6 +81,7 @@ updateSOSStatus ::
updateSOSStatus config req = case config of
ERSSConfig erssCfg -> ERSS.updateSOSStatus erssCfg req
GJ112Config gj112Cfg -> GJ112.updateSOSStatus gj112Cfg req
TrinityConfig trCfg -> Trinity.updateSOSStatus trCfg req

-- | Upload Media File - dispatches to appropriate provider
uploadMedia ::
Expand All @@ -96,3 +100,4 @@ uploadMedia ::
uploadMedia config phoneNumber fileName filePath = case config of
ERSSConfig erssCfg -> ERSS.uploadMedia erssCfg phoneNumber fileName filePath
GJ112Config _gj112Cfg -> pure $ SOSMediaUploadRes False (Just "Media upload not implemented for GJ112")
TrinityConfig _trCfg -> pure $ SOSMediaUploadRes False (Just "Media upload not implemented for Trinity")
127 changes: 127 additions & 0 deletions lib/mobility-core/src/Kernel/External/SOS/Interface/Trinity.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
{-
Copyright 2022-23, Juspay India Pvt Ltd

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License

as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is

distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS

FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero

General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
-}

module Kernel.External.SOS.Interface.Trinity
( sendInitialSOS,
sendSOSTrace,
updateSOSStatus,
)
where

import Kernel.External.Encryption
import qualified Kernel.External.SOS.Interface.Types as Interface
import Kernel.External.SOS.Trinity.Config
import qualified Kernel.External.SOS.Trinity.Flow as TRFlow
import qualified Kernel.External.SOS.Trinity.Types as TR
import Kernel.Prelude
import qualified Kernel.Storage.Hedis as Redis
import Kernel.Tools.Metrics.CoreMetrics (CoreMetrics)
import Kernel.Types.Common
import Kernel.Utils.Common (HasRequestId)

-- | Send Initial SOS - converts interface types to Trinity types and delegates
sendInitialSOS ::
( EncFlow m r,
CoreMetrics m,
Redis.HedisFlow m r,
MonadFlow m,
HasRequestId r,
MonadReader r m
) =>
TrinityCfg ->
Interface.InitialSOSReq ->
m Interface.InitialSOSRes
sendInitialSOS config req = do
let trReq = toTrinitySOSReq config req
trRes <- TRFlow.sendSOS config trReq
pure $ fromTrinitySOSRes trRes

-- | Send SOS Trace - Trinity does not support trace, return success
sendSOSTrace ::
( EncFlow m r,
CoreMetrics m,
Redis.HedisFlow m r,
MonadFlow m,
HasRequestId r,
MonadReader r m
) =>
TrinityCfg ->
Interface.SOSTraceReq ->
m Interface.SOSTraceRes
sendSOSTrace _config _req =
pure $
Interface.SOSTraceRes
{ success = True,
errorMessage = Nothing
}

-- | Update SOS Status - Trinity does not support status update, return success
updateSOSStatus ::
( EncFlow m r,
CoreMetrics m,
Redis.HedisFlow m r,
MonadFlow m,
HasRequestId r,
MonadReader r m
) =>
TrinityCfg ->
Interface.SOSStatusUpdateReq ->
m Interface.SOSStatusUpdateRes
updateSOSStatus _config _req =
pure $
Interface.SOSStatusUpdateRes
{ success = True,
errorMessage = Nothing
}

-- Conversion helpers

toTrinitySOSReq :: TrinityCfg -> Interface.InitialSOSReq -> TR.TrinitySOSReq
toTrinitySOSReq config Interface.InitialSOSReq {..} =
TR.TrinitySOSReq
{ clientId = Just config.clientId,
clientCode = Just config.clientCode,
name = fromMaybe "" senderName,
city = city,
address = address,
deviceUuid = imeiNo,
email = email,
relativeName1 = emergencyContact1Name,
relativeName2 = emergencyContact2Name,
relativeContact1 = emergencyContact1Phone,
relativeContact2 = emergencyContact2Phone,
gender = gender,
simNo = mobileNo,
datetime = Just dateTime,
emergencyMessage = emergencyMessage,
latitude = show latitude,
longitude = show longitude,
videoPath = videoPath,
driverName = driverName,
driverContactNo = driverContactNo,
vehicleNo = vehicleNo,
vehicleModel = vehicleModel,
vehLat = maybe (show latitude) show vehicleLat,
vehLng = maybe (show longitude) show vehicleLon,
deviceType = fromMaybe 6 deviceType,
vehLocUrl = vehicleLocationUrl
}

fromTrinitySOSRes :: TR.TrinitySOSRes -> Interface.InitialSOSRes
fromTrinitySOSRes res =
Interface.InitialSOSRes
{ success = res.status,
trackingId = res.caseId,
errorMessage = if res.status then Nothing else res.message
}
2 changes: 2 additions & 0 deletions lib/mobility-core/src/Kernel/External/SOS/Interface/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import Data.Aeson
import Deriving.Aeson
import qualified Kernel.External.SOS.ERSS.Config as ERSSConfig
import qualified Kernel.External.SOS.GJ112.Config as GJ112Config
import qualified Kernel.External.SOS.Trinity.Config as TrinityConfig
import Kernel.Prelude

-- | Handler pattern for SOS services
Expand All @@ -44,6 +45,7 @@ data SOSHandler m = SOSHandler
data SOSServiceConfig
= ERSSConfig ERSSConfig.ERSSCfg
| GJ112Config GJ112Config.GJ112Cfg
| TrinityConfig TrinityConfig.TrinityCfg
deriving (Show, Eq, Generic)
deriving (FromJSON, ToJSON) via CustomJSON '[SumTaggedObject "tag" "content"] SOSServiceConfig

Expand Down
45 changes: 45 additions & 0 deletions lib/mobility-core/src/Kernel/External/SOS/Trinity/API.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{-
Copyright 2022-23, Juspay India Pvt Ltd

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License

as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is

distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS

FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero

General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
-}

module Kernel.External.SOS.Trinity.API where

import Kernel.External.SOS.Trinity.Types
import Kernel.Prelude
import Servant

-- | Authentication API
-- Endpoint: POST /trinityPlatform/1.0.0/getAccessToken
type TrinityAuthAPI =
"trinityPlatform"
:> "1.0.0"
:> "getAccessToken"
:> ReqBody '[JSON] TrinityAuthReq
:> Post '[JSON] TrinityAuthRes

-- | SOS Trigger API
-- Endpoint: POST /ngcadIntService/1.0.0/externalIntegration/triggerSOSMessage
type TrinitySOSAPI =
"ngcadIntService"
:> "1.0.0"
:> "externalIntegration"
:> "triggerSOSMessage"
:> Header "Authorization" TrinityAuthToken
:> ReqBody '[JSON] TrinitySOSReq
:> Post '[JSON] TrinitySOSRes

trinityAuthAPI :: Proxy TrinityAuthAPI
trinityAuthAPI = Proxy

trinitySOSAPI :: Proxy TrinitySOSAPI
trinitySOSAPI = Proxy
121 changes: 121 additions & 0 deletions lib/mobility-core/src/Kernel/External/SOS/Trinity/Auth.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
{-
Copyright 2022-23, Juspay India Pvt Ltd

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License

as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is

distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS

FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero

General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

module Kernel.External.SOS.Trinity.Auth
( getTrinityToken,
TrinityToken (..),
redisTrinityKey,
)
where

import qualified Data.Time.Clock as Time
import qualified EulerHS.Types as ET
import Kernel.External.Encryption
import Kernel.External.SOS.Trinity.API
import Kernel.External.SOS.Trinity.Config
import Kernel.External.SOS.Trinity.Types
import Kernel.Prelude
import qualified Kernel.Storage.Hedis as Redis
import Kernel.Tools.Metrics.CoreMetrics (CoreMetrics)
import Kernel.Types.Common
import Kernel.Utils.Common

-- | Cached token with expiry timestamp
data TrinityToken = TrinityToken
{ token :: Text,
expiresAtUTC :: UTCTime
}
deriving (Show, Eq, Generic, ToJSON, FromJSON)

redisTrinityKey :: Text
redisTrinityKey = "Core:trinity_token"

-- | Get valid Trinity token (check cache, validate, get new if needed)
getTrinityToken ::
( EncFlow m r,
CoreMetrics m,
Redis.HedisFlow m r,
MonadFlow m,
HasRequestId r,
MonadReader r m
) =>
TrinityCfg ->
m TrinityToken
getTrinityToken config = do
let redisKey = config.tokenKeyPrefix <> ":" <> redisTrinityKey
cached <- Redis.get redisKey
now <- liftIO Time.getCurrentTime
case cached of
Nothing -> getNewToken config
Just cachedToken ->
if now < cachedToken.expiresAtUTC
then pure cachedToken
else getNewToken config

-- | Get new token using authorizationKey + credentials
getNewToken ::
( EncFlow m r,
CoreMetrics m,
Redis.HedisFlow m r,
MonadFlow m,
HasRequestId r,
MonadReader r m
) =>
TrinityCfg ->
m TrinityToken
getNewToken config = do
authKeyDecrypted <- decrypt config.authorizationKey
usernameDecrypted <- decrypt config.username
passwordDecrypted <- decrypt config.password

let req =
TrinityAuthReq
{ authorizationKey = authKeyDecrypted,
username = usernameDecrypted,
password = passwordDecrypted,
grantType = "password",
scope = "default",
apim = "subscribe"
}

res <-
callTrinityAuthAPI
config.baseUrl
(ET.client trinityAuthAPI req)
"trinityAuth"
trinityAuthAPI

now <- liftIO Time.getCurrentTime
let expiresIn = fromMaybe 3600 res.expires_in
bufferedExpiry = Time.addUTCTime (fromIntegral expiresIn - 60) now

let trinityToken =
TrinityToken
{ token = res.access_token,
expiresAtUTC = bufferedExpiry
}

let redisKey = config.tokenKeyPrefix <> ":" <> redisTrinityKey
Redis.set redisKey trinityToken
pure trinityToken

callTrinityAuthAPI :: CallAPI m r api a
callTrinityAuthAPI =
callApiUnwrappingApiError
(identity @TrinityError)
Nothing
(Just "TRINITY_AUTH_ERROR")
Nothing
31 changes: 31 additions & 0 deletions lib/mobility-core/src/Kernel/External/SOS/Trinity/Config.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{-
Copyright 2022-23, Juspay India Pvt Ltd

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License

as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is

distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS

FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero

General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
-}

module Kernel.External.SOS.Trinity.Config where

import Kernel.External.Encryption
import Kernel.Prelude

-- | Configuration for Trinity (Bangalore Safe City)
data TrinityCfg = TrinityCfg
{ baseUrl :: BaseUrl,
sosUrl :: BaseUrl,
authorizationKey :: EncryptedField 'AsEncrypted Text,
username :: EncryptedField 'AsEncrypted Text,
password :: EncryptedField 'AsEncrypted Text,
clientId :: Text,
clientCode :: Text,
tokenKeyPrefix :: Text
}
deriving (Show, Eq, Generic, ToJSON, FromJSON)
Loading
Loading