Skip to content

Commit e71a3e4

Browse files
committed
Sync churches CLI
1 parent 9fd2947 commit e71a3e4

File tree

4 files changed

+218
-0
lines changed

4 files changed

+218
-0
lines changed

backend/cmd/sync-churches/main.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log/slog"
7+
"os"
8+
"time"
9+
10+
"github.com/bcc-media/wayfarer/internal/auth0"
11+
"github.com/bcc-media/wayfarer/internal/config"
12+
"github.com/bcc-media/wayfarer/internal/database"
13+
"github.com/bcc-media/wayfarer/internal/database/sqlc"
14+
"github.com/bcc-media/wayfarer/internal/logger"
15+
"github.com/bcc-media/wayfarer/internal/members"
16+
"github.com/bcc-media/wayfarer/internal/ulid"
17+
"github.com/sony/gobreaker/v2"
18+
)
19+
20+
func main() {
21+
ctx := context.Background()
22+
23+
// Load config
24+
cfg, err := config.Load()
25+
if err != nil {
26+
slog.Error("Failed to load config", "error", err)
27+
os.Exit(1)
28+
}
29+
30+
// Initialize logger
31+
lgr := logger.New(cfg.Server.Environment, logger.ParseLevel(cfg.Log.Level))
32+
slog.SetDefault(lgr)
33+
34+
slog.Info("Starting church sync")
35+
36+
// Connect to database
37+
db, err := database.Connect(ctx, cfg.Database)
38+
if err != nil {
39+
slog.Error("Failed to connect to database", "error", err)
40+
os.Exit(1)
41+
}
42+
defer db.Close()
43+
44+
// Validate required configuration
45+
if cfg.Auth0.Domain == "" || cfg.Auth0.ClientID == "" || cfg.Members.Domain == "" {
46+
slog.Error("Missing required configuration",
47+
"auth0_domain", cfg.Auth0.Domain,
48+
"members_domain", cfg.Members.Domain,
49+
)
50+
os.Exit(1)
51+
}
52+
53+
// Initialize Auth0 client
54+
auth0Client := auth0.New(auth0.Config{
55+
Domain: cfg.Auth0.Domain,
56+
ClientID: cfg.Auth0.ClientID,
57+
ClientSecret: cfg.Auth0.ClientSecret,
58+
})
59+
60+
// Create circuit breaker for Members API
61+
membersBreaker := gobreaker.NewCircuitBreaker[[]byte](gobreaker.Settings{
62+
Name: "members-api-sync",
63+
Timeout: 5 * time.Second,
64+
})
65+
66+
// Initialize Members API client
67+
membersClient := members.New(
68+
members.Config{Domain: cfg.Members.Domain},
69+
auth0Client,
70+
membersBreaker,
71+
)
72+
73+
// Execute sync
74+
if err := syncChurches(ctx, membersClient, db.Queries); err != nil {
75+
slog.Error("Church sync failed", "error", err)
76+
os.Exit(1)
77+
}
78+
79+
slog.Info("Church sync completed successfully")
80+
}
81+
82+
func syncChurches(ctx context.Context, membersClient *members.Client, queries *sqlc.Queries) error {
83+
// Fetch all organizations from Members API
84+
slog.Info("Fetching organizations from Members API")
85+
orgs, err := membersClient.GetAllOrganizations(ctx)
86+
if err != nil {
87+
return fmt.Errorf("failed to fetch organizations: %w", err)
88+
}
89+
slog.Info("Fetched organizations", "count", len(orgs))
90+
91+
// Warn if exactly 999 orgs (possible pagination issue)
92+
if len(orgs) == 999 {
93+
slog.Warn("Fetched exactly 999 organizations - may be hitting pagination limit")
94+
}
95+
96+
// Process each organization
97+
var successCount, errorCount int
98+
for i, org := range orgs {
99+
if err := upsertChurch(ctx, queries, org); err != nil {
100+
slog.Error("Failed to upsert church",
101+
"external_id", org.OrgID,
102+
"name", org.Name,
103+
"error", err,
104+
)
105+
errorCount++
106+
continue
107+
}
108+
successCount++
109+
110+
// Log progress every 100 churches
111+
if (i+1)%100 == 0 {
112+
slog.Info("Progress", "processed", i+1, "total", len(orgs))
113+
}
114+
}
115+
116+
// Log final results
117+
slog.Info("Sync completed",
118+
"total", len(orgs),
119+
"success", successCount,
120+
"errors", errorCount,
121+
)
122+
123+
if errorCount > 0 {
124+
return fmt.Errorf("sync completed with %d errors", errorCount)
125+
}
126+
127+
return nil
128+
}
129+
130+
func upsertChurch(ctx context.Context, queries *sqlc.Queries, org members.Organization) error {
131+
// Handle empty country code
132+
country := org.VisitingAddress.CountryCode
133+
if country == "" {
134+
country = "UNKNOWN"
135+
}
136+
137+
// Generate new church ID (will only be used if insert happens)
138+
churchID := ulid.NewChurchID()
139+
140+
// Convert OrgID to int32 pointer
141+
externalID := int32(org.OrgID)
142+
143+
// Execute upsert
144+
_, err := queries.UpsertChurch(ctx, sqlc.UpsertChurchParams{
145+
ID: churchID,
146+
ExternalID: &externalID,
147+
Name: org.Name,
148+
Country: country,
149+
Category: "S",
150+
})
151+
152+
return err
153+
}

backend/internal/database/queries/churches.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,13 @@ WHERE
3939
INSERT INTO churches (id, external_id, name, country, category)
4040
VALUES (@id, @external_id, @name, @country, @category)
4141
RETURNING id, external_id, name, country, category;
42+
43+
-- name: UpsertChurch :one
44+
INSERT INTO churches (id, external_id, name, country, category)
45+
VALUES (@id, @external_id, @name, @country, @category)
46+
ON CONFLICT (external_id)
47+
DO UPDATE SET
48+
name = EXCLUDED.name,
49+
country = EXCLUDED.country,
50+
updated_at = NOW()
51+
RETURNING id, external_id, name, country, category;

backend/internal/database/sqlc/churches.sql.go

Lines changed: 46 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/internal/members/lookup.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,12 @@ func (c *Client) GetOrganizationsByIDs(ctx context.Context, ids []uuid.UUID) ([]
103103

104104
return out, nil
105105
}
106+
107+
// GetAllOrganizations returns all organizations from the Members API.
108+
func (c *Client) GetAllOrganizations(ctx context.Context) ([]Organization, error) {
109+
orgs, err := get[[]Organization](ctx, c, fmt.Sprintf("v2/orgs?limit=999&fields=%s", orgFields))
110+
if err != nil {
111+
return nil, fmt.Errorf("failed to get organizations: %w", err)
112+
}
113+
return *orgs, nil
114+
}

0 commit comments

Comments
 (0)