forked from NdoleStudio/httpsms
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathphone_api_key_service.go
More file actions
210 lines (172 loc) · 8.29 KB
/
Copy pathphone_api_key_service.go
File metadata and controls
210 lines (172 loc) · 8.29 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
199
200
201
202
203
204
205
206
207
208
209
210
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/google/uuid"
"github.com/palantir/stacktrace"
)
// 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 {
msg := fmt.Sprintf("could not fetch phone API Keys with params [%+#v]", params)
return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
}
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.Propagate(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 {
msg := fmt.Sprintf("cannot create PhoneAPIKey for user [%s]", authContext.ID)
return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
}
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 {
msg := fmt.Sprintf("cannot load [%T] with ID [%s] for user [%s]", &entities.PhoneAPIKey{}, phoneAPIKeyID, userID.String())
return stacktrace.Propagate(err, msg)
}
if err = service.repository.Delete(ctx, phoneAPIKey); err != nil {
msg := fmt.Sprintf("cannot delete [%T] with ID [%s] for user [%s]", phoneAPIKey, phoneAPIKey.ID, phoneAPIKey.UserID)
return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
}
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 {
msg := fmt.Sprintf("cannot load [%T] with ID [%s] for user [%s]", &entities.Phone{}, phoneID, userID.String())
return stacktrace.Propagate(err, msg)
}
phoneAPIKey, err := service.repository.Load(ctx, userID, phoneAPIKeyID)
if err != nil {
msg := fmt.Sprintf("cannot load [%T] with ID [%s] for user [%s]", &entities.PhoneAPIKey{}, phoneAPIKeyID, userID.String())
return stacktrace.Propagate(err, msg)
}
if err = service.repository.RemovePhone(ctx, phoneAPIKey, phone); err != nil {
msg := fmt.Sprintf("cannot remove [%T] with ID [%s] from phone API key with ID [%s] for user [%s]", phone, phone.ID, phoneAPIKey.ID, userID)
return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
}
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 {
msg := fmt.Sprintf("cannot remove [%T] with ID [%s] and number [%s] for user [%s]", &entities.Phone{}, phoneID, phoneNumber, userID.String())
return stacktrace.Propagate(err, msg)
}
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 {
msg := fmt.Sprintf("cannot delete all [%T] for user ID [%s]", &entities.PhoneAPIKey{}, userID)
return stacktrace.Propagate(err, msg)
}
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 {
msg := fmt.Sprintf("cannot load [%T] with ID [%s] for user [%s]", &entities.Phone{}, phoneID, userID.String())
return stacktrace.Propagate(err, msg)
}
phoneAPIKey, err := service.repository.Load(ctx, userID, phoneAPIKeyID)
if err != nil {
msg := fmt.Sprintf("cannot load [%T] with ID [%s] for user [%s]", &entities.PhoneAPIKey{}, phoneAPIKeyID, userID.String())
return stacktrace.Propagate(err, msg)
}
if err = service.repository.AddPhone(ctx, phoneAPIKey, phone); err != nil {
msg := fmt.Sprintf("cannot add [%T] with ID [%s] to phone API key with ID [%s] for user [%s]", phone, phone.ID, phoneAPIKey.ID, userID)
return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))
}
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.Propagate(err, fmt.Sprintf("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.Propagate(err, fmt.Sprintf("cannot generate [%d] random bytes", n))
}