Passed
Push — main ( 2cb7aa...7c8444 )
by Acho
01:28
created

di.*Container.initializeGoogleTraceProvider   B

Complexity

Conditions 6

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 22
nop 2
dl 0
loc 32
rs 8.4186
c 0
b 0
f 0
1
package di
2
3
import (
4
	"context"
5
	"crypto/tls"
6
	"fmt"
7
	"net/http"
8
	"os"
9
	"strconv"
10
	"time"
11
12
	otelMetric "go.opentelemetry.io/otel/metric"
13
14
	"github.com/dgraph-io/ristretto"
15
16
	"github.com/gofiber/contrib/otelfiber"
17
	"gorm.io/plugin/opentelemetry/tracing"
18
19
	"github.com/NdoleStudio/httpsms/pkg/discord"
20
21
	mexporter "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric"
22
	cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
23
	"github.com/NdoleStudio/httpsms/pkg/cache"
24
	"github.com/NdoleStudio/lemonsqueezy-go"
25
	"github.com/hashicorp/go-retryablehttp"
26
	"github.com/redis/go-redis/extra/redisotel/v9"
27
	"github.com/redis/go-redis/v9"
28
	"go.opentelemetry.io/otel/sdk/metric"
29
30
	"github.com/NdoleStudio/go-otelroundtripper"
31
32
	"github.com/jinzhu/now"
33
34
	"github.com/uptrace/uptrace-go/uptrace"
35
36
	"github.com/NdoleStudio/httpsms/pkg/emails"
37
38
	cloudtasks "cloud.google.com/go/cloudtasks/apiv2"
39
40
	"go.opentelemetry.io/otel"
41
	"go.opentelemetry.io/otel/sdk/resource"
42
	semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
43
44
	"firebase.google.com/go/messaging"
45
	"github.com/hirosassa/zerodriver"
46
	"github.com/rs/zerolog"
47
	"go.opentelemetry.io/otel/sdk/trace"
48
49
	firebase "firebase.google.com/go"
50
	"firebase.google.com/go/auth"
51
	"github.com/NdoleStudio/httpsms/pkg/middlewares"
52
	"google.golang.org/api/option"
53
54
	"github.com/gofiber/fiber/v2/middleware/cors"
55
56
	"github.com/NdoleStudio/httpsms/pkg/entities"
57
	"github.com/NdoleStudio/httpsms/pkg/listeners"
58
	"github.com/NdoleStudio/httpsms/pkg/repositories"
59
	"github.com/NdoleStudio/httpsms/pkg/services"
60
	"github.com/gofiber/fiber/v2"
61
	fiberLogger "github.com/gofiber/fiber/v2/middleware/logger"
62
	"github.com/gofiber/swagger"
63
	"github.com/palantir/stacktrace"
64
	ttlCache "github.com/patrickmn/go-cache"
65
	"gorm.io/gorm"
66
67
	"github.com/NdoleStudio/httpsms/pkg/handlers"
68
	"github.com/NdoleStudio/httpsms/pkg/telemetry"
69
	"github.com/NdoleStudio/httpsms/pkg/validators"
70
	"gorm.io/driver/postgres"
71
	gormLogger "gorm.io/gorm/logger"
72
)
73
74
// Container is used to resolve services at runtime
75
type Container struct {
76
	projectID       string
77
	db              *gorm.DB
78
	dedicatedDB     *gorm.DB
79
	version         string
80
	app             *fiber.App
81
	eventDispatcher *services.EventDispatcher
82
	logger          telemetry.Logger
83
}
84
85
// NewLiteContainer creates a Container without any routes or listeners
86
func NewLiteContainer() (container *Container) {
87
	// Set location to UTC
88
	now.DefaultConfig = &now.Config{
89
		TimeLocation: time.UTC,
90
	}
91
92
	return &Container{
93
		logger: logger(3).WithService(fmt.Sprintf("%T", container)),
94
	}
95
}
96
97
// NewContainer creates a new dependency injection container
98
func NewContainer(projectID string, version string) (container *Container) {
99
	// Set location to UTC
100
	now.DefaultConfig = &now.Config{
101
		TimeLocation: time.UTC,
102
	}
103
104
	container = &Container{
105
		projectID: projectID,
106
		version:   version,
107
		logger:    logger(3).WithService(fmt.Sprintf("%T", container)),
108
	}
109
110
	container.InitializeTraceProvider()
111
112
	container.RegisterMessageListeners()
113
	container.RegisterMessageRoutes()
114
	container.RegisterBulkMessageRoutes()
115
116
	container.RegisterMessageThreadRoutes()
117
	container.RegisterMessageThreadListeners()
118
119
	container.RegisterHeartbeatRoutes()
120
	container.RegisterHeartbeatListeners()
121
122
	container.RegisterUserRoutes()
123
	container.RegisterUserListeners()
124
125
	container.RegisterPhoneRoutes()
126
127
	container.RegisterEventRoutes()
128
129
	container.RegisterNotificationListeners()
130
	container.RegisterEmailNotificationListeners()
131
132
	container.RegisterBillingRoutes()
133
	container.RegisterBillingListeners()
134
135
	container.RegisterWebhookRoutes()
136
	container.RegisterWebhookListeners()
137
138
	container.RegisterLemonsqueezyRoutes()
139
140
	container.RegisterIntegration3CXRoutes()
141
	container.RegisterIntegration3CXListeners()
142
143
	container.RegisterDiscordRoutes()
144
	container.RegisterDiscordListeners()
145
146
	// this has to be last since it registers the /* route
147
	container.RegisterSwaggerRoutes()
148
149
	return container
150
}
151
152
// App creates a new instance of fiber.App
153
func (container *Container) App() (app *fiber.App) {
154
	if container.app != nil {
155
		return container.app
156
	}
157
158
	container.logger.Debug(fmt.Sprintf("creating %T", app))
159
160
	app = fiber.New()
161
162
	if os.Getenv("USE_HTTP_LOGGER") == "true" {
163
		app.Use(fiberLogger.New())
164
	}
165
166
	app.Use(otelfiber.Middleware())
167
	app.Use(cors.New())
168
	app.Use(middlewares.HTTPRequestLogger(container.Tracer(), container.Logger()))
169
170
	app.Use(middlewares.BearerAuth(container.Logger(), container.Tracer(), container.FirebaseAuthClient()))
171
	app.Use(middlewares.APIKeyAuth(container.Logger(), container.Tracer(), container.UserRepository()))
172
173
	container.app = app
174
	return app
175
}
176
177
// BearerAPIKeyMiddleware creates a new instance of middlewares.BearerAPIKeyAuth
178
func (container *Container) BearerAPIKeyMiddleware() fiber.Handler {
179
	container.logger.Debug("creating middlewares.BearerAPIKeyAuth")
180
	return middlewares.BearerAPIKeyAuth(container.Logger(), container.Tracer(), container.UserRepository())
181
}
182
183
// AuthenticatedMiddleware creates a new instance of middlewares.Authenticated
184
func (container *Container) AuthenticatedMiddleware() fiber.Handler {
185
	container.logger.Debug("creating middlewares.Authenticated")
186
	return middlewares.Authenticated(container.Tracer())
187
}
188
189
// AuthRouter creates router for authenticated requests
190
func (container *Container) AuthRouter() fiber.Router {
191
	container.logger.Debug("creating authRouter")
192
	return container.App().Group("v1").Use(container.AuthenticatedMiddleware())
193
}
194
195
// Logger creates a new instance of telemetry.Logger
196
func (container *Container) Logger(skipFrameCount ...int) telemetry.Logger {
197
	container.logger.Debug("creating telemetry.Logger")
198
	if len(skipFrameCount) > 0 {
199
		return logger(skipFrameCount[0])
200
	}
201
	return logger(3)
202
}
203
204
// GormLogger creates a new instance of gormLogger.Interface
205
func (container *Container) GormLogger() gormLogger.Interface {
206
	container.logger.Debug("creating gormLogger.Interface")
207
	return telemetry.NewGormLogger(
208
		container.Tracer(),
209
		container.Logger(6),
210
	)
211
}
212
213
// DedicatedDB creates an instance of gorm.DB if it has not been created already
214
func (container *Container) DedicatedDB() (db *gorm.DB) {
215
	container.logger.Debug(fmt.Sprintf("creating %T", db))
216
	if container.dedicatedDB != nil {
217
		return container.dedicatedDB
218
	}
219
220
	config := &gorm.Config{
221
		TranslateError: true,
222
	}
223
	if isLocal() {
224
		config = &gorm.Config{Logger: container.GormLogger()}
225
	}
226
227
	db, err := gorm.Open(postgres.Open(os.Getenv("DATABASE_URL_DEDICATED")), config)
228
	if err != nil {
229
		container.logger.Fatal(err)
230
	}
231
232
	if err = db.Use(tracing.NewPlugin()); err != nil {
233
		container.logger.Fatal(stacktrace.Propagate(err, "cannot use GORM tracing plugin"))
234
	}
235
236
	container.logger.Debug(fmt.Sprintf("Running migrations for dedicated [%T]", db))
237
	if err = db.AutoMigrate(&entities.Heartbeat{}); err != nil {
238
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.Heartbeat{})))
239
	}
