Skip to content

Commit 969f570

Browse files
committed
Implement CalendarFactory pattern and add GetEvent method
1 parent 630fc8d commit 969f570

File tree

8 files changed

+243
-223
lines changed

8 files changed

+243
-223
lines changed

add.go

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ import (
77
"log"
88
"os"
99
"strings"
10-
11-
"google.golang.org/api/calendar/v3"
12-
"google.golang.org/api/option"
1310
)
1411

1512
func addCalendar() {
@@ -44,17 +41,18 @@ func addCalendar() {
4441

4542
ctx := context.Background()
4643
var providerConfig string
44+
45+
// Use CalendarFactory to create and validate provider
46+
calendarFactory := NewCalendarFactory(ctx, config, db)
4747

4848
// Validate calendar access based on provider type
4949
if providerType == "google" {
50-
client := getClient(ctx, oauthConfig, db, accountName, config)
51-
52-
calendarService, err := calendar.NewService(ctx, option.WithHTTPClient(client))
50+
provider, err := calendarFactory.CreateCalendarProvider(providerType, accountName, "")
5351
if err != nil {
54-
log.Fatalf("Error creating calendar client: %v", err)
52+
log.Fatalf("Error creating Google calendar provider: %v", err)
5553
}
56-
57-
_, err = calendarService.CalendarList.Get(calendarID).Do()
54+
55+
err = calendarFactory.ValidateCalendarAccess(provider, calendarID)
5856
if err != nil {
5957
log.Fatalf("Error retrieving Google calendar: %v", err)
6058
}
@@ -64,10 +62,6 @@ func addCalendar() {
6462
log.Fatalf("Error: No CalDAV server configurations found in .gcalsync.toml")
6563
}
6664

67-
// Use the server configuration
68-
var serverName string
69-
var serverConfig CalDAVConfig
70-
7165
// List available servers for selection
7266
fmt.Println("Available CalDAV servers:")
7367
servers := make([]string, 0, len(config.CalDAVs))
@@ -92,17 +86,18 @@ func addCalendar() {
9286
log.Fatalf("Error: Invalid server selection")
9387
}
9488

95-
serverName = servers[serverIndex]
96-
serverConfig = config.CalDAVs[serverName]
89+
serverName := servers[serverIndex]
90+
serverConfig := config.CalDAVs[serverName]
9791

9892
fmt.Printf("Using CalDAV server: %s\n", serverConfig.ServerURL)
9993

100-
caldavProvider, err := NewCalDAVProvider(ctx, serverConfig.ServerURL, serverConfig.Username, serverConfig.Password)
94+
// Create and validate provider using the factory
95+
provider, err := calendarFactory.CreateCalendarProvider(providerType, accountName, serverName)
10196
if err != nil {
102-
log.Fatalf("Error connecting to CalDAV server: %v", err)
97+
log.Fatalf("Error creating CalDAV provider: %v", err)
10398
}
10499

105-
err = caldavProvider.GetCalendar(calendarID)
100+
err = calendarFactory.ValidateCalendarAccess(provider, calendarID)
106101
if err != nil {
107102
log.Fatalf("Error retrieving CalDAV calendar: %v", err)
108103
}

caldav_calendar.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,63 @@ func (c *CalDAVProvider) ListEvents(calendarID string, timeMin, timeMax time.Tim
237237
return result, nil
238238
}
239239

240+
func (c *CalDAVProvider) GetEvent(calendarID string, eventID string) (*Event, error) {
241+
calURL, err := url.Parse(calendarID)
242+
if err != nil {
243+
return nil, fmt.Errorf("invalid calendar URL: %w", err)
244+
}
245+
246+
// Construct the path to the event file
247+
path := calURL.Path + "/" + eventID + ".ics"
248+
249+
// Get the calendar object
250+
object, err := c.client.GetCalendarObject(c.ctx, path)
251+
if err != nil {
252+
return nil, fmt.Errorf("failed to get event: %w", err)
253+
}
254+
255+
calendar := object.Data
256+
257+
// Find the VEVENT component
258+
var eventComp *ical.Component
259+
for _, comp := range calendar.Component.Children {
260+
if comp.Name == "VEVENT" {
261+
eventComp = comp
262+
break
263+
}
264+
}
265+
266+
if eventComp == nil {
267+
return nil, fmt.Errorf("no VEVENT component found in calendar object")
268+
}
269+
270+
// Extract event properties
271+
uid := getTextProp(eventComp.Props, "UID")
272+
summary := getTextProp(eventComp.Props, "SUMMARY")
273+
description := getTextProp(eventComp.Props, "DESCRIPTION")
274+
status := getTextProp(eventComp.Props, "STATUS")
275+
if status == "" {
276+
status = "confirmed" // Default status if not specified
277+
} else {
278+
// Convert from iCalendar status format (e.g., "CONFIRMED") to lowercase
279+
status = strings.ToLower(status)
280+
}
281+
282+
// Parse dates
283+
start, _ := eventComp.Props.DateTime("DTSTART", time.UTC)
284+
end, _ := eventComp.Props.DateTime("DTEND", time.UTC)
285+
286+
// Create Event object
287+
return &Event{
288+
ID: uid,
289+
Summary: summary,
290+
Description: description,
291+
Start: start,
292+
End: end,
293+
Status: status,
294+
}, nil
295+
}
296+
240297
// Helper function to get text property safely
241298
func getTextProp(props ical.Props, name string) string {
242299
prop := props.Get(name)

calendar_factory.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"fmt"
7+
)
8+
9+
// CalendarFactory handles creation and management of calendar providers
10+
type CalendarFactory struct {
11+
config *Config
12+
db *sql.DB
13+
ctx context.Context
14+
}
15+
16+
// NewCalendarFactory creates a new calendar factory instance
17+
func NewCalendarFactory(ctx context.Context, config *Config, db *sql.DB) *CalendarFactory {
18+
return &CalendarFactory{
19+
config: config,
20+
db: db,
21+
ctx: ctx,
22+
}
23+
}
24+
25+
// GetAllCalendars returns all calendar providers for all accounts
26+
func (cf *CalendarFactory) GetAllCalendars() (map[string]map[string]CalendarProvider, map[string][]CalendarInfo, error) {
27+
calendars := getCalendarsFromDB(cf.db)
28+
providers := make(map[string]map[string]CalendarProvider)
29+
30+
for accountName, calendarInfos := range calendars {
31+
providers[accountName] = make(map[string]CalendarProvider)
32+
33+
// Initialize providers for each type needed by this account
34+
for i, calInfo := range calendarInfos {
35+
switch calInfo.ProviderType {
36+
case "google":
37+
if _, exists := providers[accountName]["google"]; !exists {
38+
client := getClient(cf.ctx, oauthConfig, cf.db, accountName, cf.config)
39+
googleProvider, err := NewGoogleCalendarProvider(cf.ctx, client)
40+
if err != nil {
41+
return nil, nil, fmt.Errorf("error creating Google calendar provider: %w", err)
42+
}
43+
providers[accountName]["google"] = googleProvider
44+
}
45+
46+
case "caldav":
47+
// Get the server configuration based on provider_config
48+
var serverConfig CalDAVConfig
49+
serverName := calInfo.ProviderConfig
50+
51+
// If there's no server name stored, we need to ask the user to reconfigure this calendar
52+
if serverName == "" || serverName == "default" {
53+
return nil, nil, fmt.Errorf("calendar references removed legacy CalDAV configuration; please remove and re-add this calendar")
54+
}
55+
56+
// Use the server from the CalDAV servers config
57+
if server, ok := cf.config.CalDAVs[serverName]; ok {
58+
serverConfig = server
59+
} else {
60+
return nil, nil, fmt.Errorf("CalDAV server '%s' not found in configuration", serverName)
61+
}
62+
63+
// Create a provider key that includes the server name to allow multiple servers
64+
providerKey := "caldav-" + serverName
65+
66+
// Only create the provider if we don't already have one for this server
67+
if _, exists := providers[accountName][providerKey]; !exists {
68+
caldavProvider, err := NewCalDAVProvider(cf.ctx, serverConfig.ServerURL, serverConfig.Username, serverConfig.Password)
69+
if err != nil {
70+
return nil, nil, fmt.Errorf("error connecting to CalDAV server %s: %w", serverName, err)
71+
}
72+
providers[accountName][providerKey] = caldavProvider
73+
}
74+
75+
// Update the calendar info to use the correct provider key
76+
calendarInfos[i].ProviderKey = providerKey
77+
78+
default:
79+
return nil, nil, fmt.Errorf("unsupported provider type: %s", calInfo.ProviderType)
80+
}
81+
}
82+
}
83+
84+
return providers, calendars, nil
85+
}
86+
87+
// CreateCalendarProvider creates a specific calendar provider
88+
func (cf *CalendarFactory) CreateCalendarProvider(providerType string, accountName string, serverName string) (CalendarProvider, error) {
89+
switch providerType {
90+
case "google":
91+
client := getClient(cf.ctx, oauthConfig, cf.db, accountName, cf.config)
92+
return NewGoogleCalendarProvider(cf.ctx, client)
93+
94+
case "caldav":
95+
// Get the server configuration
96+
if serverName == "" || serverName == "default" {
97+
return nil, fmt.Errorf("no server name provided for CalDAV provider")
98+
}
99+
100+
// Use the server from the CalDAV servers config
101+
var serverConfig CalDAVConfig
102+
if server, ok := cf.config.CalDAVs[serverName]; ok {
103+
serverConfig = server
104+
} else {
105+
return nil, fmt.Errorf("CalDAV server '%s' not found in configuration", serverName)
106+
}
107+
108+
return NewCalDAVProvider(cf.ctx, serverConfig.ServerURL, serverConfig.Username, serverConfig.Password)
109+
110+
default:
111+
return nil, fmt.Errorf("unsupported provider type: %s", providerType)
112+
}
113+
}
114+
115+
// ValidateCalendarAccess checks if the provided calendar ID is accessible
116+
func (cf *CalendarFactory) ValidateCalendarAccess(provider CalendarProvider, calendarID string) error {
117+
return provider.GetCalendar(calendarID)
118+
}

calendar_provider.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ type CalendarProvider interface {
1010
UpdateEvent(calendarID string, eventID string, event *Event) error
1111
DeleteEvent(calendarID string, eventID string) error
1212
ListEvents(calendarID string, timeMin, timeMax time.Time) ([]*Event, error)
13+
GetEvent(calendarID string, eventID string) (*Event, error)
1314
}
1415

1516
type Event struct {
@@ -19,4 +20,4 @@ type Event struct {
1920
Start time.Time
2021
End time.Time
2122
Status string
22-
}
23+
}

cleanup.go

Lines changed: 8 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -22,66 +22,17 @@ func cleanupCalendars() {
2222
}
2323
defer db.Close()
2424

25-
calendars := getCalendarsFromDB(db)
26-
2725
ctx := context.Background()
2826

29-
// Create provider instances for each account
30-
providers = make(map[string]map[string]CalendarProvider)
31-
27+
// Use the calendar factory to get all providers
28+
calendarFactory := NewCalendarFactory(ctx, config, db)
29+
providers, calendars, err := calendarFactory.GetAllCalendars()
30+
if err != nil {
31+
log.Fatalf("Error initializing calendar providers: %v", err)
32+
}
33+
3234
for accountName, calendarInfos := range calendars {
3335
fmt.Printf("📅 Setting up account: %s\n", accountName)
34-
providers[accountName] = make(map[string]CalendarProvider)
35-
36-
// Initialize providers for each type needed by this account
37-
for i, calInfo := range calendarInfos {
38-
switch calInfo.ProviderType {
39-
case "google":
40-
if _, exists := providers[accountName]["google"]; !exists {
41-
client := getClient(ctx, oauthConfig, db, accountName, config)
42-
googleProvider, err := NewGoogleCalendarProvider(ctx, client)
43-
if err != nil {
44-
log.Fatalf("Error creating Google calendar provider: %v", err)
45-
}
46-
providers[accountName]["google"] = googleProvider
47-
}
48-
49-
case "caldav":
50-
// Get the server configuration from provider_config
51-
var serverConfig CalDAVConfig
52-
serverName := calInfo.ProviderConfig
53-
54-
// If there's no server name, we need the user to reconfigure
55-
if serverName == "" || serverName == "default" {
56-
log.Fatalf("Error: Calendar references removed legacy CalDAV configuration. Please remove and re-add this calendar using: ./gcalsync add")
57-
}
58-
59-
// Use the server from CalDAV servers config
60-
if server, ok := config.CalDAVs[serverName]; ok {
61-
serverConfig = server
62-
} else {
63-
log.Fatalf("Error: CalDAV server '%s' not found in configuration", serverName)
64-
}
65-
66-
// Create a provider key that includes the server name
67-
providerKey := "caldav-" + serverName
68-
69-
// Only create the provider if we don't already have one for this server
70-
if _, exists := providers[accountName][providerKey]; !exists {
71-
caldavProvider, err := NewCalDAVProvider(ctx, serverConfig.ServerURL, serverConfig.Username, serverConfig.Password)
72-
if err != nil {
73-
log.Fatalf("Error connecting to CalDAV server %s: %v", serverName, err)
74-
}
75-
providers[accountName][providerKey] = caldavProvider
76-
}
77-
78-
// Update the calendar info to use the correct provider key
79-
calendarInfos[i].ProviderKey = providerKey
80-
81-
default:
82-
log.Fatalf("Error: Unsupported provider type: %s", calInfo.ProviderType)
83-
}
84-
}
8536

8637
for _, calInfo := range calendarInfos {
8738
fmt.Printf("🧹 Cleaning up calendar: %s\n", calInfo.ID)
@@ -102,7 +53,7 @@ func cleanupCalendars() {
10253
}
10354
}
10455

105-
fmt.Println("Calendars desynced successfully")
56+
fmt.Println("Calendars cleaned up successfully")
10657
}
10758

10859
// Legacy function for backward compatibility

0 commit comments

Comments
 (0)