graphql-engine/server/src-lib/Data/Parser/JSONPath.hs
Karthikeyan Chinnakonda a26bc80496
accept a new argument claims_namespace_path in JWT config (#4365)
* add new optional field `claims_namespace_path` in JWT config

* return value when empty array is found in executeJSONPath

* update the docs related to claims_namespace_path

* improve encodeJSONPath, add property tests for parseJSONPath

* throw error if both claims_namespace_path and claims_namespace are set

* refactor the Data.Parser.JsonPath to Data.Parser.JSONPathSpec

* update the JWT docs

Co-Authored-By: Marion Schleifer <marion@hasura.io>

Co-authored-by: Marion Schleifer <marion@hasura.io>
Co-authored-by: rakeshkky <12475069+rakeshkky@users.noreply.github.com>
Co-authored-by: Tirumarai Selvan <tirumarai.selvan@gmail.com>
2020-04-16 12:15:21 +05:30

65 lines
2.3 KiB
Haskell

module Data.Parser.JSONPath
( parseJSONPath
, JSONPathElement(..)
, JSONPath
) where
import Control.Applicative ((<|>))
import Data.Aeson.Internal (JSONPath, JSONPathElement (..))
import Data.Attoparsec.Text
import Data.Bool (bool)
import Prelude hiding (takeWhile)
import Text.Read (readMaybe)
import qualified Data.Text as T
parseSimpleKeyText :: Parser T.Text
parseSimpleKeyText = takeWhile1 (inClass "a-zA-Z0-9_-")
parseKey :: Parser JSONPathElement
parseKey = Key <$>
( (char '.' *> parseSimpleKeyText) -- Parse `.key`
<|> T.pack <$> ((string ".['" <|> string "['") *> manyTill anyChar (string "']")) -- Parse `['key']` or `.['key']`
<|> fail "invalid key element"
)
parseIndex :: Parser JSONPathElement
parseIndex = Index <$>
( ((char '[' *> manyTill anyChar (char ']')) >>= maybe (fail "invalid array index") pure . readMaybe) -- Parse `[Int]`
<|> fail "invalid index element"
)
parseElements :: Parser JSONPath
parseElements = skipWhile (== '$') *> parseRemaining
where
parseFirstKey = Key <$> parseSimpleKeyText
parseElements' = many1 (parseIndex <|> parseKey)
parseRemaining = do
maybeFirstChar <- peekChar
case maybeFirstChar of
Nothing -> pure []
Just firstChar ->
-- If first char is not any of '.' and '[', then parse first key
-- Eg:- Parse "key1.key2[0]"
if firstChar `notElem` (".[" :: String) then do
firstKey <- parseFirstKey
remainingElements <- parseElements'
pure $ firstKey:remainingElements
else parseElements'
-- | Parse jsonpath String value
parseJSONPath :: T.Text -> Either String JSONPath
parseJSONPath = parseResult . parse parseElements
where
parseResult = \case
Fail _ pos err ->
Left $ bool (head pos) err (null pos)
Partial p -> parseResult $ p ""
Done remain r ->
if not $ T.null remain then
Left $ invalidMessage remain
else
Right r
invalidMessage s = "invalid property name: " ++ T.unpack s
++ ". Accept letters, digits, underscore (_) or hyphen (-) only"
++ ". Use single quotes enclosed in bracket if there are any special characters"