240
241
	if err = db.AutoMigrate(&entities.HeartbeatMonitor{}); err != nil {
242
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.HeartbeatMonitor{})))
243
	}
244
245
	container.dedicatedDB = db
246
	return container.dedicatedDB
247
}
248
249
// DB creates an instance of gorm.DB if it has not been created already
250
func (container *Container) DB() (db *gorm.DB) {
251
	if container.db != nil {
252
		return container.db
253
	}
254
255
	container.logger.Debug(fmt.Sprintf("creating %T", db))
256
257
	config := &gorm.Config{TranslateError: true}
258
	if isLocal() {
259
		config.Logger = container.GormLogger()
260
	}
261
262
	db, err := gorm.Open(postgres.Open(os.Getenv("DATABASE_URL")), config)
263
	if err != nil {
264
		container.logger.Fatal(err)
265
	}
266
	container.db = db
267
268
	if err = db.Use(tracing.NewPlugin()); err != nil {
269
		container.logger.Fatal(stacktrace.Propagate(err, "cannot use GORM tracing plugin"))
270
	}
271
272
	container.logger.Debug(fmt.Sprintf("Running migrations for %T", db))
273
274
	// This prevents a bug in the Gorm AutoMigrate where it tries to delete this no existent constraints
275
	db.Exec(`
276
ALTER TABLE users ADD CONSTRAINT IF NOT EXISTS uni_users_api_key CHECK (api_key IS NOT NULL);
277
ALTER TABLE discords ADD CONSTRAINT IF NOT EXISTS uni_discords_server_id CHECK (server_id IS NOT NULL);`)
278
279
	if err = db.AutoMigrate(&entities.Message{}); err != nil {
280
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.Message{})))
281
	}
282
283
	if err = db.AutoMigrate(&entities.MessageThread{}); err != nil {
284
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.MessageThread{})))
285
	}
286
287
	if err = db.AutoMigrate(&entities.User{}); err != nil {
288
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.User{})))
289
	}
290
291
	if err = db.AutoMigrate(&entities.Phone{}); err != nil {
292
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.Phone{})))
293
	}
294
295
	if err = db.AutoMigrate(&entities.PhoneNotification{}); err != nil {
296
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.PhoneNotification{})))
297
	}
298
299
	if err = db.AutoMigrate(&entities.BillingUsage{}); err != nil {
300
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.BillingUsage{})))
301
	}
302
303
	if err = db.AutoMigrate(&entities.Webhook{}); err != nil {
304
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.Webhook{})))
305
	}
306
307
	if err = db.AutoMigrate(&entities.Discord{}); err != nil {
308
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.Discord{})))
309
	}
310
311
	if err = db.AutoMigrate(&entities.Integration3CX{}); err != nil {
312
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.Integration3CX{})))
313
	}
314
315
	return container.db
