Coalesce types UniformObjects and Payload into the new PayloadJSON

This commit is contained in:
Diogo Biazus
2016-11-27 23:55:26 -05:00
parent 16ac034aab
commit 6c275fcec2
4 changed files with 32 additions and 38 deletions
+9 -9
View File
@@ -36,8 +36,8 @@ import Network.Wai.Parse (parseHttpAccept)
import PostgREST.RangeQuery (NonnegRange, rangeRequested, restrictRange, rangeGeq, allRange, rangeLimit, rangeOffset) import PostgREST.RangeQuery (NonnegRange, rangeRequested, restrictRange, rangeGeq, allRange, rangeLimit, rangeOffset)
import Data.Ranged.Boundaries import Data.Ranged.Boundaries
import PostgREST.Types (QualifiedIdentifier (..), import PostgREST.Types (QualifiedIdentifier (..),
Schema, Payload(..), Schema,
UniformObjects(..)) PayloadJSON(..))
import Data.Ranged.Ranges (Range(..), singletonRange, rangeIntersection, emptyRange) import Data.Ranged.Ranges (Range(..), singletonRange, rangeIntersection, emptyRange)
type RequestBody = BL.ByteString type RequestBody = BL.ByteString
@@ -95,7 +95,7 @@ data ApiRequest = ApiRequest {
-- | Content types the client will accept, [CTAny] if no Accept header -- | Content types the client will accept, [CTAny] if no Accept header
, iAccepts :: [ContentType] , iAccepts :: [ContentType]
-- | Data sent by client and used for mutation actions -- | Data sent by client and used for mutation actions
, iPayload :: Maybe Payload , iPayload :: Maybe PayloadJSON
-- | If client wants created items echoed back -- | If client wants created items echoed back
, iPreferRepresentation :: PreferRepresentation , iPreferRepresentation :: PreferRepresentation
-- | If client wants first row as raw object -- | If client wants first row as raw object
@@ -150,13 +150,13 @@ userApiRequest schema req reqBody
CTApplicationJSON -> CTApplicationJSON ->
either Left (\val -> case ensureUniform (pluralize val) of either Left (\val -> case ensureUniform (pluralize val) of
Nothing -> Left "All object keys must match" Nothing -> Left "All object keys must match"
Just json -> Right $ PayloadJSON json) (JSON.eitherDecode reqBody) Just json -> Right json) (JSON.eitherDecode reqBody)
CTTextCSV -> CTTextCSV ->
either Left (\val -> case ensureUniform (csvToJson val) of either Left (\val -> case ensureUniform (csvToJson val) of
Nothing -> Left "All lines must have same number of fields" Nothing -> Left "All lines must have same number of fields"
Just json -> Right $ PayloadJSON json) (CSV.decodeByName reqBody) Just json -> Right json) (CSV.decodeByName reqBody)
CTOther "application/x-www-form-urlencoded" -> CTOther "application/x-www-form-urlencoded" ->
Right . PayloadJSON . UniformObjects . V.singleton . M.fromList Right . PayloadJSON . V.singleton . M.fromList
. map (toS *** JSON.String . toS) . parseSimpleQuery . map (toS *** JSON.String . toS) . parseSimpleQuery
$ toS reqBody $ toS reqBody
ct -> ct ->
@@ -283,8 +283,8 @@ pluralize (JSON.Array arr) = arr
pluralize _ = V.empty pluralize _ = V.empty
-- | Test that Array contains only Objects having the same keys -- | Test that Array contains only Objects having the same keys
-- and if so mark it as UniformObjects -- and if so mark it as PayloadJSON
ensureUniform :: JSON.Array -> Maybe UniformObjects ensureUniform :: JSON.Array -> Maybe PayloadJSON
ensureUniform arr = ensureUniform arr =
let objs :: V.Vector JSON.Object let objs :: V.Vector JSON.Object
objs = foldr -- filter non-objects, map to raw objects objs = foldr -- filter non-objects, map to raw objects
@@ -297,5 +297,5 @@ ensureUniform arr =
areKeysUniform = all (==canonicalKeys) keysPerObj in areKeysUniform = all (==canonicalKeys) keysPerObj in
if (V.length objs == V.length arr) && areKeysUniform if (V.length objs == V.length arr) && areKeysUniform
then Just (UniformObjects objs) then Just (PayloadJSON objs)
else Nothing else Nothing
+8 -9
View File
@@ -128,7 +128,7 @@ app dbStructure conf apiRequest =
) )
] (toS body) ] (toS body)
(ActionCreate, TargetIdent qi@(QualifiedIdentifier _ table), Just payload@(PayloadJSON uniform@(UniformObjects rows))) -> (ActionCreate, TargetIdent qi@(QualifiedIdentifier _ table), Just payload@(PayloadJSON rows)) ->
case mutateSqlParts of case mutateSqlParts of
Left errorResponse -> return errorResponse Left errorResponse -> return errorResponse
Right (sq, mq) -> do Right (sq, mq) -> do
@@ -142,7 +142,7 @@ app dbStructure conf apiRequest =
|] |]
let pKeys = map pkName $ filter (filterPk schema table) allPrKeys -- would it be ok to move primary key detection in the query itself? let pKeys = map pkName $ filter (filterPk schema table) allPrKeys -- would it be ok to move primary key detection in the query itself?
let stm = createWriteStatement qi sq mq isSingle (iPreferRepresentation apiRequest) pKeys (contentType == CTTextCSV) payload let stm = createWriteStatement qi sq mq isSingle (iPreferRepresentation apiRequest) pKeys (contentType == CTTextCSV) payload
row <- H.query uniform stm row <- H.query payload stm
let (_, _, fs, body) = extractQueryResult row let (_, _, fs, body) = extractQueryResult row
headers = catMaybes [ headers = catMaybes [
if null fs if null fs
@@ -159,13 +159,13 @@ app dbStructure conf apiRequest =
if iPreferRepresentation apiRequest == Full if iPreferRepresentation apiRequest == Full
then toS body else "" then toS body else ""
(ActionUpdate, TargetIdent qi, Just payload@(PayloadJSON uniform)) -> (ActionUpdate, TargetIdent qi, Just payload) ->
case mutateSqlParts of case mutateSqlParts of
Left errorResponse -> return errorResponse Left errorResponse -> return errorResponse
Right (sq, mq) -> do Right (sq, mq) -> do
let singular = iPreferSingular apiRequest let singular = iPreferSingular apiRequest
stm = createWriteStatement qi sq mq singular (iPreferRepresentation apiRequest) [] (contentType == CTTextCSV) payload stm = createWriteStatement qi sq mq singular (iPreferRepresentation apiRequest) [] (contentType == CTTextCSV) payload
row <- H.query uniform stm row <- H.query payload stm
let (_, queryTotal, _, body) = extractQueryResult row let (_, queryTotal, _, body) = extractQueryResult row
when (singular && queryTotal > 1) $ when (singular && queryTotal > 1) $
HT.sql [P6.q| DO $$ HT.sql [P6.q| DO $$
@@ -187,10 +187,9 @@ app dbStructure conf apiRequest =
case mutateSqlParts of case mutateSqlParts of
Left errorResponse -> return errorResponse Left errorResponse -> return errorResponse
Right (sq, mq) -> do Right (sq, mq) -> do
let emptyUniform = UniformObjects V.empty let emptyPayload = PayloadJSON V.empty
fakeload = PayloadJSON emptyUniform stm = createWriteStatement qi sq mq False (iPreferRepresentation apiRequest) [] (contentType == CTTextCSV) emptyPayload
stm = createWriteStatement qi sq mq False (iPreferRepresentation apiRequest) [] (contentType == CTTextCSV) fakeload row <- H.query emptyPayload stm
row <- H.query emptyUniform stm
let (_, queryTotal, _, body) = extractQueryResult row let (_, queryTotal, _, body) = extractQueryResult row
r = contentRangeH 1 0 $ r = contentRangeH 1 0 $
toInteger <$> if shouldCount then Just queryTotal else Nothing toInteger <$> if shouldCount then Just queryTotal else Nothing
@@ -208,7 +207,7 @@ app dbStructure conf apiRequest =
let acceptH = (hAllow, if tableInsertable table then "GET,POST,PATCH,DELETE" else "GET") in let acceptH = (hAllow, if tableInsertable table then "GET,POST,PATCH,DELETE" else "GET") in
return $ responseLBS status200 [allOrigins, acceptH] "" return $ responseLBS status200 [allOrigins, acceptH] ""
(ActionInvoke, TargetProc qi, Just (PayloadJSON (UniformObjects payload))) -> (ActionInvoke, TargetProc qi, Just (PayloadJSON payload)) ->
case readSqlParts of case readSqlParts of
Left errorResponse -> return errorResponse Left errorResponse -> return errorResponse
Right (q, cq) -> do Right (q, cq) -> do
+10 -10
View File
@@ -83,12 +83,12 @@ decodeStandardMay =
HD.maybeRow standardRow HD.maybeRow standardRow
{-| JSON and CSV payloads from the client are given to us as {-| JSON and CSV payloads from the client are given to us as
UniformObjects (objects who all have the same keys), PayloadJSON (objects who all have the same keys),
and we turn this into an old fasioned JSON array and we turn this into an old fasioned JSON array
-} -}
encodeUniformObjs :: HE.Params UniformObjects encodeUniformObjs :: HE.Params PayloadJSON
encodeUniformObjs = encodeUniformObjs =
contramap (JSON.Array . V.map JSON.Object . unUniformObjects) (HE.value HE.json) contramap (JSON.Array . V.map JSON.Object . unPayloadJSON) (HE.value HE.json)
createReadStatement :: SqlQuery -> SqlQuery -> Bool -> Bool -> Bool -> createReadStatement :: SqlQuery -> SqlQuery -> Bool -> Bool -> Bool ->
H.Query () ResultsWithCount H.Query () ResultsWithCount
@@ -111,10 +111,10 @@ createReadStatement selectQuery countQuery isSingle countTotal asCsv =
| otherwise = asJsonF | otherwise = asJsonF
createWriteStatement :: QualifiedIdentifier -> SqlQuery -> SqlQuery -> Bool -> createWriteStatement :: QualifiedIdentifier -> SqlQuery -> SqlQuery -> Bool ->
PreferRepresentation -> [Text] -> Bool -> Payload -> PreferRepresentation -> [Text] -> Bool -> PayloadJSON ->
H.Query UniformObjects (Maybe ResultsWithCount) H.Query PayloadJSON (Maybe ResultsWithCount)
createWriteStatement _ _ mutateQuery _ None createWriteStatement _ _ mutateQuery _ None
_ _ (PayloadJSON (UniformObjects _)) = _ _ (PayloadJSON _) =
unicodeStatement sql encodeUniformObjs decodeStandardMay True unicodeStatement sql encodeUniformObjs decodeStandardMay True
where where
sql = [qc| sql = [qc|
@@ -122,7 +122,7 @@ createWriteStatement _ _ mutateQuery _ None
SELECT '', 0, {noLocationF}, '' |] SELECT '', 0, {noLocationF}, '' |]
createWriteStatement qi _ mutateQuery isSingle HeadersOnly createWriteStatement qi _ mutateQuery isSingle HeadersOnly
pKeys _ (PayloadJSON (UniformObjects _)) = pKeys _ (PayloadJSON _) =
unicodeStatement sql encodeUniformObjs decodeStandardMay True unicodeStatement sql encodeUniformObjs decodeStandardMay True
where where
sql = [qc| sql = [qc|
@@ -137,7 +137,7 @@ createWriteStatement qi _ mutateQuery isSingle HeadersOnly
] ]
createWriteStatement qi selectQuery mutateQuery isSingle Full createWriteStatement qi selectQuery mutateQuery isSingle Full
pKeys asCsv (PayloadJSON (UniformObjects _)) = pKeys asCsv (PayloadJSON _) =
unicodeStatement sql encodeUniformObjs decodeStandardMay True unicodeStatement sql encodeUniformObjs decodeStandardMay True
where where
sql = [qc| sql = [qc|
@@ -381,7 +381,7 @@ requestToQuery schema isParent (DbRead (Node (Select colSelects tbls conditions
--getQueryParts is not total but requestToQuery is called only after addJoinConditions which ensures the only --getQueryParts is not total but requestToQuery is called only after addJoinConditions which ensures the only
--posible relations are Child Parent Many --posible relations are Child Parent Many
getQueryParts _ _ = undefined --error "undefined getQueryParts" getQueryParts _ _ = undefined --error "undefined getQueryParts"
requestToQuery schema _ (DbMutate (Insert mainTbl (PayloadJSON (UniformObjects rows)))) = requestToQuery schema _ (DbMutate (Insert mainTbl (PayloadJSON rows))) =
let qi = QualifiedIdentifier schema mainTbl let qi = QualifiedIdentifier schema mainTbl
cols = map pgFmtIdent $ fromMaybe [] (HM.keys <$> (rows V.!? 0)) cols = map pgFmtIdent $ fromMaybe [] (HM.keys <$> (rows V.!? 0))
colsString = intercalate ", " cols colsString = intercalate ", " cols
@@ -393,7 +393,7 @@ requestToQuery schema _ (DbMutate (Insert mainTbl (PayloadJSON (UniformObjects r
else ["SELECT", colsString, "FROM json_populate_recordset(null::" , fromQi qi, ", $1)"] in else ["SELECT", colsString, "FROM json_populate_recordset(null::" , fromQi qi, ", $1)"] in
insInto <> vals insInto <> vals
requestToQuery schema _ (DbMutate (Update mainTbl (PayloadJSON (UniformObjects rows)) conditions)) = requestToQuery schema _ (DbMutate (Update mainTbl (PayloadJSON rows) conditions)) =
case rows V.!? 0 of case rows V.!? 0 of
Just obj -> Just obj ->
let assignments = map let assignments = map
+5 -10
View File
@@ -101,16 +101,11 @@ data Relation = Relation {
-- | An array of JSON objects that has been verified to have -- | An array of JSON objects that has been verified to have
-- the same keys in every object -- the same keys in every object
newtype UniformObjects = UniformObjects (V.Vector Object) newtype PayloadJSON = PayloadJSON (V.Vector Object)
deriving (Show, Eq) deriving (Show, Eq)
unUniformObjects :: UniformObjects -> V.Vector Object unPayloadJSON :: PayloadJSON -> V.Vector Object
unUniformObjects (UniformObjects objs) = objs unPayloadJSON (PayloadJSON objs) = objs
-- | When Hasql supports the COPY command then we can
-- have a special payload just for CSV, but until
-- then CSV is converted to a JSON array.
data Payload = PayloadJSON UniformObjects deriving (Show, Eq)
data Proxy = Proxy { data Proxy = Proxy {
proxyScheme :: Text proxyScheme :: Text
@@ -130,9 +125,9 @@ type NodeName = Text
type SelectItem = (Field, Maybe Cast, Maybe Alias) type SelectItem = (Field, Maybe Cast, Maybe Alias)
type Path = [Text] type Path = [Text]
data ReadQuery = Select { select::[SelectItem], from::[TableName], flt_::[Filter], order::Maybe [OrderTerm], range_::NonnegRange } deriving (Show, Eq) data ReadQuery = Select { select::[SelectItem], from::[TableName], flt_::[Filter], order::Maybe [OrderTerm], range_::NonnegRange } deriving (Show, Eq)
data MutateQuery = Insert { in_::TableName, qPayload::Payload } data MutateQuery = Insert { in_::TableName, qPayload::PayloadJSON }
| Delete { in_::TableName, where_::[Filter] } | Delete { in_::TableName, where_::[Filter] }
| Update { in_::TableName, qPayload::Payload, where_::[Filter] } deriving (Show, Eq) | Update { in_::TableName, qPayload::PayloadJSON, where_::[Filter] } deriving (Show, Eq)
data Filter = Filter {field::Field, operator::Operator, value::FValue} deriving (Show, Eq) data Filter = Filter {field::Field, operator::Operator, value::FValue} deriving (Show, Eq)
type ReadNode = (ReadQuery, (NodeName, Maybe Relation, Maybe Alias)) type ReadNode = (ReadQuery, (NodeName, Maybe Relation, Maybe Alias))
type ReadRequest = Tree ReadNode type ReadRequest = Tree ReadNode