Move source and state from membership to profile

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-02-13 21:21:11 +04:00
parent ec3d8fad40
commit 56ec0ab3c3
33 changed files with 1066 additions and 1220 deletions

View File

@@ -25,24 +25,17 @@ import (
"github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page"
)
type (
Membership struct {
ID gid.GID `db:"id"`
IdentityID gid.GID `db:"identity_id"`
OrganizationID gid.GID `db:"organization_id"`
Role MembershipRole `db:"role"`
Source MembershipSource `db:"source"`
State MembershipState `db:"state"`
// FIXME: remove after scim is based on profile
EmailAddress mail.Addr `db:"-"`
FullName string `db:"-"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
IdentityID gid.GID `db:"identity_id"`
OrganizationID gid.GID `db:"organization_id"`
Role MembershipRole `db:"role"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
Memberships []*Membership
@@ -66,8 +59,6 @@ SELECT
identity_id,
organization_id,
role,
source,
state,
created_at,
updated_at
FROM
@@ -109,8 +100,6 @@ INSERT INTO
identity_id,
organization_id,
role,
source,
state,
created_at,
updated_at
)
@@ -120,8 +109,6 @@ VALUES (
@identity_id,
@organization_id,
@role,
@source,
@state,
@created_at,
@updated_at
);
@@ -133,8 +120,6 @@ VALUES (
"identity_id": m.IdentityID,
"organization_id": m.OrganizationID,
"role": m.Role,
"source": m.Source,
"state": m.State,
"created_at": m.CreatedAt,
"updated_at": m.UpdatedAt,
}
@@ -168,8 +153,6 @@ SELECT
identity_id,
organization_id,
role,
source,
state,
created_at,
updated_at
FROM
@@ -209,8 +192,7 @@ func (m *Membership) AuthorizationAttributes(ctx context.Context, conn pg.Conn)
SELECT
identity_id,
organization_id,
role,
source
role
FROM
iam_memberships
WHERE
@@ -221,12 +203,10 @@ LIMIT 1;
var identityID gid.GID
var organizationID gid.GID
var role MembershipRole
var source MembershipSource
if err := conn.QueryRow(ctx, q, m.ID).Scan(
&identityID,
&organizationID,
&role,
&source,
); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrResourceNotFound
@@ -238,7 +218,6 @@ LIMIT 1;
"identity_id": identityID.String(),
"organization_id": organizationID.String(),
"role": role.String(),
"source": source.String(),
}, nil
}
@@ -255,8 +234,6 @@ SELECT
identity_id,
organization_id,
role,
source,
state,
created_at,
updated_at
FROM
@@ -299,8 +276,6 @@ UPDATE
iam_memberships
SET
role = @role,
source = @source,
state = @state,
updated_at = @updated_at
WHERE
id = @id
@@ -312,8 +287,6 @@ WHERE
args := pgx.StrictNamedArgs{
"id": m.ID,
"role": m.Role,
"source": m.Source,
"state": m.State,
"updated_at": m.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
@@ -358,180 +331,51 @@ WHERE
return nil
}
func (m *Memberships) LoadByOrganizationID(
func (m *Membership) LoadActiveByIdentityIDAndOrganizationID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
identityID gid.GID,
organizationID gid.GID,
cursor *page.Cursor[MembershipOrderField],
filter *MembershipFilter,
) error {
query := `
q := `
SELECT
id,
identity_id,
organization_id,
role,
source,
state,
created_at,
updated_at
FROM
iam_memberships
WHERE
%s
AND %s
organization_id = @organization_id
AND %s
`
query = fmt.Sprintf(query, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{
"organization_id": organizationID,
}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, query, args)
if err != nil {
return fmt.Errorf("cannot query memberships: %w", err)
}
memberships, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Membership])
if err != nil {
return fmt.Errorf("cannot collect memberships: %w", err)
}
*m = memberships
return nil
}
func (m *Memberships) CountByOrganizationID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
filter *MembershipFilter,
) (int, error) {
query := `
SELECT
COUNT(*)
m.id,
m.identity_id,
m.organization_id,
m.role,
m.created_at,
m.updated_at
FROM
iam_memberships m
JOIN
identities i ON m.identity_id = i.id
INNER JOIN iam_membership_profiles p
ON p.identity_id = m.identity_id AND p.organization_id = m.organization_id
WHERE
m.organization_id = @organization_id
AND m.%s
AND %s
p.state = @state
AND m.identity_id = @identity_id
AND m.organization_id = @organization_id
LIMIT 1
`
query = fmt.Sprintf(query, scope.SQLFragment(), filter.SQLFragment())
args := pgx.StrictNamedArgs{
"state": ProfileStateActive,
"identity_id": identityID,
"organization_id": organizationID,
}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
row := conn.QueryRow(ctx, query, args)
var count int
if err := row.Scan(&count); err != nil {
return 0, fmt.Errorf("cannot count memberships: %w", err)
}
return count, nil
}
func (m *Memberships) CountByIdentityID(
ctx context.Context,
conn pg.Conn,
identityID gid.GID,
) (int, error) {
query := `
SELECT
COUNT(*)
FROM
iam_memberships
WHERE
identity_id = @identity_id
AND state = 'ACTIVE'
`
args := pgx.StrictNamedArgs{
"identity_id": identityID,
}
row := conn.QueryRow(ctx, query, args)
var count int
if err := row.Scan(&count); err != nil {
return 0, fmt.Errorf("cannot count memberships: %w", err)
}
return count, nil
}
func (m *Memberships) LoadAllByIdentityID(
ctx context.Context,
conn pg.Conn,
identityID gid.GID,
) error {
q := `
SELECT
id,
identity_id,
organization_id,
role,
source,
state,
created_at,
updated_at
FROM
iam_memberships
WHERE
identity_id = $1
;
`
rows, err := conn.Query(ctx, q, identityID)
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query memberships: %w", err)
}
memberships, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Membership])
membership, err := pgx.CollectExactlyOneRow(rows, pgx.RowToAddrOfStructByName[Membership])
if err != nil {
return fmt.Errorf("cannot collect memberships: %w", err)
}
*m = memberships
return nil
}
func (m *Memberships) ResetSCIMSources(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
) error {
q := `
UPDATE iam_memberships
SET
source = 'MANUAL',
updated_at = @updated_at
WHERE
%s
AND organization_id = @organization_id
AND source = 'SCIM'
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.NamedArgs{
"organization_id": organizationID,
"updated_at": time.Now(),
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot reset SCIM membership sources: %w", err)
if errors.Is(err, pgx.ErrNoRows) {
return ErrResourceNotFound
}
return fmt.Errorf("cannot collect membership: %w", err)
}
*m = *membership
return nil
}

