Files
graphql-engine/server/src-lib/Data/URL/Template.hs
Vamshi Surabhi b84db36ebb allow custom mutations through actions (#3042)
* basic doc for actions

* custom_types, sync and async actions

* switch to graphql-parser-hs on github

* update docs

* metadata import/export

* webhook calls are now supported

* relationships in sync actions

* initialise.sql is now in sync with the migration file

* fix metadata tests

* allow specifying arguments of actions

* fix blacklist check on check_build_worthiness job

* track custom_types and actions related tables

* handlers are now triggered on async actions

* default to pgjson unless a field is involved in relationships, for generating definition list

* use 'true' for action filter for non admin role

* fix create_action_permission sql query

* drop permissions when dropping an action

* add a hdb_role view (and relationships) to fetch all roles in the system

* rename 'webhook' key in action definition to 'handler'

* allow templating actions wehook URLs with env vars

* add 'update_action' /v1/query type

* allow forwarding client headers by setting `forward_client_headers` in action definition

* add 'headers' configuration in action definition

* handle webhook error response based on status codes

* support array relationships for custom types

* implement single row mutation, see https://github.com/hasura/graphql-engine/issues/3731

* single row mutation: rename 'pk_columns' -> 'columns' and no-op refactor

* use top level primary key inputs for delete_by_pk & account select permissions for single row mutations

* use only REST semantics to resolve the webhook response

* use 'pk_columns' instead of 'columns' for update_by_pk input

* add python basic tests for single row mutations

* add action context (name) in webhook payload

* Async action response is accessible for non admin roles only if
  the request session vars equals to action's

* clean nulls, empty arrays for actions, custom types in export metadata

* async action mutation returns only the UUID of the action

* unit tests for URL template parser

* Basic sync actions python tests

* fix output in async query & add async tests

* add admin secret header in async actions python test

* document async action architecture in Resolve/Action.hs file

* support actions returning array of objects

* tests for list type response actions

* update docs with actions and custom types metadata API reference

* update actions python tests as per #f8e1330

Co-authored-by: Tirumarai Selvan <tirumarai.selvan@gmail.com>
Co-authored-by: Aravind Shankar <face11301@gmail.com>
Co-authored-by: Rakesh Emmadi <12475069+rakeshkky@users.noreply.github.com>
2020-02-13 23:08:23 +05:30

95 lines
3.0 KiB
Haskell

-- | A simple URL templating that enables interpolating environment variables
module Data.URL.Template
( URLTemplate
, TemplateItem
, Variable
, printURLTemplate
, parseURLTemplate
, renderURLTemplate
, genURLTemplate
)
where
import Hasura.Prelude
import qualified Data.Text as T
import Data.Attoparsec.Combinator (lookAhead)
import Data.Attoparsec.Text
import Instances.TH.Lift ()
import Language.Haskell.TH.Syntax (Lift)
import System.Environment (lookupEnv)
import Test.QuickCheck
newtype Variable = Variable {unVariable :: Text}
deriving (Show, Eq, Lift, Generic)
printVariable :: Variable -> Text
printVariable var = "{{" <> unVariable var <> "}}"
data TemplateItem
= TIText !Text
| TIVariable !Variable
deriving (Show, Eq, Lift, Generic)
printTemplateItem :: TemplateItem -> Text
printTemplateItem = \case
TIText t -> t
TIVariable v -> printVariable v
-- | A String with environment variables enclosed in '{{' and '}}'
-- http://{{APP_HOST}}:{{APP_PORT}}/v1/api
newtype URLTemplate = URLTemplate {unURLTemplate :: [TemplateItem]}
deriving (Show, Eq, Lift, Generic)
printURLTemplate :: URLTemplate -> Text
printURLTemplate = T.concat . map printTemplateItem . unURLTemplate
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)
<|> (TIText . T.pack <$> manyTill anyChar (lookAhead $ string "{{"))
parseVariable :: Parser Variable
parseVariable =
string "{{" *> (Variable . T.pack <$> manyTill anyChar (string "}}"))
renderURLTemplate :: MonadIO m => URLTemplate -> m (Either String Text)
renderURLTemplate template = do
eitherResults <- mapM renderTemplateItem $ unURLTemplate template
let errorVariables = lefts eitherResults
pure $ case errorVariables of
[] -> Right $ T.concat $ rights eitherResults
_ -> Left $ T.unpack $ "Value for environment variables not found: "
<> T.intercalate ", " errorVariables
where
renderTemplateItem = \case
TIText t -> pure $ Right t
TIVariable (Variable var) -> do
maybeEnvValue <- liftIO $ lookupEnv $ T.unpack var
pure $ case maybeEnvValue of
Nothing -> Left var
Just value -> Right $ T.pack value
-- QuickCheck generators
instance Arbitrary Variable where
arbitrary = Variable . T.pack <$> listOf1 (elements $ alphaNumerics <> "-_")
instance Arbitrary URLTemplate where
arbitrary = URLTemplate <$> listOf (oneof [genText, genVariable])
where
genText = (TIText . T.pack) <$> listOf1 (elements $ alphaNumerics <> "://")
genVariable = TIVariable <$> arbitrary
genURLTemplate :: Gen URLTemplate
genURLTemplate = arbitrary