Skip to content

Commit

Permalink
Add support for selecting a schema for postgrest. (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
floitsch authored Apr 2, 2024
1 parent 4401ef1 commit b7a09fa
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 10 deletions.
2 changes: 1 addition & 1 deletion package.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk: ^2.0.0-alpha.124
sdk: ^2.0.0-alpha.128
prefixes:
host: pkg-host
http: pkg-http
Expand Down
39 changes: 31 additions & 8 deletions src/supabase.toit
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ class Client:
--query/string? = null
--query_parameters/Map? = null
--headers/http.Headers? = null
--payload/any = null:
--payload/any = null
--schema/string? = null:

if query and query_parameters:
throw "Cannot provide both query and query_parameters"
Expand All @@ -235,6 +236,12 @@ class Client:

headers.add "apikey" anon_

if schema:
if method == http.GET or method == http.HEAD:
headers.add "Accept-Profile" schema
else:
headers.add "Content-Profile" schema

question_mark_pos := path.index_of "?"
if question_mark_pos >= 0:
// Replace the existing query parameters with ours.
Expand Down Expand Up @@ -304,7 +311,8 @@ class Client:
--query_parameters/Map? = null
--headers/http.Headers? = null
--parse_response_json/bool = true
--payload/any = null:
--payload/any = null
--schema/string? = null:
response := request_
--raw_response
--path=path
Expand All @@ -314,6 +322,7 @@ class Client:
--query_parameters=query_parameters
--headers=headers
--payload=payload
--schema=schema

body := response.body
if not is_success_status_code_ response.status_code:
Expand Down Expand Up @@ -438,12 +447,13 @@ class PostgRest:
The $filters must be instances of the $Filter class.
*/
select table/string --filters/List=[] -> List:
select table/string --filters/List=[] --schema/string?=null -> List:
query_filters := encode_filters_ filters
return client_.request_
--method=http.GET
--path="/rest/v1/$table"
--query=query_filters
--schema=schema

/**
Inserts a new row to the table.
Expand All @@ -452,7 +462,11 @@ class PostgRest:
If $return_inserted is true, then returns the inserted row.
*/
insert table/string payload/Map --return_inserted/bool=true -> Map?:
insert -> Map?
table/string
payload/Map
--return_inserted/bool=true
--schema/string?=null:
headers := http.Headers
headers.add "Prefer" "return=$(return_inserted ? RETURN_REPRESENTATION_ : RETURN_MINIMAL_)"
response := client_.request_
Expand All @@ -461,6 +475,7 @@ class PostgRest:
--path="/rest/v1/$table"
--payload=payload
--parse_response_json=return_inserted
--schema=schema
if return_inserted:
return response.size == 0 ? null : response[0]
return null
Expand All @@ -470,7 +485,7 @@ class PostgRest:
The $filters must be instances of the $Filter class.
*/
update table/string payload/Map --filters/List -> none:
update table/string payload/Map --filters/List --schema/string?=null -> none:
query_filters := encode_filters_ filters
// We are not using the response. Use the minimal response.
headers := http.Headers
Expand All @@ -482,6 +497,7 @@ class PostgRest:
--payload=payload
--parse_response_json=false
--query=query_filters
--schema=schema

/**
Performs an 'upsert' operation on a table.
Expand All @@ -490,7 +506,11 @@ class PostgRest:
If adding a row would violate a unique constraint, then the row is
updated instead.
*/
upsert table/string payload/Map --ignore_duplicates/bool=false -> none:
upsert -> none
table/string
payload/Map
--ignore_duplicates/bool=false
--schema/string?=null:
// TODO(florian): add support for '--on_conflict'.
// In that case the conflict detection is on the column given by
// on_column (which must be 'UNIQUE').
Expand All @@ -508,6 +528,7 @@ class PostgRest:
--path="/rest/v1/$table"
--payload=payload
--parse_response_json=false
--schema=schema

/**
Deletes all rows that match the filters.
Expand All @@ -518,7 +539,7 @@ class PostgRest:
The $filters must be instances of the $Filter class.
*/
delete table/string --filters/List -> none:
delete table/string --filters/List --schema/string?=null -> none:
query_filters := encode_filters_ filters
// We are not using the response. Use the minimal response.
headers := http.Headers
Expand All @@ -529,15 +550,17 @@ class PostgRest:
--path="/rest/v1/$table"
--parse_response_json=false
--query=query_filters
--schema=schema

/**
Performs a remote procedure call (RPC).
*/
rpc name/string payload/Map -> any:
rpc name/string payload/Map --schema/string?=null -> any:
return client_.request_
--method=http.POST
--path="/rest/v1/rpc/$name"
--payload=payload
--schema=schema

class Storage:
client_/Client
Expand Down
6 changes: 6 additions & 0 deletions tests/supabase/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Supabase tests

The supabase tests are in a subdirectory of tests, so that we can
include the package in the external tests of the Toit repository.

This way the supabase tests are ignored by the Toit framework.
86 changes: 86 additions & 0 deletions tests/supabase/supabase_schema_test.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (C) 2023 Toitware ApS.
// Use of this source code is governed by a Zero-Clause BSD license that can
// be found in the TESTS_LICENSE file.
import supabase
import supabase.filter
import .supabase_local_server

import expect show *

main:
config := get_supabase_config --sub_directory="supabase/supabase_test"

client = supabase.Client --server_config=config
--certificate_provider=: unreachable

try:
test_schema
finally:
client.close

TEST_SCHEMA ::= "other_schema"
TEST_TABLE ::= "some_table"

client/supabase.Client := ?

test_schema:
client.rest.delete TEST_TABLE
--schema=TEST_SCHEMA
--filters=[
// Delete requires a where clause.
filter.greater-than-or-equal "id" 0,
]

inserted := client.rest.insert TEST_TABLE
--schema=TEST_SCHEMA
{ "value": 499 }

expect-equals 499 inserted["value"]

rows := client.rest.select TEST_TABLE --schema=TEST_SCHEMA
expect-equals 1 rows.size
expect-equals 499 rows[0]["value"]

client.rest.update TEST_TABLE
--schema=TEST_SCHEMA
--filters=[
filter.equals "id" rows[0]["id"],
]
{ "value": 500 }
rows = client.rest.select TEST_TABLE --schema=TEST_SCHEMA
expect-equals 1 rows.size
expect-equals 500 rows[0]["value"]

client.rest.upsert TEST_TABLE
--schema=TEST_SCHEMA
{
"id": rows[0]["id"],
"value": 501,
}
rows = client.rest.select TEST_TABLE --schema=TEST_SCHEMA
expect-equals 1 rows.size
expect-equals 501 rows[0]["value"]

client.rest.upsert TEST_TABLE
--schema=TEST_SCHEMA
{
"id": rows[0]["id"] + 1,
"value": 502,
}
rows = client.rest.select TEST_TABLE --schema=TEST_SCHEMA
expect-equals 2 rows.size
expect-equals 501 rows[0]["value"]
expect-equals 502 rows[1]["value"]

client.rest.delete TEST_TABLE
--schema=TEST_SCHEMA
--filters=[
filter.equals "id" rows[0]["id"],
]
rows = client.rest.select TEST_TABLE --schema=TEST_SCHEMA
expect-equals 1 rows.size
expect-equals 502 rows[0]["value"]

rpc-value := client.rest.rpc "fun" --schema=TEST_SCHEMA {:}
expect-equals 42 rpc-value
2 changes: 1 addition & 1 deletion tests/supabase/supabase_test/supabase/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ project_id = "supabase_test"
port = 49321
# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API
# endpoints. public and storage are always included.
schemas = []
schemas = ["other_schema"]
# Extra schemas to add to the search_path of every request.
extra_search_path = ["extensions"]
# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-- Copyright (C) 2024 Toitware ApS.
-- Use of this source code is governed by a Zero-Clause BSD license that can
-- be found in the TESTS_LICENSE file.

CREATE SCHEMA IF NOT EXISTS other_schema;

GRANT USAGE ON SCHEMA other_schema TO anon, authenticated, service_role;
GRANT ALL ON ALL TABLES IN SCHEMA other_schema TO anon, authenticated, service_role;
GRANT ALL ON ALL ROUTINES IN SCHEMA other_schema TO anon, authenticated, service_role;
GRANT ALL ON ALL SEQUENCES IN SCHEMA other_schema TO anon, authenticated, service_role;
ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA other_schema GRANT ALL ON TABLES TO anon, authenticated, service_role;
ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA other_schema GRANT ALL ON ROUTINES TO anon, authenticated, service_role;
ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA other_schema GRANT ALL ON SEQUENCES TO anon, authenticated, service_role;

CREATE TABLE other_schema.some_table (
id SERIAL PRIMARY KEY,
value INTEGER
);

CREATE FUNCTION other_schema.fun()
RETURNS INTEGER
LANGUAGE plpgsql
SECURITY INVOKER
AS $$
BEGIN
RETURN 42;
END;
$$;

0 comments on commit b7a09fa

Please sign in to comment.