calendar

package
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 19, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package calendar wraps Google Calendar event creation behind a small interface so the rest of JobForge stays vendor-agnostic. Eventually this could grow Outlook/CalDAV implementations behind the same Client.

Token storage and OAuth lifecycle live in oauth.go. The Google API surface is intentionally tiny: create event, delete event. We are not trying to be a calendar app.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoToken = errors.New("no Google OAuth token; run `jobforge calendar auth` first")

ErrNoToken signals that no stored OAuth token was found at Paths.GoogleTokenFile. Callers should prompt the user to run `jobforge calendar auth` and not treat it as a fatal error.

View Source
var ErrNotConfigured = errors.New("google.client_id and google.client_secret are not set in config.json")

ErrNotConfigured signals that config.json has no google.client_id or google.client_secret. Different from ErrNoToken — auth can't even start until the user pastes their credentials in.

Functions

func DeleteToken

func DeleteToken(path string) error

DeleteToken removes the token file. Used by `calendar revoke`. Returns nil if the file is already gone.

func HTTPClient

func HTTPClient(ctx context.Context, s config.GoogleSettings, tokenFile string) (*http.Client, error)

HTTPClient returns an *http.Client whose transport auto-refreshes tokens and persists any new ones to tokenFile. Pass it to the Google Calendar service constructor.

func LoadToken

func LoadToken(path string) (*oauth2.Token, error)

LoadToken reads a previously-saved token. Returns ErrNoToken if the file doesn't exist so callers can prompt the user to authorize.

func SaveToken

func SaveToken(path string, tok *oauth2.Token) error

SaveToken writes the OAuth token to path. We do best-effort permission tightening: 0o600 on Unix, plus an icacls invocation on Windows that grants only the current user. ACL failure is logged but not fatal — %APPDATA% on Windows is already per-user.

Types

type AuthResult

type AuthResult struct {
	Token   *oauth2.Token
	Email   string // best-effort: the Google account email, blank if not derivable
	SavedAt string // tokenFile path (for the success message)
}

AuthResult is what Authorize hands back to the CLI. The token is also persisted to tokenFile inside Authorize, so callers don't need to save it themselves.

func Authorize

func Authorize(ctx context.Context, s config.GoogleSettings, tokenFile string, openBrowser func(url string) error) (AuthResult, error)

Authorize runs the full OAuth 2.0 loopback flow:

  1. Start a local HTTP server on a random port to receive Google's redirect.
  2. Open the user's browser to Google's consent screen.
  3. Wait for the redirect with the auth code.
  4. Exchange the code for an access + refresh token.
  5. Persist the token to tokenFile.

The caller (CLI) handles printing instructions, opening the browser, and surfacing errors. We block until either the redirect lands or ctx cancels.

type Client

type Client interface {
	// CreateEvent creates one event and returns the provider's event ID.
	// The ID is needed later for deletion or updates.
	CreateEvent(ctx context.Context, e Event) (string, error)

	// DeleteEvent removes an event by provider ID. Idempotent: deleting
	// an already-gone event returns nil.
	DeleteEvent(ctx context.Context, eventID string) error
}

Client is the abstraction over a calendar provider. Implementations are responsible for their own auth + retries.

func NewGoogleClient

func NewGoogleClient(ctx context.Context, s config.GoogleSettings, tokenFile string) (Client, error)

NewGoogleClient builds a Client wired to the user's stored token. Returns ErrNoToken if the user hasn't run `calendar auth` yet, and ErrNotConfigured if config.json lacks ClientID/ClientSecret.

type Event

type Event struct {
	Summary     string    // "Interview: Backend Engineer at Acme"
	Description string    // multi-line: job URL, notes, prep links
	Location    string    // "Zoom", "Acme HQ - 555 Market St", etc.
	Start       time.Time // event start
	End         time.Time // event end (required unless AllDay)
	AllDay      bool      // ignore Start/End times, use Start's Date
	Attendees   []string  // email addresses
}

Event is the calendar-agnostic event we hand off to the provider. Times are timezone-aware (use time.Local or an explicit Location). AllDay events ignore Start/End times — Date alone is used.

func EventForJobDeadline

func EventForJobDeadline(jobTitle, company, jobURL string, when time.Time) Event

EventForJobDeadline constructs an all-day event reminding the user about an application deadline. We use all-day because deadlines are date-granular ("apply by Friday") and we don't want them blocking a specific hour on the user's calendar.

func EventForJobInterview

func EventForJobInterview(jobTitle, company, jobURL string, when time.Time, duration time.Duration, location string, attendees []string) Event

EventForJobInterview constructs an Event for an interview tied to a job. Pure function, no I/O — extracted so the CLI can show the user the exact event payload before we call CreateEvent.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL