Documentation
¶
Index ¶
- Constants
- Variables
- func IsNetworkError(err error) bool
- type APIBook
- type APIContribution
- type APIGoal
- type APIImage
- type APIReadingJournal
- type APIUserBook
- type APIUserBookRead
- type AuthError
- type BookDetail
- type Client
- func (c *Client) BookDetail(ctx context.Context, id int) (*BookDetail, error)
- func (c *Client) BookJournal(ctx context.Context, bookID int) ([]JournalEntry, error)
- func (c *Client) CreateJournalEntry(ctx context.Context, bookID int, event, entry string, privacy int) (int, error)
- func (c *Client) ExportLibrary(ctx context.Context) ([]APIUserBook, error)
- func (c *Client) GetAccountPrivacySetting(ctx context.Context) (int, error)
- func (c *Client) GetBookRatingsByIDs(ctx context.Context, ids []int) (map[int]model.Book, error)
- func (c *Client) GetMe(ctx context.Context) (int, string, error)
- func (c *Client) GoalSettings(ctx context.Context, id int) (GoalInput, error)
- func (c *Client) ImportReads(ctx context.Context, userBookID int, reads []APIUserBookRead) error
- func (c *Client) InsertProgressJournal(ctx context.Context, bookID, progressPages, totalPages, privacySettingID int) error
- func (c *Client) InsertUserBook(ctx context.Context, bookID int, statusID int) (int, error)
- func (c *Client) InsertUserBookRead(ctx context.Context, userBookID int, progressPages int) (*APIUserBookRead, error)
- func (c *Client) ListGoals(ctx context.Context) ([]APIGoal, error)
- func (c *Client) ListReadingJournals(ctx context.Context, userID int, since time.Time) ([]APIReadingJournal, error)
- func (c *Client) ListUserBooks(ctx context.Context, statusID int) ([]APIUserBook, error)
- func (c *Client) LookupISBN(ctx context.Context, isbn string) (int, error)
- func (c *Client) QueueBooks(ctx context.Context, listID int, books []RankedBook) error
- func (c *Client) ReadingQueue(ctx context.Context, create bool) (int, []RankedBook, error)
- func (c *Client) SaveGoal(ctx context.Context, id int, g GoalInput) (int, error)
- func (c *Client) SearchBooks(ctx context.Context, query string, perPage int, mode model.SearchMode) ([]model.SearchResult, error)
- func (c *Client) SetQueuePositions(ctx context.Context, listID int, rows []RankedBook) error
- func (c *Client) Trending(ctx context.Context, duration string, limit int) ([]APIBook, error)
- func (c *Client) UpdateReadProgress(ctx context.Context, userBookReadID int, progressPages int) error
- func (c *Client) UpdateUserBookRating(ctx context.Context, userBookID int, rating float64) error
- func (c *Client) UpdateUserBookReviewAndRating(ctx context.Context, userBookID int, rating float64, review string, ...) error
- func (c *Client) UpdateUserBookStatus(ctx context.Context, userBookID int, statusID int) error
- func (c *Client) UpsertUserBookReads(ctx context.Context, userBookID int, progressPages int) error
- type FlexibleFloat
- type FlexibleInt
- type GoalInput
- type ISBNMatchError
- type InsertReadingJournalResponse
- type InsertUserBookReadResponse
- type InsertUserBookResponse
- type JournalEntry
- type MeResponse
- type MeUser
- type NetworkError
- type RankedBook
- type RankedList
- type RatingBucket
- type ReadingJournalsResponse
- type SearchResponse
- type StatusError
- type TypesenseBookDoc
- type TypesenseHit
- type TypesenseImage
- type TypesenseResults
- type UpdateUserBookReadResponse
- type UpdateUserBookResponse
- type UpsertUserBookReadsResponse
- type UserBooksResponse
Constants ¶
const TBRListName = "Oku reading queue"
Variables ¶
var ( ErrBadRequest = errors.New("bad request") // 400 ErrInsufficientScope = errors.New("insufficient scope") // 403 ErrUnsupportedOp = errors.New("unsupported operation") // 403 ErrTopLevelLimit = errors.New("too many top-level queries") // 403 ErrOverCapacity = errors.New("request exceeds capacity") // 403 ErrForbidden = errors.New("forbidden") // 403 ErrNotFound = errors.New("not found") // 404 ErrTimeout = errors.New("server timeout") // 408 ErrRateLimited = errors.New("rate limited") // 429 )
Sentinels for errors.Is against a *StatusError. See https://docs.hardcover.app/api/getting-started/#api-response-codes
var Version = "dev"
Version identifies the build in the User-Agent header. It defaults to "dev"; main can overwrite it with the binary's version at startup.
Functions ¶
func IsNetworkError ¶
IsNetworkError reports whether an error should map to network/API exit code 2.
Types ¶
type APIBook ¶
type APIBook struct {
ID int `json:"id"`
Title string `json:"title"`
Pages int `json:"pages"`
Slug string `json:"slug"`
Rating float64 `json:"rating"`
RatingsCount int `json:"ratings_count"`
ReviewsCount int `json:"reviews_count"`
UsersCount int `json:"users_count"`
UsersReadCount int `json:"users_read_count"`
ReleaseDate *string `json:"release_date"`
CachedTags json.RawMessage `json:"cached_tags"`
Contributions []APIContribution `json:"contributions"`
Image *APIImage `json:"image"`
}
APIBook represents a book from the API.
type APIContribution ¶
type APIContribution struct {
Author struct {
Name string `json:"name"`
} `json:"author"`
}
APIContribution represents an author contribution to a book.
type APIGoal ¶ added in v0.6.0
type APIGoal struct {
ID int `json:"id"`
Goal int `json:"goal"`
Metric string `json:"metric"`
Progress FlexibleFloat `json:"progress"`
State string `json:"state"`
StartDate *string `json:"start_date"`
EndDate *string `json:"end_date"`
}
APIGoal represents a reading goal from the API.
type APIImage ¶
type APIImage struct {
URL string `json:"url"`
}
APIImage represents an image URL from the API.
type APIReadingJournal ¶ added in v0.6.0
type APIReadingJournal struct {
ID int `json:"id"`
ActionAt *string `json:"action_at"`
Event string `json:"event"`
}
APIReadingJournal represents a reading journal entry from the API.
type APIUserBook ¶
type APIUserBook struct {
ID int `json:"id"`
StatusID int `json:"status_id"`
Rating *float64 `json:"rating"`
ReviewRaw *string `json:"review_raw"`
ReviewedAt *string `json:"reviewed_at"`
UpdatedAt *string `json:"updated_at"`
UserBookReads []APIUserBookRead `json:"user_book_reads"`
Book APIBook `json:"book"`
}
APIUserBook represents a user-book relationship from the API.
type APIUserBookRead ¶
type APIUserBookRead struct {
ID int `json:"id"`
ProgressPages int `json:"progress_pages"`
StartedAt *string `json:"started_at"`
FinishedAt *string `json:"finished_at"`
}
APIUserBookRead represents a reading progress entry from the API.
type AuthError ¶ added in v0.4.5
type AuthError struct{}
AuthError reports that the API response did not contain an authenticated user.
type BookDetail ¶ added in v0.10.0
type BookDetail struct {
APIBook
Description string `json:"description"`
Headline string `json:"headline"`
EditionsCount int `json:"editions_count"`
RatingsDistribution json.RawMessage `json:"ratings_distribution"`
}
BookDetail contains community metadata fetched on demand, outside library sync.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps the machinebox/graphql client with authentication and rate limiting.
func NewClient ¶
NewClient creates a new Hardcover API client with the given auth token. The token is normalised to include a "Bearer " prefix if missing.
func NewOAuthClient ¶ added in v0.12.0
func NewOAuthClient(ctx context.Context, src oauth2.TokenSource) *Client
NewOAuthClient creates a Hardcover API client authorized by src. The Authorization header is set per-request by src's own oauth2.Transport, which refreshes an expired token on demand.
func (*Client) BookDetail ¶ added in v0.10.0
func (*Client) BookJournal ¶ added in v0.10.0
func (*Client) CreateJournalEntry ¶ added in v0.10.0
func (*Client) ExportLibrary ¶ added in v0.10.0
func (c *Client) ExportLibrary(ctx context.Context) ([]APIUserBook, error)
ExportLibrary pages by primary key order and includes every read, not only the latest.
func (*Client) GetAccountPrivacySetting ¶ added in v0.6.0
GetAccountPrivacySetting returns the user's default privacy setting ID (1=Public, 2=Followers, 3=Private).
func (*Client) GetBookRatingsByIDs ¶
GetBookRatingsByIDs fetches rating metadata for a set of book IDs.
func (*Client) GoalSettings ¶ added in v0.10.0
GoalSettings preserves filters, dates, description and privacy when editing a target.
func (*Client) ImportReads ¶ added in v0.10.0
ImportReads replaces read history only for a newly inserted library record.
func (*Client) InsertProgressJournal ¶ added in v0.6.0
func (c *Client) InsertProgressJournal(ctx context.Context, bookID, progressPages, totalPages, privacySettingID int) error
InsertProgressJournal creates a "progress_updated" reading journal entry. Hardcover's website builds its activity heatmap from reading_journals, which the site creates on every progress update; update_user_book_read alone never produces one, so API-driven updates stay invisible on the calendar without this.
func (*Client) InsertUserBook ¶
InsertUserBook adds a book to the user's library with the given status and returns the user_book ID.
func (*Client) InsertUserBookRead ¶
func (c *Client) InsertUserBookRead(ctx context.Context, userBookID int, progressPages int) (*APIUserBookRead, error)
InsertUserBookRead creates a new reading progress entry for the given user book.
func (*Client) ListReadingJournals ¶ added in v0.6.0
func (c *Client) ListReadingJournals(ctx context.Context, userID int, since time.Time) ([]APIReadingJournal, error)
ListReadingJournals returns the user's reading journal entries since the given date (inclusive). Only the fields needed for activity aggregation are fetched.
func (*Client) ListUserBooks ¶
ListUserBooks returns the user's books filtered by status ID.
func (*Client) LookupISBN ¶ added in v0.10.0
func (*Client) QueueBooks ¶ added in v0.10.0
QueueBooks appends at most five entries per request, respecting Hardcover's top-level field limit and avoiding one round trip per book on initial setup.
func (*Client) ReadingQueue ¶ added in v0.10.0
func (*Client) SearchBooks ¶
func (c *Client) SearchBooks(ctx context.Context, query string, perPage int, mode model.SearchMode) ([]model.SearchResult, error)
SearchBooks searches for books by query string and returns parsed results. User input travels as a GraphQL variable, never interpolated into the query document, so no escaping is needed.
func (*Client) SetQueuePositions ¶ added in v0.10.0
SetQueuePositions updates the affected ranks in one database mutation request.
func (*Client) UpdateReadProgress ¶
func (c *Client) UpdateReadProgress(ctx context.Context, userBookReadID int, progressPages int) error
UpdateReadProgress updates the progress pages on an existing user book read.
func (*Client) UpdateUserBookRating ¶ added in v0.4.1
UpdateUserBookRating updates a user-book rating.
func (*Client) UpdateUserBookReviewAndRating ¶ added in v0.4.1
func (c *Client) UpdateUserBookReviewAndRating( ctx context.Context, userBookID int, rating float64, review string, reviewedAt string, ) error
UpdateUserBookReviewAndRating updates review text, reviewed timestamp, and rating.
func (*Client) UpdateUserBookStatus ¶
UpdateUserBookStatus changes the status of an existing user book.
type FlexibleFloat ¶
type FlexibleFloat float64
FlexibleFloat handles APIs that may return a number or a quoted number.
func (*FlexibleFloat) UnmarshalJSON ¶
func (v *FlexibleFloat) UnmarshalJSON(data []byte) error
type FlexibleInt ¶
type FlexibleInt int
FlexibleInt handles APIs that sometimes return a number and sometimes a quoted number.
func (*FlexibleInt) UnmarshalJSON ¶
func (v *FlexibleInt) UnmarshalJSON(data []byte) error
type GoalInput ¶ added in v0.10.0
type ISBNMatchError ¶ added in v0.10.0
ISBNMatchError distinguishes unmatched data from API/authentication failures.
func (*ISBNMatchError) Error ¶ added in v0.10.0
func (e *ISBNMatchError) Error() string
type InsertReadingJournalResponse ¶ added in v0.6.0
type InsertReadingJournalResponse struct {
InsertReadingJournal struct {
ReadingJournal *struct {
ID int `json:"id"`
} `json:"reading_journal"`
} `json:"insert_reading_journal"`
}
InsertReadingJournalResponse is the response shape for inserting a reading journal entry.
type InsertUserBookReadResponse ¶
type InsertUserBookReadResponse struct {
InsertUserBookRead struct {
ID *int `json:"id"`
Error *string `json:"error"`
UserBookRead *APIUserBookRead `json:"user_book_read"`
} `json:"insert_user_book_read"`
}
InsertUserBookReadResponse is the response shape for inserting a user book read.
type InsertUserBookResponse ¶
type InsertUserBookResponse struct {
InsertUserBook struct {
ID *int `json:"id"`
UserBook *struct {
ID int `json:"id"`
} `json:"user_book"`
Error *string `json:"error"`
} `json:"insert_user_book"`
}
InsertUserBookResponse is the response shape for inserting a user book.
type JournalEntry ¶ added in v0.10.0
type MeResponse ¶
type MeResponse struct {
Me []MeUser `json:"me"`
}
MeResponse is the response shape for the me query.
type MeUser ¶
type MeUser struct {
ID int `json:"id"`
Username string `json:"username"`
AccountPrivacySettingID *int `json:"account_privacy_setting_id"`
UserBooks []APIUserBook `json:"user_books"`
Goals []APIGoal `json:"goals"`
}
MeUser represents a single user entry from the me query.
type NetworkError ¶
type NetworkError struct {
Err error
}
NetworkError marks transient request failures (timeouts, 429, 5xx, transport errors). The CLI maps these to exit code 2.
func (*NetworkError) Error ¶
func (e *NetworkError) Error() string
func (*NetworkError) Unwrap ¶
func (e *NetworkError) Unwrap() error
type RankedBook ¶ added in v0.10.0
type RankedList ¶ added in v0.10.0
type RatingBucket ¶ added in v0.10.0
func ParseRatingDistribution ¶ added in v0.10.0
func ParseRatingDistribution(raw json.RawMessage) ([]RatingBucket, error)
ParseRatingDistribution accepts Hardcover's array and older keyed-object shape.
type ReadingJournalsResponse ¶ added in v0.6.0
type ReadingJournalsResponse struct {
ReadingJournals []APIReadingJournal `json:"reading_journals"`
}
ReadingJournalsResponse is the response shape for listing reading journals.
type SearchResponse ¶
type SearchResponse struct {
Search struct {
Results json.RawMessage `json:"results"`
} `json:"search"`
}
SearchResponse is the response shape for book search queries. The results field contains raw Typesense JSON.
type StatusError ¶ added in v0.4.4
type StatusError struct {
Code int
// APIError, Description and Scope are parsed from a JSON error body;
// all are empty when the body isn't JSON.
APIError string // e.g. "invalid_token", "insufficient_scope"
Description string
Scope string // permissions missing, for insufficient_scope
// Body holds up to maxErrorBody bytes of the response body, with runs of
// whitespace collapsed, so failures carry the server's explanation
// instead of just a status number.
Body string
// RetryAfter is the parsed Retry-After header, or 0 when absent or unparseable.
RetryAfter time.Duration
}
StatusError reports a non-2xx HTTP response from the API. The graphql library never exposes the status itself, so statusTransport converts non-2xx responses into this typed error at the transport layer.
func (*StatusError) Error ¶ added in v0.4.4
func (e *StatusError) Error() string
func (*StatusError) Is ¶ added in v0.12.0
func (e *StatusError) Is(target error) bool
Is lets callers match a StatusError with errors.Is. The specific 403 variants also satisfy ErrForbidden.
type TypesenseBookDoc ¶
type TypesenseBookDoc struct {
ID FlexibleInt `json:"id"`
Title string `json:"title"`
AuthorNames []string `json:"author_names"`
Pages FlexibleInt `json:"pages"`
Slug string `json:"slug"`
Rating FlexibleFloat `json:"rating"`
Ratings FlexibleInt `json:"ratings_count"`
Image *TypesenseImage `json:"image"`
}
TypesenseBookDoc represents a book document from Typesense search.
type TypesenseHit ¶ added in v0.4.5
type TypesenseHit struct {
Document TypesenseBookDoc `json:"document"`
}
TypesenseHit represents one search hit.
type TypesenseImage ¶
type TypesenseImage struct {
URL string `json:"url"`
}
TypesenseImage represents an image in Typesense search results.
type TypesenseResults ¶
type TypesenseResults struct {
Hits []TypesenseHit `json:"hits"`
}
TypesenseResults represents parsed Typesense search results.
func (*TypesenseResults) UnmarshalJSON ¶ added in v0.4.5
func (r *TypesenseResults) UnmarshalJSON(data []byte) error
UnmarshalJSON skips malformed individual hits while retaining valid results.
type UpdateUserBookReadResponse ¶
type UpdateUserBookReadResponse struct {
UpdateUserBookRead struct {
ID *int `json:"id"`
Error *string `json:"error"`
} `json:"update_user_book_read"`
}
UpdateUserBookReadResponse is the response shape for updating a user book read.
type UpdateUserBookResponse ¶
type UpdateUserBookResponse struct {
UpdateUserBook struct {
ID *int `json:"id"`
Error *string `json:"error"`
} `json:"update_user_book"`
}
UpdateUserBookResponse is the response shape for updating a user book.
type UpsertUserBookReadsResponse ¶
type UpsertUserBookReadsResponse struct {
UpsertUserBookReads struct {
Error *string `json:"error"`
UserBookID *int `json:"user_book_id"`
} `json:"upsert_user_book_reads"`
}
UpsertUserBookReadsResponse is the response shape for upserting user book reads.
type UserBooksResponse ¶
type UserBooksResponse struct {
Me []MeUser `json:"me"`
}
UserBooksResponse is the response shape for listing user books.