diff --git a/CHANGELOG.md b/CHANGELOG.md index 21be5a72a..088c06d65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Accept posts from HTML forms - @begriffs - Ability to order embedded entities - @ruslantalpa - Ability to paginate using &limit and &offset parameters - @ruslantalpa -- Ability to apply limits to embedded entities and enforce --max-rows on all levels - @ruslantalpa, @begriffs +- Ability to apply limits to embedded entities and enforce --max-rows on all levels - @ruslantalpa, @begriffs ### Fixed - Return 401 or 403 for access denied rather than 404 - @begriffs @@ -22,6 +22,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Include entities from the same parent table using two different foreign keys - @ruslantalpa - Ensure that Location header in 201 response is URL-encoded - @league - Fix garbage collector CPU leak - @ruslantalpa et al. +- Return deleted items when return=representation header is sent - @ruslantalpa ## [0.3.1.1] - 2016-03-28 diff --git a/src/PostgREST/ApiRequest.hs b/src/PostgREST/ApiRequest.hs index 07d1f68be..02fa5da37 100644 --- a/src/PostgREST/ApiRequest.hs +++ b/src/PostgREST/ApiRequest.hs @@ -150,9 +150,7 @@ userApiRequest schema req reqBody = , iPreferSingular = singular , iPreferCount = not $ singular || hasPrefer "count=none" , iFilters = [ (cs k, fromJust v) | (k,v) <- qParams, isJust v, k /= "select", k /= "offset", not (endingIn ["order", "limit"] k) ] - , iSelect = if method == "DELETE" - then "*" - else fromMaybe "*" $ fromMaybe (Just "*") $ lookup "select" qParams + , iSelect = fromMaybe "*" $ fromMaybe (Just "*") $ lookup "select" qParams , iOrder = [(cs k, fromJust v) | (k,v) <- qParams, isJust v, endingIn ["order"] k ] , iCanonicalQS = urlEncodeVars . sortBy (comparing fst) diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index e07b26366..8d8ca604c 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -161,13 +161,16 @@ app dbStructure conf apiRequest = Left e -> return $ responseLBS status400 [jsonH] $ cs e Right (sq,mq) -> do let emptyUniform = UniformObjects V.empty - let fakeload = PayloadJSON emptyUniform - let stm = createWriteStatement qi sq mq False (iPreferRepresentation apiRequest) [] (contentType == TextCSV) fakeload + fakeload = PayloadJSON emptyUniform + stm = createWriteStatement qi sq mq False (iPreferRepresentation apiRequest) [] (contentType == TextCSV) fakeload row <- H.query emptyUniform stm - let (_, queryTotal, _, _) = extractQueryResult row + let (_, queryTotal, _, body) = extractQueryResult row + r = contentRangeH 1 0 (toInteger <$> Just queryTotal) return $ if queryTotal == 0 then notFound - else responseLBS status204 [("Content-Range", "*/"<> cs (show queryTotal))] "" + else if iPreferRepresentation apiRequest == Full + then responseLBS status200 [contentTypeH, r] (cs body) + else responseLBS status204 [r] "" (ActionInfo, TargetIdent (QualifiedIdentifier tSchema tTable), Nothing) -> if isJust $ find (\t -> tableName t == tTable && tableSchema t == tSchema) (dbTables dbStructure) @@ -346,6 +349,7 @@ buildReadRequest maxRows allRels apiRequest = relations = case action of ActionCreate -> fakeSourceRelations ++ allRels ActionUpdate -> fakeSourceRelations ++ allRels + ActionDelete -> fakeSourceRelations ++ allRels _ -> allRels where fakeSourceRelations = mapMaybe (toSourceRelation rootTableName) allRels -- see comment in toSourceRelation diff --git a/test/Feature/DeleteSpec.hs b/test/Feature/DeleteSpec.hs index 76d1fcc64..b82b85537 100644 --- a/test/Feature/DeleteSpec.hs +++ b/test/Feature/DeleteSpec.hs @@ -19,6 +19,28 @@ spec = , matchHeaders = ["Content-Range" <:> "*/1"] } + it "returns the deleted item" $ + request methodDelete "/items?id=eq.2" [("Prefer", "return=representation")] "" + `shouldRespondWith` ResponseMatcher { + matchBody = Just [str|[{"id":2}]|] + , matchStatus = 200 + , matchHeaders = ["Content-Range" <:> "*/1"] + } + it "returns the deleted item and shapes the response" $ + request methodDelete "/complex_items?id=eq.2&select=id,name" [("Prefer", "return=representation")] "" + `shouldRespondWith` ResponseMatcher { + matchBody = Just [str|[{"id":2,"name":"Two"}]|] + , matchStatus = 200 + , matchHeaders = ["Content-Range" <:> "*/1"] + } + it "can embed (parent) entities" $ + request methodDelete "/tasks?id=eq.8&select=id,name,project{id}" [("Prefer", "return=representation")] "" + `shouldRespondWith` ResponseMatcher { + matchBody = Just [str|[{"id":8,"name":"Code OSX","project":{"id":4}}]|] + , matchStatus = 200 + , matchHeaders = ["Content-Range" <:> "*/1"] + } + it "actually clears items ouf the db" $ do _ <- request methodDelete "/items?id=lt.15" [] "" get "/items"