postgrest 6.0.1 → 6.0.2
raw patch · 11 files changed
+50/−33 lines, 11 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +12/−0
- postgrest.cabal +1/−1
- src/PostgREST/ApiRequest.hs +4/−4
- src/PostgREST/Config.hs +6/−12
- src/PostgREST/DbStructure.hs +1/−1
- src/PostgREST/Error.hs +1/−1
- src/PostgREST/OpenAPI.hs +1/−1
- src/PostgREST/Parsers.hs +1/−1
- test/Feature/QuerySpec.hs +11/−10
- test/Feature/RpcSpec.hs +1/−1
- test/Feature/StructureSpec.hs +11/−1
CHANGELOG.md view
@@ -9,6 +9,18 @@ ### Fixed +## [6.0.2] - 2019-08-22++### Fixed++- #1369, Change `raw-media-types` to accept a string of comma separated MIME types - @Dansvidania+- #1368, Fix long column descriptions being truncated at 63 characters in PostgreSQL 12 - @amedeedaboville+- #1348, Go back to converting plus "+" to space " " in querystrings by default - @steve-chavez++### Deprecated++- #1348, Deprecate `.` symbol for disambiguating resource embedding(added in #918). The url-safe '!' should be used instead. We refrained from using `+` as part of our syntax because it conflicts with some http clients and proxies.+ ## [6.0.1] - 2019-07-30 ### Added
postgrest.cabal view
@@ -1,5 +1,5 @@ name: postgrest-version: 6.0.1+version: 6.0.2 synopsis: REST API for any Postgres database description: Reads the schema of a PostgreSQL database and creates RESTful routes for the tables and views, supporting all HTTP verbs that security
src/PostgREST/ApiRequest.hs view
@@ -138,14 +138,14 @@ , iCanonicalQS = toS $ urlEncodeVars . L.sortOn fst . map (join (***) toS . second (fromMaybe BS.empty))- $ queryStringWPlus+ $ qString , iJWT = tokenStr , iHeaders = [ (toS $ CI.foldedCase k, toS v) | (k,v) <- hdrs, k /= hAuthorization, k /= hCookie] , iCookies = maybe [] parseCookiesText $ lookupHeader "Cookie" } where- -- queryString with '+' not converted to ' '- queryStringWPlus = parseQueryReplacePlus False $ rawQueryString req+ -- queryString with '+' converted to ' '(space)+ qString = parseQueryReplacePlus True $ rawQueryString req -- rpcQParams = Rpc query params e.g. /rpc/name?param1=val1, similar to filter but with no operator(eq, lt..) (filters, rpcQParams) = case action of@@ -215,7 +215,7 @@ path = pathInfo req method = requestMethod req hdrs = requestHeaders req- qParams = [(toS k, v)|(k,v) <- queryStringWPlus]+ qParams = [(toS k, v)|(k,v) <- qString] lookupHeader = flip lookup hdrs hasPrefer :: Text -> Bool hasPrefer val = any (\(h,v) -> h == "Prefer" && val `elem` split v) hdrs
src/PostgREST/Config.hs view
@@ -167,9 +167,9 @@ <*> pure False <*> (fmap (fmap coerceText) <$> C.subassocs "app.settings" C.value) <*> (maybe (Right [JSPKey "role"]) parseRoleClaimKey <$> optValue "role-claim-key")- <*> (maybe ["public"] splitExtraSearchPath <$> optValue "db-extra-search-path")+ <*> (maybe ["public"] splitOnCommas <$> optValue "db-extra-search-path") <*> ((\x y -> QualifiedIdentifier x <$> y) <$> dbSchema <*> optString "root-spec")- <*> (fmap encodeUtf8 <$> optionalListOfText "raw-media-types")+ <*> (maybe [] (fmap encodeUtf8 . splitOnCommas) <$> optValue "raw-media-types") parseJwtAudience :: C.Key -> C.Parser C.Config (Maybe StringOrURI) parseJwtAudience k =@@ -180,12 +180,6 @@ (Just "") -> pure Nothing aud' -> pure aud' - optionalListOfText :: C.Key -> C.Parser C.Config [Text]- optionalListOfText k =- C.optional k (C.list C.string) >>= \case- Nothing -> pure []- Just types -> pure types- reqString :: C.Key -> C.Parser C.Config Text reqString k = C.required k C.string @@ -219,9 +213,9 @@ parseRoleClaimKey (C.String s) = pRoleClaimKey s parseRoleClaimKey v = pRoleClaimKey $ show v - splitExtraSearchPath :: C.Value -> [Text]- splitExtraSearchPath (C.String s) = strip <$> splitOn "," s- splitExtraSearchPath _ = []+ splitOnCommas :: C.Value -> [Text]+ splitOnCommas (C.String s) = strip <$> splitOn "," s+ splitOnCommas _ = [] opts = info (helper <*> pathParser) $ fullDesc@@ -283,7 +277,7 @@ |# root-spec = "stored_proc_name" | |## content types to produce raw output- |# raw-media-types=["image/png","image/jpg"]+ |# raw-media-types="image/png, image/jpg" |] pathParser :: Parser FilePath
src/PostgREST/DbStructure.hs view
@@ -437,7 +437,7 @@ nc.nspname::information_schema.sql_identifier AS table_schema, c.relname::information_schema.sql_identifier AS table_name, a.attname::information_schema.sql_identifier AS column_name,- d.description::information_schema.sql_identifier AS description,+ d.description AS description, a.attnum::information_schema.cardinal_number AS ordinal_position, pg_get_expr(ad.adbin, ad.adrelid)::information_schema.character_data AS column_default, CASE
src/PostgREST/Error.hs view
@@ -224,7 +224,7 @@ toJSON GucHeadersError = JSON.object [ "message" .= ("response.headers guc must be a JSON array composed of objects with a single key and a string value" :: Text)] toJSON (BinaryFieldError ct) = JSON.object [- "message" .= ((toS (toMime ct) <> " requested but a single column was not selected") :: Text)]+ "message" .= ((toS (toMime ct) <> " requested but more than one column was selected") :: Text)] toJSON ConnectionLostError = JSON.object [ "message" .= ("Database connection lost, retrying the connection." :: Text)]
src/PostgREST/OpenAPI.hs view
@@ -218,7 +218,7 @@ ) ) postOp = tOp- & parameters .~ map ref ["body." <> tn, "preferReturn"]+ & parameters .~ map ref ["body." <> tn, "select", "preferReturn"] & at 201 ?~ "Created" patchOp = tOp & parameters .~ map ref (rs <> ["body." <> tn, "preferReturn"])
src/PostgREST/Parsers.hs view
@@ -131,7 +131,7 @@ alias <- optionMaybe ( try(pFieldName <* aliasSeparator) ) fld <- pField relationDetail <- optionMaybe (- try ( char '+' *> pFieldName ) <|>+ try ( char '!' *> pFieldName ) <|> try ( char '.' *> pFieldName ) -- TODO deprecated, remove in next major version )
test/Feature/QuerySpec.hs view
@@ -480,12 +480,12 @@ describe "path fixed" $ do it "works when requesting children 2 levels" $- get "/clients?id=eq.1&select=id,projects:projects%2Bclient_id(id,tasks(id))" `shouldRespondWith`+ get "/clients?id=eq.1&select=id,projects:projects!client_id(id,tasks(id))" `shouldRespondWith` [json|[{"id":1,"projects":[{"id":1,"tasks":[{"id":1},{"id":2}]},{"id":2,"tasks":[{"id":3},{"id":4}]}]}]|] { matchHeaders = [matchContentTypeJson] } it "works with parent relation" $- get "/message?select=id,body,sender:person%2Bsender(name),recipient:person%2Brecipient(name)&id=lt.4" `shouldRespondWith`+ get "/message?select=id,body,sender:person!sender(name),recipient:person!recipient(name)&id=lt.4" `shouldRespondWith` [json| [{"id":1,"body":"Hello Jane","sender":{"name":"John"},"recipient":{"name":"Jane"}}, {"id":2,"body":"Hi John","sender":{"name":"Jane"},"recipient":{"name":"John"}},@@ -499,7 +499,7 @@ , matchHeaders = [matchContentTypeJson] } it "works with a parent view relation" $- get "/message?select=id,body,sender:person_detail%2Bsender(name,sent),recipient:person_detail%2Brecipient(name,received)&id=lt.4" `shouldRespondWith`+ get "/message?select=id,body,sender:person_detail!sender(name,sent),recipient:person_detail!recipient(name,received)&id=lt.4" `shouldRespondWith` [json| [{"id":1,"body":"Hello Jane","sender":{"name":"John","sent":2},"recipient":{"name":"Jane","received":2}}, {"id":2,"body":"Hi John","sender":{"name":"Jane","sent":1},"recipient":{"name":"John","received":1}},@@ -507,10 +507,11 @@ { matchHeaders = [matchContentTypeJson] } it "works with many<->many relation" $- get "/tasks?select=id,users:users%2Busers_tasks(id)" `shouldRespondWith`+ get "/tasks?select=id,users:users!users_tasks(id)" `shouldRespondWith` [json|[{"id":1,"users":[{"id":1},{"id":3}]},{"id":2,"users":[{"id":1}]},{"id":3,"users":[{"id":1}]},{"id":4,"users":[{"id":1}]},{"id":5,"users":[{"id":2},{"id":3}]},{"id":6,"users":[{"id":2}]},{"id":7,"users":[{"id":2}]},{"id":8,"users":[]}]|] { matchHeaders = [matchContentTypeJson] } + -- TODO Remove in next major version(7.0) describe "old dot '.' symbol, deprecated" $ it "still works" $ do get "/clients?id=eq.1&select=id,projects:projects.client_id(id,tasks(id))" `shouldRespondWith`@@ -588,7 +589,7 @@ { matchHeaders = [matchContentTypeJson] } it "embeds childs recursively" $- get "/family_tree?id=eq.1&select=id,name, childs:family_tree%2Bparent(id,name,childs:family_tree%2Bparent(id,name))" `shouldRespondWith`+ get "/family_tree?id=eq.1&select=id,name, childs:family_tree!parent(id,name,childs:family_tree!parent(id,name))" `shouldRespondWith` [json|[{ "id": "1", "name": "Parental Unit", "childs": [ { "id": "2", "name": "Kid One", "childs": [ { "id": "4", "name": "Grandkid One" } ] },@@ -597,7 +598,7 @@ }]|] { matchHeaders = [matchContentTypeJson] } it "embeds parent and then embeds childs" $- get "/family_tree?id=eq.2&select=id,name,parent(id,name,childs:family_tree%2Bparent(id,name))" `shouldRespondWith`+ get "/family_tree?id=eq.2&select=id,name,parent(id,name,childs:family_tree!parent(id,name))" `shouldRespondWith` [json|[{ "id": "2", "name": "Kid One", "parent": { "id": "1", "name": "Parental Unit", "childs": [ { "id": "2", "name": "Kid One" }, { "id": "3", "name": "Kid Two"} ]@@ -620,7 +621,7 @@ }]|] { matchHeaders = [matchContentTypeJson] } it "embeds childs" $ do- get "/organizations?select=id,name,refereeds:organizations%2Breferee(id,name)&id=eq.1" `shouldRespondWith`+ get "/organizations?select=id,name,refereeds:organizations!referee(id,name)&id=eq.1" `shouldRespondWith` [json|[{ "id": 1, "name": "Referee Org", "refereeds": [@@ -634,7 +635,7 @@ } ] }]|] { matchHeaders = [matchContentTypeJson] }- get "/organizations?select=id,name,auditees:organizations%2Bauditor(id,name)&id=eq.2" `shouldRespondWith`+ get "/organizations?select=id,name,auditees:organizations!auditor(id,name)&id=eq.2" `shouldRespondWith` [json|[{ "id": 2, "name": "Auditor Org", "auditees": [@@ -668,7 +669,7 @@ "manager":{"name":"Referee Manager"}}} }]|] { matchHeaders = [matchContentTypeJson] } - get "/organizations?select=name,manager(name),auditees:organizations%2Bauditor(name,manager(name),refereeds:organizations%2Breferee(name,manager(name)))&id=eq.2" `shouldRespondWith`+ get "/organizations?select=name,manager(name),auditees:organizations!auditor(name,manager(name),refereeds:organizations!referee(name,manager(name)))&id=eq.2" `shouldRespondWith` [json|[{ "name":"Auditor Org", "manager":{"name":"Auditor Manager"},@@ -950,7 +951,7 @@ it "fails if a single column is not selected" $ do request methodGet "/images?select=img,name&name=eq.A.png" (acceptHdrs "application/octet-stream") "" `shouldRespondWith`- [json| {"message":"application/octet-stream requested but a single column was not selected"} |]+ [json| {"message":"application/octet-stream requested but more than one column was selected"} |] { matchStatus = 406 , matchHeaders = [matchContentTypeJson] }
test/Feature/RpcSpec.hs view
@@ -482,7 +482,7 @@ it "fails if a single column is not selected" $ request methodPost "/rpc/ret_rows_with_base64_bin" (acceptHdrs "application/octet-stream") "" `shouldRespondWith`- [json| {"message":"application/octet-stream requested but a single column was not selected"} |]+ [json| {"message":"application/octet-stream requested but more than one column was selected"} |] { matchStatus = 406 , matchHeaders = [matchContentTypeJson] }
test/Feature/StructureSpec.hs view
@@ -45,6 +45,7 @@ childGetSummary = r ^? method "get" . key "summary" childGetDescription = r ^? method "get" . key "description" getParameters = r ^? method "get" . key "parameters"+ postParameters = r ^? method "post" . key "parameters" postResponse = r ^? method "post" . key "responses" . key "201" . key "description" patchResponse = r ^? method "patch" . key "responses" . key "204" . key "description" deleteResponse = r ^? method "delete" . key "responses" . key "204" . key "description"@@ -79,6 +80,15 @@ ] |] + postParameters `shouldBe` Just+ [aesonQQ|+ [+ { "$ref": "#/parameters/body.child_entities" },+ { "$ref": "#/parameters/select" },+ { "$ref": "#/parameters/preferReturn" }+ ]+ |]+ postResponse `shouldBe` Just "Created" patchResponse `shouldBe` Just "No Content"@@ -125,7 +135,7 @@ "type": "integer" }, "name": {- "description": "child_entities name comment",+ "description": "child_entities name comment. Can be longer than sixty-three characters long", "format": "text", "type": "string" },