316
}
317
318
// FirebaseApp creates a new instance of firebase.App
319
func (container *Container) FirebaseApp() (app *firebase.App) {
320
	container.logger.Debug(fmt.Sprintf("creating %T", app))
321
	app, err := firebase.NewApp(context.Background(), nil, option.WithCredentialsJSON(container.FirebaseCredentials()))
322
	if err != nil {
323
		msg := "cannot initialize firebase application"
324
		container.logger.Fatal(stacktrace.Propagate(err, msg))
325
	}
326
	return app
327
}
328
329
// InMemoryCache creates a new instance of the in memory cache.Cache
330
func (container *Container) InMemoryCache() cache.Cache {
331
	container.logger.Debug("creating an in memory cache")
332
	c := ttlCache.New(time.Hour, time.Hour*2)
333
	return cache.NewMemoryCache(container.Tracer(), c)
334
}
335
336
// Cache creates a new instance of cache.Cache
337
func (container *Container) Cache() cache.Cache {
338
	container.logger.Debug("creating cache.Cache")
339
	opt, err := redis.ParseURL(os.Getenv("REDIS_URL"))
340
	if err != nil {
341
		container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot parse redis url [%s]", os.Getenv("REDIS_URL"))))
342
	}
343
	opt.TLSConfig = &tls.Config{
344
		MinVersion: tls.VersionTLS12,
345
	}
346
347
	redisClient := redis.NewClient(opt)
348
349
	// Enable tracing instrumentation.
350
	if err = redisotel.InstrumentTracing(redisClient); err != nil {
351
		container.logger.Error(stacktrace.Propagate(err, "cannot instrument redis tracing"))
352
	}
353
354
	// Enable metrics instrumentation.
355
	if err = redisotel.InstrumentMetrics(redisClient); err != nil {
356
		container.logger.Fatal(stacktrace.Propagate(err, "cannot instrument redis metrics"))
357
	}
358
359
	return cache.NewRedisCache(container.Tracer(), redisClient)
360
}
361
362
// FirebaseAuthClient creates a new instance of auth.Client
363
func (container *Container) FirebaseAuthClient() (client *auth.Client) {
364
	container.logger.Debug(fmt.Sprintf("creating %T", client))
365
	authClient, err := container.FirebaseApp().Auth(context.Background())
366
	if err != nil {
367
		msg := "cannot initialize firebase auth client"
368
		container.logger.Fatal(stacktrace.Propagate(err, msg))
369
	}
370
	return authClient
371
}
372
373
// CloudTasksClient creates a new instance of cloudtasks.Client
374
func (container *Container) CloudTasksClient() (client *cloudtasks.Client) {
375
	container.logger.Debug(fmt.Sprintf("creating %T", client))
376
377
	client, err := cloudtasks.NewClient(context.Background(), option.WithCredentialsJSON(container.FirebaseCredentials()))
378
	if err != nil {
379
		container.logger.Fatal(stacktrace.Propagate(err, "cannot initialize cloud tasks client"))
380
	}
381
382
	return client
383
}
384
385
// EventsQueueConfiguration creates a new instance of services.PushQueueConfig
386
func (container *Container) EventsQueueConfiguration() (config services.PushQueueConfig) {
387
	container.logger.Debug(fmt.Sprintf("creating %T", config))
388
389
	return services.PushQueueConfig{
390
		UserAPIKey:       os.Getenv("EVENTS_QUEUE_USER_API_KEY"),
391
		Name:             os.Getenv("EVENTS_QUEUE_NAME"),
392
		UserID:           entities.UserID(os.Getenv("EVENTS_QUEUE_USER_ID")),
393
		ConsumerEndpoint: os.Getenv("EVENTS_QUEUE_ENDPOINT"),
394
	}
395
}
396
397
// EventsQueue creates a new instance of services.PushQueue
398
func (container *Container) EventsQueue() (queue services.PushQueue) {
399
	container.logger.Debug("creating events services.PushQueue")
400
401
	if os.Getenv("EVENTS_QUEUE_TYPE") == "emulator" {
402
		return container.EmulatorEventsQueue()
403
	}
404
405
	return container.CloudTaskEventsQueue()
406
}
407
408
// EmulatorEventsQueue creates an in process instance of events services.PushQueue
409
func (container *Container) EmulatorEventsQueue() (queue services.PushQueue) {
410
	container.logger.Debug("creating emulator events services.PushQueue")
411
	return services.EmulatorPushQueue(
412
		container.Logger(),
413
		container.Tracer(),
414
		container.HTTPClient("emulator_events_queue"),
415
		container.EventsQueueConfiguration(),
416
	)
417
}
418
419
// CloudTaskEventsQueue creates a Google cloud task instance of events services.PushQueue
420
func (container *Container) CloudTaskEventsQueue() (queue services.PushQueue) {
421
	container.logger.Debug("creating cloud task events services.PushQueue")
422
	return services.NewGooglePushQueue(
423
		container.Logger(),
424
		container.Tracer(),
425
		container.CloudTasksClient(),
426
		container.EventsQueueConfiguration(),
427
	)
428
}
429
430
// FirebaseMessagingClient creates a new instance of messaging.Client
431
func (container *Container) FirebaseMessagingClient() (client *messaging.Client) {
432
	container.logger.Debug(fmt.Sprintf("creating %T", client))
433
	messagingClient, err := container.FirebaseApp().Messaging(context.Background())
434
	if err != nil {
435
		msg := "cannot initialize firebase messaging client"
436
		container.logger.Fatal(stacktrace.Propagate(err, msg))
437
	}
438
	return messagingClient
439
}
440
441
// FirebaseCredentials returns firebase credentials as bytes.
442
func (container *Container) FirebaseCredentials() []byte {
443
	container.logger.Debug("creating firebase credentials")
444
	return []byte(os.Getenv("FIREBASE_CREDENTIALS"))
445
}
446
447
// Tracer creates a new instance of telemetry.Tracer
448
func (container *Container) Tracer() (t telemetry.Tracer) {
449
	container.logger.Debug("creating telemetry.Tracer")
450
	return telemetry.NewOtelLogger(
451
		container.projectID,
452
		container.Logger(),
453
	)
454
}
455
456
// MessageHandlerValidator creates a new instance of validators.MessageHandlerValidator
457
func (container *Container) MessageHandlerValidator() (validator *validators.MessageHandlerValidator) {
458
	container.logger.Debug(fmt.Sprintf("creating %T", validator))
459
	return validators.NewMessageHandlerValidator(
460
		container.Logger(),
461
		container.Tracer(),
462
		container.PhoneService(),
463
	)
464
}
465
466
// BulkMessageHandlerValidator creates a new instance of validators.BulkMessageHandlerValidator
467
func (container *Container) BulkMessageHandlerValidator() (validator *validators.BulkMessageHandlerValidator) {
468
	container.logger.Debug(fmt.Sprintf("creating %T", validator))
469
	return validators.NewBulkMessageHandlerValidator(
470
		container.Logger(),
471
		container.Tracer(),
472
		container.PhoneService(),
473
		container.UserService(),
474
	)
475
}
476
477
// HeartbeatHandler creates a new instance of handlers.HeartbeatHandler
478
func (container *Container) HeartbeatHandler() (h *handlers.HeartbeatHandler) {
479
	container.logger.Debug(fmt.Sprintf("creating %T", h))
480
	return handlers.NewHeartbeatHandler(
481
		container.Logger(),
482
		container.Tracer(),
483
		container.HeartbeatHandlerValidator(),
484
		container.HeartbeatService(),
485
	)
486
}
487
488
// BillingHandler creates a new instance of handlers.BillingHandler
489
func (container *Container) BillingHandler() (h *handlers.BillingHandler) {
490
	container.logger.Debug(fmt.Sprintf("creating %T", h))
491
	return handlers.NewBillingHandler(
492
		container.Logger(),
493
		container.Tracer(),
494
		container.BillingHandlerValidator(),
495
		container.BillingService(),
496
	)
497
}
498
499
// WebhookHandler creates a new instance of handlers.WebhookHandler
500
func (container *Container) WebhookHandler() (h *handlers.WebhookHandler) {
501
	container.logger.Debug(fmt.Sprintf("creating %T", h))
502
	return handlers.NewWebhookHandler(
503
		container.Logger(),
504
		container.Tracer(),
505
		container.WebhookService(),
506
		container.WebhookHandlerValidator(),
507
	)
508
}
509
510
// HeartbeatHandlerValidator creates a new instance of validators.HeartbeatHandlerValidator
511
func (container *Container) HeartbeatHandlerValidator() (validator *validators.HeartbeatHandlerValidator) {
512
	container.logger.Debug(fmt.Sprintf("creating %T", validator))
513
	return validators.NewHeartbeatHandlerValidator(
514
		container.Logger(),
515
		container.Tracer(),
516
	)
517
}
518
519
// BillingHandlerValidator creates a new instance of validators.BillingHandlerValidator
520
func (container *Container) BillingHandlerValidator() (validator *validators.BillingHandlerValidator) {
521
	container.logger.Debug(fmt.Sprintf("creating %T", validator))
522
	return validators.NewBillingHandlerValidator(
523
		container.Logger(),
524
		container.Tracer(),
525
	)
526
}
527
528
// DiscordHandlerValidator creates a new instance of validators.DiscordHandlerValidator
529
func (container *Container) DiscordHandlerValidator() (validator *validators.DiscordHandlerValidator) {
530
	container.logger.Debug(fmt.Sprintf("creating %T", validator))
531
	return validators.NewDiscordHandlerValidator(
532
		container.Logger(),
533
		container.Tracer(),
534
		container.DiscordClient(),
535
	)
536
}
537
538
// WebhookHandlerValidator creates a new instance of validators.WebhookHandlerValidator
539
func (container *Container) WebhookHandlerValidator() (validator *validators.WebhookHandlerValidator) {
540
	container.logger.Debug(fmt.Sprintf("creating %T", validator))
541
	return validators.NewWebhookHandlerValidator(
542
		container.Logger(),
543
		container.Tracer(),
544
		container.PhoneService(),
545
	)
546
}
547
548
// MessageThreadHandler creates a new instance of handlers.MessageThreadHandler
549
func (container *Container) MessageThreadHandler() (h *handlers.MessageThreadHandler) {
550
	container.logger.Debug(fmt.Sprintf("creating %T", h))
551
	return handlers.NewMessageThreadHandler(
552
		container.Logger(),
553
		container.Tracer(),
554
		container.MessageThreadHandlerValidator(),
555
		container.MessageThreadService(),
556
	)
557
}
558
559
// MessageThreadHandlerValidator creates a new instance of validators.MessageThreadHandlerValidator
560
func (container *Container) MessageThreadHandlerValidator() (validator *validators.MessageThreadHandlerValidator) {
561
	container.logger.Debug(fmt.Sprintf("creating %T", validator))
562
	return validators.NewMessageThreadHandlerValidator(
563
		container.Logger(),
564
		container.Tracer(),
565
	)
566
}
567
568
// PhoneHandlerValidator creates a new instance of validators.PhoneHandlerValidator
569
func (container *Container) PhoneHandlerValidator() (validator *validators.PhoneHandlerValidator) {
570
	container.logger.Debug(fmt.Sprintf("creating %T", validator))
571
	return validators.NewPhoneHandlerValidator(
572
		container.Logger(),
573
		container.Tracer(),
574
	)
575
}
576
577
// UserHandlerValidator creates a new instance of validators.UserHandlerValidator
578
func (container *Container) UserHandlerValidator() (validator *validators.UserHandlerValidator) {
579
	container.logger.Debug(fmt.Sprintf("creating %T", validator))
580
	return validators.NewUserHandlerValidator(
581
		container.Logger(),
582
		container.Tracer(),
583
	)
584
}
585
586
// EventDispatcher creates a new instance of services.EventDispatcher
587
func (container *Container) EventDispatcher() (dispatcher *services.EventDispatcher) {
588
	if container.eventDispatcher != nil {
589
		return container.eventDispatcher
590
	}
591
592
	container.logger.Debug(fmt.Sprintf("creating %T", dispatcher))
593
	dispatcher = services.NewEventDispatcher(
594
		container.Logger(),
595
		container.Tracer(),
596
		container.Float64Histogram("event.publisher.duration", "ms", "measures the duration of processing CloudEvents"),
597
		container.EventsQueue(),
598
		container.EventsQueueConfiguration(),
599
	)
600
601
	container.eventDispatcher = dispatcher
602
	return dispatcher
603
}
604
605
// Float64Histogram creates a new instance of metric.Float64Histogram
606
func (container *Container) Float64Histogram(name, unit, description string) otelMetric.Float64Histogram {
607
	container.logger.Debug("creating GORM repositories.MessageRepository")
608
	meter := otel.GetMeterProvider().Meter(
609
		container.projectID,
610
		otelMetric.WithInstrumentationVersion(otel.Version()),
611
	)
612
	histogram, err := meter.Float64Histogram(name, otelMetric.WithUnit(unit), otelMetric.WithDescription(description))
613
	if err != nil {
614
		container.logger.Fatal(stacktrace.Propagate(err, "cannot create float64 histogram"))
615
	}
616
	return histogram
617
}
618
619
// MessageRepository creates a new instance of repositories.MessageRepository
620
func (container *Container) MessageRepository() (repository repositories.MessageRepository) {
621
	container.logger.Debug("creating GORM repositories.MessageRepository")
622
	return repositories.NewGormMessageRepository(
623
		container.Logger(),
624
		container.Tracer(),
625
		container.DB(),
626
	)
627
}
628
629
// Integration3CXRepository creates a new instance of repositories.Integration3CxRepository
630
func (container *Container) Integration3CXRepository() (repository repositories.Integration3CxRepository) {
631
	container.logger.Debug("creating GORM repositories.Integration3CxRepository")
632
	return repositories.NewGormIntegration3CXRepository(
633
		container.Logger(),
634
		container.Tracer(),
635
		container.DB(),
636
	)
637
}
638
639
// PhoneRepository creates a new instance of repositories.PhoneRepository
640
func (container *Container) PhoneRepository() (repository repositories.PhoneRepository) {
641
	container.logger.Debug("creating GORM repositories.PhoneRepository")
642
	return repositories.NewGormPhoneRepository(
643
		container.Logger(),
644
		container.Tracer(),
645
		container.DB(),
646
	)
647
}
648
649
// BillingUsageRepository creates a new instance of repositories.BillingUsageRepository
650
func (container *Container) BillingUsageRepository() (repository repositories.BillingUsageRepository) {
651
	container.logger.Debug("creating GORM repositories.BillingUsageRepository")
652
	return repositories.NewGormBillingUsageRepository(
653
		container.Logger(),
654
		container.Tracer(),
655
		container.DB(),
656
	)
657
}
658
659
// DiscordRepository creates a new instance of repositories.DiscordRepository
660
func (container *Container) DiscordRepository() (repository repositories.DiscordRepository) {
661
	container.logger.Debug("creating GORM repositories.DiscordRepository")
662
	return repositories.NewGormDiscordRepository(
663
		container.Logger(),
664
		container.Tracer(),
665
		container.DB(),
666
	)
667
}
668
669
// WebhookRepository creates a new instance of repositories.WebhookRepository
670
func (container *Container) WebhookRepository() (repository repositories.WebhookRepository) {
671
	container.logger.Debug("creating GORM repositories.WebhookRepository")
672
	return repositories.NewGormWebhookRepository(
673
		container.Logger(),
674
		container.Tracer(),
675
		container.DB(),
676
	)
677
}
678
679
// PhoneNotificationRepository creates a new instance of repositories.PhoneNotificationRepository
680
func (container *Container) PhoneNotificationRepository() (repository repositories.PhoneNotificationRepository) {
681
	container.logger.Debug("creating GORM repositories.PhoneNotificationRepository")
682
	return repositories.NewGormPhoneNotificationRepository(
683
		container.Logger(),
684
		container.Tracer(),
685
		container.DB(),
686
	)
687
}
688
689
// MessageThreadRepository creates a new instance of repositories.MessageThreadRepository
690
func (container *Container) MessageThreadRepository() (repository repositories.MessageThreadRepository) {
691
	container.logger.Debug("creating GORM repositories.MessageThreadRepository")
692
	return repositories.NewGormMessageThreadRepository(
693
		container.Logger(),
694
		container.Tracer(),
695
		container.DB(),
696
	)
697
}
698
699
// EventRepository creates a new instance of repositories.EventRepository
700
func (container *Container) EventRepository() (repository repositories.EventRepository) {
701
	container.logger.Debug("creating GORM repositories.EventRepository")
702
	return repositories.NewGormEventRepository(
703
		container.Logger(),
704
		container.Tracer(),
705
		container.DB(),
706
	)
707
}
708
709
// HeartbeatMonitorRepository creates a new instance of repositories.HeartbeatMonitorRepository
710
func (container *Container) HeartbeatMonitorRepository() (repository repositories.HeartbeatMonitorRepository) {
711
	container.logger.Debug("creating GORM repositories.HeartbeatMonitorRepository")
712
	return repositories.NewGormHeartbeatMonitorRepository(
713
		container.Logger(),
714
		container.Tracer(),
715
		container.DedicatedDB(),
716
	)
717
}
718
719
// EventListenerLogRepository creates a new instance of repositories.EventListenerLogRepository
720
func (container *Container) EventListenerLogRepository() (repository repositories.EventListenerLogRepository) {
721
	container.logger.Debug("creating GORM repositories.EventListenerLogRepository")
722
	return repositories.NewGormEventListenerLogRepository(
723
		container.Logger(),
724
		container.Tracer(),
725
		container.DB(),
726
	)
727
}
728
729
// HeartbeatService creates a new instance of services.HeartbeatService
730
func (container *Container) HeartbeatService() (service *services.HeartbeatService) {
731
	container.logger.Debug(fmt.Sprintf("creating %T", service))
732
	return services.NewHeartbeatService(
733
		container.Logger(),
734
		container.Tracer(),
735
		container.HeartbeatRepository(),
736
		container.HeartbeatMonitorRepository(),
737
		container.EventDispatcher(),
738
	)
739
}
740
741
// BillingService creates a new instance of services.BillingService
742
func (container *Container) BillingService() (service *services.BillingService) {
743
	container.logger.Debug(fmt.Sprintf("creating %T", service))
744
	return services.NewBillingService(
745
		container.Logger(),
746
		container.Tracer(),
747
		container.InMemoryCache(),
748
		container.Mailer(),
749
		container.UserEmailFactory(),
750
		container.BillingUsageRepository(),
751
		container.UserRepository(),
752
	)
753
}
754
755
// DiscordService creates a new instance of services.DiscordService
756
func (container *Container) DiscordService() (service *services.DiscordService) {
757
	container.logger.Debug(fmt.Sprintf("creating %T", service))
758
	return services.NewDiscordService(
759
		container.Logger(),
760
		container.Tracer(),
761
		container.DiscordClient(),
762
		container.DiscordRepository(),
763
		container.EventDispatcher(),
764
	)
765
}
766
767
// WebhookService creates a new instance of services.WebhookService
768
func (container *Container) WebhookService() (service *services.WebhookService) {
769
	container.logger.Debug(fmt.Sprintf("creating %T", service))
770
	return services.NewWebhookService(
771
		container.Logger(),
772
		container.Tracer(),
773
		container.HTTPClient("webhook"),
774
		container.WebhookRepository(),
775
		container.EventDispatcher(),
776
	)
777
}
778
779
// Integration3CXService creates a new instance of services.Integration3CXService
780
func (container *Container) Integration3CXService() (service *services.Integration3CXService) {
781
	container.logger.Debug(fmt.Sprintf("creating %T", service))
782
	return services.NewIntegration3CXService(
783
		container.Logger(),
784
		container.Tracer(),
785
		container.HTTPClient("integration_3cx"),
786
		container.Integration3CXRepository(),
787
	)
788
}
789
790
// HTTPClient creates a new http.Client
791
func (container *Container) HTTPClient(name string) *http.Client {
792
	container.logger.Debug(fmt.Sprintf("creating %s %T", name, http.DefaultClient))
793
	return &http.Client{
794
		Timeout:   60 * time.Second,
795
		Transport: container.HTTPRoundTripper(name),
796
	}
797
}
798
799
// HTTPRoundTripper creates an open telemetry http.RoundTripper
800
func (container *Container) HTTPRoundTripper(name string) http.RoundTripper {
801
	container.logger.Debug(fmt.Sprintf("Debug: initializing %s %T", name, http.DefaultTransport))
802
	return otelroundtripper.New(
803
		otelroundtripper.WithName(name),
804
		otelroundtripper.WithParent(container.RetryHTTPRoundTripper()),
805
		otelroundtripper.WithMeter(otel.GetMeterProvider().Meter(container.projectID)),
806
		otelroundtripper.WithAttributes(container.OtelResources(container.version, container.projectID).Attributes()...),
807
	)
808
}
809
810
// OtelResources generates default open telemetry resources
811
func (container *Container) OtelResources(version string, namespace string) *resource.Resource {
812
	return resource.NewWithAttributes(
813
		semconv.SchemaURL,
814
		semconv.ServiceNameKey.String(namespace),
815
		semconv.ServiceVersionKey.String(version),
816
		semconv.ServiceInstanceIDKey.String(hostName()),
817
		semconv.DeploymentEnvironmentKey.String(os.Getenv("ENV")),
818
	)
819
}
820
821
// RetryHTTPRoundTripper creates a retryable http.RoundTripper
822
func (container *Container) RetryHTTPRoundTripper() http.RoundTripper {
823
	container.logger.Debug(fmt.Sprintf("initializing retry %T", http.DefaultTransport))
824
	retryClient := retryablehttp.NewClient()
825
	retryClient.Logger = container.Logger()
826
	return retryClient.StandardClient().Transport
827
}
828
829
// PhoneService creates a new instance of services.PhoneService
830
func (container *Container) PhoneService() (service *services.PhoneService) {
831
	container.logger.Debug(fmt.Sprintf("creating %T", service))
832
	return services.NewPhoneService(
833
		container.Logger(),
834
		container.Tracer(),
835
		container.PhoneRepository(),
836
		container.EventDispatcher(),
837
	)
838
}
839
840
// MarketingService creates a new instance of services.MarketingService
841
func (container *Container) MarketingService() (service *services.MarketingService) {
842
	container.logger.Debug(fmt.Sprintf("creating %T", service))
843
	return services.NewMarketingService(
844
		container.Logger(),
845
		container.Tracer(),
846
		container.FirebaseAuthClient(),
847
		os.Getenv("SENDGRID_API_KEY"),
848
		os.Getenv("SENDGRID_LIST_ID"),
849
	)
850
}
851
852
// UserService creates a new instance of services.UserService
853
func (container *Container) UserService() (service *services.UserService) {
854
	container.logger.Debug(fmt.Sprintf("creating %T", service))
855
	return services.NewUserService(
856
		container.Logger(),
857
		container.Tracer(),
858
		container.UserRepository(),
859
		container.Mailer(),
860
		container.UserEmailFactory(),
861
		container.MarketingService(),
862
		container.LemonsqueezyClient(),
863
		container.EventDispatcher(),
864
	)
865
}
866
867
// Mailer creates a new instance of emails.Mailer
868
func (container *Container) Mailer() (mailer emails.Mailer) {
869
	container.logger.Debug("creating emails.Mailer")
870
	return emails.NewSMTPEmailService(
871
		container.Tracer(),
872
		emails.SMTPConfig{
873
			FromName:  os.Getenv("SMTP_FROM_NAME"),
874
			FromEmail: os.Getenv("SMTP_FROM_EMAIL"),
875
			Username:  os.Getenv("SMTP_USERNAME"),
876
			Password:  os.Getenv("SMTP_PASSWORD"),
877
			Hostname:  os.Getenv("SMTP_HOST"),
878
			Port:      os.Getenv("SMTP_PORT"),
879
		},
880
	)
881
}
882
883
// UserEmailFactory creates a new instance of emails.UserEmailFactory
884
func (container *Container) UserEmailFactory() (factory emails.UserEmailFactory) {
885
	container.logger.Debug("creating emails.UserEmailFactory")
886
	return emails.NewHermesUserEmailFactory(&emails.HermesGeneratorConfig{
887
		AppURL:     os.Getenv("APP_URL"),
888
		AppName:    os.Getenv("APP_NAME"),
889
		AppLogoURL: os.Getenv("APP_LOGO_URL"),
890
	})
891
}
892
893
// NotificationEmailFactory creates a new instance of emails.NotificationEmailFactory
894
func (container *Container) NotificationEmailFactory() (factory emails.NotificationEmailFactory) {
895
	container.logger.Debug("creating emails.UserEmailFactory")
896
	return emails.NewHermesNotificationEmailFactory(&emails.HermesGeneratorConfig{
897
		AppURL:     os.Getenv("APP_URL"),
898
		AppName:    os.Getenv("APP_NAME"),
899
		AppLogoURL: os.Getenv("APP_LOGO_URL"),
900
	})
901
}
902
903
// MessageThreadService creates a new instance of services.MessageService
904
func (container *Container) MessageThreadService() (service *services.MessageThreadService) {
905
	container.logger.Debug(fmt.Sprintf("creating %T", service))
906
	return services.NewMessageThreadService(
907
		container.Logger(),
908
		container.Tracer(),
909
		container.MessageThreadRepository(),
910
		container.EventDispatcher(),
911
	)
912
}
913
914
// EmailNotificationService creates a new instance of services.EmailNotificationService
915
func (container *Container) EmailNotificationService() (service *services.EmailNotificationService) {
916
	container.logger.Debug(fmt.Sprintf("creating %T", service))
917
	return services.NewEmailNotificationService(
918
		container.Logger(),
919
		container.Tracer(),
920
		container.UserRepository(),
921
		container.NotificationEmailFactory(),
922
		container.Mailer(),
923
		container.Cache(),
924
	)
925
}
926
927
// MessageHandler creates a new instance of handlers.MessageHandler
928
func (container *Container) MessageHandler() (handler *handlers.MessageHandler) {
929
	container.logger.Debug(fmt.Sprintf("creating %T", handler))
930
	return handlers.NewMessageHandler(
931
		container.Logger(),
932
		container.Tracer(),
933
		container.MessageHandlerValidator(),
934
		container.BillingService(),
935
		container.MessageService(),
936
	)
937
}
938
939
// BulkMessageHandler creates a new instance of handlers.BulkMessageHandler
940
func (container *Container) BulkMessageHandler() (handler *handlers.BulkMessageHandler) {
941
	container.logger.Debug(fmt.Sprintf("creating %T", handler))
942
	return handlers.NewBulkMessageHandler(
943
		container.Logger(),
944
		container.Tracer(),
945
		container.BulkMessageHandlerValidator(),
946
		container.BillingService(),
947
		container.MessageService(),
948
	)
949
}
950
951
// UserHandler creates a new instance of handlers.MessageHandler
952
func (container *Container) UserHandler() (handler *handlers.UserHandler) {
953
	container.logger.Debug(fmt.Sprintf("creating %T", handler))
954
	return handlers.NewUserHandler(
955
		container.Logger(),
956
		container.Tracer(),
957
		container.UserHandlerValidator(),
958
		container.UserService(),
959
	)
960
}
961
962
// PhoneHandler creates a new instance of handlers.PhoneHandler
963
func (container *Container) PhoneHandler() (handler *handlers.PhoneHandler) {
964
	container.logger.Debug(fmt.Sprintf("creating %T", handler))
965
	return handlers.NewPhoneHandler(
966
		container.Logger(),
967
		container.Tracer(),
968
		container.PhoneService(),
969
		container.PhoneHandlerValidator(),
970
	)
971
}
972
973
// EventsHandler creates a new instance of handlers.EventsHandler
974
func (container *Container) EventsHandler() (handler *handlers.EventsHandler) {
975
	container.logger.Debug(fmt.Sprintf("creating %T", handler))
976
977
	return handlers.NewEventsHandler(
978
		container.Logger(),
979
		container.Tracer(),
980
		container.EventsQueueConfiguration(),
981
		container.EventDispatcher(),
982
	)
983
}
984
985
// RegisterMessageListeners registers event listeners for listeners.MessageListener
986
func (container *Container) RegisterMessageListeners() {
987
	container.logger.Debug(fmt.Sprintf("registering listners for %T", listeners.MessageListener{}))
988
	_, routes := listeners.NewMessageListener(
989
		container.Logger(),
990
		container.Tracer(),
991
		container.MessageService(),
992
	)
993
994
	for event, handler := range routes {
995
		container.EventDispatcher().Subscribe(event, handler)
996
	}
997
}
998
999
// LemonsqueezyService creates a new instance of services.LemonsqueezyService
1000
func (container *Container) LemonsqueezyService() (service *services.LemonsqueezyService) {
1001
	container.logger.Debug(fmt.Sprintf("creating %T", service))
1002
	return services.NewLemonsqueezyService(
1003
		container.Logger(),
1004
		container.Tracer(),
1005
		container.UserRepository(),
1006
		container.EventDispatcher(),
1007
	)
1008
}
1009
1010
// LemonsqueezyHandler creates a new instance of handlers.LemonsqueezyHandler
1011
func (container *Container) LemonsqueezyHandler() (handler *handlers.LemonsqueezyHandler) {
1012
	container.logger.Debug(fmt.Sprintf("creating %T", handler))
1013
1014
	return handlers.NewLemonsqueezyHandler(
1015
		container.Logger(),
1016
		container.Tracer(),
1017
		container.LemonsqueezyService(),
1018
		container.LemonsqueezyHandlerValidator(),
1019
	)
1020
}
1021
1022
// Integration3CXHandler creates a new instance of handlers.Integration3CXHandler
1023
func (container *Container) Integration3CXHandler() (handler *handlers.Integration3CXHandler) {
1024
	container.logger.Debug(fmt.Sprintf("creating %T", handler))
1025
1026
	return handlers.NewIntegration3CxHandler(
1027
		container.Logger(),
1028
		container.Tracer(),
1029
		container.MessageService(),
1030
		container.BillingService(),
1031
	)
1032
}
1033
1034
// DiscordHandler creates a new instance of handlers.DiscordHandler
1035
func (container *Container) DiscordHandler() (handler *handlers.DiscordHandler) {
1036
	container.logger.Debug(fmt.Sprintf("creating %T", handler))
1037
1038
	return handlers.NewDiscordHandler(
1039
		container.Logger(),
1040
		container.Tracer(),
1041
		container.DiscordHandlerValidator(),
1042
		container.DiscordService(),
1043
		container.MessageService(),
1044
		container.BillingService(),
1045
		container.MessageHandlerValidator(),
1046
	)
1047
}
1048
1049
// LemonsqueezyHandlerValidator creates a new instance of validators.LemonsqueezyHandlerValidator
1050
func (container *Container) LemonsqueezyHandlerValidator() (validator *validators.LemonsqueezyHandlerValidator) {
1051
	container.logger.Debug(fmt.Sprintf("creating %T", validator))
1052
	return validators.NewLemonsqueezyHandlerValidator(
1053
		container.Logger(),
1054
		container.Tracer(),
1055
		container.LemonsqueezyClient(),
1056
	)
1057
}
1058
1059
// LemonsqueezyClient creates a new instance of lemonsqueezy.Client
1060
func (container *Container) LemonsqueezyClient() (client *lemonsqueezy.Client) {
1061
	container.logger.Debug(fmt.Sprintf("creating %T", client))
1062
	return lemonsqueezy.New(
1063
		lemonsqueezy.WithHTTPClient(container.HTTPClient("lemonsqueezy")),
1064
		lemonsqueezy.WithAPIKey(os.Getenv("LEMONSQUEEZY_API_KEY")),
1065
		lemonsqueezy.WithSigningSecret(os.Getenv("LEMONSQUEEZY_SIGNING_SECRET")),
1066
	)
1067
}
1068
1069
// DiscordClient creates a new instance of discord.Client
1070
func (container *Container) DiscordClient() (client *discord.Client) {
1071
	container.logger.Debug(fmt.Sprintf("creating %T", client))
1072
	return discord.New(
1073
		discord.WithHTTPClient(container.HTTPClient("discord")),
1074
		discord.WithApplicationID(os.Getenv("DISCORD_APPLICATION_ID")),
1075
		discord.WithBotToken(os.Getenv("DISCORD_BOT_TOKEN")),
1076
	)
1077
}
1078
1079
// RegisterLemonsqueezyRoutes registers routes for the /lemonsqueezy prefix
1080
func (container *Container) RegisterLemonsqueezyRoutes() {
1081
	container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.LemonsqueezyHandler{}))
