Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ An isomorphic Go client for Supabase.
- Realtime listeners for database changes
- [x] Integration with [Postgrest](https://github.com/supabase-community/postgrest-go)
- Access your database using a REST API generated from your schema & database functions
- [x] Integration with [Gotrue](https://github.com/supabase-community/gotrue-go)
- [x] Integration with [Auth](https://github.com/supabase-community/auth-go)
- User authentication, including OAuth, ***email/password***, and native sign-in
- [x] Integration with [Supabase Storage](https://github.com/supabase-community/storage-go)
- Store files in S3 with additional managed metadata
Expand Down
22 changes: 7 additions & 15 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,24 @@ import (
"log"
"time"

auth "github.com/supabase-community/auth-go"
"github.com/supabase-community/auth-go/types"
"github.com/supabase-community/functions-go"
"github.com/supabase-community/gotrue-go"
"github.com/supabase-community/gotrue-go/types"
postgrest "github.com/supabase-community/postgrest-go"
storage_go "github.com/supabase-community/storage-go"
)

const (
REST_URL = "/rest/v1"
STORAGE_URL = "/storage/v1"
STORAGE_URL = "/storage/v1"
AUTH_URL = "/auth/v1"
FUNCTIONS_URL = "/functions/v1"
)

type Client struct {
// Why is this a private field??
rest *postgrest.Client
Storage *storage_go.Client
// Auth is an interface. We don't need a pointer to an interface.
Auth gotrue.Client
rest *postgrest.Client
Storage *storage_go.Client
Auth auth.Client
Functions *functions.Client
options clientOptions
}
Expand Down Expand Up @@ -62,7 +60,6 @@ func NewClient(url, key string, options *ClientOptions) (*Client, error) {

client := &Client{}
client.options.url = url
// map is pass by reference, so this gets updated by rest of function
client.options.headers = headers

var schema string
Expand All @@ -74,9 +71,7 @@ func NewClient(url, key string, options *ClientOptions) (*Client, error) {

client.rest = postgrest.NewClient(url+REST_URL, schema, headers)
client.Storage = storage_go.NewClient(url+STORAGE_URL, key, headers)
// ugly to make auth client use custom URL
tmp := gotrue.New(url, key)
client.Auth = tmp.WithCustomGoTrueURL(url + AUTH_URL)
client.Auth = auth.New(url+AUTH_URL, key)
client.Functions = functions.NewClient(url+FUNCTIONS_URL, key, headers)

return client, nil
Expand Down Expand Up @@ -124,7 +119,6 @@ func (c *Client) EnableTokenAutoRefresh(session types.Session) {
time.Sleep(sleepDuration)
}

// Refresh the token
newSession, err := c.RefreshToken(session.RefreshToken)
if err != nil {
attempt++
Expand All @@ -138,7 +132,6 @@ func (c *Client) EnableTokenAutoRefresh(session types.Session) {
continue
}

// Update the session, reset the attempt counter, and update the expiresAt time
c.UpdateAuthSession(newSession)
session = newSession
attempt = 0
Expand All @@ -162,5 +155,4 @@ func (c *Client) UpdateAuthSession(session types.Session) {
c.options.headers["Authorization"] = "Bearer " + session.AccessToken
c.Storage = storage_go.NewClient(c.options.url+STORAGE_URL, session.AccessToken, c.options.headers)
c.Functions = functions.NewClient(c.options.url+FUNCTIONS_URL, session.AccessToken, c.options.headers)

}
137 changes: 130 additions & 7 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@ package supabase_test
import (
"fmt"
"testing"
"time"

"github.com/supabase-community/supabase-go"
"github.com/supabase-community/auth-go/types"

supa "github.com/supabase-community/supabase-go"
)

const (
API_URL = "https://your-company.supabase.co"
API_KEY = "your-api-key"
API_URL = "https://your-company.supabase.co"
API_KEY = "your-api-key"
TEST_EMAIL = "test@example.com"
TEST_PASSWORD = "test123456"
TEST_PHONE = "+1234567890"
)

func TestFrom(t *testing.T) {
client, err := supabase.NewClient(API_URL, API_KEY, nil)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why prefer supa over supabase? The latter feels better to me.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. It was to quickly distinguish my old closet from a new revision.

client, err := supa.NewClient(API_URL, API_KEY, nil)
if err != nil {
fmt.Println("cannot initalize client", err)
}
Expand All @@ -22,7 +28,7 @@ func TestFrom(t *testing.T) {
}

func TestRpc(t *testing.T) {
client, err := supabase.NewClient(API_URL, API_KEY, nil)
client, err := supa.NewClient(API_URL, API_KEY, nil)
if err != nil {
fmt.Println("cannot initalize client", err)
}
Expand All @@ -31,7 +37,7 @@ func TestRpc(t *testing.T) {
}

func TestStorage(t *testing.T) {
client, err := supabase.NewClient(API_URL, API_KEY, nil)
client, err := supa.NewClient(API_URL, API_KEY, nil)
if err != nil {
fmt.Println("cannot initalize client", err)
}
Expand All @@ -40,10 +46,127 @@ func TestStorage(t *testing.T) {
}

func TestFunctions(t *testing.T) {
client, err := supabase.NewClient(API_URL, API_KEY, nil)
client, err := supa.NewClient(API_URL, API_KEY, nil)
if err != nil {
fmt.Println("cannot initalize client", err)
}
result, err := client.Functions.Invoke("hello_world", map[string]interface{}{"name": "world"})
fmt.Println(result, err)
}

// use the new auth package to test auth functions
func TestAuth(t *testing.T) {
client, err := supa.NewClient(API_URL, API_KEY, nil)
if err != nil {
t.Fatalf("cannot initialize client: %v", err)
}

t.Run("SignUp with Email", func(t *testing.T) {
signupReq := types.SignupRequest{
Email: TEST_EMAIL,
Password: TEST_PASSWORD,
}

resp, err := client.Auth.Signup(signupReq)
if err != nil {
t.Errorf("SignUp failed: %v", err)
}
if resp.User.Email != TEST_EMAIL {
t.Errorf("Expected email %s, got %s", TEST_EMAIL, resp.User.Email)
}
})

t.Run("SignIn with Email", func(t *testing.T) {
tokenReq := types.TokenRequest{
GrantType: "password",
Email: TEST_EMAIL,
Password: TEST_PASSWORD,
}

resp, err := client.Auth.Token(tokenReq)
if err != nil {
t.Errorf("SignIn failed: %v", err)
}
if resp.User.Email != TEST_EMAIL {
t.Errorf("Expected email %s, got %s", TEST_EMAIL, resp.User.Email)
}
})

t.Run("SignIn with Phone", func(t *testing.T) {
tokenReq := types.TokenRequest{
GrantType: "password",
Phone: TEST_PHONE,
Password: TEST_PASSWORD,
}

resp, err := client.Auth.Token(tokenReq)
if err != nil {
t.Errorf("Phone SignIn failed: %v", err)
}
if resp.User.Phone != TEST_PHONE {
t.Errorf("Expected phone %s, got %s", TEST_PHONE, resp.User.Phone)
}
})

t.Run("Get User", func(t *testing.T) {
// First sign in to get a token
tokenReq := types.TokenRequest{
GrantType: "password",
Email: TEST_EMAIL,
Password: TEST_PASSWORD,
}

authResp, err := client.Auth.Token(tokenReq)
if err != nil {
t.Errorf("SignIn failed: %v", err)
}

// Use the token to get user info
client.UpdateAuthSession(authResp.Session)
user, err := client.Auth.GetUser()
if err != nil {
t.Errorf("Get user failed: %v", err)
}
if user.Email != TEST_EMAIL {
t.Errorf("Expected email %s, got %s", TEST_EMAIL, user.Email)
}
})

t.Run("Refresh Token", func(t *testing.T) {
// First sign in to get a token
tokenReq := types.TokenRequest{
GrantType: "password",
Email: TEST_EMAIL,
Password: TEST_PASSWORD,
}

authResp, err := client.Auth.Token(tokenReq)
if err != nil {
t.Errorf("SignIn failed: %v", err)
}

// Wait a moment before refreshing
time.Sleep(1 * time.Second)

// Refresh the token
refreshReq := types.TokenRequest{
GrantType: "refresh_token",
RefreshToken: authResp.RefreshToken,
}

newResp, err := client.Auth.Token(refreshReq)
if err != nil {
t.Errorf("Token refresh failed: %v", err)
}
if newResp.AccessToken == authResp.AccessToken {
t.Error("Expected new access token to be different")
}
})

t.Run("Logout", func(t *testing.T) {
err := client.Auth.Logout()
if err != nil {
t.Errorf("Logout failed: %v", err)
}
})
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ toolchain go1.22.1

require (
github.com/joho/godotenv v1.5.1
github.com/supabase-community/auth-go v1.3.2
github.com/supabase-community/functions-go v0.0.0-20220927045802-22373e6cb51d
github.com/supabase-community/gotrue-go v1.2.0
github.com/supabase-community/postgrest-go v0.0.11
github.com/supabase-community/storage-go v0.7.0
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ github.com/roja/postgrest-go v0.0.11 h1:vGgsfL4+Tvkpbukneo4fPy/43gbeWSSlKmum9+pw
github.com/roja/postgrest-go v0.0.11/go.mod h1:cw6LfzMyK42AOSBA1bQ/HZ381trIJyuui2GWhraW7Cc=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/supabase-community/gotrue-go v1.2.0 h1:Zm7T5q3qbuwPgC6xyomOBKrSb7X5dvmjDZEmNST7MoE=
github.com/supabase-community/gotrue-go v1.2.0/go.mod h1:86DXBiAUNcbCfgbeOPEh0PQxScLfowUbYgakETSFQOw=
github.com/supabase-community/auth-go v1.3.2 h1:ScKhTXGRS8766J8hEeWURRnrTRDAvKwQs1JPTXBEdcY=
github.com/supabase-community/auth-go v1.3.2/go.mod h1:NR/6b0237xb8oUJt/eOmtnp7UyPpaVMrxOAKJsuRTtw=
github.com/supabase-community/storage-go v0.7.0 h1:cJ8HLbbnL54H5rHPtHfiwtpRwcbDfA3in9HL/ucHnqA=
github.com/supabase-community/storage-go v0.7.0/go.mod h1:oBKcJf5rcUXy3Uj9eS5wR6mvpwbmvkjOtAA+4tGcdvQ=
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y=
Expand Down
10 changes: 2 additions & 8 deletions test/remote_client.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// This is basic example for postgrest-go library usage.
// For now this example is represent wanted syntax and bindings for library.
// After core development this test files will be used for CI tests.

package main

import (
Expand All @@ -10,7 +6,7 @@ import (
"os"

"github.com/joho/godotenv"
"github.com/supabase-community/supabase-go"
supa "github.com/supabase-community/supabase-go"
)

func main() {
Expand All @@ -24,17 +20,15 @@ func main() {
email := os.Getenv("TESTUSER")
password := os.Getenv("TESTUSERPASSWORD")

client, err := supabase.NewClient(projectURL, anonKey, nil)
client, err := supa.NewClient(projectURL, anonKey, nil)
if err != nil {
fmt.Println("cannot initalize client", err)
}
client.SignInWithEmailPassword(email, password)

//
rooms, _, err := client.From("rooms").Select("*", "", false).ExecuteString()
if err != nil {
panic(err)
}
fmt.Println(rooms)

}