Skip to content

Commit 21dce9b

Browse files
committed
telemetry
1 parent 3a8d410 commit 21dce9b

10 files changed

Lines changed: 2395 additions & 7 deletions

File tree

internal/service/telemetry_service.go

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ type TelemetryMetrics struct {
2424
MessagesCount int `json:"messages_count"`
2525
ListsCount int `json:"lists_count"`
2626
APIEndpoint string `json:"api_endpoint"`
27+
28+
// Integration flags - boolean for each email provider
29+
Mailgun bool `json:"mailgun"`
30+
AmazonSES bool `json:"amazonses"`
31+
Mailjet bool `json:"mailjet"`
32+
SendGrid bool `json:"sendgrid"`
33+
Postmark bool `json:"postmark"`
34+
SMTP bool `json:"smtp"`
35+
S3 bool `json:"s3"`
2736
}
2837

2938
const (
@@ -82,7 +91,7 @@ func (t *TelemetryService) SendMetricsForAllWorkspaces(ctx context.Context) erro
8291

8392
// Collect and send metrics for each workspace
8493
for _, workspace := range workspaces {
85-
if err := t.sendMetricsForWorkspace(ctx, workspace.ID); err != nil {
94+
if err := t.sendMetricsForWorkspace(ctx, workspace); err != nil {
8695
// Continue with other workspaces on error
8796
}
8897
}
@@ -91,10 +100,10 @@ func (t *TelemetryService) SendMetricsForAllWorkspaces(ctx context.Context) erro
91100
}
92101

93102
// sendMetricsForWorkspace collects and sends telemetry metrics for a specific workspace
94-
func (t *TelemetryService) sendMetricsForWorkspace(ctx context.Context, workspaceID string) error {
103+
func (t *TelemetryService) sendMetricsForWorkspace(ctx context.Context, workspace *domain.Workspace) error {
95104
// Create SHA1 hash of workspace ID
96105
hasher := sha1.New()
97-
hasher.Write([]byte(workspaceID))
106+
hasher.Write([]byte(workspace.ID))
98107
workspaceIDSHA1 := hex.EncodeToString(hasher.Sum(nil))
99108

100109
// Collect metrics
@@ -103,8 +112,11 @@ func (t *TelemetryService) sendMetricsForWorkspace(ctx context.Context, workspac
103112
APIEndpoint: t.apiEndpoint,
104113
}
105114

106-
// Get workspace database connection
107-
db, err := t.workspaceRepo.GetConnection(ctx, workspaceID)
115+
// Set integration flags from workspace integrations
116+
t.setIntegrationFlagsFromWorkspace(workspace, &metrics)
117+
118+
// Get workspace database connection for counting metrics
119+
db, err := t.workspaceRepo.GetConnection(ctx, workspace.ID)
108120
if err != nil {
109121
// Continue without database metrics
110122
} else {
@@ -140,7 +152,7 @@ func (t *TelemetryService) sendMetricsForWorkspace(ctx context.Context, workspac
140152

141153
// countContacts counts the total number of contacts in a workspace
142154
func (t *TelemetryService) countContacts(ctx context.Context, db *sql.DB) (int, error) {
143-
query := `SELECT COUNT(DISTINCT email) FROM contacts`
155+
query := `SELECT COUNT(*) FROM contacts`
144156
var count int
145157
err := db.QueryRowContext(ctx, query).Scan(&count)
146158
if err != nil {
@@ -184,7 +196,7 @@ func (t *TelemetryService) countMessages(ctx context.Context, db *sql.DB) (int,
184196

185197
// countLists counts the total number of lists in a workspace
186198
func (t *TelemetryService) countLists(ctx context.Context, db *sql.DB) (int, error) {
187-
query := `SELECT COUNT(*) FROM lists WHERE deleted_at IS NULL`
199+
query := `SELECT COUNT(*) FROM lists`
188200
var count int
189201
err := db.QueryRowContext(ctx, query).Scan(&count)
190202
if err != nil {
@@ -193,6 +205,39 @@ func (t *TelemetryService) countLists(ctx context.Context, db *sql.DB) (int, err
193205
return count, nil
194206
}
195207

208+
// setIntegrationFlagsFromWorkspace sets boolean flags for each integration type from workspace integrations
209+
func (t *TelemetryService) setIntegrationFlagsFromWorkspace(workspace *domain.Workspace, metrics *TelemetryMetrics) {
210+
// Iterate through workspace integrations and set flags based on email provider kind
211+
for _, integration := range workspace.Integrations {
212+
if integration.Type == domain.IntegrationTypeEmail {
213+
switch integration.EmailProvider.Kind {
214+
case domain.EmailProviderKindMailgun:
215+
metrics.Mailgun = true
216+
case domain.EmailProviderKindSES:
217+
metrics.AmazonSES = true
218+
case domain.EmailProviderKindMailjet:
219+
metrics.Mailjet = true
220+
case domain.EmailProviderKindPostmark:
221+
metrics.Postmark = true
222+
case domain.EmailProviderKindSMTP:
223+
metrics.SMTP = true
224+
case domain.EmailProviderKindSparkPost:
225+
metrics.SendGrid = true // SparkPost maps to SendGrid for telemetry
226+
}
227+
}
228+
}
229+
230+
// Check if S3-compatible file storage is configured
231+
if t.isS3FileStorageConfigured(&workspace.Settings.FileManager) {
232+
metrics.S3 = true
233+
}
234+
}
235+
236+
// isS3FileStorageConfigured checks if S3-compatible file storage is configured in workspace settings
237+
func (t *TelemetryService) isS3FileStorageConfigured(fileManager *domain.FileManagerSettings) bool {
238+
return fileManager.Endpoint != "" && fileManager.Bucket != "" && fileManager.AccessKey != ""
239+
}
240+
196241
// sendMetrics sends the collected metrics to the telemetry endpoint
197242
func (t *TelemetryService) sendMetrics(ctx context.Context, metrics TelemetryMetrics) error {
198243
// Marshal metrics to JSON

internal/service/telemetry_service_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,75 @@ func TestTelemetryService_HardcodedEndpoint(t *testing.T) {
142142
// Verify that the hardcoded endpoint is used
143143
assert.Equal(t, "https://telemetry.notifuse.com", TelemetryEndpoint)
144144
}
145+
146+
func TestTelemetryService_SetIntegrationFlags(t *testing.T) {
147+
config := TelemetryServiceConfig{
148+
Enabled: true,
149+
APIEndpoint: "https://api.example.com",
150+
Logger: logger.NewLoggerWithLevel("debug"),
151+
}
152+
153+
service := NewTelemetryService(config)
154+
155+
// Test workspace with various integrations
156+
workspace := &domain.Workspace{
157+
ID: "test-workspace",
158+
Name: "Test Workspace",
159+
Integrations: domain.Integrations{
160+
{
161+
ID: "mailgun-integration",
162+
Name: "Mailgun",
163+
Type: domain.IntegrationTypeEmail,
164+
EmailProvider: domain.EmailProvider{
165+
Kind: domain.EmailProviderKindMailgun,
166+
},
167+
},
168+
{
169+
ID: "ses-integration",
170+
Name: "Amazon SES",
171+
Type: domain.IntegrationTypeEmail,
172+
EmailProvider: domain.EmailProvider{
173+
Kind: domain.EmailProviderKindSES,
174+
},
175+
},
176+
{
177+
ID: "smtp-integration",
178+
Name: "SMTP",
179+
Type: domain.IntegrationTypeEmail,
180+
EmailProvider: domain.EmailProvider{
181+
Kind: domain.EmailProviderKindSMTP,
182+
},
183+
},
184+
},
185+
}
186+
187+
// Test the integration flag setting
188+
metrics := TelemetryMetrics{}
189+
service.setIntegrationFlagsFromWorkspace(workspace, &metrics)
190+
191+
// Verify that the correct flags are set
192+
assert.True(t, metrics.Mailgun, "Mailgun flag should be true")
193+
assert.True(t, metrics.AmazonSES, "AmazonSES flag should be true")
194+
assert.True(t, metrics.SMTP, "SMTP flag should be true")
195+
assert.False(t, metrics.Mailjet, "Mailjet flag should be false")
196+
assert.False(t, metrics.SendGrid, "SendGrid flag should be false")
197+
assert.False(t, metrics.Postmark, "Postmark flag should be false")
198+
199+
// Test empty workspace
200+
emptyWorkspace := &domain.Workspace{
201+
ID: "empty-workspace",
202+
Name: "Empty Workspace",
203+
Integrations: domain.Integrations{},
204+
}
205+
206+
emptyMetrics := TelemetryMetrics{}
207+
service.setIntegrationFlagsFromWorkspace(emptyWorkspace, &emptyMetrics)
208+
209+
// Verify all flags are false
210+
assert.False(t, emptyMetrics.Mailgun, "All flags should be false for empty workspace")
211+
assert.False(t, emptyMetrics.AmazonSES, "All flags should be false for empty workspace")
212+
assert.False(t, emptyMetrics.SMTP, "All flags should be false for empty workspace")
213+
assert.False(t, emptyMetrics.Mailjet, "All flags should be false for empty workspace")
214+
assert.False(t, emptyMetrics.SendGrid, "All flags should be false for empty workspace")
215+
assert.False(t, emptyMetrics.Postmark, "All flags should be false for empty workspace")
216+
}

telemetry/.gcloudignore

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# This file specifies files that are *not* uploaded to Google Cloud
2+
# using gcloud. It follows the same syntax as .gitignore, with the addition of
3+
# "#!include" directives (which insert the entries of the given .gitignore-style
4+
# file at that point).
5+
#
6+
# For more information, run:
7+
# $ gcloud topic gcloudignore
8+
#
9+
.gcloudignore
10+
# If you would like to upload your .git directory, .gitignore file or files
11+
# from your .gitignore file, remove the corresponding line below:
12+
.git
13+
.gitignore
14+
15+
# Node.js dependencies:
16+
node_modules/
17+
18+
# Python pycache:
19+
__pycache__/
20+
# Ignored by the build system
21+
/setup.cfg
22+
23+
# Go build artifacts
24+
*.exe
25+
*.exe~
26+
*.dll
27+
*.so
28+
*.dylib
29+
30+
# Test binary, built with `go test -c`
31+
*.test
32+
33+
# Output of the go coverage tool
34+
*.out
35+
36+
# Dependency directories
37+
vendor/
38+
39+
# IDE files
40+
.vscode/
41+
.idea/
42+
*.swp
43+
*.swo
44+
45+
# OS generated files
46+
.DS_Store
47+
.DS_Store?
48+
._*
49+
.Spotlight-V100
50+
.Trashes
51+
ehthumbs.db
52+
Thumbs.db
53+
54+
# Local development files
55+
.env
56+
.env.local
57+
*.log
58+
59+
# Documentation
60+
README.md

0 commit comments

Comments
 (0)