This commit is contained in:
Joe Nelson
2015-11-14 17:52:05 -08:00
27 changed files with 1820 additions and 1670 deletions
+42 -44
View File
@@ -18,55 +18,53 @@ spec = beforeAll
it "hides tables that anonymous does not own" $
get "/authors_only" `shouldRespondWith` 404
it "indicates login failure (BasicAuth)" $ do
let auth = authHeaderBasic "postgrest_test_author" "fakefake"
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 401
it "allows users with permissions to see their tables (BasicAuth)" $ do
_ <- post "/postgrest/users" [json| { "id": "jdoe", "pass": "1234", "role": "postgrest_test_author" } |]
let auth = authHeaderBasic "jdoe" "1234"
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 200
it "respects database constraints for role" $
post "/postgrest/users" [json| { "id": "ssmith", "pass": "1234", "role": "SUPER_ADMIN_TRUNCATE_POWERS" } |]
`shouldRespondWith` 400
it "does not send a value when no role is provided" $ do
post "/postgrest/users" [json| { "id": "bdeey", "pass": "1234" } |]
`shouldRespondWith` 201
let auth = authHeaderBasic "jdoe" "1234"
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 200
it "recovers after 400 error with logged in user" $ do
_ <- post "/postgrest/users" [json| { "id": "jdoe", "pass": "1234", "role": "postgrest_test_author" } |]
let auth = authHeaderBasic "jdoe" "1234"
_ <- request methodPost "/rpc/problem" [auth] ""
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 200
it "allows users to login (JWT)" $ do
_ <- post "/postgrest/users" [json| { "id": "jdoe", "pass": "1234", "role": "postgrest_test_author" } |]
post "/postgrest/tokens" [json| { "id": "jdoe", "pass": "1234" } |]
it "returns jwt functions as jwt tokens" $
post "/rpc/login" [json| { "id": "jdoe", "pass": "1234" } |]
`shouldRespondWith` ResponseMatcher {
matchBody = Just [json| {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.y4vZuu1dDdwAl0-S00MCRWRYMlJ5YAMSir6Es6WtWx0"} |]
, matchStatus = 201
, matchStatus = 200
, matchHeaders = ["Content-Type" <:> "application/json"]
}
it "indicates login failure (JWT)" $ do
_ <- post "/postgrest/users" [json| { "id": "jdoe", "pass": "1234", "role": "postgrest_test_author" } |]
post "/postgrest/tokens" [json| { "id": "jdoe", "pass": "NOPE" } |]
`shouldRespondWith` ResponseMatcher {
matchBody = Just [json| {"message":"Failed authentication."} |]
, matchStatus = 401
, matchHeaders = ["Content-Type" <:> "application/json"]
}
it "allows users with permissions to see their tables (JWT)" $ do
_ <- post "/postgrest/users" [json| { "id": "jdoe", "pass": "1234", "role": "postgrest_test_author" } |]
it "allows users with permissions to see their tables" $ do
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.y4vZuu1dDdwAl0-S00MCRWRYMlJ5YAMSir6Es6WtWx0"
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 200
it "works with tokens which have extra fields" $ do
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIiwia2V5MSI6InZhbHVlMSIsImtleTIiOiJ2YWx1ZTIiLCJrZXkzIjoidmFsdWUzIiwiYSI6MSwiYiI6MiwiYyI6M30.GfydCh-F4wnM379xs0n1zUgalwJIsb6YoBapCo8HlFk"
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 200
-- this test will stop working 9999999999s after the UNIX EPOCH
it "succeeds with an unexpired token" $ do
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk5OTk5OTk5OTksInJvbGUiOiJwb3N0Z3Jlc3RfdGVzdF9hdXRob3IiLCJpZCI6Impkb2UifQ.QaPPLWTuyydMu_q7H4noMT7Lk6P4muet1OpJXF6ofhc"
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 200
it "fails with an expired token" $ do
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NDY2NzgxNDksInJvbGUiOiJwb3N0Z3Jlc3RfdGVzdF9hdXRob3IiLCJpZCI6Impkb2UifQ.enk_qZ_u6gZsXY4R8bREKB_HNExRpM0lIWSLktk9JJQ"
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 400
it "hides tables from users with invalid JWT" $ do
let auth = authHeaderJWT "ey9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.y4vZuu1dDdwAl0-S00MCRWRYMlJ5YAMSir6Es6WtWx0"
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 400
it "should fail when jwt contains no claims" $ do
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.MKYc_lOECtB0LJOiykilAdlHodB-I0_id2qHKq35dmc"
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 400
it "hides tables from users with JWT that contain no claims about role" $ do
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Impkb2UifQ.zyohGMnrDy4_8eJTl6I2AUXO3MeCCiwR24aGWRkTE9o"
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 400
it "recovers after 400 error with logged in user" $ do
_ <- post "/authors_only" [json| { "owner": "jdoe", "secret": "test content" } |]
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.y4vZuu1dDdwAl0-S00MCRWRYMlJ5YAMSir6Es6WtWx0"
_ <- request methodPost "/rpc/problem" [auth] ""
request methodGet "/authors_only" [auth] ""
`shouldRespondWith` 200
+1 -1
View File
@@ -41,7 +41,7 @@ spec = around withApp $ describe "CORS" $ do
"true"
respHeaders `shouldSatisfy` matchHeader
"Access-Control-Allow-Methods"
"GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD"
"GET, POST, PATCH, DELETE, OPTIONS, HEAD"
respHeaders `shouldSatisfy` matchHeader
"Access-Control-Allow-Headers"
"Authentication, Foo, Bar, Accept, Accept-Language, Content-Language"
+113 -41
View File
@@ -1,6 +1,6 @@
module Feature.InsertSpec where
import Test.Hspec
import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import Network.Wai.Test (SResponse(simpleBody,simpleHeaders,simpleStatus))
@@ -19,16 +19,38 @@ import TestTypes(IncPK(..), CompoundPK(..))
spec :: Spec
spec = afterAll_ resetDb $ around withApp $ do
describe "Posting new record" $ do
after_ (clearTable "menagerie") . it "accepts disparate json types" $ do
p <- post "/menagerie"
[json| {
"integer": 13, "double": 3.14159, "varchar": "testing!"
, "boolean": false, "date": "1900-01-01", "money": "$3.99"
, "enum": "foo"
} |]
liftIO $ do
simpleBody p `shouldBe` ""
simpleStatus p `shouldBe` created201
after_ (clearTable "menagerie") . context "disparate csv types" $ do
it "accepts disparate json types" $ do
p <- post "/menagerie"
[json| {
"integer": 13, "double": 3.14159, "varchar": "testing!"
, "boolean": false, "date": "1900-01-01", "money": "$3.99"
, "enum": "foo"
} |]
liftIO $ do
simpleBody p `shouldBe` ""
simpleStatus p `shouldBe` created201
it "filters columns in result using &select" $
request methodPost "/menagerie?select=integer,varchar" [("Prefer", "return=representation")]
[json| {
"integer": 14, "double": 3.14159, "varchar": "testing!"
, "boolean": false, "date": "1900-01-01", "money": "$3.99"
, "enum": "foo"
} |] `shouldRespondWith` ResponseMatcher {
matchBody = Just [str|{"integer":14,"varchar":"testing!"}|]
, matchStatus = 201
, matchHeaders = ["Content-Type" <:> "application/json"]
}
it "includes related data after insert" $
request methodPost "/projects?select=id,name,clients(id,name)" [("Prefer", "return=representation")]
[str|{"id":5,"name":"New Project","client_id":2}|] `shouldRespondWith` ResponseMatcher {
matchBody = Just [str|{"id":5,"name":"New Project","clients":{"id":2,"name":"Apple"}}|]
, matchStatus = 201
, matchHeaders = ["Content-Type" <:> "application/json", "Location" <:> "/projects?id=eq.5"]
}
context "with no pk supplied" $ do
context "into a table with auto-incrementing pk" . after_ (clearTable "auto_incrementing_pk") $
@@ -92,55 +114,99 @@ spec = afterAll_ resetDb $ around withApp $ do
context "jsonb" . after_ (clearTable "json") $ do
it "serializes nested object" $ do
let inserted = [json| { "data": { "foo":"bar" } } |]
p <- request methodPost "json" [("Prefer", "return=representation")] inserted
liftIO $ do
simpleBody p `shouldBe` inserted
simpleHeaders p `shouldSatisfy` matchHeader hLocation "/json\\?data=eq\\.%7B%22foo%22%3A%22bar%22%7D"
simpleStatus p `shouldBe` created201
request methodPost "/json"
[("Prefer", "return=representation")]
inserted
`shouldRespondWith` ResponseMatcher {
matchBody = Just inserted
, matchStatus = 201
, matchHeaders = ["Location" <:> [str|/json?data=eq.{"foo":"bar"}|]]
}
-- TODO! the test above seems right, why was the one below working before and not now
-- p <- request methodPost "/json" [("Prefer", "return=representation")] inserted
-- liftIO $ do
-- simpleBody p `shouldBe` inserted
-- simpleHeaders p `shouldSatisfy` matchHeader hLocation "/json\\?data=eq\\.%7B%22foo%22%3A%22bar%22%7D"
-- simpleStatus p `shouldBe` created201
it "serializes nested array" $ do
let inserted = [json| { "data": [1,2,3] } |]
p <- request methodPost "json" [("Prefer", "return=representation")] inserted
liftIO $ do
simpleBody p `shouldBe` inserted
simpleHeaders p `shouldSatisfy` matchHeader hLocation "/json\\?data=eq\\.%5B1%2C2%2C3%5D"
simpleStatus p `shouldBe` created201
request methodPost "/json"
[("Prefer", "return=representation")]
inserted
`shouldRespondWith` ResponseMatcher {
matchBody = Just inserted
, matchStatus = 201
, matchHeaders = ["Location" <:> [str|/json?data=eq.[1,2,3]|]]
}
-- TODO! the test above seems right, why was the one below working before and not now
-- p <- request methodPost "/json" [("Prefer", "return=representation")] inserted
-- liftIO $ do
-- simpleBody p `shouldBe` inserted
-- simpleHeaders p `shouldSatisfy` matchHeader hLocation "/json\\?data=eq\\.%5B1%2C2%2C3%5D"
-- simpleStatus p `shouldBe` created201
describe "CSV insert" $ do
after_ (clearTable "menagerie") . context "disparate csv types" $
it "succeeds with multipart response" $ do
p <- request methodPost "/menagerie" [("Content-Type", "text/csv")]
[str|integer,double,varchar,boolean,date,money,enum
|13,3.14159,testing!,false,1900-01-01,$3.99,foo
|12,0.1,a string,true,1929-10-01,12,bar
|]
liftIO $ do
simpleBody p `shouldBe` "Content-Type: application/json\nLocation: /menagerie?integer=eq.13\n\n\n--postgrest_boundary\nContent-Type: application/json\nLocation: /menagerie?integer=eq.12\n\n"
simpleStatus p `shouldBe` created201
pendingWith "Decide on what to do with CSV insert"
let inserted = [str|integer,double,varchar,boolean,date,money,enum
|13,3.14159,testing!,false,1900-01-01,$3.99,foo
|12,0.1,a string,true,1929-10-01,12,bar
|]
request methodPost "/menagerie" [("Content-Type", "text/csv"), ("Accept", "text/csv"), ("Prefer", "return=representation")] inserted
`shouldRespondWith` ResponseMatcher {
matchBody = Just inserted
, matchStatus = 201
, matchHeaders = ["Content-Type" <:> "text/csv"]
}
-- p <- request methodPost "/menagerie" [("Content-Type", "text/csv")]
-- [str|integer,double,varchar,boolean,date,money,enum
-- |13,3.14159,testing!,false,1900-01-01,$3.99,foo
-- |12,0.1,a string,true,1929-10-01,12,bar
-- |]
-- liftIO $ do
-- simpleBody p `shouldBe` "Content-Type: application/json\nLocation: /menagerie?integer=eq.13\n\n\n--postgrest_boundary\nContent-Type: application/json\nLocation: /menagerie?integer=eq.12\n\n"
-- simpleStatus p `shouldBe` created201
after_ (clearTable "no_pk") . context "requesting full representation" $ do
it "returns full details of inserted record" $
request methodPost "/no_pk"
[("Content-Type", "text/csv"), ("Prefer", "return=representation")]
[("Content-Type", "text/csv"), ("Accept", "text/csv"), ("Prefer", "return=representation")]
"a,b\nbar,baz"
`shouldRespondWith` ResponseMatcher {
matchBody = Just [json| { "a":"bar", "b":"baz" } |]
matchBody = Just "a,b\nbar,baz"
, matchStatus = 201
, matchHeaders = ["Content-Type" <:> "application/json",
, matchHeaders = ["Content-Type" <:> "text/csv",
"Location" <:> "/no_pk?a=eq.bar&b=eq.baz"]
}
-- it "can post nulls (old way)" $ do
-- pendingWith "changed the response when in csv mode"
-- request methodPost "/no_pk"
-- [("Content-Type", "text/csv"), ("Prefer", "return=representation")]
-- "a,b\nNULL,foo"
-- `shouldRespondWith` ResponseMatcher {
-- matchBody = Just [json| { "a":null, "b":"foo" } |]
-- , matchStatus = 201
-- , matchHeaders = ["Content-Type" <:> "application/json",
-- "Location" <:> "/no_pk?a=is.null&b=eq.foo"]
-- }
it "can post nulls" $
request methodPost "/no_pk"
[("Content-Type", "text/csv"), ("Prefer", "return=representation")]
[("Content-Type", "text/csv"), ("Accept", "text/csv"), ("Prefer", "return=representation")]
"a,b\nNULL,foo"
`shouldRespondWith` ResponseMatcher {
matchBody = Just [json| { "a":null, "b":"foo" } |]
matchBody = Just "a,b\n,foo"
, matchStatus = 201
, matchHeaders = ["Content-Type" <:> "application/json",
, matchHeaders = ["Content-Type" <:> "text/csv",
"Location" <:> "/no_pk?a=is.null&b=eq.foo"]
}
after_ (clearTable "no_pk") . context "with wrong number of columns" $ do
it "fails for too few" $ do
p <- request methodPost "/no_pk" [("Content-Type", "text/csv")] "a,b\nfoo,bar\nbaz"
@@ -159,7 +225,8 @@ spec = afterAll_ resetDb $ around withApp $ do
context "to a known uri" $ do
context "without a fully-specified primary key" $
it "is not an allowed operation" $
it "is not an allowed operation" $ do
pendingWith "Decide on PUT usefullness"
request methodPut "/compound_pk?k1=eq.12" []
[json| { "k1":12, "k2":42 } |]
`shouldRespondWith` 405
@@ -167,13 +234,15 @@ spec = afterAll_ resetDb $ around withApp $ do
context "with a fully-specified primary key" $ do
context "not specifying every column in the table" $
it "is rejected for lack of idempotence" $
it "is rejected for lack of idempotence" $ do
pendingWith "Decide on PUT usefullness"
request methodPut "/compound_pk?k1=eq.12&k2=eq.42" []
[json| { "k1":12, "k2":42 } |]
`shouldRespondWith` 400
context "specifying every column in the table" . after_ (clearTable "compound_pk") $ do
it "can create a new record" $ do
pendingWith "Decide on PUT usefullness"
p <- request methodPut "/compound_pk?k1=eq.12&k2=eq.42" []
[json| { "k1":12, "k2":42, "extra":3 } |]
liftIO $ do
@@ -190,6 +259,7 @@ spec = afterAll_ resetDb $ around withApp $ do
compoundExtra record `shouldBe` Just 3
it "can update an existing record" $ do
pendingWith "Decide on PUT usefullness"
_ <- request methodPut "/compound_pk?k1=eq.12&k2=eq.42" []
[json| { "k1":12, "k2":42, "extra":4 } |]
_ <- request methodPut "/compound_pk?k1=eq.12&k2=eq.42" []
@@ -204,7 +274,8 @@ spec = afterAll_ resetDb $ around withApp $ do
context "with an auto-incrementing primary key" . after_ (clearTable "auto_incrementing_pk") $
it "succeeds with 204" $
it "succeeds with 204" $ do
pendingWith "Decide on PUT usefullness"
request methodPut "/auto_incrementing_pk?id=eq.1" []
[json| {
"id":1,
@@ -284,14 +355,15 @@ spec = afterAll_ resetDb $ around withApp $ do
describe "Row level permission" $
it "set user_id when inserting rows" $ do
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.y4vZuu1dDdwAl0-S00MCRWRYMlJ5YAMSir6Es6WtWx0"
_ <- post "/postgrest/users" [json| { "id":"jdoe", "pass": "1234", "role": "postgrest_test_author" } |]
_ <- post "/postgrest/users" [json| { "id":"jroe", "pass": "1234", "role": "postgrest_test_author" } |]
p1 <- request methodPost "/authors_only"
[ authHeaderBasic "jdoe" "1234", ("Prefer", "return=representation") ]
[ auth, ("Prefer", "return=representation") ]
[json| { "secret": "nyancat" } |]
liftIO $ do
simpleBody p1 `shouldBe` [json| { "owner":"jdoe", "secret":"nyancat" } |]
simpleBody p1 `shouldBe` [str|{"owner":"jdoe","secret":"nyancat"}|]
simpleStatus p1 `shouldBe` created201
p2 <- request methodPost "/authors_only"
@@ -299,5 +371,5 @@ spec = afterAll_ resetDb $ around withApp $ do
[ authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqcm9lIn0.YuF_VfmyIxWyuceT7crnNKEprIYXsJAyXid3rjPjIow", ("Prefer", "return=representation") ]
[json| { "secret": "lolcat", "owner": "hacker" } |]
liftIO $ do
simpleBody p2 `shouldBe` [json| { "owner":"jroe", "secret":"lolcat" } |]
simpleBody p2 `shouldBe` [str|{"owner":"jroe","secret":"lolcat"}|]
simpleStatus p2 `shouldBe` created201
+14 -2
View File
@@ -7,10 +7,13 @@ import Network.HTTP.Types
import Network.Wai.Test (SResponse(simpleHeaders))
import SpecHelper
import Text.Heredoc
spec :: Spec
spec =
beforeAll (clearTable "items" >> createItems 15)
. beforeAll clearProjectsTable
. beforeAll (clearTable "complex_items" >> createComplexItems)
. beforeAll (clearTable "nullable_integer" >> createNullInteger)
. beforeAll (
@@ -134,11 +137,20 @@ spec =
get "/clients?select=id,projects(id,tasks(id,name))&projects.tasks.name=like.Design*" `shouldRespondWith`
"[{\"id\":1,\"projects\":[{\"id\":1,\"tasks\":[{\"id\":1,\"name\":\"Design w7\"}]},{\"id\":2,\"tasks\":[{\"id\":3,\"name\":\"Design w10\"}]}]},{\"id\":2,\"projects\":[{\"id\":3,\"tasks\":[{\"id\":5,\"name\":\"Design IOS\"}]},{\"id\":4,\"tasks\":[{\"id\":7,\"name\":\"Design OSX\"}]}]}]"
it "matches with @> operator" $
get "/complex_items?select=id&arr_data=@>.{2}" `shouldRespondWith`
[str|[{"id":2},{"id":3}]|]
it "matches with <@ operator" $
get "/complex_items?select=id&arr_data=<@.{1,2,4}" `shouldRespondWith`
[str|[{"id":1},{"id":2}]|]
describe "Shaping response with select parameter" $ do
it "selectStar works in absense of parameter" $
get "/complex_items?id=eq.3" `shouldRespondWith`
"[{\"id\":3,\"name\":\"Three\",\"settings\":{\"foo\":{\"int\":1,\"bar\":\"baz\"}}}]"
[str|[{"id":3,"name":"Three","settings":{"foo":{"int":1,"bar":"baz"}},"arr_data":[1,2,3]}]|]
it "one simple column" $
get "/complex_items?select=id" `shouldRespondWith`
@@ -272,7 +284,7 @@ spec =
request methodGet "/simple_pk"
(acceptHdrs "text/csv; version=1") ""
`shouldRespondWith` ResponseMatcher {
matchBody = Just "k,extra\rxyyx,u\rxYYx,v"
matchBody = Just "k,extra\nxyyx,u\nxYYx,v"
, matchStatus = 200
, matchHeaders = ["Content-Type" <:> "text/csv"]
}
+140 -121
View File
@@ -14,42 +14,42 @@ spec = around withApp $ do
it "lists views in schema" $
request methodGet "/" [] ""
`shouldRespondWith` [json| [
{"schema":"1","name":"auto_incrementing_pk","insertable":true}
, {"schema":"1","name":"clients","insertable":true}
, {"schema":"1","name":"comments","insertable":true}
, {"schema":"1","name":"complex_items","insertable":true}
, {"schema":"1","name":"compound_pk","insertable":true}
, {"schema":"1","name":"has_count_column","insertable":false}
, {"schema":"1","name":"has_fk","insertable":true}
, {"schema":"1","name":"insertable_view_with_join","insertable":true}
, {"schema":"1","name":"items","insertable":true}
, {"schema":"1","name":"json","insertable":true}
, {"schema":"1","name":"materialized_view","insertable":false}
, {"schema":"1","name":"menagerie","insertable":true}
, {"schema":"1","name":"no_pk","insertable":true}
, {"schema":"1","name":"nullable_integer","insertable":true}
, {"schema":"1","name":"projects","insertable":true}
, {"schema":"1","name":"projects_view","insertable":true}
, {"schema":"1","name":"simple_pk","insertable":true}
, {"schema":"1","name":"tasks","insertable":true}
, {"schema":"1","name":"tsearch","insertable":true}
, {"schema":"1","name":"users","insertable":true}
, {"schema":"1","name":"users_projects","insertable":true}
, {"schema":"1","name":"users_tasks","insertable":true}
{"schema":"test","name":"articleStars","insertable":true}
, {"schema":"test","name":"articles","insertable":true}
, {"schema":"test","name":"auto_incrementing_pk","insertable":true}
, {"schema":"test","name":"clients","insertable":true}
, {"schema":"test","name":"comments","insertable":true}
, {"schema":"test","name":"complex_items","insertable":true}
, {"schema":"test","name":"compound_pk","insertable":true}
, {"schema":"test","name":"has_count_column","insertable":false}
, {"schema":"test","name":"has_fk","insertable":true}
, {"schema":"test","name":"insertable_view_with_join","insertable":true}
, {"schema":"test","name":"items","insertable":true}
, {"schema":"test","name":"json","insertable":true}
, {"schema":"test","name":"materialized_view","insertable":false}
, {"schema":"test","name":"menagerie","insertable":true}
, {"schema":"test","name":"no_pk","insertable":true}
, {"schema":"test","name":"nullable_integer","insertable":true}
, {"schema":"test","name":"projects","insertable":true}
, {"schema":"test","name":"projects_view","insertable":true}
, {"schema":"test","name":"simple_pk","insertable":true}
, {"schema":"test","name":"tasks","insertable":true}
, {"schema":"test","name":"tsearch","insertable":true}
, {"schema":"test","name":"users","insertable":true}
, {"schema":"test","name":"users_projects","insertable":true}
, {"schema":"test","name":"users_tasks","insertable":true}
] |]
{matchStatus = 200}
it "lists only views user has permission to see" $ do
_ <- post "/postgrest/users" [json| { "id":"jdoe", "pass": "1234", "role": "postgrest_test_author" } |]
let auth = authHeaderBasic "jdoe" "1234"
let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.y4vZuu1dDdwAl0-S00MCRWRYMlJ5YAMSir6Es6WtWx0"
request methodGet "/" [auth] ""
`shouldRespondWith` [json| [
{"schema":"1","name":"authors_only","insertable":true}
{"schema":"test","name":"authors_only","insertable":true}
] |]
{matchStatus = 200}
describe "Table info" $ do
it "is available with OPTIONS verb" $
request methodOptions "/menagerie" [] "" `shouldRespondWith`
@@ -61,7 +61,7 @@ spec = around withApp $ do
"default": null,
"precision": 32,
"updatable": true,
"schema": "1",
"schema": "test",
"name": "integer",
"type": "integer",
"maxLen": null,
@@ -74,7 +74,7 @@ spec = around withApp $ do
"default": null,
"precision": 53,
"updatable": true,
"schema": "1",
"schema": "test",
"name": "double",
"type": "double precision",
"maxLen": null,
@@ -86,7 +86,7 @@ spec = around withApp $ do
"default": null,
"precision": null,
"updatable": true,
"schema": "1",
"schema": "test",
"name": "varchar",
"type": "character varying",
"maxLen": null,
@@ -99,7 +99,7 @@ spec = around withApp $ do
"default": null,
"precision": null,
"updatable": true,
"schema": "1",
"schema": "test",
"name": "boolean",
"type": "boolean",
"maxLen": null,
@@ -111,7 +111,7 @@ spec = around withApp $ do
"default": null,
"precision": null,
"updatable": true,
"schema": "1",
"schema": "test",
"name": "date",
"type": "date",
"maxLen": null,
@@ -123,7 +123,7 @@ spec = around withApp $ do
"default": null,
"precision": null,
"updatable": true,
"schema": "1",
"schema": "test",
"name": "money",
"type": "money",
"maxLen": null,
@@ -136,7 +136,7 @@ spec = around withApp $ do
"default": null,
"precision": null,
"updatable": true,
"schema": "1",
"schema": "test",
"name": "enum",
"type": "USER-DEFINED",
"maxLen": null,
@@ -154,98 +154,57 @@ spec = around withApp $ do
|]
it "it includes primary and foreign keys for views" $
request methodOptions "/insertable_view_with_join" [] "" `shouldRespondWith`
request methodOptions "/projects_view" [] "" `shouldRespondWith`
[json|
{
"pkey":[
"id"
],
"columns":[
{
"references":null,
"default":null,
"precision":64,
"updatable":false,
"schema":"1",
"name":"id",
"type":"bigint",
"maxLen":null,
"enum":[],
"nullable":true,
"position":1
{
"references":null,
"default":null,
"precision":32,
"updatable":true,
"schema":"test",
"name":"id",
"type":"integer",
"maxLen":null,
"enum":[],
"nullable":true,
"position":1
},
{
"references":null,
"default":null,
"precision":null,
"updatable":true,
"schema":"test",
"name":"name",
"type":"text",
"maxLen":null,
"enum":[],
"nullable":true,
"position":2
},
{
"references": {
"schema":"test",
"column":"id",
"table":"clients"
},
{
"references":{
"column":"id",
"table":"auto_incrementing_pk"
},
"default":null,
"precision":32,
"updatable":false,
"schema":"1",
"name":"auto_inc_fk",
"type":"integer",
"maxLen":null,
"enum":[],
"nullable":true,
"position":2
},
{
"references":{
"column":"k",
"table":"simple_pk"
},
"default":null,
"precision":null,
"updatable":false,
"schema":"1",
"name":"simple_fk",
"type":"character varying",
"maxLen":255,
"enum":[],
"nullable":true,
"position":3
},
{
"references":null,
"default":null,
"precision":null,
"updatable":false,
"schema":"1",
"name":"nullable_string",
"type":"character varying",
"maxLen":null,
"enum":[],
"nullable":true,
"position":4
},
{
"references":null,
"default":null,
"precision":null,
"updatable":false,
"schema":"1",
"name":"non_nullable_string",
"type":"character varying",
"maxLen":null,
"enum":[],
"nullable":true,
"position":5
},
{
"references":null,
"default":null,
"precision":null,
"updatable":false,
"schema":"1",
"name":"inserted_at",
"type":"timestamp with time zone",
"maxLen":null,
"enum":[],
"nullable":true,
"position":6
}
]
"default":null,
"precision":32,
"updatable":true,
"schema":"test",
"name":"client_id",
"type":"integer",
"maxLen":null,
"enum":[],
"nullable":true,
"position":3
}
]
}
|]
@@ -261,7 +220,7 @@ spec = around withApp $ do
"default": "nextval('\"1\".has_fk_id_seq'::regclass)",
"precision": 64,
"updatable": true,
"schema": "1",
"schema": "test",
"name": "id",
"type": "bigint",
"maxLen": null,
@@ -273,7 +232,7 @@ spec = around withApp $ do
"default": null,
"precision": 32,
"updatable": true,
"schema": "1",
"schema": "test",
"name": "auto_inc_fk",
"type": "integer",
"maxLen": null,
@@ -285,7 +244,7 @@ spec = around withApp $ do
"default": null,
"precision": null,
"updatable": true,
"schema": "1",
"schema": "test",
"name": "simple_fk",
"type": "character varying",
"maxLen": 255,
@@ -297,3 +256,63 @@ spec = around withApp $ do
]
}
|]
it "includes all information on views for renamed columns, and raises relations to correct schema" $
request methodOptions "/articleStars" [] ""
`shouldRespondWith` [json|
{
"pkey": [
"articleId",
"userId"
],
"columns": [
{
"references": {
"schema": "test",
"column": "id",
"table": "articles"
},
"default": null,
"precision": 32,
"updatable": true,
"schema": "test",
"name": "articleId",
"type": "integer",
"maxLen": null,
"enum": [],
"nullable": true,
"position": 1
},
{
"references": {
"schema": "test",
"column": "id",
"table": "users"
},
"default": null,
"precision": 32,
"updatable": true,
"schema": "test",
"name": "userId",
"type": "integer",
"maxLen": null,
"enum": [],
"nullable": true,
"position": 2
},
{
"references": null,
"default": null,
"precision": null,
"updatable": true,
"schema": "test",
"name": "createdAt",
"type": "timestamp without time zone",
"maxLen": null,
"enum": [],
"nullable": true,
"position": 3
}
]
}
|]
+29 -37
View File
@@ -30,25 +30,23 @@ import PostgREST.App (app)
import PostgREST.Config (AppConfig(..))
import PostgREST.Middleware
import PostgREST.Error(errResponse)
import PostgREST.PgStructure
import PostgREST.Types
import PostgREST.DbStructure
dbString :: String
dbString = "postgres://postgrest_test@localhost:5432/postgrest_test"
isLeft :: Either a b -> Bool
isLeft (Left _ ) = True
isLeft _ = False
cfg :: AppConfig
cfg = AppConfig "postgrest_test" 5432 "postgrest_test" "" "localhost" 3000 "postgrest_anonymous" False 10 "1" "safe"
cfg = AppConfig dbString 3000 "postgrest_anonymous" "test" "safe" 10
testPoolOpts :: PoolSettings
testPoolOpts = fromMaybe (error "bad settings") $ H.poolSettings 1 30
pgSettings :: P.Settings
pgSettings = P.ParamSettings (cs $ configDbHost cfg)
(fromIntegral $ configDbPort cfg)
(cs $ configDbUser cfg)
(cs $ configDbPass cfg)
(cs $ configDbName cfg)
pgSettings = P.StringSettings $ cs dbString
withApp :: ActionWith Application -> IO ()
withApp perform = do
@@ -56,30 +54,16 @@ withApp perform = do
<- H.acquirePool pgSettings testPoolOpts
let txSettings = Just (H.ReadCommitted, Just True)
metadata <- H.session pool $ H.tx txSettings $ do
tabs <- allTables
rels <- allRelations
cols <- allColumns rels
keys <- allPrimaryKeys
return (tabs, rels, cols, keys)
dbstructure <- case metadata of
Left e -> fail $ show e
Right (tabs, rels, cols, keys) ->
return $ DbStructure {
tables=tabs
, columns=cols
, relations=rels
, primaryKeys=keys
}
dbOrError <- H.session pool $ H.tx txSettings $ getDbStructure (cs $ configSchema cfg)
db <- either (fail . show) return dbOrError
perform $ middle $ \req resp -> do
body <- strictRequestBody req
result <- liftIO $ H.session pool $ H.tx txSettings
$ authenticated cfg (app dbstructure cfg body) req
$ runWithClaims cfg (app db cfg body) req
either (resp . errResponse) resp result
where middle = defaultMiddle False
where middle = defaultMiddle
resetDb :: IO ()
@@ -88,7 +72,7 @@ resetDb = do
<- H.acquirePool pgSettings testPoolOpts
void . liftIO $ H.session pool $
H.tx Nothing $ do
H.unitEx [H.stmt| drop schema if exists "1" cascade |]
H.unitEx [H.stmt| drop schema if exists test cascade |]
H.unitEx [H.stmt| drop schema if exists private cascade |]
H.unitEx [H.stmt| drop schema if exists postgrest cascade |]
@@ -129,7 +113,14 @@ clearTable :: Text -> IO ()
clearTable table = do
pool <- testPool
void . liftIO $ H.session pool $ H.tx Nothing $
H.unitEx $ B.Stmt ("delete from \"1\"."<>table) V.empty True
H.unitEx $ B.Stmt ("delete from test."<>table) V.empty True
clearProjectsTable :: IO ()
clearProjectsTable = do
pool <- testPool
void . liftIO $ H.session pool $ H.tx Nothing $
H.unitEx $ B.Stmt "delete from test.projects where id > 4" V.empty True
createItems :: Int -> IO ()
createItems n = do
@@ -137,7 +128,7 @@ createItems n = do
void . liftIO $ H.session pool $ H.tx Nothing txn
where
txn = mapM_ H.unitEx stmts
stmts = map [H.stmt|insert into "1".items (id) values (?)|] [1..n]
stmts = map [H.stmt|insert into test.items (id) values (?)|] [1..n]
createComplexItems :: IO ()
createComplexItems = do
@@ -145,11 +136,12 @@ createComplexItems = do
void . liftIO $ H.session pool $ H.tx Nothing txn
where
txn = mapM_ H.unitEx stmts
stmts = getZipList $ [H.stmt|insert into "1".complex_items (id, name, settings) values (?,?,?)|]
stmts = getZipList $ [H.stmt|insert into test.complex_items (id, name, settings, arr_data) values (?,?,?,?)|]
<$> ZipList ([1..3]::[Int])
<*> ZipList (["One", "Two", "Three"]::[Text])
<*> ZipList ([jobj,jobj,jobj])
jobj = (J.object [("foo", J.object [("int", J.Number 1),("bar", J.String "baz")])])
<*> ZipList [jobj,jobj,jobj]
<*> ZipList ([[1], [1,2], [1,2,3]]::[[Int]])
jobj = J.object [("foo", J.object [("int", J.Number 1),("bar", J.String "baz")])]
createNulls :: Int -> IO ()
createNulls n = do
@@ -157,14 +149,14 @@ createNulls n = do
void . liftIO $ H.session pool $ H.tx Nothing txn
where
txn = mapM_ H.unitEx (stmt':stmts)
stmt' = [H.stmt|insert into "1".no_pk (a,b) values (null,null)|]
stmts = map [H.stmt|insert into "1".no_pk (a,b) values (?,0)|] [1..n]
stmt' = [H.stmt|insert into test.no_pk (a,b) values (null,null)|]
stmts = map [H.stmt|insert into test.no_pk (a,b) values (?,0)|] [1..n]
createNullInteger :: IO ()
createNullInteger = do
pool <- testPool
void . liftIO $ H.session pool $ H.tx Nothing $
H.unitEx $ [H.stmt| insert into "1".nullable_integer (a) values (null) |]
H.unitEx $ [H.stmt| insert into "test".nullable_integer (a) values (null) |]
createLikableStrings :: IO ()
createLikableStrings = do
@@ -174,7 +166,7 @@ createLikableStrings = do
H.unitEx $ insertSimplePk "xYYx" "v"
where
insertSimplePk :: Text -> Text -> H.Stmt P.Postgres
insertSimplePk = [H.stmt|insert into "1".simple_pk (k, extra) values (?,?)|]
insertSimplePk = [H.stmt|insert into test.simple_pk (k, extra) values (?,?)|]
createJsonData :: IO ()
createJsonData = do
@@ -182,7 +174,7 @@ createJsonData = do
void . liftIO $ H.session pool $ H.tx Nothing $
H.unitEx $
[H.stmt|
insert into "1".json (data) values (?)
insert into test.json (data) values (?)
|]
(J.object [("id", J.Number 1)
,("foo", J.object [("bar", J.String "baz")])
@@ -1,7 +1,7 @@
module Unit.PgStructureSpec where
module Unit.DbStructureSpec where
import Test.Hspec
import PgStructure (Table(..), tables, Column(..), columns, ForeignKey(..),
import DbStructure (Table(..), tables, Column(..), columns, ForeignKey(..),
foreignKeys)
import Database.HDBC (quickQuery)
@@ -12,25 +12,25 @@ spec :: Spec
spec = around dbWithSchema $ beforeWith setRole $ do
describe "tables" $
it "shows all the tables" $ \conn -> do
ts <- tables "1" conn
ts <- tables "test" conn
map tableName ts `shouldBe` ["authors_only","auto_incrementing_pk",
"compound_pk","has_fk","insertable_view_with_join","items","menagerie","no_pk", "simple_pk"]
describe "columns" $ do
it "responds with each column for the table" $ \conn -> do
cs <- columns "1" "auto_incrementing_pk" conn
cs <- columns "test" "auto_incrementing_pk" conn
map colName cs `shouldBe` ["id","nullable_string","non_nullable_string",
"inserted_at"]
it "includes foreign key data" $ \conn -> do
cs <- columns "1" "has_fk" conn
cs <- columns "test" "has_fk" conn
map colFK cs `shouldBe` [Nothing,
Just $ ForeignKey "auto_incrementing_pk" "id",
Just $ ForeignKey "simple_pk" "k"]
describe "foreignKeys" $
it "has a description of the foreign key columns" $ \conn ->
foreignKeys "1" "has_fk" conn `shouldReturn` M.fromList [
foreignKeys "test" "has_fk" conn `shouldReturn` M.fromList [
("auto_inc_fk", ForeignKey {fkTable="auto_incrementing_pk", fkCol="id"}),
("simple_fk", ForeignKey { fkTable="simple_pk", fkCol="k"})]
+5 -5
View File
@@ -32,7 +32,7 @@ spec = around dbWithSchema $ do
describe "insert" $
describe "with an auto-increment key" $ do
it "inserts and responds with a full object description" $ \conn -> do
r <- insert "1" "auto_incrementing_pk" (SqlRow [
r <- insert "test" "auto_incrementing_pk" (SqlRow [
("non_nullable_string", toSql ("a string"::String))]) conn
let returnRow = incFromList . toList $ r
incStr returnRow `shouldBe` "a string"
@@ -43,19 +43,19 @@ spec = around dbWithSchema $ do
[returnRow] `shouldBe` map incFromList tRows
it "throws an exception if the PK is not unique" $ \conn -> do
r <- insert "1" "auto_incrementing_pk" (SqlRow [
r <- insert "test" "auto_incrementing_pk" (SqlRow [
("non_nullable_string", toSql ("a string"::String))]) conn
let row = SqlRow . map (Control.Arrow.first cs) . toList $ r
insert "1" "auto_incrementing_pk" row conn `shouldThrow` \e ->
insert "test" "auto_incrementing_pk" row conn `shouldThrow` \e ->
seState e == "23505" -- uniqueness violation code
it "throws an exception if a required value is missing" $ \conn ->
insert "1" "auto_incrementing_pk" (SqlRow [
insert "test" "auto_incrementing_pk" (SqlRow [
("nullable_string", toSql ("a string"::String))]) conn
`shouldThrow` \e -> seState e == "23502"
it "generates a default values query if no data is provided" $ \c -> do
r <- insert "1" "items" (SqlRow []) c
r <- insert "test" "items" (SqlRow []) c
let [row] = toList r
quickALQuery c "select * from \"1\".items where id = ?" [snd row]
`shouldReturn` [[row]]
+117 -112
View File
@@ -5,10 +5,10 @@ SET check_function_bodies = false;
SET client_min_messages = warning;
CREATE SCHEMA "1";
CREATE SCHEMA test;
ALTER SCHEMA "1" OWNER TO postgrest_test;
ALTER SCHEMA test OWNER TO postgrest_test;
CREATE SCHEMA postgrest;
@@ -30,7 +30,7 @@ CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
SET search_path = "1", pg_catalog;
SET search_path = test, pg_catalog;
CREATE TYPE enum_menagerie_type AS ENUM (
@@ -39,7 +39,7 @@ CREATE TYPE enum_menagerie_type AS ENUM (
);
ALTER TYPE "1".enum_menagerie_type OWNER TO postgrest_test;
ALTER TYPE test.enum_menagerie_type OWNER TO postgrest_test;
SET search_path = postgrest, pg_catalog;
@@ -76,25 +76,25 @@ CREATE FUNCTION set_authors_only_owner() RETURNS trigger
LANGUAGE plpgsql
AS $$
begin
NEW.owner = current_setting('user_vars.user_id');
NEW.owner = current_setting('postgrest.claims.id');
RETURN NEW;
end
$$;
ALTER FUNCTION postgrest.set_authors_only_owner() OWNER TO postgrest_test;
CREATE FUNCTION "1".insert_insertable_view_with_join() RETURNS trigger
CREATE FUNCTION test.insert_insertable_view_with_join() RETURNS trigger
LANGUAGE plpgsql
AS $$
begin
INSERT INTO "1".auto_incrementing_pk (nullable_string, non_nullable_string) VALUES (NEW.nullable_string, NEW.non_nullable_string);
INSERT INTO test.auto_incrementing_pk (nullable_string, non_nullable_string) VALUES (NEW.nullable_string, NEW.non_nullable_string);
RETURN NEW;
end;
$$;
ALTER FUNCTION "1".insert_insertable_view_with_join() OWNER TO postgrest_test;
ALTER FUNCTION test.insert_insertable_view_with_join() OWNER TO postgrest_test;
SET search_path = "1", pg_catalog;
SET search_path = test, pg_catalog;
SET default_tablespace = '';
@@ -107,7 +107,7 @@ CREATE TABLE authors_only (
);
ALTER TABLE "1".authors_only OWNER TO postgrest_test_author;
ALTER TABLE test.authors_only OWNER TO postgrest_test_author;
CREATE TABLE auto_incrementing_pk (
@@ -118,7 +118,7 @@ CREATE TABLE auto_incrementing_pk (
);
ALTER TABLE "1".auto_incrementing_pk OWNER TO postgrest_test;
ALTER TABLE test.auto_incrementing_pk OWNER TO postgrest_test;
CREATE SEQUENCE auto_incrementing_pk_id_seq
@@ -129,7 +129,7 @@ CREATE SEQUENCE auto_incrementing_pk_id_seq
CACHE 1;
ALTER TABLE "1".auto_incrementing_pk_id_seq OWNER TO postgrest_test;
ALTER TABLE test.auto_incrementing_pk_id_seq OWNER TO postgrest_test;
ALTER SEQUENCE auto_incrementing_pk_id_seq OWNED BY auto_incrementing_pk.id;
@@ -143,7 +143,7 @@ CREATE TABLE compound_pk (
);
ALTER TABLE "1".compound_pk OWNER TO postgrest_test;
ALTER TABLE test.compound_pk OWNER TO postgrest_test;
CREATE TABLE has_fk (
@@ -153,7 +153,7 @@ CREATE TABLE has_fk (
);
ALTER TABLE "1".has_fk OWNER TO postgrest_test;
ALTER TABLE test.has_fk OWNER TO postgrest_test;
CREATE SEQUENCE has_fk_id_seq
@@ -164,18 +164,18 @@ CREATE SEQUENCE has_fk_id_seq
CACHE 1;
ALTER TABLE "1".has_fk_id_seq OWNER TO postgrest_test;
ALTER TABLE test.has_fk_id_seq OWNER TO postgrest_test;
ALTER SEQUENCE has_fk_id_seq OWNED BY has_fk.id;
CREATE MATERIALIZED VIEW "1".materialized_view AS
CREATE MATERIALIZED VIEW test.materialized_view AS
SELECT
version();
ALTER TABLE "1".materialized_view OWNER TO postgrest_test;
ALTER TABLE test.materialized_view OWNER TO postgrest_test;
CREATE VIEW "1".insertable_view_with_join AS
CREATE VIEW test.insertable_view_with_join AS
SELECT has_fk.id,
has_fk.auto_inc_fk,
has_fk.simple_fk,
@@ -186,12 +186,12 @@ CREATE VIEW "1".insertable_view_with_join AS
JOIN auto_incrementing_pk USING (id));
ALTER TABLE "1".insertable_view_with_join OWNER TO postgrest_test;
ALTER TABLE test.insertable_view_with_join OWNER TO postgrest_test;
CREATE VIEW "1".has_count_column AS
CREATE VIEW test.has_count_column AS
SELECT 1 AS count;
ALTER TABLE "1".insertable_view_with_join OWNER TO postgrest_test;
ALTER TABLE test.insertable_view_with_join OWNER TO postgrest_test;
CREATE TABLE items (
@@ -199,50 +199,51 @@ CREATE TABLE items (
);
ALTER TABLE "1".items OWNER TO postgrest_test;
ALTER TABLE test.items OWNER TO postgrest_test;
CREATE TABLE complex_items (
id bigint NOT NULL,
name text,
settings json
settings json,
arr_data INTEGER[]
);
ALTER TABLE "1".complex_items OWNER TO postgrest_test;
ALTER TABLE test.complex_items OWNER TO postgrest_test;
--- Structure for testing table relations
CREATE TABLE clients(
id INT PRIMARY KEY NOT NULL,
name TEXT NOT NULL
);
ALTER TABLE "1".clients OWNER TO postgrest_test;
ALTER TABLE test.clients OWNER TO postgrest_test;
CREATE TABLE projects(
id INT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
client_id INT REFERENCES clients(id)
);
ALTER TABLE "1".projects OWNER TO postgrest_test;
ALTER TABLE test.projects OWNER TO postgrest_test;
CREATE TABLE tasks(
id INT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
project_id INT REFERENCES projects(id)
);
ALTER TABLE "1".tasks OWNER TO postgrest_test;
ALTER TABLE test.tasks OWNER TO postgrest_test;
CREATE TABLE users(
id INT PRIMARY KEY NOT NULL,
name TEXT NOT NULL
);
ALTER TABLE "1".users OWNER TO postgrest_test;
ALTER TABLE test.users OWNER TO postgrest_test;
CREATE TABLE users_tasks(
user_id INT REFERENCES users(id),
task_id INT REFERENCES tasks(id),
CONSTRAINT task_user PRIMARY KEY (task_id,user_id)
);
ALTER TABLE "1".users_tasks OWNER TO postgrest_test;
ALTER TABLE test.users_tasks OWNER TO postgrest_test;
CREATE TABLE comments(
id INT PRIMARY KEY NOT NULL,
@@ -252,31 +253,22 @@ task_id INT NOT NULL,
content TEXT NOT NULL,
FOREIGN KEY (task_id,user_id) REFERENCES users_tasks (task_id,user_id)
);
ALTER TABLE "1".comments OWNER TO postgrest_test;
ALTER TABLE test.comments OWNER TO postgrest_test;
CREATE TABLE users_projects(
user_id INT REFERENCES users(id),
project_id INT REFERENCES projects(id),
CONSTRAINT project_user PRIMARY KEY (project_id, user_id)
);
ALTER TABLE "1".users_projects OWNER TO postgrest_test;
ALTER TABLE test.users_projects OWNER TO postgrest_test;
CREATE VIEW "1".projects_view AS
CREATE VIEW test.projects_view AS
SELECT
projects.id,
projects.name,
projects.client_id
FROM projects;
ALTER TABLE "1".projects_view OWNER TO postgrest_test;
------- SAMPLE DATA -----
INSERT INTO clients VALUES (1, 'Microsoft'),(2, 'Apple');
INSERT INTO projects VALUES (1,'Windows 7', 1),(2,'Windows 10', 1),(3,'IOS', 2),(4,'OSX', 2);
INSERT INTO tasks VALUES (1,'Design w7',1),(2,'Code w7',1),(3,'Design w10',2),(4,'Code w10',2),(5,'Design IOS',3),(6,'Code IOS',3),(7,'Design OSX',4),(8,'Code OSX',4);
INSERT INTO users VALUES (1, 'Angela Martin'),(2, 'Michael Scott'),(3, 'Dwight Schrute');
INSERT INTO users_projects VALUES(1,1),(1,2),(2,3),(2,4),(3,1),(3,3);
INSERT INTO users_tasks VALUES(1,1),(1,2),(1,3),(1,4),(2,5),(2,6),(2,7),(3,1),(3,5);
INSERT INTO comments VALUES (1, 1, 2, 6, 'Needs to be delivered ASAP');
----------------
ALTER TABLE test.projects_view OWNER TO postgrest_test;
CREATE SEQUENCE items_id_seq
START WITH 1
@@ -286,27 +278,36 @@ CREATE SEQUENCE items_id_seq
CACHE 1;
ALTER TABLE "1".items_id_seq OWNER TO postgrest_test;
ALTER TABLE test.items_id_seq OWNER TO postgrest_test;
ALTER SEQUENCE items_id_seq OWNED BY items.id;
CREATE FUNCTION "1".getitemrange(min bigint, max bigint) RETURNS SETOF "1".items AS $$
SELECT * FROM "1".items WHERE id > $1 AND id <= $2;
CREATE FUNCTION test.getitemrange(min bigint, max bigint) RETURNS SETOF test.items AS $$
SELECT * FROM test.items WHERE id > $1 AND id <= $2;
$$ LANGUAGE SQL;
CREATE FUNCTION "1".test_empty_rowset() RETURNS SETOF int AS $$
CREATE FUNCTION test_empty_rowset() RETURNS SETOF int AS $$
SELECT null::int FROM (SELECT 1) a WHERE false;
$$ LANGUAGE SQL;
CREATE FUNCTION "1".sayhello(name text) RETURNS text AS $$
CREATE TYPE public.jwt_claims AS (role text, id text);
CREATE FUNCTION test.login(id text, pass text)
RETURNS public.jwt_claims
SECURITY DEFINER
AS $$
SELECT rolname::text, id::text FROM postgrest.auth WHERE id = id AND pass = pass;
$$ LANGUAGE SQL;
CREATE FUNCTION test.sayhello(name text) RETURNS text AS $$
SELECT 'Hello, ' || $1;
$$ LANGUAGE SQL;
CREATE FUNCTION "1".problem() RETURNS void LANGUAGE plpgsql AS
CREATE FUNCTION test.problem() RETURNS void LANGUAGE plpgsql AS
$$
BEGIN
RAISE 'bad thing';
@@ -325,7 +326,7 @@ CREATE TABLE menagerie (
);
ALTER TABLE "1".menagerie OWNER TO postgrest_test;
ALTER TABLE test.menagerie OWNER TO postgrest_test;
CREATE TABLE no_pk (
@@ -334,7 +335,7 @@ CREATE TABLE no_pk (
);
ALTER TABLE "1".no_pk OWNER TO postgrest_test;
ALTER TABLE test.no_pk OWNER TO postgrest_test;
CREATE TABLE nullable_integer (
@@ -342,7 +343,7 @@ CREATE TABLE nullable_integer (
);
ALTER TABLE "1".nullable_integer OWNER TO postgrest_test;
ALTER TABLE test.nullable_integer OWNER TO postgrest_test;
CREATE TABLE simple_pk (
@@ -351,7 +352,7 @@ CREATE TABLE simple_pk (
);
ALTER TABLE "1".simple_pk OWNER TO postgrest_test;
ALTER TABLE test.simple_pk OWNER TO postgrest_test;
CREATE TABLE json
@@ -360,14 +361,14 @@ CREATE TABLE json
);
ALTER TABLE "1".json OWNER TO postgrest_test;
ALTER TABLE test.json OWNER TO postgrest_test;
CREATE TABLE tsearch (
text_search_vector tsvector
);
ALTER TABLE "1".tsearch OWNER TO postgrest_test;
ALTER TABLE test.tsearch OWNER TO postgrest_test;
SET search_path = postgrest, pg_catalog;
@@ -385,8 +386,8 @@ SET search_path = private, pg_catalog;
CREATE TABLE articles (
id integer PRIMARY KEY NOT NULL,
body text,
id integer NOT NULL,
owner name NOT NULL
);
@@ -394,22 +395,32 @@ CREATE TABLE articles (
ALTER TABLE private.articles OWNER TO postgrest_test;
CREATE SEQUENCE articles_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
CREATE TABLE article_stars (
article_id int REFERENCES articles(id),
user_id int REFERENCES test.users(id),
created_at timestamp NOT NULL DEFAULT now(),
CONSTRAINT user_article PRIMARY KEY (article_id, user_id)
);
ALTER TABLE private.article_stars OWNER TO postgrest_test;
ALTER TABLE private.articles_id_seq OWNER TO postgrest_test;
SET search_path = test, pg_catalog;
ALTER SEQUENCE articles_id_seq OWNED BY articles.id;
CREATE VIEW "articleStars" AS
SELECT article_id AS "articleId", user_id AS "userId", created_at AS "createdAt"
FROM private.article_stars;
SET search_path = "1", pg_catalog;
ALTER TABLE test."articleStars" OWNER TO postgrest_test;
CREATE VIEW articles AS
SELECT *
FROM private.articles;
ALTER TABLE test.articles OWNER TO postgrest_test;
ALTER TABLE ONLY auto_incrementing_pk ALTER COLUMN id SET DEFAULT nextval('auto_incrementing_pk_id_seq'::regclass);
@@ -417,36 +428,10 @@ ALTER TABLE ONLY auto_incrementing_pk ALTER COLUMN id SET DEFAULT nextval('auto_
ALTER TABLE ONLY has_fk ALTER COLUMN id SET DEFAULT nextval('has_fk_id_seq'::regclass);
ALTER TABLE ONLY items ALTER COLUMN id SET DEFAULT nextval('items_id_seq'::regclass);
SET search_path = private, pg_catalog;
ALTER TABLE ONLY articles ALTER COLUMN id SET DEFAULT nextval('articles_id_seq'::regclass);
SET search_path = "1", pg_catalog;
SELECT pg_catalog.setval('auto_incrementing_pk_id_seq', 1, true);
SELECT pg_catalog.setval('has_fk_id_seq', 1, false);
@@ -469,24 +454,20 @@ SET search_path = private, pg_catalog;
SET search_path = test, pg_catalog;
SELECT pg_catalog.setval('articles_id_seq', 1, false);
SET search_path = "1", pg_catalog;
CREATE FUNCTION public.always_true("1".items) RETURNS boolean
CREATE FUNCTION public.always_true(test.items) RETURNS boolean
LANGUAGE sql STABLE
AS $$ SELECT true $$;
ALTER FUNCTION public.always_true("1".items) OWNER TO postgrest_test;
ALTER FUNCTION public.always_true(test.items) OWNER TO postgrest_test;
ALTER TABLE ONLY authors_only
ADD CONSTRAINT authors_only_pkey PRIMARY KEY (secret);
CREATE TRIGGER insert_insertable_view_with_join INSTEAD OF INSERT ON "1".insertable_view_with_join FOR EACH ROW EXECUTE PROCEDURE "1".insert_insertable_view_with_join();
CREATE TRIGGER insert_insertable_view_with_join INSTEAD OF INSERT ON test.insertable_view_with_join FOR EACH ROW EXECUTE PROCEDURE test.insert_insertable_view_with_join();
CREATE TRIGGER secrets_owner_track BEFORE INSERT OR UPDATE ON authors_only FOR EACH ROW EXECUTE PROCEDURE postgrest.set_authors_only_owner();
@@ -533,10 +514,6 @@ ALTER TABLE ONLY auth
SET search_path = private, pg_catalog;
ALTER TABLE ONLY articles
ADD CONSTRAINT articles_pkey PRIMARY KEY (id);
SET search_path = postgrest, pg_catalog;
@@ -549,7 +526,7 @@ SET search_path = private, pg_catalog;
CREATE TRIGGER articles_owner_track BEFORE INSERT OR UPDATE ON articles FOR EACH ROW EXECUTE PROCEDURE postgrest.update_owner();
SET search_path = "1", pg_catalog;
SET search_path = test, pg_catalog;
ALTER TABLE ONLY has_fk
@@ -562,11 +539,11 @@ ALTER TABLE ONLY has_fk
REVOKE ALL ON SCHEMA "1" FROM PUBLIC;
REVOKE ALL ON SCHEMA "1" FROM postgrest_test;
GRANT ALL ON SCHEMA "1" TO postgrest_test;
GRANT USAGE ON SCHEMA "1" TO postgrest_anonymous;
GRANT USAGE ON SCHEMA "1" TO postgrest_test_author;
REVOKE ALL ON SCHEMA test FROM PUBLIC;
REVOKE ALL ON SCHEMA test FROM postgrest_test;
GRANT ALL ON SCHEMA test TO postgrest_test;
GRANT USAGE ON SCHEMA test TO postgrest_anonymous;
GRANT USAGE ON SCHEMA test TO postgrest_test_author;
@@ -657,6 +634,14 @@ REVOKE ALL ON TABLE projects_view FROM PUBLIC;
REVOKE ALL ON TABLE projects_view FROM postgrest_test;
GRANT ALL ON TABLE projects_view TO postgrest_test;
GRANT ALL ON TABLE projects_view TO postgrest_anonymous;
REVOKE ALL ON TABLE articles FROM PUBLIC;
REVOKE ALL ON TABLE articles FROM postgrest_test;
GRANT ALL ON TABLE articles TO postgrest_test;
GRANT ALL ON TABLE articles TO postgrest_anonymous;
REVOKE ALL ON TABLE "articleStars" FROM PUBLIC;
REVOKE ALL ON TABLE "articleStars" FROM postgrest_test;
GRANT ALL ON TABLE "articleStars" TO postgrest_test;
GRANT ALL ON TABLE "articleStars" TO postgrest_anonymous;
---------
@@ -670,6 +655,11 @@ REVOKE ALL ON FUNCTION test_empty_rowset() FROM postgrest_test;
GRANT EXECUTE ON FUNCTION test_empty_rowset() TO postgrest_test;
GRANT EXECUTE ON FUNCTION test_empty_rowset() TO postgrest_anonymous;
REVOKE ALL ON FUNCTION login(text, text) FROM PUBLIC;
REVOKE ALL ON FUNCTION login(text, text) FROM postgrest_test;
GRANT EXECUTE ON FUNCTION login(text, text) TO postgrest_test;
GRANT EXECUTE ON FUNCTION login(text, text) TO postgrest_anonymous;
REVOKE ALL ON FUNCTION sayhello(text) FROM PUBLIC;
REVOKE ALL ON FUNCTION sayhello(text) FROM postgrest_test;
GRANT EXECUTE ON FUNCTION sayhello(text) TO postgrest_test;
@@ -743,10 +733,10 @@ REVOKE ALL ON TABLE has_count_column FROM postgrest_test;
GRANT ALL ON TABLE has_count_column TO postgrest_test;
GRANT ALL ON TABLE has_count_column TO postgrest_anonymous;
REVOKE ALL ON FUNCTION public.always_true("1".items) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.always_true("1".items) FROM postgrest_test;
GRANT ALL ON FUNCTION public.always_true("1".items) TO postgrest_test;
GRANT ALL ON FUNCTION public.always_true("1".items) TO postgrest_anonymous;
REVOKE ALL ON FUNCTION public.always_true(test.items) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.always_true(test.items) FROM postgrest_test;
GRANT ALL ON FUNCTION public.always_true(test.items) TO postgrest_test;
GRANT ALL ON FUNCTION public.always_true(test.items) TO postgrest_anonymous;
SET search_path = postgrest, pg_catalog;
@@ -764,3 +754,18 @@ SET search_path = private, pg_catalog;
REVOKE ALL ON TABLE articles FROM PUBLIC;
REVOKE ALL ON TABLE articles FROM postgrest_test;
GRANT ALL ON TABLE articles TO postgrest_test;
SET search_path = test, private, postgrest, public, pg_catalog;
------- SAMPLE DATA -----
INSERT INTO clients VALUES (1, 'Microsoft'),(2, 'Apple');
INSERT INTO projects VALUES (1,'Windows 7', 1),(2,'Windows 10', 1),(3,'IOS', 2),(4,'OSX', 2);
INSERT INTO tasks VALUES (1,'Design w7',1),(2,'Code w7',1),(3,'Design w10',2),(4,'Code w10',2),(5,'Design IOS',3),(6,'Code IOS',3),(7,'Design OSX',4),(8,'Code OSX',4);
INSERT INTO users VALUES (1, 'Angela Martin'),(2, 'Michael Scott'),(3, 'Dwight Schrute');
INSERT INTO users_projects VALUES(1,1),(1,2),(2,3),(2,4),(3,1),(3,3);
INSERT INTO users_tasks VALUES(1,1),(1,2),(1,3),(1,4),(2,5),(2,6),(2,7),(3,1),(3,5);
INSERT INTO comments VALUES (1, 1, 2, 6, 'Needs to be delivered ASAP');
INSERT INTO postgrest.auth (id, pass, rolname) VALUES ('jdoe', '1234', 'postgrest_test_author');
INSERT INTO private.articles (id, body, owner) VALUES (1, 'No… It''s a thing; it''s like a plan, but with more greatness.', 2), (2, 'Stop talking, brain thinking. Hush.', 3), (3, 'It''s a fez. I wear a fez now. Fezes are cool.', 1);
INSERT INTO private.article_stars (article_id, user_id) VALUES (1,1), (1,2), (2,3), (3,2), (1,3);
----------------