Passed
Push — main ( 570b65...2ff189 )
by Acho
02:48
created

User   A

Complexity

Conditions 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
dl 0
loc 10
rs 10
c 0
b 0
f 0
nop 2
1
package repositories
2
3
import (
4
	"context"
5
	"errors"
6
	"fmt"
7
8
	"github.com/google/uuid"
9
10
	"gorm.io/gorm/clause"
11
12
	"github.com/NdoleStudio/httpsms/pkg/entities"
13
	"github.com/NdoleStudio/httpsms/pkg/telemetry"
14
	"github.com/palantir/stacktrace"
15
	"gorm.io/gorm"
16
)
17
18
// gormMessageThreadRepository is responsible for persisting entities.MessageThread
19
type gormMessageThreadRepository struct {
20
	logger telemetry.Logger
21
	tracer telemetry.Tracer
22
	db     *gorm.DB
23
}
24
25
// NewGormMessageThreadRepository creates the GORM version of the MessageRepository
26
func NewGormMessageThreadRepository(
27
	logger telemetry.Logger,
28
	tracer telemetry.Tracer,
29
	db *gorm.DB,
30
) MessageThreadRepository {
31
	return &gormMessageThreadRepository{
32
		logger: logger.WithService(fmt.Sprintf("%T", &gormMessageThreadRepository{})),
33
		tracer: tracer,
34
		db:     db,
35
	}
36
}
37
38
func (repository *gormMessageThreadRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error {
39
	ctx, span := repository.tracer.Start(ctx)
40
	defer span.End()
41
42
	if err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.MessageThread{}).Error; err != nil {
43
		msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.MessageThread{}, userID)
44
		return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
45
	}
46
47
	return nil
48
}
49
50
// Delete the message thread for a user
51
func (repository *gormMessageThreadRepository) Delete(ctx context.Context, userID entities.UserID, messageThreadID uuid.UUID) error {
52
	ctx, span := repository.tracer.Start(ctx)
53
	defer span.End()
54
55
	err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Where("id = ?", messageThreadID).Delete(&entities.MessageThread{}).Error
56
	if err != nil {
57
		msg := fmt.Sprintf("cannot delete message thread with ID [%s] for user with ID [%s]", messageThreadID, userID)
58
		return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
59
	}
60
61
	return nil
62
}
63
64
// UpdateAfterDeletedMessage updates a thread after the original message has been deleted
65
func (repository *gormMessageThreadRepository) UpdateAfterDeletedMessage(ctx context.Context, userID entities.UserID, messageID uuid.UUID) error {
66
	ctx, span := repository.tracer.Start(ctx)
67
	defer span.End()
68
69
	err := repository.db.WithContext(ctx).Model(&entities.MessageThread{}).
70
		Where("user_id = ?", userID).
71
		Where("last_message_id = ?", messageID).
72
		Updates(map[string]any{
73
			"last_message_id":      nil,
74
			"last_message_content": nil,
75
			"status":               entities.MessageStatusDeleted,
76
		}).Error
77
	if err != nil {
78
		msg := fmt.Sprintf("cannot update thread after message is deleted with userID [%s] and messageID [%s]", userID, messageID)
79
		return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
80
	}
81
82
	return nil
83
}
84
85
// Store a new entities.MessageThread
86
func (repository *gormMessageThreadRepository) Store(ctx context.Context, thread *entities.MessageThread) error {
87
	ctx, span := repository.tracer.Start(ctx)
88
	defer span.End()
89
90
	if err := repository.db.WithContext(ctx).Clauses(clause.OnConflict{DoNothing: true}).Create(thread).Error; err != nil {
91
		msg := fmt.Sprintf("cannot save message thread with ID [%s]", thread.ID)
92
		return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
93
	}
94
95
	return nil
96
}
97
98
// Update a new entities.MessageThread
99
func (repository *gormMessageThreadRepository) Update(ctx context.Context, thread *entities.MessageThread) error {
100
	ctx, span := repository.tracer.Start(ctx)
101
	defer span.End()
102
103
	if err := repository.db.WithContext(ctx).Save(thread).Error; err != nil {
104
		msg := fmt.Sprintf("cannot update message thread thread with ID [%s]", thread.ID)
105
		return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
106
	}
107
108
	return nil
109
}
110
111
// LoadByOwnerContact a thread between 2 users
112
func (repository *gormMessageThreadRepository) LoadByOwnerContact(ctx context.Context, userID entities.UserID, owner string, contact string) (*entities.MessageThread, error) {
113
	ctx, span := repository.tracer.Start(ctx)
114
	defer span.End()
115
116
	thread := new(entities.MessageThread)
117
118
	err := repository.db.
119
		WithContext(ctx).
120
		Where("user_id = ?", userID).
121
		Where("owner = ?", owner).
122
		Where("contact = ?", contact).
123
		First(thread).
124
		Error
125
	if errors.Is(err, gorm.ErrRecordNotFound) {
126
		msg := fmt.Sprintf("thread with owner [%s] and contact [%s] does not exist", owner, contact)
127
		return nil, repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg))
128
	}
129
130
	if err != nil {
131
		msg := fmt.Sprintf("cannot load thread with owner [%s] and contact [%s]", owner, contact)
132
		return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
133
	}
134
135
	return thread, nil
136
}
137
138
// Load an entities.MessageThread by ID
139
func (repository *gormMessageThreadRepository) Load(ctx context.Context, userID entities.UserID, ID uuid.UUID) (*entities.MessageThread, error) {
140
	ctx, span := repository.tracer.Start(ctx)
141
	defer span.End()
142
143
	thread := new(entities.MessageThread)
144
145
	err := repository.db.
146
		WithContext(ctx).
147
		Where("user_id = ?", userID).
148
		Where("id = ?", ID).
149
		First(thread).
150
		Error
151
	if errors.Is(err, gorm.ErrRecordNotFound) {
152
		msg := fmt.Sprintf("thread with id [%s] not found", ID)
153
		return nil, repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg))
154
	}
155
156
	if err != nil {
157
		msg := fmt.Sprintf("thread with id [%s]", ID)
158
		return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
159
	}
160
161
	return thread, nil
162
}
163
164
// Index message threads for an owner
165
func (repository *gormMessageThreadRepository) Index(ctx context.Context, userID entities.UserID, owner string, isArchived bool, params IndexParams) (*[]entities.MessageThread, error) {
166
	ctx, span := repository.tracer.Start(ctx)
167
	defer span.End()
168
169
	query := repository.db.
170
		WithContext(ctx).
171
		Where("user_id = ?", userID).
172
		Where("owner = ?", owner)
173
174
	if isArchived {
175
		query.Where("is_archived = ?", isArchived)
176
	} else {
177
		query.Where(repository.db.Where("is_archived = ?", isArchived).Or("is_archived IS NULL"))
178
	}
179
180
	if len(params.Query) > 0 {
181
		queryPattern := "%" + params.Query + "%"
182
		query.Where(
183
			repository.db.Where("last_message_content ILIKE ?", queryPattern).
184
				Or("owner ILIKE ?", queryPattern).
185
				Or("contact ILIKE ?", queryPattern),
186
		)
187
	}
188
189
	threads := new([]entities.MessageThread)
190
	if err := query.Order("order_timestamp DESC").Limit(params.Limit).Offset(params.Skip).Find(&threads).Error; err != nil {
191
		msg := fmt.Sprintf("cannot fetch message threads with owner [%s] and params [%+#v]", owner, params)
192
		return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
193
	}
194
195
	return threads, nil
196
}
197