Compare commits

...

4 Commits

Author SHA1 Message Date
github-actions[bot]
d5fd3cb59c release(services/auth): 0.42.4 (#3618)
Co-authored-by: dbarrosop <dbarrosop@users.noreply.github.com>
2025-10-20 13:25:21 +02:00
David Barroso
f36d360b9e fix(auth): apply relationships on new projects (#3617) 2025-10-20 13:23:33 +02:00
github-actions[bot]
61af5087fd release(services/auth): 0.42.3 (#3608)
Co-authored-by: dbarrosop <dbarrosop@users.noreply.github.com>
2025-10-20 12:42:03 +02:00
David Barroso
7429d8ae3f fix(auth): always apply expected metadata (#3616) 2025-10-20 12:37:52 +02:00
2 changed files with 383 additions and 30 deletions

View File

@@ -2,6 +2,23 @@
All notable changes to this project will be documented in this file.
## [auth@0.42.4] - 2025-10-20
### 🐛 Bug Fixes
- *(auth)* Apply relationships on new projects (#3617)
## [auth@0.42.3] - 2025-10-20
### 🐛 Bug Fixes
- *(auth)* Always apply expected metadata (#3616)
### ⚙️ Miscellaneous Tasks
- *(storage)* Migrate to urfave and slog libraries (#3606)
## [auth@0.42.2] - 2025-10-13
### ⚙️ Miscellaneous Tasks

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
@@ -11,8 +12,10 @@ import (
)
const (
timeout = 10
hasuraDBName = "default"
timeout = 10
hasuraDBName = "default"
errorCodeAlreadyTracked = "already-tracked"
errorCodeAlreadyExists = "already-exists"
)
type hasuraErrResponse struct {
@@ -21,6 +24,19 @@ type hasuraErrResponse struct {
Code string `json:"code"`
}
type metadataError struct {
code string
msg string
}
func (e *metadataError) Error() string {
return e.msg
}
func (e *metadataError) Code() string {
return e.code
}
func postMetadata(ctx context.Context, url, hasuraSecret string, data any) error {
client := &http.Client{ //nolint: exhaustruct
Timeout: time.Second * timeout,
@@ -57,8 +73,12 @@ func postMetadata(ctx context.Context, url, hasuraSecret string, data any) error
)
}
if errResponse.Code == "already-tracked" || errResponse.Code == "already-exists" {
return nil
if errResponse.Code == errorCodeAlreadyTracked ||
errResponse.Code == errorCodeAlreadyExists {
return &metadataError{
code: errResponse.Code,
msg: errResponse.Error,
}
}
return fmt.Errorf("status_code: %d\nresponse: %s", resp.StatusCode, b) //nolint: err113
@@ -68,8 +88,9 @@ func postMetadata(ctx context.Context, url, hasuraSecret string, data any) error
}
type TrackTable struct {
Type string `json:"type"`
Args PgTrackTableArgs `json:"args"`
Type string `json:"type"`
Args PgTrackTableArgs `json:"args"`
IsEnum bool `json:"is_enum,omitempty"` //nolint: tagliatelle
}
type Table struct {
@@ -96,9 +117,29 @@ type Configuration struct {
}
type PgTrackTableArgs struct {
Source string `json:"source"`
Table Table `json:"table"`
Configuration Configuration `json:"configuration"`
Source string `json:"source"`
Table Table `json:"table"`
Configuration Configuration `json:"configuration"`
ObjectRelationships []ObjectRelationshipConfig `json:"object_relationships,omitempty"` //nolint: tagliatelle
ArrayRelationships []ArrayRelationshipConfig `json:"array_relationships,omitempty"` //nolint: tagliatelle
}
type ObjectRelationshipConfig struct {
Name string `json:"name"`
Using ObjectRelationshipConfigUsing `json:"using"`
}
type ObjectRelationshipConfigUsing struct {
ForeignKeyConstraintOn any `json:"foreign_key_constraint_on"` //nolint: tagliatelle
}
type ArrayRelationshipConfig struct {
Name string `json:"name"`
Using ArrayRelationshipConfigUsing `json:"using"`
}
type ArrayRelationshipConfigUsing struct {
ForeignKeyConstraintOn ForeignKeyConstraintOn `json:"foreign_key_constraint_on"` //nolint: tagliatelle
}
type CreateObjectRelationship struct {
@@ -150,14 +191,126 @@ type DropRelationshipArgs struct {
Relationship string `json:"relationship"`
}
type SetTableCustomization struct {
Type string `json:"type"`
Args SetTableCustomizationArgs `json:"args"`
}
type SetTableCustomizationArgs struct {
Source string `json:"source"`
Table Table `json:"table"`
Configuration Configuration `json:"configuration"`
}
func applyTableCustomization(
ctx context.Context,
url, hasuraSecret string,
table TrackTable,
) error {
customization := SetTableCustomization{
Type: "pg_set_table_customization",
Args: SetTableCustomizationArgs{
Source: table.Args.Source,
Table: table.Args.Table,
Configuration: table.Args.Configuration,
},
}
return postMetadata(ctx, url, hasuraSecret, customization)
}
func applyObjectRelationships(
ctx context.Context,
url, hasuraSecret string,
table TrackTable,
) error {
for _, rel := range table.Args.ObjectRelationships {
relationship := CreateObjectRelationship{
Type: "pg_create_object_relationship",
Args: CreateObjectRelationshipArgs{
Source: table.Args.Source,
Table: table.Args.Table,
Name: rel.Name,
Using: CreateObjectRelationshipUsing{
ForeignKeyConstraintOn: func() []string {
// Handle both string and array cases
switch v := rel.Using.ForeignKeyConstraintOn.(type) {
case string:
return []string{v}
case []string:
return v
default:
return []string{}
}
}(),
},
},
}
if err := postMetadata(ctx, url, hasuraSecret, relationship); err != nil {
var metaErr *metadataError
if ok := errors.As(err, &metaErr); ok && metaErr.Code() == errorCodeAlreadyExists {
continue // Skip if relationship already exists
}
return fmt.Errorf(
"problem creating object relationship %s for table %s.%s: %w",
rel.Name,
table.Args.Table.Schema,
table.Args.Table.Name,
err,
)
}
}
return nil
}
func applyArrayRelationships(
ctx context.Context,
url, hasuraSecret string,
table TrackTable,
) error {
for _, rel := range table.Args.ArrayRelationships {
relationship := CreateArrayRelationship{
Type: "pg_create_array_relationship",
Args: CreateArrayRelationshipArgs{
Source: table.Args.Source,
Table: table.Args.Table,
Name: rel.Name,
Using: CreateArrayRelationshipUsing{
ForeignKeyConstraintOn: rel.Using.ForeignKeyConstraintOn,
},
},
}
if err := postMetadata(ctx, url, hasuraSecret, relationship); err != nil {
var metaErr *metadataError
if ok := errors.As(err, &metaErr); ok && metaErr.Code() == errorCodeAlreadyExists {
continue // Skip if relationship already exists
}
return fmt.Errorf(
"problem creating array relationship %s for table %s.%s: %w",
rel.Name,
table.Args.Table.Schema,
table.Args.Table.Name,
err,
)
}
}
return nil
}
func ApplyHasuraMetadata( //nolint: funlen,maintidx
ctx context.Context,
url, hasuraSecret string,
) error {
authTables := []TrackTable{
{
{ //nolint:exhaustruct
Type: "pg_track_table",
Args: PgTrackTableArgs{
Args: PgTrackTableArgs{ //nolint:exhaustruct
Source: hasuraDBName,
Table: Table{
Schema: "auth",
@@ -184,8 +337,9 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
},
},
{
Type: "pg_track_table",
Args: PgTrackTableArgs{
Type: "pg_track_table",
IsEnum: true,
Args: PgTrackTableArgs{ //nolint:exhaustruct
Source: hasuraDBName,
Table: Table{
Schema: "auth",
@@ -209,11 +363,25 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
"comment": "comment",
},
},
ArrayRelationships: []ArrayRelationshipConfig{
{
Name: "refreshTokens",
Using: ArrayRelationshipConfigUsing{
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
Table: Table{
Schema: "auth",
Name: "refresh_tokens",
},
Columns: []string{"type"},
},
},
},
},
},
},
{
{ //nolint:exhaustruct
Type: "pg_track_table",
Args: PgTrackTableArgs{
Args: PgTrackTableArgs{ //nolint:exhaustruct
Source: hasuraDBName,
Table: Table{
Schema: "auth",
@@ -239,11 +407,19 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
"user_id": "userId",
},
},
ObjectRelationships: []ObjectRelationshipConfig{
{
Name: "user",
Using: ObjectRelationshipConfigUsing{
ForeignKeyConstraintOn: "user_id",
},
},
},
},
},
{
{ //nolint:exhaustruct
Type: "pg_track_table",
Args: PgTrackTableArgs{
Args: PgTrackTableArgs{ //nolint:exhaustruct
Source: hasuraDBName,
Table: Table{
Schema: "auth",
@@ -266,11 +442,37 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
"role": "role",
},
},
ArrayRelationships: []ArrayRelationshipConfig{
{
Name: "userRoles",
Using: ArrayRelationshipConfigUsing{
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
Table: Table{
Schema: "auth",
Name: "user_roles",
},
Columns: []string{"role"},
},
},
},
{
Name: "usersByDefaultRole",
Using: ArrayRelationshipConfigUsing{
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
Table: Table{
Schema: "auth",
Name: "users",
},
Columns: []string{"default_role"},
},
},
},
},
},
},
{
{ //nolint:exhaustruct
Type: "pg_track_table",
Args: PgTrackTableArgs{
Args: PgTrackTableArgs{ //nolint:exhaustruct
Source: hasuraDBName,
Table: Table{
Schema: "auth",
@@ -300,11 +502,25 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
"provider_user_id": "providerUserId",
},
},
ObjectRelationships: []ObjectRelationshipConfig{
{
Name: "user",
Using: ObjectRelationshipConfigUsing{
ForeignKeyConstraintOn: "user_id",
},
},
{
Name: "provider",
Using: ObjectRelationshipConfigUsing{
ForeignKeyConstraintOn: "provider_id",
},
},
},
},
},
{
{ //nolint:exhaustruct
Type: "pg_track_table",
Args: PgTrackTableArgs{
Args: PgTrackTableArgs{ //nolint:exhaustruct
Source: hasuraDBName,
Table: Table{
Schema: "auth",
@@ -330,9 +546,23 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
"role": "role",
},
},
ObjectRelationships: []ObjectRelationshipConfig{
{
Name: "user",
Using: ObjectRelationshipConfigUsing{
ForeignKeyConstraintOn: "user_id",
},
},
{
Name: "roleByRole",
Using: ObjectRelationshipConfigUsing{
ForeignKeyConstraintOn: "role",
},
},
},
},
},
{
{ //nolint:exhaustruct
Type: "pg_track_table",
Args: PgTrackTableArgs{
Source: hasuraDBName,
@@ -380,11 +610,69 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
"webauthn_current_challenge": "currentChallenge",
},
},
ObjectRelationships: []ObjectRelationshipConfig{
{
Name: "defaultRoleByRole",
Using: ObjectRelationshipConfigUsing{
ForeignKeyConstraintOn: "default_role",
},
},
},
ArrayRelationships: []ArrayRelationshipConfig{
{
Name: "userProviders",
Using: ArrayRelationshipConfigUsing{
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
Table: Table{
Schema: "auth",
Name: "user_providers",
},
Columns: []string{"user_id"},
},
},
},
{
Name: "roles",
Using: ArrayRelationshipConfigUsing{
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
Table: Table{
Schema: "auth",
Name: "user_roles",
},
Columns: []string{"user_id"},
},
},
},
{
Name: "refreshTokens",
Using: ArrayRelationshipConfigUsing{
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
Table: Table{
Schema: "auth",
Name: "refresh_tokens",
},
Columns: []string{"user_id"},
},
},
},
{
Name: "securityKeys",
Using: ArrayRelationshipConfigUsing{
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
Table: Table{
Schema: "auth",
Name: "user_security_keys",
},
Columns: []string{"user_id"},
},
},
},
},
},
},
{
{ //nolint:exhaustruct
Type: "pg_track_table",
Args: PgTrackTableArgs{
Args: PgTrackTableArgs{ //nolint:exhaustruct
Source: hasuraDBName,
Table: Table{
Schema: "auth",
@@ -407,11 +695,25 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
"id": "id",
},
},
ArrayRelationships: []ArrayRelationshipConfig{
{
Name: "userProviders",
Using: ArrayRelationshipConfigUsing{
ForeignKeyConstraintOn: ForeignKeyConstraintOn{
Table: Table{
Schema: "auth",
Name: "user_providers",
},
Columns: []string{"provider_id"},
},
},
},
},
},
},
{
{ //nolint:exhaustruct
Type: "pg_track_table",
Args: PgTrackTableArgs{
Args: PgTrackTableArgs{ //nolint:exhaustruct
Source: hasuraDBName,
Table: Table{
Schema: "auth",
@@ -437,15 +739,49 @@ func ApplyHasuraMetadata( //nolint: funlen,maintidx
"credential_public_key": "credentialPublicKey",
},
},
ObjectRelationships: []ObjectRelationshipConfig{
{
Name: "user",
Using: ObjectRelationshipConfigUsing{
ForeignKeyConstraintOn: "user_id",
},
},
},
},
},
}
// Track each table (will skip if already tracked due to existing error handling)
for _, table := range authTables {
if err := postMetadata(ctx, url, hasuraSecret, table); err != nil {
return fmt.Errorf("problem adding metadata for table %s.%s: %w",
table.Args.Table.Schema, table.Args.Table.Name, err)
err := postMetadata(ctx, url, hasuraSecret, table)
if err != nil {
var metaErr *metadataError
if ok := errors.As(err, &metaErr); ok && metaErr.Code() == errorCodeAlreadyTracked {
if err := applyTableCustomization(ctx, url, hasuraSecret, table); err != nil {
return fmt.Errorf(
"problem updating customization for table %s.%s: %w",
table.Args.Table.Schema,
table.Args.Table.Name,
err,
)
}
} else {
return fmt.Errorf(
"problem adding metadata for table %s.%s: %w",
table.Args.Table.Schema,
table.Args.Table.Name,
err,
)
}
}
}
for _, table := range authTables {
if err := applyObjectRelationships(ctx, url, hasuraSecret, table); err != nil {
return err
}
if err := applyArrayRelationships(ctx, url, hasuraSecret, table); err != nil {
return err
}
}