mirror of
https://github.com/sstent/go-garth.git
synced 2026-01-25 16:42:28 +00:00
porting - part 12 done
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"garmin-connect/garth/errors"
|
||||||
"garmin-connect/garth/sso"
|
"garmin-connect/garth/sso"
|
||||||
"garmin-connect/garth/types"
|
"garmin-connect/garth/types"
|
||||||
)
|
)
|
||||||
@@ -31,7 +32,12 @@ func NewClient(domain string) (*Client, error) {
|
|||||||
|
|
||||||
jar, err := cookiejar.New(nil)
|
jar, err := cookiejar.New(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create cookie jar: %w", err)
|
return nil, &errors.IOError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to create cookie jar",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
@@ -41,7 +47,13 @@ func NewClient(domain string) (*Client, error) {
|
|||||||
Timeout: 30 * time.Second,
|
Timeout: 30 * time.Second,
|
||||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
if len(via) >= 10 {
|
if len(via) >= 10 {
|
||||||
return fmt.Errorf("too many redirects")
|
return &errors.APIError{
|
||||||
|
GarthHTTPError: errors.GarthHTTPError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Too many redirects",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@@ -54,12 +66,21 @@ func (c *Client) Login(email, password string) error {
|
|||||||
ssoClient := sso.NewClient(c.Domain)
|
ssoClient := sso.NewClient(c.Domain)
|
||||||
oauth2Token, mfaContext, err := ssoClient.Login(email, password)
|
oauth2Token, mfaContext, err := ssoClient.Login(email, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("SSO login failed: %w", err)
|
return &errors.AuthenticationError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "SSO login failed",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle MFA required
|
// Handle MFA required
|
||||||
if mfaContext != nil {
|
if mfaContext != nil {
|
||||||
return fmt.Errorf("MFA required - not implemented yet")
|
return &errors.AuthenticationError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "MFA required - not implemented yet",
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.OAuth2Token = oauth2Token
|
c.OAuth2Token = oauth2Token
|
||||||
@@ -68,7 +89,12 @@ func (c *Client) Login(email, password string) error {
|
|||||||
// Get user profile to set username
|
// Get user profile to set username
|
||||||
profile, err := c.GetUserProfile()
|
profile, err := c.GetUserProfile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get user profile after login: %w", err)
|
return &errors.AuthenticationError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to get user profile after login",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.Username = profile.UserName
|
c.Username = profile.UserName
|
||||||
|
|
||||||
@@ -81,7 +107,14 @@ func (c *Client) GetUserProfile() (*UserProfile, error) {
|
|||||||
|
|
||||||
req, err := http.NewRequest("GET", profileURL, nil)
|
req, err := http.NewRequest("GET", profileURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create profile request: %w", err)
|
return nil, &errors.APIError{
|
||||||
|
GarthHTTPError: errors.GarthHTTPError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to create profile request",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Authorization", c.AuthToken)
|
req.Header.Set("Authorization", c.AuthToken)
|
||||||
@@ -89,18 +122,38 @@ func (c *Client) GetUserProfile() (*UserProfile, error) {
|
|||||||
|
|
||||||
resp, err := c.HTTPClient.Do(req)
|
resp, err := c.HTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get user profile: %w", err)
|
return nil, &errors.APIError{
|
||||||
|
GarthHTTPError: errors.GarthHTTPError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to get user profile",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
return nil, fmt.Errorf("profile request failed with status %d: %s", resp.StatusCode, string(body))
|
return nil, &errors.APIError{
|
||||||
|
GarthHTTPError: errors.GarthHTTPError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Response: string(body),
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Profile request failed",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var profile UserProfile
|
var profile UserProfile
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&profile); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&profile); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse profile: %w", err)
|
return nil, &errors.IOError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to parse profile",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &profile, nil
|
return &profile, nil
|
||||||
@@ -116,7 +169,14 @@ func (c *Client) GetActivities(limit int) ([]types.Activity, error) {
|
|||||||
|
|
||||||
req, err := http.NewRequest("GET", activitiesURL, nil)
|
req, err := http.NewRequest("GET", activitiesURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create activities request: %w", err)
|
return nil, &errors.APIError{
|
||||||
|
GarthHTTPError: errors.GarthHTTPError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to create activities request",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Authorization", c.AuthToken)
|
req.Header.Set("Authorization", c.AuthToken)
|
||||||
@@ -124,18 +184,38 @@ func (c *Client) GetActivities(limit int) ([]types.Activity, error) {
|
|||||||
|
|
||||||
resp, err := c.HTTPClient.Do(req)
|
resp, err := c.HTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get activities: %w", err)
|
return nil, &errors.APIError{
|
||||||
|
GarthHTTPError: errors.GarthHTTPError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to get activities",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
return nil, fmt.Errorf("activities request failed with status %d: %s", resp.StatusCode, string(body))
|
return nil, &errors.APIError{
|
||||||
|
GarthHTTPError: errors.GarthHTTPError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Response: string(body),
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Activities request failed",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var activities []types.Activity
|
var activities []types.Activity
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&activities); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&activities); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse activities: %w", err)
|
return nil, &errors.IOError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to parse activities",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return activities, nil
|
return activities, nil
|
||||||
@@ -151,11 +231,21 @@ func (c *Client) SaveSession(filename string) error {
|
|||||||
|
|
||||||
data, err := json.MarshalIndent(session, "", " ")
|
data, err := json.MarshalIndent(session, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to marshal session: %w", err)
|
return &errors.IOError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to marshal session",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(filename, data, 0600); err != nil {
|
if err := os.WriteFile(filename, data, 0600); err != nil {
|
||||||
return fmt.Errorf("failed to write session file: %w", err)
|
return &errors.IOError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to write session file",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -165,12 +255,22 @@ func (c *Client) SaveSession(filename string) error {
|
|||||||
func (c *Client) LoadSession(filename string) error {
|
func (c *Client) LoadSession(filename string) error {
|
||||||
data, err := os.ReadFile(filename)
|
data, err := os.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read session file: %w", err)
|
return &errors.IOError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to read session file",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var session types.SessionData
|
var session types.SessionData
|
||||||
if err := json.Unmarshal(data, &session); err != nil {
|
if err := json.Unmarshal(data, &session); err != nil {
|
||||||
return fmt.Errorf("failed to unmarshal session: %w", err)
|
return &errors.IOError{
|
||||||
|
GarthError: errors.GarthError{
|
||||||
|
Message: "Failed to unmarshal session",
|
||||||
|
Cause: err,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Domain = session.Domain
|
c.Domain = session.Domain
|
||||||
|
|||||||
@@ -2,12 +2,38 @@ package errors
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// AuthenticationError represents authentication failures
|
// GarthError represents the base error type for all custom errors in Garth
|
||||||
type AuthenticationError struct {
|
type GarthError struct {
|
||||||
Message string
|
Message string
|
||||||
Cause error
|
Cause error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *GarthError) Error() string {
|
||||||
|
if e.Cause != nil {
|
||||||
|
return fmt.Sprintf("garth error: %s: %v", e.Message, e.Cause)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("garth error: %s", e.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GarthHTTPError represents HTTP-related errors in API calls
|
||||||
|
type GarthHTTPError struct {
|
||||||
|
GarthError
|
||||||
|
StatusCode int
|
||||||
|
Response string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *GarthHTTPError) Error() string {
|
||||||
|
if e.Cause != nil {
|
||||||
|
return fmt.Sprintf("HTTP error (%d): %s: %v", e.StatusCode, e.Response, e.Cause)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("HTTP error (%d): %s", e.StatusCode, e.Response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticationError represents authentication failures
|
||||||
|
type AuthenticationError struct {
|
||||||
|
GarthError
|
||||||
|
}
|
||||||
|
|
||||||
func (e *AuthenticationError) Error() string {
|
func (e *AuthenticationError) Error() string {
|
||||||
if e.Cause != nil {
|
if e.Cause != nil {
|
||||||
return fmt.Sprintf("authentication error: %s: %v", e.Message, e.Cause)
|
return fmt.Sprintf("authentication error: %s: %v", e.Message, e.Cause)
|
||||||
@@ -17,8 +43,7 @@ func (e *AuthenticationError) Error() string {
|
|||||||
|
|
||||||
// OAuthError represents OAuth token-related errors
|
// OAuthError represents OAuth token-related errors
|
||||||
type OAuthError struct {
|
type OAuthError struct {
|
||||||
Message string
|
GarthError
|
||||||
Cause error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *OAuthError) Error() string {
|
func (e *OAuthError) Error() string {
|
||||||
@@ -30,22 +55,12 @@ func (e *OAuthError) Error() string {
|
|||||||
|
|
||||||
// APIError represents errors from API calls
|
// APIError represents errors from API calls
|
||||||
type APIError struct {
|
type APIError struct {
|
||||||
StatusCode int
|
GarthHTTPError
|
||||||
Response string
|
|
||||||
Cause error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *APIError) Error() string {
|
|
||||||
if e.Cause != nil {
|
|
||||||
return fmt.Sprintf("API error (status %d): %s: %v", e.StatusCode, e.Response, e.Cause)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("API error (status %d): %s", e.StatusCode, e.Response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IOError represents file I/O errors
|
// IOError represents file I/O errors
|
||||||
type IOError struct {
|
type IOError struct {
|
||||||
Message string
|
GarthError
|
||||||
Cause error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *IOError) Error() string {
|
func (e *IOError) Error() string {
|
||||||
@@ -57,8 +72,8 @@ func (e *IOError) Error() string {
|
|||||||
|
|
||||||
// ValidationError represents input validation failures
|
// ValidationError represents input validation failures
|
||||||
type ValidationError struct {
|
type ValidationError struct {
|
||||||
Message string
|
GarthError
|
||||||
Field string
|
Field string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ValidationError) Error() string {
|
func (e *ValidationError) Error() string {
|
||||||
|
|||||||
Reference in New Issue
Block a user