azure-functions-worker-0.0.0.0: src/Azure/Functions/Bindings/Blob.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Azure.Functions.Bindings.Blob
( ConnectionName(..)
, BlobBinding(..)
, ReceivedBlob(..)
, Blob(..)
)
where
import Azure.Functions.Bindings.Class
import Azure.Functions.Bindings.Shared
import Azure.Functions.Internal.Lens (orError)
import Control.Arrow ((&&&))
import Data.Aeson (FromJSON, ToJSON (..), Value (Null), decodeStrict', object, (.=))
import Data.ByteString (ByteString)
import Data.Coerce (coerce)
import Data.Functor ((<&>))
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.ProtoLens.Runtime.Data.ProtoLens (defMessage)
import qualified Data.Set as Set
import Data.Text (Text)
import qualified Data.Text.Encoding as Text
import GHC.Generics (Generic)
import Lens.Family (view, (&), (.~), (^.))
import Lens.Family.Stock (at)
import Network.URI (URI, parseURI)
import Proto.FunctionRpc
import Proto.FunctionRpc_Fields
data BlobBinding = BlobBinding
{ blobBindingConnectionName :: ConnectionName
, blobBindingPathPattern :: Text
} deriving (Generic)
data ReceivedBlob = ReceivedBlob
{ receivedBlobContent :: ByteString
, receivedBlobName :: Text
, receivedBlobUri :: URI
, receivedBlobMetadata :: Map Text Text
, receivedBlobTriggerMetadata :: Map Text Text
, receivedBlobProperties :: Value
} deriving (Show, Generic)
data Blob = Blob
{ sentBlobContent :: ByteString
} deriving (Show, Generic)
instance ToInBinding BlobBinding where
toInBindingJSON v = object
[ "type" .= ("blobTrigger" :: Text)
, "direction" .= ("in" :: Text)
, "name" .= ("blobData" :: Text)
, "path" .= blobBindingPathPattern v
, "connection" .= coerce @_ @Text (blobBindingConnectionName v)
]
instance ToOutBinding BlobBinding where
toOutBindingJSON v = object
[ "type" .= ("blob" :: Text)
, "direction" .= ("out" :: Text)
, "name" .= ("$return" :: Text)
, "path" .= blobBindingPathPattern v
, "connection" .= coerce @_ @Text (blobBindingConnectionName v)
]
instance InMessage ReceivedBlob where
type InBinding ReceivedBlob = BlobBinding
fromInvocationRequest req = do
let idata = req ^. inputData <&> (view name &&& view data') & Map.fromList
let tmeta = req ^. triggerMetadata
let orMissing fld = orError ("Unable to parse " <> fld)
content <- idata ^. at "blobData" >>= getBytes & orMissing "blobData"
name <- tmeta ^. at "BlobTrigger" >>= getText & orMissing "BlobTrigger"
uri <- tmeta ^. at "Uri" >>= decodeJson >>= parseURI & orMissing "Uri"
props <- tmeta ^. at "Properties" >>= decodeJson & maybe (pure Null) pure
bmeta <- tmeta ^. at "Metadata" >>= decodeJson & maybe (pure mempty) pure
pure ReceivedBlob
{ receivedBlobContent = content
, receivedBlobName = name
, receivedBlobUri = uri
, receivedBlobMetadata = bmeta
, receivedBlobTriggerMetadata = Map.withoutKeys tmeta (Set.fromList ["Properties", "sys", "Metadata", "BlobTrigger"]) & Map.mapMaybe getText
, receivedBlobProperties = props
}
instance OutMessage Blob where
type OutBinding Blob = BlobBinding
toInvocationResponse resp =
let
td = defMessage @TypedData
& maybe'data' .~ Just (TypedData'Bytes (sentBlobContent resp))
in defMessage @InvocationResponse
& returnValue .~ td
& result .~ (defMessage & status .~ StatusResult'Success)