packages feed

hurl-workbench-core-0.1.0.0: src/HurlWorkbench/Check/Resolve.hs

-- | Suite-wide inputs only; argv templates never accept Secret or Generated.
module HurlWorkbench.Check.Resolve (ResolvedCheck (..), CheckResolutionError (..), prepareCheck, renderCheckResolutionError) where

import Data.Generics.Labels ()
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Data.Text qualified as Text
import Data.Time.Clock (NominalDiffTime)
import HurlWorkbench.Check.Template
import HurlWorkbench.Parameter.Resolve
import HurlWorkbench.Prelude
import HurlWorkbench.Service.Resolve
import HurlWorkbench.Service.Run (prepareCommand)
import HurlWorkbench.Workspace.Context
import HurlWorkbench.Workspace.Types

-- The command's Show instance redacts all resolved values.
data ResolvedCheck = ResolvedCheck
  { name :: !CheckName,
    processConfig :: !ResolvedCommand,
    expectedStatus :: !Int,
    timeout :: !NominalDiffTime
  }
  deriving stock (Generic, Eq, Show)

data CheckResolutionError = CheckBindingFailed !BindingError | CheckCommandFailed !ServiceError | CheckTemplateInvalid | CheckDefinitionMissing
  deriving stock (Generic, Eq, Show)

prepareCheck :: [(String, String)] -> ValidatedWorkspace -> BindingInput -> CheckName -> IO (Either CheckResolutionError ResolvedCheck)
prepareCheck ambient workspace input name = case (lookupCheck name workspace, lookupCheckExecutable name workspace) of
  (Just check, Just executable) -> case traverse parseTemplate (check ^. #command . #arguments) of
    Left _ -> pure (Left CheckTemplateInvalid)
    Right templates -> do
      let required = Set.fromList ([p | templateParts <- templates, Placeholder p <- templateParts] <> map (view #parameter) (check ^. #command . #environment))
      resolveParameterSubsetInEnvironment ambient workspace required input <&> \case
        Left err -> Left (CheckBindingFailed err)
        Right bindings -> do
          let render (Literal value) = Right value
              render (Placeholder parameter) = case lookupParameter parameter workspace of
                Just definition | definition ^. #kind == Plain -> maybe (Left CheckTemplateInvalid) (Right . hurlValueLiteralText) (Map.lookup parameter (bindings ^. #variables))
                _ -> Left CheckTemplateInvalid
          arguments <- traverse (fmap Text.unpack . fmap Text.concat . traverse render) templates
          command <- either (Left . CheckCommandFailed) Right (resolveCommand workspace bindings (check ^. #command))
          frozen <- either (Left . CheckCommandFailed) Right (prepareCommand ambient (ServiceName (unCheckName name)) "check" (command & #executable .~ executable & #arguments .~ arguments))
          pure (ResolvedCheck name frozen (fromIntegral (check ^. #expectedStatus)) (maybe 60 fromIntegral (check ^. #timeoutSeconds)))
  _ -> pure (Left CheckDefinitionMissing)

renderCheckResolutionError :: CheckResolutionError -> Text
renderCheckResolutionError = \case
  CheckBindingFailed err -> renderBindingError err
  CheckCommandFailed (ServiceEnvironmentMissing _ _ names) -> "check requires missing environment variables: " <> Text.intercalate ", " names
  CheckCommandFailed _ -> "check command preparation failed"
  CheckTemplateInvalid -> "invalid check argument template"
  CheckDefinitionMissing -> "check definition unavailable"