diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f92b46d0..f53e13eb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - Option to specify nulls first or last, eg `/people?order=age.desc.nullsfirst` - Filter nulls, `?col=is.null` and `?col=isnot.null` +### Fixed +- Allow NULL values in posts ## [0.2.7.0] - 2015-03-03 ### Added diff --git a/src/App.hs b/src/App.hs index f0c09a11b..00b3b3cf5 100644 --- a/src/App.hs +++ b/src/App.hs @@ -112,7 +112,7 @@ app v1schema reqBody req = then inserted else filterWithKey (const . (`elem` primaryKeys)) inserted let params = urlEncodeVars - $ map (\t -> (cs $ fst t, "eq." <> cs (unquoted $ snd t))) + $ map (\t -> (cs $ fst t, cs (paramFilter $ snd t))) $ sortBy (comparing fst) $ toList primaries return $ responseLBS status201 [ jsonH diff --git a/src/PgQuery.hs b/src/PgQuery.hs index 38a230156..9526cd24b 100644 --- a/src/PgQuery.hs +++ b/src/PgQuery.hs @@ -115,7 +115,7 @@ insertInto t cols vals = B.Stmt ("insert into " <> fromQt t <> " (" <> T.intercalate ", " (map pgFmtIdent cols) <> ") values (" - <> T.intercalate ", " (map ((<> "::unknown") . pgFmtLit . unquoted) vals) + <> T.intercalate ", " (map insertableValue vals) <> ") returning row_to_json(" <> fromQt t <> ".*)") empty True @@ -126,7 +126,7 @@ insertSelect t cols vals = B.Stmt ("insert into " <> fromQt t <> " (" <> T.intercalate ", " (map pgFmtIdent cols) <> ") select " - <> T.intercalate ", " (map ((<> "::unknown") . pgFmtLit . unquoted) vals)) + <> T.intercalate ", " (map insertableValue vals)) empty True update :: QualifiedTable -> [T.Text] -> [JSON.Value] -> PStmt @@ -134,7 +134,7 @@ update t cols vals = B.Stmt ("update " <> fromQt t <> " set (" <> T.intercalate ", " (map pgFmtIdent cols) <> ") = (" - <> T.intercalate ", " (map ((<> "::unknown") . pgFmtLit . unquoted) vals) + <> T.intercalate ", " (map insertableValue vals) <> ")") empty True @@ -233,3 +233,11 @@ unquoted (JSON.Number n) = cs $ formatScientific Fixed (if isInteger n then Just 0 else Nothing) n unquoted (JSON.Bool b) = cs . show $ b unquoted _ = "" + +insertableValue :: JSON.Value -> T.Text +insertableValue JSON.Null = "null" +insertableValue v = ((<> "::unknown") . pgFmtLit . unquoted) v + +paramFilter :: JSON.Value -> T.Text +paramFilter JSON.Null = "is.null" +paramFilter v = "eq." <> unquoted v diff --git a/test/Feature/InsertSpec.hs b/test/Feature/InsertSpec.hs index d593421cb..3117791ed 100644 --- a/test/Feature/InsertSpec.hs +++ b/test/Feature/InsertSpec.hs @@ -66,6 +66,15 @@ spec = afterAll_ resetDb $ around withApp $ do simpleHeaders p `shouldSatisfy` matchHeader hLocation "/no_pk\\?a=eq.bar&b=eq.baz" simpleStatus p `shouldBe` created201 + it "can post nulls" $ do + p <- request methodPost "/no_pk" + [("Prefer", "return=representation")] + [json| { "a":null, "b":"foo" } |] + liftIO $ do + simpleBody p `shouldBe` [json| { "a":null, "b":"foo" } |] + simpleHeaders p `shouldSatisfy` matchHeader hLocation "/no_pk\\?a=is.null&b=eq.foo" + simpleStatus p `shouldBe` created201 + context "with compound pk supplied" . after_ (clearTable "compound_pk") $ it "builds response location header appropriately" $ post "/compound_pk" [json| { "k1":12, "k2":42 } |]