packages feed

compaREST-0.1.0.0: src/Data/OpenApi/Compare/Validate/PathFragment.hs

module Data.OpenApi.Compare.Validate.PathFragment
  ( parsePath,
    PathFragment (..),
    PathFragmentParam,
  )
where

import qualified Data.Aeson as A
import Data.OpenApi
import Data.OpenApi.Compare.Behavior
import Data.OpenApi.Compare.Subtree
import Data.OpenApi.Compare.Validate.Param
import Data.Text (Text)
import qualified Data.Text as T

-- TODO: templates can be only part of the PathFragment. Currently only supports templates as full PathFragment.
-- #23
parsePath :: FilePath -> [PathFragment Text]
parsePath = fmap partition . T.splitOn "/" . T.pack
  where
    partition :: Text -> PathFragment Text
    partition t
      | Just ('{', rest) <- T.uncons t
        , Just (ref, '}') <- T.unsnoc rest =
        DynamicPath ref
    partition t = StaticPath t

-- | Fragment parameterized by parameter. The dynamic part may be either
-- reference to some parameter (in context of operation) or dereferenced
-- parameter itself.
data PathFragment param
  = StaticPath Text
  | DynamicPath param
  deriving stock (Eq, Ord, Show, Functor)

type PathFragmentParam = PathFragment (Traced Param)

instance (Typeable param) => Steppable (PathFragment param) Param where
  data Step (PathFragment param) Param = StaticPathParam Text
    deriving stock (Eq, Ord, Show)

tracedPathFragmentParam :: Traced PathFragmentParam -> Traced Param
tracedPathFragmentParam pfp = case extract pfp of
  StaticPath s ->
    traced (ask pfp >>> step (StaticPathParam s)) $
      mempty
        { _paramRequired = Just True
        , _paramIn = ParamPath
        , _paramAllowEmptyValue = Just False
        , _paramAllowReserved = Just False
        , _paramSchema = Just $ Inline $ staticStringSchema s
        }
  DynamicPath p -> p

staticStringSchema :: Text -> Schema
staticStringSchema t =
  mempty
    { _schemaNullable = Just False
    , _schemaType = Just OpenApiString
    , _schemaEnum = Just [A.String t]
    }

instance Subtree PathFragmentParam where
  type SubtreeLevel PathFragmentParam = 'PathFragmentLevel
  type
    CheckEnv PathFragmentParam =
      '[ProdCons (Traced (Definitions Schema))]

  -- Not much to compare at this level
  checkStructuralCompatibility _ _ = structuralIssue

  -- This case isn't strictly needed. It is here for optimization.
  checkSemanticCompatibility _ beh (ProdCons (extract -> StaticPath x) (extract -> StaticPath y)) =
    if x == y
      then pure ()
      else issueAt beh (PathFragmentsDontMatch (ProdCons x y))
  checkSemanticCompatibility env beh prodCons = do
    checkCompatibility beh env (tracedPathFragmentParam <$> prodCons)