Introduce pkg/mail.Addr

Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
Émile Ré
2025-12-12 15:32:46 +01:00
parent cab9d6f2ed
commit c0b2a5702d
85 changed files with 842 additions and 646 deletions

View File

@@ -24,6 +24,7 @@ import (
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page"
)
@@ -701,7 +702,7 @@ func (p *Document) IsLastSignableVersionSignedByUserEmail(
conn pg.Conn,
scope Scoper,
documentID gid.GID,
userEmail string,
userEmail mail.Addr,
) (bool, error) {
q := `
WITH last_signable_version AS (

View File

@@ -16,6 +16,7 @@ package coredata
import (
"github.com/jackc/pgx/v5"
"go.probo.inc/probo/pkg/mail"
)
type (
@@ -23,7 +24,7 @@ type (
query *string
trustCenterVisibilities []TrustCenterVisibility
published *bool
userEmail *string
userEmail *mail.Addr
}
)
@@ -47,7 +48,7 @@ func (f *DocumentFilter) WithPublished(published *bool) *DocumentFilter {
return f
}
func (f *DocumentFilter) WithUserEmail(userEmail *string) *DocumentFilter {
func (f *DocumentFilter) WithUserEmail(userEmail *mail.Addr) *DocumentFilter {
f.userEmail = userEmail
return f
}

View File

@@ -16,11 +16,12 @@ package coredata
import (
"github.com/jackc/pgx/v5"
"go.probo.inc/probo/pkg/mail"
)
type (
DocumentVersionFilter struct {
userEmail *string
userEmail *mail.Addr
}
)
@@ -28,7 +29,7 @@ func NewDocumentVersionFilter() *DocumentVersionFilter {
return &DocumentVersionFilter{}
}
func (f *DocumentVersionFilter) WithUserEmail(userEmail *string) *DocumentVersionFilter {
func (f *DocumentVersionFilter) WithUserEmail(userEmail *mail.Addr) *DocumentVersionFilter {
f.userEmail = userEmail
return f
}

View File

@@ -25,6 +25,7 @@ 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"
)
@@ -424,7 +425,7 @@ func (pvs *DocumentVersionSignature) IsSignedByUserEmail(
conn pg.Conn,
scope Scoper,
documentVersionID gid.GID,
userEmail string,
userEmail mail.Addr,
) (bool, error) {
q := `
SELECT EXISTS (

View File

@@ -23,6 +23,7 @@ import (
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
)
type (
@@ -45,7 +46,7 @@ var (
func NewEmail(
recipientName string,
recipientEmail string,
recipientEmail mail.Addr,
subject string,
textBody string,
htmlBody *string,
@@ -54,7 +55,7 @@ func NewEmail(
return &Email{
ID: gid.New(gid.NilTenant, EmailEntityType),
RecipientName: recipientName,
RecipientEmail: recipientEmail,
RecipientEmail: recipientEmail.String(),
Subject: subject,
TextBody: textBody,
HtmlBody: htmlBody,

View File

@@ -11,31 +11,32 @@ import (
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
)
type (
ExportJob struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
Type ExportJobType `db:"type"`
Arguments json.RawMessage `db:"arguments"`
Error *string `db:"error"`
Status ExportJobStatus `db:"status"`
FileID *gid.GID `db:"file_id"`
RecipientEmail string `db:"recipient_email"`
RecipientName string `db:"recipient_name"`
CreatedAt time.Time `db:"created_at"`
StartedAt *time.Time `db:"started_at"`
CompletedAt *time.Time `db:"completed_at"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
Type ExportJobType `db:"type"`
Arguments json.RawMessage `db:"arguments"`
Error *string `db:"error"`
Status ExportJobStatus `db:"status"`
FileID *gid.GID `db:"file_id"`
RecipientEmail mail.Addr `db:"recipient_email"`
RecipientName string `db:"recipient_name"`
CreatedAt time.Time `db:"created_at"`
StartedAt *time.Time `db:"started_at"`
CompletedAt *time.Time `db:"completed_at"`
}
ExportJobs []*ExportJob
DocumentExportArguments struct {
DocumentIDs []gid.GID `json:"document_ids"`
WithWatermark bool `json:"with_watermark"`
WatermarkEmail *string `json:"watermark_email"`
WithSignatures bool `json:"with_signatures"`
DocumentIDs []gid.GID `json:"document_ids"`
WithWatermark bool `json:"with_watermark"`
WatermarkEmail *mail.Addr `json:"watermark_email"`
WithSignatures bool `json:"with_signatures"`
}
FrameworkExportArguments struct {

View File

@@ -24,6 +24,7 @@ import (
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page"
)
@@ -31,9 +32,9 @@ type (
Invitation struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
Email string `db:"email"`
Email mail.Addr `db:"email"`
FullName string `db:"full_name"`
Role MembershipRole `db:"role"`
Role MembershipRole `db:"role"`
Status InvitationStatus `db:"status"`
ExpiresAt time.Time `db:"expires_at"`
AcceptedAt *time.Time `db:"accepted_at"`
@@ -43,10 +44,10 @@ type (
Invitations []*Invitation
InvitationData struct {
InvitationID gid.GID `json:"invitation_id"`
OrganizationID gid.GID `json:"organization_id"`
Email string `json:"email"`
FullName string `json:"full_name"`
InvitationID gid.GID `json:"invitation_id"`
OrganizationID gid.GID `json:"organization_id"`
Email mail.Addr `json:"email"`
FullName string `json:"full_name"`
Role MembershipRole `json:"role"`
}
@@ -241,7 +242,7 @@ func (i *Invitations) LoadByEmail(
ctx context.Context,
conn pg.Conn,
scope Scoper,
email string,
email mail.Addr,
cursor *page.Cursor[InvitationOrderField],
filter *InvitationFilter,
) error {

View File

@@ -26,6 +26,7 @@ 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"
)
@@ -36,7 +37,7 @@ type (
OrganizationID gid.GID `db:"organization_id"`
Role MembershipRole `db:"role"`
FullName string `db:"full_name"`
EmailAddress string `db:"email_address"`
EmailAddress mail.Addr `db:"email_address"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}

View File

@@ -24,22 +24,23 @@ import (
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page"
)
type (
People struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
Kind PeopleKind `db:"kind"`
FullName string `db:"full_name"`
PrimaryEmailAddress string `db:"primary_email_address"`
AdditionalEmailAddresses []string `db:"additional_email_addresses"`
Position *string `db:"position"`
ContractStartDate *time.Time `db:"contract_start_date"`
ContractEndDate *time.Time `db:"contract_end_date"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
Kind PeopleKind `db:"kind"`
FullName string `db:"full_name"`
PrimaryEmailAddress mail.Addr `db:"primary_email_address"`
AdditionalEmailAddresses []mail.Addr `db:"additional_email_addresses"`
Position *string `db:"position"`
ContractStartDate *time.Time `db:"contract_start_date"`
ContractEndDate *time.Time `db:"contract_end_date"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
Peoples []*People
@@ -180,7 +181,7 @@ func (p *People) LoadByEmailAndOrganizationID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
primaryEmailAddress string,
primaryEmailAddress mail.Addr,
organizationID gid.GID,
) error {
q := `
@@ -221,7 +222,7 @@ LIMIT 1;
people, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[People])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return &ErrPeopleNotFound{Identifier: primaryEmailAddress}
return &ErrPeopleNotFound{Identifier: primaryEmailAddress.String()}
}
return fmt.Errorf("cannot collect people: %w", err)

View File

@@ -20,9 +20,9 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
)
type SAMLConfiguration struct {

View File

@@ -24,23 +24,24 @@ import (
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
)
type (
SlackMessage struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
Type SlackMessageType `db:"type"`
Body map[string]any `db:"body"`
MessageTS *string `db:"message_ts"`
ChannelID *string `db:"channel_id"`
RequesterEmail *string `db:"requester_email"`
Metadata map[string]any `db:"metadata"`
InitialSlackMessageID gid.GID `db:"initial_slack_message_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
SentAt *time.Time `db:"sent_at"`
Error *string `db:"error"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
Type SlackMessageType `db:"type"`
Body map[string]any `db:"body"`
MessageTS *string `db:"message_ts"`
ChannelID *string `db:"channel_id"`
RequesterEmail *mail.Addr `db:"requester_email"`
Metadata map[string]any `db:"metadata"`
InitialSlackMessageID gid.GID `db:"initial_slack_message_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
SentAt *time.Time `db:"sent_at"`
Error *string `db:"error"`
}
ErrNoUnsentSlackMessage struct{}
@@ -61,7 +62,6 @@ func NewSlackMessage(
organizationID gid.GID,
messageType SlackMessageType,
body map[string]any,
requesterEmail *string,
) *SlackMessage {
now := time.Now()
id := gid.New(scope.GetTenantID(), SlackMessageEntityType)
@@ -70,7 +70,6 @@ func NewSlackMessage(
OrganizationID: organizationID,
Type: messageType,
Body: body,
RequesterEmail: requesterEmail,
InitialSlackMessageID: id,
CreatedAt: now,
UpdatedAt: now,
@@ -438,7 +437,7 @@ func (s *SlackMessage) LoadLatestByRequesterEmailAndType(
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
requesterEmail string,
requesterEmail mail.Addr,
messageType SlackMessageType,
since time.Time,
) error {

View File

@@ -26,24 +26,25 @@ 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 (
TrustCenterAccess struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
TenantID gid.TenantID `db:"tenant_id"`
TrustCenterID gid.GID `db:"trust_center_id"`
Email string `db:"email"`
Name string `db:"name"`
Active bool `db:"active"`
HasAcceptedNonDisclosureAgreement bool `db:"has_accepted_non_disclosure_agreement"`
HasAcceptedNonDisclosureAgreementMetadata json.RawMessage `db:"has_accepted_non_disclosure_agreement_metadata"`
NDAFileID *gid.GID `db:"nda_file_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
LastTokenExpiresAt *time.Time `db:"last_token_expires_at"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
TenantID gid.TenantID `db:"tenant_id"`
TrustCenterID gid.GID `db:"trust_center_id"`
Email mail.Addr `db:"email"`
Name string `db:"name"`
Active bool `db:"active"`
HasAcceptedNonDisclosureAgreement bool `db:"has_accepted_non_disclosure_agreement"`
HasAcceptedNonDisclosureAgreementMetadata json.RawMessage `db:"has_accepted_non_disclosure_agreement_metadata"`
NDAFileID *gid.GID `db:"nda_file_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
LastTokenExpiresAt *time.Time `db:"last_token_expires_at"`
}
TrustCenterAccesses []*TrustCenterAccess
@@ -132,7 +133,7 @@ func (tca *TrustCenterAccess) LoadByTrustCenterIDAndEmail(
conn pg.Conn,
scope Scoper,
trustCenterID gid.GID,
email string,
email mail.Addr,
) error {
q := `
SELECT

View File

@@ -22,23 +22,24 @@ import (
"strings"
"time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"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 (
User struct {
ID gid.GID `db:"id"`
EmailAddress string `db:"email_address"`
HashedPassword []byte `db:"hashed_password"`
FullName string `db:"fullname"`
EmailAddressVerified bool `db:"email_address_verified"`
SAMLSubject *string `db:"saml_subject"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
EmailAddress mail.Addr `db:"email_address"`
HashedPassword []byte `db:"hashed_password"`
FullName string `db:"fullname"`
EmailAddressVerified bool `db:"email_address_verified"`
SAMLSubject *string `db:"saml_subject"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
Users []*User
@@ -150,7 +151,7 @@ WHERE
func (u *User) LoadByEmail(
ctx context.Context,
conn pg.Conn,
email string,
email mail.Addr,
) error {
q := `
SELECT
@@ -179,7 +180,7 @@ LIMIT 1;
user, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[User])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return &ErrUserNotFound{Identifier: email}
return &ErrUserNotFound{Identifier: email.String()}
}
return fmt.Errorf("cannot collect user: %w", err)
@@ -362,11 +363,11 @@ WHERE
`
args := pgx.StrictNamedArgs{
"user_id": u.ID,
"email_address": u.EmailAddress,
"email_address_verified": u.EmailAddressVerified,
"saml_subject": u.SAMLSubject,
"updated_at": u.UpdatedAt,
"user_id": u.ID,
"email_address": u.EmailAddress,
"email_address_verified": u.EmailAddressVerified,
"saml_subject": u.SAMLSubject,
"updated_at": u.UpdatedAt,
}
_, err := conn.Exec(ctx, q, args)

View File

@@ -24,22 +24,23 @@ import (
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page"
)
type (
VendorContact struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
VendorID gid.GID `db:"vendor_id"`
FullName *string `db:"full_name"`
Email *string `db:"email"`
Phone *string `db:"phone"`
Role *string `db:"role"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
VendorID gid.GID `db:"vendor_id"`
FullName *string `db:"full_name"`
Email *mail.Addr `db:"email"`
Phone *string `db:"phone"`
Role *string `db:"role"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
VendorContacts []*VendorContact