1082
	container.LemonsqueezyHandler().RegisterRoutes(container.App())
1083
}
1084
1085
// RegisterIntegration3CXRoutes registers routes for the /integration/3cx prefix
1086
func (container *Container) RegisterIntegration3CXRoutes() {
1087
	container.logger.Debug(fmt.Sprintf("registering [%T] routes", &handlers.Integration3CXHandler{}))
1088
	container.Integration3CXHandler().RegisterRoutes(container.App(), container.BearerAPIKeyMiddleware(), container.AuthenticatedMiddleware())
1089
}
1090
1091
// RegisterDiscordRoutes registers routes for the /discord prefix
1092
func (container *Container) RegisterDiscordRoutes() {
1093
	container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.DiscordHandler{}))
1094
	container.DiscordHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
1095
}
1096
1097
// RegisterMessageThreadListeners registers event listeners for listeners.MessageThreadListener
1098
func (container *Container) RegisterMessageThreadListeners() {
1099
	container.logger.Debug(fmt.Sprintf("registering listners for %T", listeners.MessageThreadListener{}))
1100
	_, routes := listeners.NewMessageThreadListener(
1101
		container.Logger(),
1102
		container.Tracer(),
1103
		container.MessageThreadService(),
1104
	)
1105
1106
	for event, handler := range routes {
1107
		container.EventDispatcher().Subscribe(event, handler)
1108
	}
1109
}
1110
1111
// RegisterEmailNotificationListeners registers event listeners for listeners.EmailNotificationListener
1112
func (container *Container) RegisterEmailNotificationListeners() {
1113
	container.logger.Debug(fmt.Sprintf("registering listners for %T", listeners.EmailNotificationListener{}))
1114
	_, routes := listeners.NewEmailNotificationListener(
1115
		container.Logger(),
1116
		container.Tracer(),
1117
		container.EmailNotificationService(),
1118
	)
1119
1120
	for event, handler := range routes {
1121
		container.EventDispatcher().Subscribe(event, handler)
1122
	}
1123
}
1124
1125
// RegisterNotificationListeners registers event listeners for listeners.PhoneNotificationListener
1126
func (container *Container) RegisterNotificationListeners() {
1127
	container.logger.Debug(fmt.Sprintf("registering listners for %T", listeners.PhoneNotificationListener{}))
1128
	_, routes := listeners.NewNotificationListener(
1129
		container.Logger(),
1130
		container.Tracer(),
1131
		container.NotificationService(),
1132
	)
1133
1134
	for event, handler := range routes {
1135
		container.EventDispatcher().Subscribe(event, handler)
1136
	}
1137
}
1138
1139
// RegisterHeartbeatListeners registers event listeners for listeners.HeartbeatListener
1140
func (container *Container) RegisterHeartbeatListeners() {
1141
	container.logger.Debug(fmt.Sprintf("registering listners for %T", listeners.HeartbeatListener{}))
1142
	_, routes := listeners.NewHeartbeatListener(
1143
		container.Logger(),
1144
		container.Tracer(),
1145
		container.HeartbeatService(),
1146
	)
1147
1148
	for event, handler := range routes {
1149
		container.EventDispatcher().Subscribe(event, handler)
1150
	}
1151
}
1152
1153
// RegisterUserListeners registers event listeners for listeners.UserListener
1154
func (container *Container) RegisterUserListeners() {
1155
	container.logger.Debug(fmt.Sprintf("registering listners for %T", listeners.UserListener{}))
1156
	_, routes := listeners.NewUserListener(
1157
		container.Logger(),
1158
		container.Tracer(),
1159
		container.UserService(),
1160
	)
1161
1162
	for event, handler := range routes {
1163
		container.EventDispatcher().Subscribe(event, handler)
1164
	}
1165
}
1166
1167
// RegisterBillingListeners registers event listeners for listeners.BillingListener
1168
func (container *Container) RegisterBillingListeners() {
1169
	container.logger.Debug(fmt.Sprintf("registering listeners for %T", listeners.BillingListener{}))
1170
	_, routes := listeners.NewBillingListener(
1171
		container.Logger(),
1172
		container.Tracer(),
1173
		container.BillingService(),
1174
	)
1175
1176
	for event, handler := range routes {
1177
		container.EventDispatcher().Subscribe(event, handler)
1178
	}
1179
}
1180
1181
// RegisterDiscordListeners registers event listeners for listeners.DiscordListener
1182
func (container *Container) RegisterDiscordListeners() {
1183
	container.logger.Debug(fmt.Sprintf("registering listeners for %T", listeners.DiscordListener{}))
1184
	_, routes := listeners.NewDiscordListener(
1185
		container.Logger(),
1186
		container.Tracer(),
1187
		container.DiscordService(),
1188
	)
1189
1190
	for event, handler := range routes {
1191
		container.EventDispatcher().Subscribe(event, handler)
1192
	}
1193
}
1194
1195
// RegisterIntegration3CXListeners registers event listeners for listeners.Integration3CXListener
1196
func (container *Container) RegisterIntegration3CXListeners() {
1197
	container.logger.Debug(fmt.Sprintf("registering listeners for %T", listeners.Integration3CXListener{}))
1198
	_, routes := listeners.NewIntegration3CXListener(
1199
		container.Logger(),
1200
		container.Tracer(),
1201
		container.Integration3CXService(),
1202
	)
1203
1204
	for event, handler := range routes {
1205
		container.EventDispatcher().Subscribe(event, handler)
1206
	}
1207
}
1208
1209
// RegisterWebhookListeners registers event listeners for listeners.WebhookListener
1210
func (container *Container) RegisterWebhookListeners() {
1211
	container.logger.Debug(fmt.Sprintf("registering listeners for %T", listeners.WebhookListener{}))
1212
	_, routes := listeners.NewWebhookListener(
1213
		container.Logger(),
1214
		container.Tracer(),
1215
		container.WebhookService(),
1216
	)
1217
1218
	for event, handler := range routes {
1219
		container.EventDispatcher().Subscribe(event, handler)
1220
	}
1221
}
1222
1223
// MessageService creates a new instance of services.MessageService
1224
func (container *Container) MessageService() (service *services.MessageService) {
1225
	container.logger.Debug(fmt.Sprintf("creating %T", service))
1226
	return services.NewMessageService(
1227
		container.Logger(),
1228
		container.Tracer(),
1229
		container.MessageRepository(),
1230
		container.EventDispatcher(),
1231
		container.PhoneService(),
1232
	)
1233
}
1234
1235
// NotificationService creates a new instance of services.PhoneNotificationService
1236
func (container *Container) NotificationService() (service *services.PhoneNotificationService) {
1237
	container.logger.Debug(fmt.Sprintf("creating %T", service))
1238
	return services.NewNotificationService(
1239
		container.Logger(),
1240
		container.Tracer(),
1241
		container.FirebaseMessagingClient(),
1242
		container.PhoneRepository(),
1243
		container.PhoneNotificationRepository(),
1244
		container.EventDispatcher(),
1245
	)
1246
}
1247
1248
// RegisterMessageRoutes registers routes for the /messages prefix
1249
func (container *Container) RegisterMessageRoutes() {
1250
	container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.MessageHandler{}))