View File

@@ -1,108 +0,0 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package coredata
import (
"github.com/jackc/pgx/v5"
"go.probo.inc/probo/pkg/mail"
)
type MembershipFilter struct {
email *mail.Addr
role *MembershipRole
state *MembershipState
source *MembershipSource
}
func NewMembershipFilter() *MembershipFilter {
return &MembershipFilter{}
}
func (f *MembershipFilter) WithEmail(email *mail.Addr) *MembershipFilter {
f.email = email
return f
}
func (f *MembershipFilter) Email() *mail.Addr {
return f.email
}
func (f *MembershipFilter) WithRole(role MembershipRole) *MembershipFilter {
f.role = &role
return f
}
func (f *MembershipFilter) Role() *MembershipRole {
return f.role
}
func (f *MembershipFilter) WithState(state MembershipState) *MembershipFilter {
f.state = &state
return f
}
func (f *MembershipFilter) State() *MembershipState {
return f.state
}
func (f *MembershipFilter) WithSource(source MembershipSource) *MembershipFilter {
f.source = &source
return f
}
func (f *MembershipFilter) Source() *MembershipSource {
return f.source
}
func (f *MembershipFilter) SQLArguments() pgx.StrictNamedArgs {
return pgx.StrictNamedArgs{
"filter_email": f.email,
"filter_role": f.role,
"filter_state": f.state,
"filter_source": f.source,
}
}
func (f *MembershipFilter) SQLFragment() string {
return `
(
CASE
WHEN @filter_email::text IS NOT NULL THEN
i.email_address = @filter_email::text
ELSE TRUE
END
)
AND (
CASE
WHEN @filter_role::text IS NOT NULL THEN
m.role = @filter_role::authz_role
ELSE TRUE
END
)
AND (
CASE
WHEN @filter_state::text IS NOT NULL THEN
m.state = @filter_state::membership_state
ELSE TRUE
END
)
AND (
CASE
WHEN @filter_source::text IS NOT NULL THEN
m.source = @filter_source::text
ELSE TRUE
END
)`
}

