Passed
Push — master ( 0d15c3...61e1d5 )
by Tolga
01:25 queued 14s
created

postgres.*TenantWriter.CreateTenant   A

Complexity

Conditions 3

Size

Total Lines 25
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 19
nop 3
dl 0
loc 25
rs 9.45
c 0
b 0
f 0
1
package postgres
2
3
import (
4
	"context"
5
	"errors"
6
	"fmt"
7
	"log/slog"
8
	"strings"
9
	"time"
10
11
	"github.com/jackc/pgx/v5"
12
13
	"go.opentelemetry.io/otel/codes"
14
15
	"google.golang.org/protobuf/types/known/timestamppb"
16
17
	"github.com/Permify/permify/internal/storage/postgres/utils"
18
	db "github.com/Permify/permify/pkg/database/postgres"
19
	base "github.com/Permify/permify/pkg/pb/base/v1"
20
)
21
22
// TenantWriter - Structure for Tenant Writer
23
type TenantWriter struct {
24
	database *db.Postgres
25
	// options
26
	txOptions pgx.TxOptions
27
}
28
29
// NewTenantWriter - Creates a new TenantWriter
30
func NewTenantWriter(database *db.Postgres) *TenantWriter {
31
	return &TenantWriter{
32
		database:  database,
33
		txOptions: pgx.TxOptions{IsoLevel: pgx.ReadCommitted, AccessMode: pgx.ReadWrite},
34
	}
35
}
36
37
// CreateTenant - Creates a new Tenant
38
func (w *TenantWriter) CreateTenant(ctx context.Context, id, name string) (result *base.Tenant, err error) {
39
	ctx, span := tracer.Start(ctx, "tenant-writer.create-tenant")
40
	defer span.End()
41
42
	slog.DebugContext(ctx, "creating new tenant", slog.Any("id", id), slog.Any("name", name))
43
44
	var createdAt time.Time
45
	err = w.database.WritePool.QueryRow(ctx, utils.InsertTenantTemplate, id, name).Scan(&createdAt)
46
	if err != nil {
47
		if strings.Contains(err.Error(), "duplicate key value") {
48
			span.RecordError(err)
49
			span.SetStatus(codes.Error, err.Error())
50
			slog.ErrorContext(ctx, "error encountered", slog.Any("error", err))
51
			return nil, errors.New(base.ErrorCode_ERROR_CODE_UNIQUE_CONSTRAINT.String())
52
		}
53
		return nil, utils.HandleError(ctx, span, err, base.ErrorCode_ERROR_CODE_EXECUTION)
54
	}
55
56
	slog.DebugContext(ctx, "successfully created Tenant", slog.Any("id", id), slog.Any("name", name), slog.Any("created_at", createdAt))
57
58
	return &base.Tenant{
59
		Id:        id,
60
		Name:      name,
61
		CreatedAt: timestamppb.New(createdAt),
62
	}, nil
63
}
64
65
// DeleteTenant - Deletes a Tenant
66
func (w *TenantWriter) DeleteTenant(ctx context.Context, tenantID string) (result *base.Tenant, err error) {
67
	ctx, span := tracer.Start(ctx, "tenant-writer.delete-tenant")
68
	defer span.End()
69
70
	slog.DebugContext(ctx, "deleting tenant", slog.Any("tenant_id", tenantID))
71
72
	tx, err := w.database.WritePool.Begin(ctx)
73
	if err != nil {
74
		return nil, utils.HandleError(ctx, span, err, base.ErrorCode_ERROR_CODE_EXECUTION)
75
	}
76
	defer tx.Rollback(ctx)
77
78
	// Prepare batch operations for deleting tenant-related records from multiple tables
79
	tables := []string{"bundles", "relation_tuples", "attributes", "schema_definitions", "transactions"}
80
	batch := &pgx.Batch{}
81
	var totalDeleted int64
82
	for _, table := range tables {
83
		query := fmt.Sprintf(utils.DeleteAllByTenantTemplate, table)
0 ignored issues
show
introduced by
can't check non-constant format in call to Sprintf
Loading history...
84
		batch.Queue(query, tenantID)
85
	}
86
	batch.Queue(utils.DeleteTenantTemplate, tenantID)
87
88
	// Execute the batch of delete queries
89
	br := tx.SendBatch(ctx, batch)
90
91
	for i := 0; i < len(tables); i++ {
92
		tag, err := br.Exec()
93
		if err != nil {
94
			err = br.Close()
95
			if err != nil {
96
				return nil, err
97
			}
98
			err = tx.Commit(ctx)
99
			if err != nil {
100
				return nil, utils.HandleError(ctx, span, err, base.ErrorCode_ERROR_CODE_EXECUTION)
101
			}
102
			return nil, utils.HandleError(ctx, span, err, base.ErrorCode_ERROR_CODE_EXECUTION)
103
		} else {
104
			totalDeleted += tag.RowsAffected()
105
		}
106
	}
107
108
	// Retrieve the tenant details after deletion
109
	var name string
110
	var createdAt time.Time
111
	err = br.QueryRow().Scan(&name, &createdAt)
112
113
	if err != nil {
114
		if totalDeleted > 0 {
115
			name = fmt.Sprintf("Affected rows: %d", totalDeleted)
116
		} else {
117
			return nil, utils.HandleError(ctx, span, err, base.ErrorCode_ERROR_CODE_EXECUTION)
118
		}
119
	}
120
121
	err = br.Close()
122
	if err != nil {
123
		return nil, utils.HandleError(ctx, span, err, base.ErrorCode_ERROR_CODE_EXECUTION)
124
	}
125
126
	err = tx.Commit(ctx)
127
	if err != nil {
128
		return nil, utils.HandleError(ctx, span, err, base.ErrorCode_ERROR_CODE_EXECUTION)
129
	}
130
131
	// Return the deleted tenant information
132
	return &base.Tenant{
133
		Id:        tenantID,
134
		Name:      name,
135
		CreatedAt: timestamppb.New(createdAt),
136
	}, nil
137
138
}
139