1251
	container.MessageHandler().RegisterRoutes(container.AuthRouter())
1252
}
1253
1254
// RegisterBulkMessageRoutes registers routes for the /bulk-messages prefix
1255
func (container *Container) RegisterBulkMessageRoutes() {
1256
	container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.BulkMessageHandler{}))
1257
	container.BulkMessageHandler().RegisterRoutes(container.AuthRouter())
1258
}
1259
1260
// RegisterMessageThreadRoutes registers routes for the /message-threads prefix
1261
func (container *Container) RegisterMessageThreadRoutes() {
1262
	container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.MessageThreadHandler{}))
1263
	container.MessageThreadHandler().RegisterRoutes(container.AuthRouter())
1264
}
1265
1266
// RegisterHeartbeatRoutes registers routes for the /heartbeats prefix
1267
func (container *Container) RegisterHeartbeatRoutes() {
1268
	container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.HeartbeatHandler{}))
1269
	container.HeartbeatHandler().RegisterRoutes(container.AuthRouter())
1270
}
1271
1272
// RegisterBillingRoutes registers routes for the /billing prefix
1273
func (container *Container) RegisterBillingRoutes() {
1274
	container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.BillingHandler{}))
1275
	container.BillingHandler().RegisterRoutes(container.AuthRouter())
