Repository navigation
Expand file tree
/
Copy pathphone_api_key_service.go
More file actions
198 lines (160 loc) · 7.98 KB
/
Copy pathphone_api_key_service.go
File metadata and controls
198 lines (160 loc) · 7.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package services
import (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
"time"
"github.com/lib/pq"
"github.com/NdoleStudio/httpsms/pkg/entities"
"github.com/NdoleStudio/httpsms/pkg/repositories"
"github.com/NdoleStudio/httpsms/pkg/telemetry"
"github.com/NdoleStudio/stacktrace"
"github.com/google/uuid"
)
// PhoneAPIKeyService is responsible for managing entities.PhoneAPIKey
type PhoneAPIKeyService struct {
service
logger telemetry.Logger
tracer telemetry.Tracer
phoneRepository repositories.PhoneRepository
repository repositories.PhoneAPIKeyRepository
}
// NewPhoneAPIKeyService creates a new PhoneAPIKeyService
func NewPhoneAPIKeyService(
logger telemetry.Logger,
tracer telemetry.Tracer,
phoneRepository repositories.PhoneRepository,
repository repositories.PhoneAPIKeyRepository,
) *PhoneAPIKeyService {
return &PhoneAPIKeyService{
logger: logger.WithService(fmt.Sprintf("%T", &PhoneAPIKeyService{})),
tracer: tracer,
phoneRepository: phoneRepository,
repository: repository,
}
}
// CountByUser returns the number of phone API keys owned by a user.
func (service *PhoneAPIKeyService) CountByUser(ctx context.Context, userID entities.UserID) (int, error) {
ctx, span := service.tracer.Start(ctx)
defer span.End()
return service.repository.CountByUser(ctx, userID)
}
// Index fetches the entities.Webhook for an entities.UserID
func (service *PhoneAPIKeyService) Index(ctx context.Context, userID entities.UserID, params repositories.IndexParams) ([]*entities.PhoneAPIKey, error) {
ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger)
defer span.End()
phoneAPIKeys, err := service.repository.Index(ctx, userID, params)
if err != nil {
return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagatef(err, "could not fetch phone API Keys with params [%+#v]", params))
}
ctxLogger.Info(fmt.Sprintf("fetched [%d] phone API Keys with prams [%+#v]", len(phoneAPIKeys), params))
return phoneAPIKeys, nil
}
// Create a new entities.PhoneAPIKey
func (service *PhoneAPIKeyService) Create(ctx context.Context, authContext entities.AuthContext, name string) (*entities.PhoneAPIKey, error) {
ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger)
defer span.End()
apiKey, err := service.generateAPIKey(64)
if err != nil {
return nil, stacktrace.Propagatef(err, "cannot generate API key")
}
phoneAPIKey := &entities.PhoneAPIKey{
ID: uuid.New(),
Name: name,
UserID: authContext.ID,
UserEmail: authContext.Email,
PhoneNumbers: pq.StringArray{},
PhoneIDs: pq.StringArray{},
APIKey: "pk_" + apiKey,
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}
if err = service.repository.Create(ctx, phoneAPIKey); err != nil {
return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagatef(err, "cannot create PhoneAPIKey for user [%s]", authContext.ID))
}
ctxLogger.Info(fmt.Sprintf("created [%T] with ID [%s] for user ID [%s]", phoneAPIKey, phoneAPIKey.ID, authContext.ID))
return phoneAPIKey, nil
}
// Delete an entities.PhoneAPIKey
func (service *PhoneAPIKeyService) Delete(ctx context.Context, userID entities.UserID, phoneAPIKeyID uuid.UUID) error {
ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger)
defer span.End()
phoneAPIKey, err := service.repository.Load(ctx, userID, phoneAPIKeyID)
if err != nil {
return stacktrace.Propagatef(err, "cannot load [%T] with ID [%s] for user [%s]", &entities.PhoneAPIKey{}, phoneAPIKeyID, userID.String())
}
if err = service.repository.Delete(ctx, phoneAPIKey); err != nil {
return service.tracer.WrapErrorSpan(span, stacktrace.Propagatef(err, "cannot delete [%T] with ID [%s] for user [%s]", phoneAPIKey, phoneAPIKey.ID, phoneAPIKey.UserID))
}
ctxLogger.Info(fmt.Sprintf("deleted [%T] with ID [%s] for user ID [%s]", phoneAPIKey, phoneAPIKey.ID, userID))
return nil
}
// RemovePhone removes the phone from the phone API key
func (service *PhoneAPIKeyService) RemovePhone(ctx context.Context, userID entities.UserID, phoneAPIKeyID uuid.UUID, phoneID uuid.UUID) error {
ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger)
defer span.End()
phone, err := service.phoneRepository.LoadByID(ctx, userID, phoneID)
if err != nil {
return stacktrace.Propagatef(err, "cannot load [%T] with ID [%s] for user [%s]", &entities.Phone{}, phoneID, userID.String())
}
phoneAPIKey, err := service.repository.Load(ctx, userID, phoneAPIKeyID)
if err != nil {
return stacktrace.Propagatef(err, "cannot load [%T] with ID [%s] for user [%s]", &entities.PhoneAPIKey{}, phoneAPIKeyID, userID.String())
}
if err = service.repository.RemovePhone(ctx, phoneAPIKey, phone); err != nil {
return service.tracer.WrapErrorSpan(span, stacktrace.Propagatef(err, "cannot remove [%T] with ID [%s] from phone API key with ID [%s] for user [%s]", phone, phone.ID, phoneAPIKey.ID, userID))
}
ctxLogger.Info(fmt.Sprintf("removed [%T] with ID [%s] from [%T] with ID [%s] for user ID [%s]", phone, phoneID, phoneAPIKey, phoneAPIKeyID, userID))
return nil
}
// RemovePhoneByID removes the phone from the phone API key by phone number and phoneID
func (service *PhoneAPIKeyService) RemovePhoneByID(ctx context.Context, userID entities.UserID, phoneID uuid.UUID, phoneNumber string) error {
ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger)
defer span.End()
if err := service.repository.RemovePhoneByID(ctx, userID, phoneID, phoneNumber); err != nil {
return stacktrace.Propagatef(err, "cannot remove [%T] with ID [%s] and number [%s] for user [%s]", &entities.Phone{}, phoneID, phoneNumber, userID.String())
}
ctxLogger.Info(fmt.Sprintf("removed phone with ID [%s] from [%T] for user ID [%s]", phoneID, &entities.PhoneAPIKey{}, userID))
return nil
}
// DeleteAllForUser removes all entities.PhoneAPIKey for a user
func (service *PhoneAPIKeyService) DeleteAllForUser(ctx context.Context, userID entities.UserID) error {
ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger)
defer span.End()
if err := service.repository.DeleteAllForUser(ctx, userID); err != nil {
return stacktrace.Propagatef(err, "cannot delete all [%T] for user ID [%s]", &entities.PhoneAPIKey{}, userID)
}
ctxLogger.Info(fmt.Sprintf("deleted all [%T] for user ID [%s]", &entities.PhoneAPIKey{}, userID))
return nil
}
// AddPhone adds a phone to the phone API key
func (service *PhoneAPIKeyService) AddPhone(ctx context.Context, userID entities.UserID, phoneAPIKeyID uuid.UUID, phoneID uuid.UUID) error {
ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger)
defer span.End()
phone, err := service.phoneRepository.LoadByID(ctx, userID, phoneID)
if err != nil {
return stacktrace.Propagatef(err, "cannot load [%T] with ID [%s] for user [%s]", &entities.Phone{}, phoneID, userID.String())
}
phoneAPIKey, err := service.repository.Load(ctx, userID, phoneAPIKeyID)
if err != nil {
return stacktrace.Propagatef(err, "cannot load [%T] with ID [%s] for user [%s]", &entities.PhoneAPIKey{}, phoneAPIKeyID, userID.String())
}
if err = service.repository.AddPhone(ctx, phoneAPIKey, phone); err != nil {
return service.tracer.WrapErrorSpan(span, stacktrace.Propagatef(err, "cannot add [%T] with ID [%s] to phone API key with ID [%s] for user [%s]", phone, phone.ID, phoneAPIKey.ID, userID))
}
ctxLogger.Info(fmt.Sprintf("added [%T] with ID [%s] to [%T] with ID [%s] for user ID [%s]", phone, phone.ID, phoneAPIKey, phoneAPIKeyID, userID))
return nil
}
func (service *PhoneAPIKeyService) generateRandomBytes(n int) ([]byte, error) {
b := make([]byte, n)
// Note that err == nil only if we read len(b) bytes.
if _, err := rand.Read(b); err != nil {
return nil, stacktrace.Propagatef(err, "cannot generate [%d] random bytes", n)
}
return b, nil
}
func (service *PhoneAPIKeyService) generateAPIKey(n int) (string, error) {
b, err := service.generateRandomBytes(n)
return base64.URLEncoding.EncodeToString(b)[0:n], stacktrace.Propagatef(err, "cannot generate [%d] random bytes", n)
}