88{-# LANGUAGE NamedFieldPuns #-}
99module PostgREST.ApiRequest.Payload
1010 ( getPayload
11+ , Payload (.. )
12+ , PgrstPatchOp (.. )
1113 ) where
1214
1315import qualified Data.Aeson as JSON
@@ -23,8 +25,10 @@ import qualified Data.Text.Encoding as T
2325import qualified Data.Vector as V
2426
2527import Control.Arrow ((***) )
28+ import Control.Monad (fail )
29+ import Data.Aeson ((.:) )
2630import Data.Aeson.Types (emptyArray , emptyObject )
27- import Data.Either.Combinators (mapBoth )
31+ import Data.Either.Combinators (mapBoth , mapLeft )
2832import Network.HTTP.Types.URI (parseSimpleQuery )
2933
3034import PostgREST.ApiRequest.QueryParams (QueryParams (.. ))
@@ -37,13 +41,37 @@ import qualified PostgREST.MediaType as MediaType
3741
3842import Protolude
3943
44+ data Payload
45+ = ProcessedJSON -- ^ Cached attributes of a JSON payload
46+ { payRaw :: LBS. ByteString
47+ -- ^ This is the raw ByteString that comes from the request body. We
48+ -- cache this instead of an Aeson Value because it was detected that for
49+ -- large payloads the encoding had high memory usage, see
50+ -- https://github.com/PostgREST/postgrest/pull/1005 for more details
51+ , payKeys :: S. Set Text
52+ -- ^ Keys of the object or if it's an array these keys are guaranteed to
53+ -- be the same across all its objects
54+ }
55+ | ProcessedUrlEncoded { payArray :: [(Text , Text )], payKeys :: S. Set Text }
56+ | RawJSON { payRaw :: LBS. ByteString }
57+ | RawPay { payRaw :: LBS. ByteString }
58+ | PgrstPatchPay { payPgrstPatch :: [PgrstPatchOp ] } -- ^ PgrstPatchPay is a list of patch updates
59+
60+ -- This type should be in the Types.hs module, but because we
61+ -- are defining a JSON.FromJSON instance for this type, we need to have
62+ -- this type here to avoid "no-orphan-instances" warning.
63+ data PgrstPatchOp
64+ = Set FieldName Text
65+ -- We can add more operations in the future
66+
4067getPayload :: RequestBody -> MediaType -> QueryParams -> Action -> Either ApiRequestError (Maybe Payload , S. Set FieldName )
4168getPayload reqBody contentMediaType QueryParams {qsColumns} action = do
4269 checkedPayload <- if shouldParsePayload then payload else Right Nothing
4370 let cols = case (checkedPayload, columns) of
4471 (Just ProcessedJSON {payKeys}, _) -> payKeys
4572 (Just ProcessedUrlEncoded {payKeys}, _) -> payKeys
4673 (Just RawJSON {}, Just cls) -> cls
74+ (Just PgrstPatchPay {}, Just cls) -> cls
4775 _ -> S. empty
4876 return (checkedPayload, cols)
4977 where
@@ -69,8 +97,12 @@ getPayload reqBody contentMediaType QueryParams{qsColumns} action = do
6997 (MTTextPlain , True ) -> Right $ RawPay reqBody
7098 (MTTextXML , True ) -> Right $ RawPay reqBody
7199 (MTOctetStream , True ) -> Right $ RawPay reqBody
100+ (MTVndPgrstPatch , False ) -> PgrstPatchPay <$> parsePgrstPatch reqBody
72101 (ct, _) -> Left $ " Content-Type not acceptable: " <> MediaType. toMime ct
73102
103+ parsePgrstPatch :: LBS. ByteString -> Either ByteString [PgrstPatchOp ]
104+ parsePgrstPatch = mapLeft BS. pack . JSON. eitherDecode
105+
74106 shouldParsePayload = case action of
75107 ActDb (ActRelationMut _ MutationDelete ) -> False
76108 ActDb (ActRelationMut _ _) -> True
@@ -88,6 +120,7 @@ getPayload reqBody contentMediaType QueryParams{qsColumns} action = do
88120 _ -> False
89121 params = (T. decodeUtf8 *** T. decodeUtf8) <$> parseSimpleQuery (LBS. toStrict reqBody)
90122
123+
91124type CsvData = V. Vector (M. Map Text LBS. ByteString )
92125
93126{-|
@@ -136,3 +169,22 @@ payloadAttributes raw json =
136169 _ -> Just emptyPJArray
137170 where
138171 emptyPJArray = ProcessedJSON (JSON. encode emptyArray) S. empty
172+
173+
174+ instance JSON. FromJSON PgrstPatchOp where
175+ parseJSON (JSON. Object o) = do
176+ op <- parseString o " op"
177+ path <- parseString o " path"
178+ -- TODO: We need to decide what JSON "value"s are allowed in our
179+ -- our Pgrst Patch implementation.
180+ case op of
181+ " set" -> Set path <$> parseString o " value"
182+ _ -> fail $ " Unknown Pgrst Patch operation " ++ show op
183+ where
184+ parseString obj key = do
185+ val <- obj .: key
186+ case val of
187+ JSON. String txt -> pure txt
188+ _ -> fail $ " Expected JSON string for " ++ show key
189+
190+ parseJSON _ = mzero
0 commit comments