1276
}
1277
1278
// RegisterWebhookRoutes registers routes for the /webhooks prefix
1279
func (container *Container) RegisterWebhookRoutes() {
1280
	container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.WebhookHandler{}))
1281
	container.WebhookHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware())
1282
}
1283
1284
// RegisterPhoneRoutes registers routes for the /phone prefix
1285
func (container *Container) RegisterPhoneRoutes() {
1286
	container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.PhoneHandler{}))
1287
	container.PhoneHandler().RegisterRoutes(container.AuthRouter())
1288
}
1289
1290
// RegisterUserRoutes registers routes for the /users prefix
1291
func (container *Container) RegisterUserRoutes() {
1292
	container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.UserHandler{}))
1293
	container.UserHandler().RegisterRoutes(container.AuthRouter())
1294
}
1295
1296
// RegisterEventRoutes registers routes for the /events prefix
1297
func (container *Container) RegisterEventRoutes() {
1298
	container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.EventsHandler{}))
1299
	container.EventsHandler().RegisterRoutes(container.AuthRouter())
1300
}
1301
1302
// RegisterSwaggerRoutes registers routes for swagger
1303
func (container *Container) RegisterSwaggerRoutes() {
1304
	container.logger.Debug(fmt.Sprintf("registering %T routes", swagger.HandlerDefault))
1305
	container.App().Get("/*", swagger.HandlerDefault)
1306
}
1307
1308
// HeartbeatRepository registers a new instance of repositories.HeartbeatRepository
1309
func (container *Container) HeartbeatRepository() repositories.HeartbeatRepository {
1310
	container.logger.Debug("creating GORM repositories.HeartbeatRepository")
1311
	return repositories.NewGormHeartbeatRepository(
1312
		container.Logger(),
1313
		container.Tracer(),
1314
		container.DedicatedDB(),
1315
	)
1316
}
1317
1318
// UserRepository registers a new instance of repositories.UserRepository
1319
func (container *Container) UserRepository() repositories.UserRepository {
1320
	container.logger.Debug("creating GORM repositories.UserRepository")
1321
	return repositories.NewGormUserRepository(
1322
		container.Logger(),
1323
		container.Tracer(),
1324
		container.UserRistrettoCache(),
1325
		container.DB(),
1326
	)
1327
}
1328
1329
// UserRistrettoCache creates an in-memory *ristretto.Cache[string, entities.AuthUser]
1330
func (container *Container) UserRistrettoCache() (cache *ristretto.Cache[string, entities.AuthUser]) {
1331
	container.logger.Debug(fmt.Sprintf("creating %T", cache))
1332
	ristrettoCache, err := ristretto.NewCache[string, entities.AuthUser](&ristretto.Config[string, entities.AuthUser]{
1333
		MaxCost:     5000,
1334
		NumCounters: 5000 * 10,
1335
		BufferItems: 64,
1336
	})
1337
	if err != nil {
1338
		container.logger.Fatal(stacktrace.Propagate(err, "cannot create user ristretto cache"))
1339
	}
1340
	return ristrettoCache
1341
}
1342
1343
// InitializeTraceProvider initializes the open telemetry trace provider
1344
func (container *Container) InitializeTraceProvider() func() {
1345
	return container.initializeUptraceProvider(container.version, container.projectID)
1346
}
1347
1348
func (container *Container) initializeGoogleTraceProvider(version string, namespace string) func() {
1349
	container.logger.Debug("initializing google trace meterProvider")
1350
1351
	traceExporter, err := cloudtrace.New(cloudtrace.WithProjectID(os.Getenv("GCP_PROJECT_ID")))
1352
	if err != nil {
1353
		container.logger.Fatal(stacktrace.Propagate(err, "cannot create cloud trace traceExporter"))
1354
	}
1355
1356
	tp := trace.NewTracerProvider(
1357
		trace.WithBatcher(traceExporter),
1358
		trace.WithSampler(trace.AlwaysSample()),
1359
		trace.WithResource(container.OtelResources(version, namespace)),
1360
	)
1361
	otel.SetTracerProvider(tp)
1362
1363
	metricExporter, err := mexporter.New(mexporter.WithProjectID(os.Getenv("GCP_PROJECT_ID")))
1364
	if err != nil {
1365
		container.logger.Fatal(stacktrace.Propagate(err, "cannot create cloud metric traceExporter"))
1366
	}
1367
1368
	meterProvider := metric.NewMeterProvider(
1369
		metric.WithReader(metric.NewPeriodicReader(metricExporter)),
1370
		metric.WithResource(container.OtelResources(version, namespace)),
1371
	)
1372
	otel.SetMeterProvider(meterProvider)
1373
1374
	return func() {
1375
		if err = metricExporter.Shutdown(context.Background()); err != nil {
1376
			container.logger.Error(stacktrace.Propagate(err, "cannot shutdown cloud metric metric exporter"))
1377
		}
1378
		if err = traceExporter.Shutdown(context.Background()); err != nil {
1379
			container.logger.Error(stacktrace.Propagate(err, "cannot shutdown cloud trace trace exporter"))
1380
		}
1381
	}
1382
}
1383
1384
func (container *Container) initializeUptraceProvider(version string, namespace string) (flush func()) {
1385
	container.logger.Debug("initializing uptrace provider")
1386
	// Configure OpenTelemetry with sensible defaults.
1387
	uptrace.ConfigureOpentelemetry(
1388
		uptrace.WithDSN(os.Getenv("UPTRACE_DSN")),
1389
		uptrace.WithServiceName(namespace),
1390
		uptrace.WithServiceVersion(version),
1391
		uptrace.WithDeploymentEnvironment(os.Getenv("ENV")),
1392
	)
1393
1394
	// Send buffered spans and free resources.
1395
	return func() {
1396
		err := uptrace.Shutdown(context.Background())
1397
		if err != nil {
1398
			container.logger.Error(err)
1399
		}
1400
	}
1401
}
1402
1403
func logger(skipFrameCount int) telemetry.Logger {
1404
	fields := map[string]string{
1405
		"pid":      strconv.Itoa(os.Getpid()),
1406
		"hostname": hostName(),
1407
	}
1408
1409
	return telemetry.NewZerologLogger(
1410
		os.Getenv("GCP_PROJECT_ID"),
1411
		fields,
1412
		logDriver(skipFrameCount),
1413
		nil,
1414
	)
1415
}
1416
1417
func logDriver(skipFrameCount int) *zerodriver.Logger {
1418
	if isLocal() {
1419
		return consoleLogger(skipFrameCount)
1420
	}
1421
	return jsonLogger(skipFrameCount)
1422
}
1423
1424
func jsonLogger(skipFrameCount int) *zerodriver.Logger {
1425
	logLevel := zerolog.DebugLevel
1426
	zerolog.SetGlobalLevel(logLevel)
1427
1428
	// See: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
1429
	logLevelSeverity := map[zerolog.Level]string{
1430
		zerolog.TraceLevel: "DEFAULT",
1431
		zerolog.DebugLevel: "DEBUG",
1432
		zerolog.InfoLevel:  "INFO",
1433
		zerolog.WarnLevel:  "WARNING",
1434
		zerolog.ErrorLevel: "ERROR",
1435
		zerolog.PanicLevel: "CRITICAL",
1436
		zerolog.FatalLevel: "CRITICAL",
1437
	}
1438
1439
	zerolog.LevelFieldName = "severity"
1440
	zerolog.LevelFieldMarshalFunc = func(l zerolog.Level) string {
1441
		return logLevelSeverity[l]
1442
	}
1443
	zerolog.TimestampFieldName = "time"
1444
	zerolog.TimeFieldFormat = time.RFC3339Nano
1445
1446
	zl := zerolog.New(os.Stderr).With().Timestamp().CallerWithSkipFrameCount(skipFrameCount).Logger()
1447
	return &zerodriver.Logger{Logger: &zl}
1448
}
1449
1450
func hostName() string {
1451
	h, err := os.Hostname()
1452
	if err != nil {
1453
		h = strconv.Itoa(os.Getpid())
1454
	}
1455
	return h
1456
}
1457
1458
func consoleLogger(skipFrameCount int) *zerodriver.Logger {
1459
	l := zerolog.New(
1460
		zerolog.ConsoleWriter{
1461
			Out: os.Stderr,
1462
		}).With().Timestamp().CallerWithSkipFrameCount(skipFrameCount).Logger()
1463
	return &zerodriver.Logger{
1464
		Logger: &l,
1465
	}
1466
}
1467
1468
func isLocal() bool {
1469
	return os.Getenv("ENV") == "local"
1470
}
1471