Files
Dušan Borovčanin 61d0427898 NOISSUE - Rename to Magistrala (#3427)
Signed-off-by: dusan <borovcanindusan1@gmail.com>
2026-04-06 15:23:42 +02:00

280 lines
7.7 KiB
Go

// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package postgres
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/absmach/magistrala/domains"
repoerr "github.com/absmach/magistrala/pkg/errors/repository"
"github.com/absmach/magistrala/pkg/postgres"
)
func (repo domainRepo) SaveInvitation(ctx context.Context, invitation domains.Invitation) (err error) {
q := `INSERT INTO invitations (invited_by, invitee_user_id, domain_id, role_id, created_at)
VALUES (:invited_by, :invitee_user_id, :domain_id, :role_id, :created_at)`
dbInv := toDBInvitation(invitation)
if _, err = repo.db.NamedExecContext(ctx, q, dbInv); err != nil {
return postgres.HandleError(repoerr.ErrCreateEntity, err)
}
return nil
}
func (repo domainRepo) RetrieveInvitation(ctx context.Context, inviteeUserID, domainID string) (domains.Invitation, error) {
q := `SELECT invited_by, invitee_user_id, domain_id, role_id, created_at, updated_at, confirmed_at, rejected_at FROM invitations WHERE invitee_user_id = :invitee_user_id AND domain_id = :domain_id;`
dbinv := dbInvitation{
InviteeUserID: inviteeUserID,
DomainID: domainID,
}
rows, err := repo.db.NamedQueryContext(ctx, q, dbinv)
if err != nil {
return domains.Invitation{}, postgres.HandleError(repoerr.ErrViewEntity, err)
}
defer rows.Close()
dbinv = dbInvitation{}
if rows.Next() {
if err = rows.StructScan(&dbinv); err != nil {
return domains.Invitation{}, postgres.HandleError(repoerr.ErrViewEntity, err)
}
return toInvitation(dbinv), nil
}
return domains.Invitation{}, repoerr.ErrNotFound
}
func (repo domainRepo) RetrieveAllInvitations(ctx context.Context, pm domains.InvitationPageMeta) (domains.InvitationPage, error) {
query := pageQuery(pm)
q := fmt.Sprintf(`
SELECT
i.invited_by,
i.invitee_user_id,
i.domain_id,
d."name" AS domain_name,
i.role_id,
dr."name" AS role_name,
i.created_at,
i.updated_at,
i.confirmed_at,
i.rejected_at
FROM
invitations i
LEFT JOIN domains d ON
i.domain_id = d.id
LEFT JOIN domains_roles dr ON
dr.id = i.role_id
%s
LIMIT :limit OFFSET :offset;
`, query)
var items []domains.Invitation
if !pm.OnlyTotal {
rows, err := repo.db.NamedQueryContext(ctx, q, pm)
if err != nil {
return domains.InvitationPage{}, postgres.HandleError(repoerr.ErrViewEntity, err)
}
defer rows.Close()
for rows.Next() {
var dbinv dbInvitation
if err = rows.StructScan(&dbinv); err != nil {
return domains.InvitationPage{}, postgres.HandleError(repoerr.ErrViewEntity, err)
}
items = append(items, toInvitation(dbinv))
}
}
tq := fmt.Sprintf(`
SELECT
COUNT(*)
FROM
invitations i
LEFT JOIN domains d ON
i.domain_id = d.id
LEFT JOIN domains_roles dr ON
dr.id = i.role_id %s
`, query)
total, err := postgres.Total(ctx, repo.db, tq, pm)
if err != nil {
return domains.InvitationPage{}, postgres.HandleError(repoerr.ErrViewEntity, err)
}
invPage := domains.InvitationPage{
Total: total,
Offset: pm.Offset,
Limit: pm.Limit,
Invitations: items,
}
return invPage, nil
}
func (repo domainRepo) UpdateConfirmation(ctx context.Context, invitation domains.Invitation) (err error) {
q := `UPDATE invitations SET confirmed_at = :confirmed_at, updated_at = :updated_at WHERE invitee_user_id = :invitee_user_id AND domain_id = :domain_id`
dbinv := toDBInvitation(invitation)
result, err := repo.db.NamedExecContext(ctx, q, dbinv)
if err != nil {
return postgres.HandleError(repoerr.ErrUpdateEntity, err)
}
if rows, _ := result.RowsAffected(); rows == 0 {
return repoerr.ErrNotFound
}
return nil
}
func (repo domainRepo) UpdateRejection(ctx context.Context, invitation domains.Invitation) (err error) {
q := `UPDATE invitations SET rejected_at = :rejected_at, updated_at = :updated_at WHERE invitee_user_id = :invitee_user_id AND domain_id = :domain_id`
dbInv := toDBInvitation(invitation)
result, err := repo.db.NamedExecContext(ctx, q, dbInv)
if err != nil {
return postgres.HandleError(repoerr.ErrUpdateEntity, err)
}
if rows, _ := result.RowsAffected(); rows == 0 {
return repoerr.ErrNotFound
}
return nil
}
func (repo domainRepo) DeleteUsersInvitations(ctx context.Context, domain string, inviteeUserIDs ...string) (err error) {
if len(inviteeUserIDs) == 0 {
return repoerr.ErrNotFound
}
q := `DELETE FROM invitations WHERE domain_id = :domain_id AND invitee_user_id = ANY(:invitee_user_ids);`
params := map[string]any{
"invitee_user_ids": inviteeUserIDs,
"domain_id": domain,
}
result, err := repo.db.NamedExecContext(ctx, q, params)
if err != nil {
return postgres.HandleError(repoerr.ErrRemoveEntity, err)
}
if rows, _ := result.RowsAffected(); rows == 0 {
return repoerr.ErrNotFound
}
return nil
}
func pageQuery(pm domains.InvitationPageMeta) string {
var query []string
var emq string
if pm.DomainID != "" {
query = append(query, "i.domain_id = :domain_id")
}
if pm.InviteeUserID != "" {
query = append(query, "i.invitee_user_id = :invitee_user_id")
}
if pm.InvitedBy != "" {
query = append(query, "i.invited_by = :invited_by")
}
if pm.RoleID != "" {
query = append(query, "i.role_id = :role_id")
}
if pm.InvitedByOrUserID != "" {
query = append(query, "(i.invited_by = :invited_by_or_user_id OR i.invitee_user_id = :invited_by_or_user_id)")
}
if pm.State == domains.Accepted {
query = append(query, "i.confirmed_at IS NOT NULL")
}
if pm.State == domains.Pending {
query = append(query, "i.confirmed_at IS NULL AND rejected_at IS NULL")
}
if pm.State == domains.Rejected {
query = append(query, "i.rejected_at IS NOT NULL")
}
if len(query) > 0 {
emq = fmt.Sprintf("WHERE %s", strings.Join(query, " AND "))
}
return emq
}
type dbInvitation struct {
InvitedBy string `db:"invited_by"`
InviteeUserID string `db:"invitee_user_id"`
DomainID string `db:"domain_id"`
DomainName sql.NullString `db:"domain_name,omitempty"`
RoleID string `db:"role_id,omitempty"`
RoleName sql.NullString `db:"role_name,omitempty"`
Relation string `db:"relation"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt sql.NullTime `db:"updated_at,omitempty"`
ConfirmedAt sql.NullTime `db:"confirmed_at,omitempty"`
RejectedAt sql.NullTime `db:"rejected_at,omitempty"`
}
func toDBInvitation(inv domains.Invitation) dbInvitation {
var updatedAt, confirmedAt, rejectedAt sql.NullTime
if inv.UpdatedAt != (time.Time{}) {
updatedAt = sql.NullTime{Time: inv.UpdatedAt, Valid: true}
}
if inv.ConfirmedAt != (time.Time{}) {
confirmedAt = sql.NullTime{Time: inv.ConfirmedAt, Valid: true}
}
if inv.RejectedAt != (time.Time{}) {
rejectedAt = sql.NullTime{Time: inv.RejectedAt, Valid: true}
}
return dbInvitation{
InvitedBy: inv.InvitedBy,
InviteeUserID: inv.InviteeUserID,
DomainID: inv.DomainID,
RoleID: inv.RoleID,
CreatedAt: inv.CreatedAt,
UpdatedAt: updatedAt,
ConfirmedAt: confirmedAt,
RejectedAt: rejectedAt,
}
}
func toInvitation(dbinv dbInvitation) domains.Invitation {
var updatedAt, confirmedAt, rejectedAt time.Time
if dbinv.UpdatedAt.Valid {
updatedAt = dbinv.UpdatedAt.Time
}
if dbinv.ConfirmedAt.Valid {
confirmedAt = dbinv.ConfirmedAt.Time.UTC()
}
if dbinv.RejectedAt.Valid {
rejectedAt = dbinv.RejectedAt.Time.UTC()
}
return domains.Invitation{
InvitedBy: dbinv.InvitedBy,
InviteeUserID: dbinv.InviteeUserID,
DomainID: dbinv.DomainID,
DomainName: toString(dbinv.DomainName),
RoleID: dbinv.RoleID,
RoleName: toString(dbinv.RoleName),
CreatedAt: dbinv.CreatedAt.UTC(),
UpdatedAt: updatedAt,
ConfirmedAt: confirmedAt,
RejectedAt: rejectedAt,
}
}
func toString(s sql.NullString) string {
if s.Valid {
return s.String
}
return ""
}