View File

@@ -34,6 +34,8 @@ type (
IdentityID gid.GID `db:"identity_id"`
OrganizationID gid.GID `db:"organization_id"`
EmailAddress mail.Addr `db:"email_address"`
Source ProfileSource `db:"source"`
State ProfileState `db:"state"`
FullName string `db:"full_name"`
Kind MembershipProfileKind `db:"kind"`
AdditionalEmailAddresses mail.Addrs `db:"additional_email_addresses"`
@@ -93,6 +95,8 @@ SELECT
p.identity_id,
p.organization_id,
i.email_address,
p.source,
p.state,
p.full_name,
p.kind,
p.additional_email_addresses,
@@ -149,6 +153,8 @@ SELECT
p.identity_id,
p.organization_id,
i.email_address,
p.source,
p.state,
p.full_name,
p.kind,
p.additional_email_addresses,
@@ -208,6 +214,8 @@ SELECT
p.identity_id,
p.organization_id,
i.email_address,
p.source,
p.state,
p.full_name,
p.kind,
p.additional_email_addresses,
@@ -257,30 +265,35 @@ func (p *MembershipProfiles) LoadByOrganizationID(
q := `
WITH profiles AS (
SELECT
id,
identity_id,
organization_id,
full_name,
kind,
additional_email_addresses,
position,
contract_start_date,
contract_end_date,
created_at,
updated_at
p.id,
p.identity_id,
p.organization_id,
i.email_address,
p.source,
p.state,
p.full_name,
p.kind,
p.additional_email_addresses,
p.position,
p.contract_start_date,
p.contract_end_date,
p.created_at,
p.updated_at
FROM
iam_membership_profiles
iam_membership_profiles p
INNER JOIN identities i ON i.id = p.identity_id
WHERE
%s
AND organization_id = @organization_id
AND %s
p.%s
AND p.organization_id = @organization_id
AND %s
)
SELECT
p.id,
p.identity_id,
p.organization_id,
i.email_address,
p.email_address,
p.source,
p.state,
p.full_name,
p.kind,
p.additional_email_addresses,
@@ -291,7 +304,8 @@ SELECT
p.created_at,
p.updated_at
FROM profiles p
INNER JOIN identities i ON i.id = p.identity_id
WHERE
%s
`
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
@@ -317,7 +331,6 @@ INNER JOIN identities i ON i.id = p.identity_id
func (p *MembershipProfiles) LoadByIdentityID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
identityID gid.GID,
cursor *page.Cursor[MembershipProfileOrderField],
filter *MembershipProfileFilter,
@@ -325,29 +338,34 @@ func (p *MembershipProfiles) LoadByIdentityID(
q := `
WITH profiles AS (
SELECT
id,
identity_id,
organization_id,
full_name,
kind,
additional_email_addresses,
position,
contract_start_date,
contract_end_date,
created_at,
updated_at
p.id,
p.identity_id,
p.organization_id,
i.email_address,
p.source,
p.state,
p.full_name,
p.kind,
p.additional_email_addresses,
p.position,
p.contract_start_date,
p.contract_end_date,
p.created_at,
p.updated_at
FROM
iam_membership_profiles
iam_membership_profiles p
INNER JOIN identities i ON i.id = p.identity_id
WHERE
%s
AND identity_id = @identity_id
p.identity_id = @identity_id
AND %s
)
SELECT
p.id,
p.identity_id,
p.organization_id,
i.email_address,
p.email_address,
p.source,
p.state,
p.full_name,
p.kind,
p.additional_email_addresses,
@@ -358,16 +376,14 @@ SELECT
p.created_at,
p.updated_at
FROM profiles p
INNER JOIN identities i ON i.id = p.identity_id
INNER JOIN organizations o ON o.id = p.organization_id
WHERE
%s
`
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
q = fmt.Sprintf(q, filter.SQLFragment(), cursor.SQLFragment())
args := pgx.NamedArgs{"identity_id": identityID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
@@ -600,6 +616,8 @@ WITH attendees AS (
p.identity_id,
p.organization_id,
i.email_address,
p.source,
p.state,
p.full_name,
p.kind,
p.additional_email_addresses,
@@ -624,6 +642,8 @@ SELECT
organization_id,
kind,
email_address,
source,
state,
full_name,
additional_email_addresses,
position,
@@ -684,6 +704,8 @@ SELECT
p.kind,
p.full_name,
i.email_address,
p.source,
p.state,
p.additional_email_addresses,
p.position,
p.contract_start_date,
@@ -725,10 +747,11 @@ func (p *MembershipProfiles) CountByIdentityID(
SELECT
COUNT(*)
FROM
iam_membership_profiles
iam_membership_profiles p
INNER JOIN identities i ON i.id = p.identity_id
WHERE
%s
AND identity_id = @identity_id
AND p.identity_id = @identity_id
`
q = fmt.Sprintf(q, filter.SQLFragment())
@@ -758,11 +781,12 @@ func (p *MembershipProfiles) CountByOrganizationID(
SELECT
COUNT(*)
FROM
iam_membership_profiles
iam_membership_profiles p
INNER JOIN identities i ON i.id = p.identity_id
WHERE
%s
p.%s
AND %s
AND organization_id = @organization_id
AND p.organization_id = @organization_id
`
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
@@ -782,6 +806,45 @@ WHERE
return count, nil
}
func (p *MembershipProfiles) CountActiveOwnerByOrganizationID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
) (int, error) {
q := `
SELECT
COUNT(*)
FROM
iam_membership_profiles p
INNER JOIN iam_memberships m ON m.identity_id = p.identity_id AND m.organization_id = p.organization_id
WHERE
%s
AND p.organization_id = @organization_id
AND p.state = @state
AND m.role = @role
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"state": ProfileStateActive,
"role": MembershipRoleOwner,
"organization_id": organizationID,
}
maps.Copy(args, scope.SQLArguments())
row := conn.QueryRow(ctx, q, args)
var count int
err := row.Scan(&count)
if err != nil {
return 0, fmt.Errorf("cannot collect count: %w", err)
}
return count, nil
}
func (p *MembershipProfile) Insert(
ctx context.Context,
conn pg.Conn,
@@ -793,6 +856,8 @@ INSERT INTO
id,
identity_id,
organization_id,
source,
state,
full_name,
kind,
additional_email_addresses,
@@ -807,6 +872,8 @@ VALUES (
@id,
@identity_id,
@organization_id,
@source,
@state,
@full_name,
@kind,
COALESCE(@additional_email_addresses, '{}'::CITEXT[]),
@@ -823,6 +890,8 @@ VALUES (
"id": p.ID,
"identity_id": p.IdentityID,
"organization_id": p.OrganizationID,
"source": p.Source,
"state": p.State,
"full_name": p.FullName,
"kind": p.Kind,
"additional_email_addresses": p.AdditionalEmailAddresses,
@@ -850,6 +919,8 @@ func (p *MembershipProfile) Update(
UPDATE
iam_membership_profiles
SET
source = @source,
state = @state,
full_name = @full_name,
kind = @kind,
additional_email_addresses = @additional_email_addresses,
@@ -866,6 +937,8 @@ WHERE
args := pgx.StrictNamedArgs{
"id": p.ID,
"source": p.Source,
"state": p.State,
"full_name": p.FullName,
"kind": p.Kind,
"additional_email_addresses": p.AdditionalEmailAddresses,
@@ -888,6 +961,38 @@ WHERE
return nil
}
func (p *MembershipProfiles) ResetSCIMSources(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
) error {
q := `
UPDATE iam_membership_profiles
SET
source = 'MANUAL',
updated_at = @updated_at
WHERE
%s
AND organization_id = @organization_id
AND source = 'SCIM'
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.NamedArgs{
"organization_id": organizationID,
"updated_at": time.Now(),
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot reset SCIM user sources: %w", err)
}
return nil
}
func (p *MembershipProfile) Delete(
ctx context.Context,
conn pg.Conn,

View File

@@ -18,12 +18,16 @@ import (
"time"
"github.com/jackc/pgx/v5"
"go.probo.inc/probo/pkg/mail"
)
type (
MembershipProfileFilter struct {
excludeContractEnded *bool
currentDate time.Time
email *mail.Addr
state *ProfileState
source *ProfileSource
}
)
@@ -34,20 +38,72 @@ func NewMembershipProfileFilter(excludeContractEnded *bool) *MembershipProfileFi
}
}
func (f *MembershipProfileFilter) WithEmail(email *mail.Addr) *MembershipProfileFilter {
f.email = email
return f
}
func (f *MembershipProfileFilter) Email() *mail.Addr {
return f.email
}
func (f *MembershipProfileFilter) WithState(state ProfileState) *MembershipProfileFilter {
f.state = &state
return f
}
func (f *MembershipProfileFilter) State() *ProfileState {
return f.state
}
func (f *MembershipProfileFilter) WithSource(source ProfileSource) *MembershipProfileFilter {
f.source = &source
return f
}
func (f *MembershipProfileFilter) Source() *ProfileSource {
return f.source
}
func (f *MembershipProfileFilter) SQLArguments() pgx.StrictNamedArgs {
return pgx.StrictNamedArgs{
"filter_email": f.email,
"exclude_contract_ended": f.excludeContractEnded,
"current_date": f.currentDate,
"filter_state": f.state,
"filter_source": f.source,
}
}
func (f *MembershipProfileFilter) SQLFragment() string {
return `
(
CASE
WHEN @filter_email::text IS NOT NULL THEN
i.email_address = @filter_email::text
ELSE TRUE
END
)
AND (
CASE
WHEN @exclude_contract_ended::boolean IS NOT NULL AND @exclude_contract_ended::boolean = true THEN
(contract_end_date IS NULL OR contract_end_date >= @current_date::date)
(p.contract_end_date IS NULL OR p.contract_end_date >= @current_date::date)
ELSE TRUE
END
)`
)
AND (
CASE
WHEN @filter_state::text IS NOT NULL THEN
p.state = @filter_state::membership_state
ELSE TRUE
END
)
AND (
CASE
WHEN @filter_source::text IS NOT NULL THEN
p.source = @filter_source::text
ELSE TRUE
END
)
`
}

View File

@@ -1,2 +1,46 @@
ALTER TABLE
iam_membership_profiles
ADD
COLUMN state membership_state NOT NULL DEFAULT 'ACTIVE',
ADD
COLUMN source TEXT NOT NULL DEFAULT 'MANUAL';
UPDATE
iam_membership_profiles p
SET
state = m.state,
source = m.source
FROM
iam_memberships m
WHERE
m.id = p.membership_id;
ALTER TABLE
iam_membership_profiles DROP COLUMN membership_id;
ALTER TABLE
iam_scim_events
ADD
COLUMN user_name CITEXT NOT NULL DEFAULT '';
WITH emails AS (
SELECT
i.email_address,
m.id
FROM
iam_memberships m
INNER JOIN identities i ON i.id = m.identity_id
)
UPDATE
iam_scim_events se
SET
user_name = e.email_address
FROM
emails e
WHERE
e.id = se.membership_id;
ALTER TABLE
iam_scim_events
ALTER COLUMN
user_name DROP DEFAULT;

View File

@@ -19,19 +19,19 @@ import (
"fmt"
)
type MembershipSource string
type ProfileSource string
const (
MembershipSourceManual MembershipSource = "MANUAL"
MembershipSourceSAML MembershipSource = "SAML"
MembershipSourceSCIM MembershipSource = "SCIM"
ProfileSourceManual ProfileSource = "MANUAL"
ProfileSourceSAML ProfileSource = "SAML"
ProfileSourceSCIM ProfileSource = "SCIM"
)
func (s MembershipSource) String() string {
func (s ProfileSource) String() string {
return string(s)
}
func (s *MembershipSource) Scan(value any) error {
func (s *ProfileSource) Scan(value any) error {
var str string
switch v := value.(type) {
case string:
@@ -39,22 +39,22 @@ func (s *MembershipSource) Scan(value any) error {
case []byte:
str = string(v)
default:
return fmt.Errorf("unsupported type for MembershipSource: %T", value)
return fmt.Errorf("unsupported type for ProfileSource: %T", value)
}
switch str {
case "MANUAL":
*s = MembershipSourceManual
*s = ProfileSourceManual
case "SAML":
*s = MembershipSourceSAML
*s = ProfileSourceSAML
case "SCIM":
*s = MembershipSourceSCIM
*s = ProfileSourceSCIM
default:
return fmt.Errorf("invalid MembershipSource value: %q", str)
return fmt.Errorf("invalid ProfileSource value: %q", str)
}
return nil
}
func (s MembershipSource) Value() (driver.Value, error) {
func (s ProfileSource) Value() (driver.Value, error) {
return s.String(), nil
}

View File

@@ -19,18 +19,18 @@ import (
"fmt"
)
type MembershipState string
type ProfileState string
const (
MembershipStateActive MembershipState = "ACTIVE"
MembershipStateInactive MembershipState = "INACTIVE"
ProfileStateActive ProfileState = "ACTIVE"
ProfileStateInactive ProfileState = "INACTIVE"
)
func (s MembershipState) String() string {
func (s ProfileState) String() string {
return string(s)
}
func (s *MembershipState) Scan(value any) error {
func (s *ProfileState) Scan(value any) error {
var str string
switch v := value.(type) {
case string:
@@ -38,20 +38,20 @@ func (s *MembershipState) Scan(value any) error {
case []byte:
str = string(v)
default:
return fmt.Errorf("unsupported type for MembershipState: %T", value)
return fmt.Errorf("unsupported type for ProfileState: %T", value)
}
switch str {
case "ACTIVE":
*s = MembershipStateActive
*s = ProfileStateActive
case "INACTIVE":
*s = MembershipStateInactive
*s = ProfileStateInactive
default:
return fmt.Errorf("invalid MembershipState value: %q", str)
return fmt.Errorf("invalid ProfileState value: %q", str)
}
return nil
}
func (s MembershipState) Value() (driver.Value, error) {
func (s ProfileState) Value() (driver.Value, error) {
return s.String(), nil
}

View File

@@ -39,7 +39,7 @@ type (
ResponseBody *string `db:"response_body"`
StatusCode int `db:"status_code"`
ErrorMessage *string `db:"error_message"`
MembershipID *gid.GID `db:"membership_id"`
UserName string `db:"user_name"`
IPAddress net.IP `db:"ip_address"`
CreatedAt time.Time `db:"created_at"`
}
@@ -87,7 +87,7 @@ SELECT
response_body,
status_code,
error_message,
membership_id,
user_name,
ip_address,
created_at
FROM
@@ -139,7 +139,7 @@ INSERT INTO iam_scim_events (
response_body,
status_code,
error_message,
membership_id,
user_name,
ip_address,
created_at
) VALUES (
@@ -153,7 +153,7 @@ INSERT INTO iam_scim_events (
@response_body,
@status_code,
@error_message,
@membership_id,
@user_name,
@ip_address,
@created_at
)
@@ -170,7 +170,7 @@ INSERT INTO iam_scim_events (
"response_body": s.ResponseBody,
"status_code": s.StatusCode,
"error_message": s.ErrorMessage,
"membership_id": s.MembershipID,
"user_name": s.UserName,
"ip_address": s.IPAddress,
"created_at": s.CreatedAt,
}
@@ -201,7 +201,7 @@ SELECT
response_body,
status_code,
error_message,
membership_id,
user_name,
ip_address,
created_at
FROM
@@ -281,7 +281,7 @@ SELECT
response_body,
status_code,
error_message,
membership_id,
user_name,
ip_address,
created_at
FROM