2020-02-13 20:38:23 +03:00
|
|
|
-- | A simple URL templating that enables interpolating environment variables
|
|
|
|
module Data.URL.Template
|
2021-09-24 01:56:37 +03:00
|
|
|
( URLTemplate,
|
|
|
|
TemplateItem,
|
|
|
|
Variable,
|
|
|
|
printURLTemplate,
|
|
|
|
mkPlainURLTemplate,
|
|
|
|
parseURLTemplate,
|
|
|
|
renderURLTemplate,
|
2020-02-13 20:38:23 +03:00
|
|
|
)
|
|
|
|
where
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
import Data.Attoparsec.Combinator (lookAhead)
|
|
|
|
import Data.Attoparsec.Text
|
|
|
|
import Data.Environment qualified as Env
|
|
|
|
import Data.Text qualified as T
|
|
|
|
import Data.Text.Extended
|
|
|
|
import Hasura.Prelude
|
|
|
|
import Test.QuickCheck
|
2020-02-13 20:38:23 +03:00
|
|
|
|
|
|
|
newtype Variable = Variable {unVariable :: Text}
|
2021-02-14 09:07:52 +03:00
|
|
|
deriving (Show, Eq, Generic, Hashable)
|
2020-02-13 20:38:23 +03:00
|
|
|
|
|
|
|
printVariable :: Variable -> Text
|
|
|
|
printVariable var = "{{" <> unVariable var <> "}}"
|
|
|
|
|
|
|
|
data TemplateItem
|
|
|
|
= TIText !Text
|
|
|
|
| TIVariable !Variable
|
2020-11-26 16:57:03 +03:00
|
|
|
deriving (Show, Eq, Generic)
|
2021-09-24 01:56:37 +03:00
|
|
|
|
2021-02-14 09:07:52 +03:00
|
|
|
instance Hashable TemplateItem
|
2020-02-13 20:38:23 +03:00
|
|
|
|
|
|
|
printTemplateItem :: TemplateItem -> Text
|
|
|
|
printTemplateItem = \case
|
2021-09-24 01:56:37 +03:00
|
|
|
TIText t -> t
|
2020-02-13 20:38:23 +03:00
|
|
|
TIVariable v -> printVariable v
|
|
|
|
|
|
|
|
-- | A String with environment variables enclosed in '{{' and '}}'
|
|
|
|
-- http://{{APP_HOST}}:{{APP_PORT}}/v1/api
|
|
|
|
newtype URLTemplate = URLTemplate {unURLTemplate :: [TemplateItem]}
|
2021-02-14 09:07:52 +03:00
|
|
|
deriving (Show, Eq, Generic, Hashable)
|
2020-02-13 20:38:23 +03:00
|
|
|
|
|
|
|
printURLTemplate :: URLTemplate -> Text
|
|
|
|
printURLTemplate = T.concat . map printTemplateItem . unURLTemplate
|
|
|
|
|
2020-12-28 15:56:00 +03:00
|
|
|
mkPlainURLTemplate :: Text -> URLTemplate
|
|
|
|
mkPlainURLTemplate =
|
|
|
|
URLTemplate . pure . TIText
|
|
|
|
|
2020-02-13 20:38:23 +03:00
|
|
|
parseURLTemplate :: Text -> Either String URLTemplate
|
|
|
|
parseURLTemplate t = parseOnly parseTemplate t
|
|
|
|
where
|
|
|
|
parseTemplate :: Parser URLTemplate
|
|
|
|
parseTemplate = do
|
|
|
|
items <- many parseTemplateItem
|
|
|
|
lastItem <- TIText <$> takeText
|
|
|
|
pure $ URLTemplate $ items <> [lastItem]
|
|
|
|
|
|
|
|
parseTemplateItem :: Parser TemplateItem
|
|
|
|
parseTemplateItem =
|
|
|
|
(TIVariable <$> parseVariable)
|
2021-09-24 01:56:37 +03:00
|
|
|
<|> (TIText . T.pack <$> manyTill anyChar (lookAhead $ string "{{"))
|
2020-02-13 20:38:23 +03:00
|
|
|
|
|
|
|
parseVariable :: Parser Variable
|
|
|
|
parseVariable =
|
|
|
|
string "{{" *> (Variable . T.pack <$> manyTill anyChar (string "}}"))
|
|
|
|
|
2020-07-14 22:00:58 +03:00
|
|
|
renderURLTemplate :: Env.Environment -> URLTemplate -> Either String Text
|
|
|
|
renderURLTemplate env template =
|
|
|
|
case errorVariables of
|
2020-02-13 20:38:23 +03:00
|
|
|
[] -> Right $ T.concat $ rights eitherResults
|
2021-09-24 01:56:37 +03:00
|
|
|
_ ->
|
|
|
|
Left $
|
|
|
|
T.unpack $
|
|
|
|
"Value for environment variables not found: "
|
|
|
|
<> commaSeparated errorVariables
|
2020-02-13 20:38:23 +03:00
|
|
|
where
|
2020-07-14 22:00:58 +03:00
|
|
|
eitherResults = map renderTemplateItem $ unURLTemplate template
|
|
|
|
errorVariables = lefts eitherResults
|
2020-02-13 20:38:23 +03:00
|
|
|
renderTemplateItem = \case
|
2020-07-14 22:00:58 +03:00
|
|
|
TIText t -> Right t
|
|
|
|
TIVariable (Variable var) ->
|
|
|
|
let maybeEnvValue = Env.lookupEnv env $ T.unpack var
|
2021-09-24 01:56:37 +03:00
|
|
|
in case maybeEnvValue of
|
|
|
|
Nothing -> Left var
|
|
|
|
Just value -> Right $ T.pack value
|
2020-02-13 20:38:23 +03:00
|
|
|
|
|
|
|
-- QuickCheck generators
|
|
|
|
instance Arbitrary Variable where
|
2020-04-16 09:45:21 +03:00
|
|
|
arbitrary = Variable . T.pack <$> listOf1 (elements $ alphaNumerics <> " -_")
|
2020-02-13 20:38:23 +03:00
|
|
|
|
|
|
|
instance Arbitrary URLTemplate where
|
|
|
|
arbitrary = URLTemplate <$> listOf (oneof [genText, genVariable])
|
|
|
|
where
|
2020-10-28 19:40:33 +03:00
|
|
|
genText = TIText . T.pack <$> listOf1 (elements $ alphaNumerics <> " ://")
|
2020-02-13 20:38:23 +03:00
|
|
|
genVariable = TIVariable <$> arbitrary
|