diff --git a/README.md b/README.md index 2d28dfa..10fbffe 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # go-openlibrary -A Go wrapper around the OpenLibrary.org API: https://openlibrary.org/dev/docs/api/books \ No newline at end of file +A simple Go wrapper around the OpenLibrary.org API: . diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..120f81e --- /dev/null +++ b/client/client.go @@ -0,0 +1,101 @@ +package client + +import ( + "encoding/json" + "fmt" + "io" + "net/http" +) + +var ( + defaultAPI = "https://openlibrary.org/api/books" +) + +// Client +type Client struct { + httpClient http.Client + apipath string +} + +// NewClient returns a new Client struct using http.DefaultClient and default api path. +func NewClient() (*Client, error) { + return &Client{ + apipath: defaultAPI, + }, nil +} + +// SetHTTPClient updates the httpClient on the client with a manually-configured one. +// If unset, the client will fall back to using http.DefaultClient. +func (c *Client) SetHTTPClient(h http.Client) { + c.httpClient = h +} + +// SetAPIPath updates the hostname and path to the API; the default is "https://openlibrary.org/api/books". +// If APIPath is set to an empty string, the client will fall back to the default option. +func (c *Client) SetAPIPath(p string) { + c.apipath = p +} + +// GetByISBN fetches details for a book based on its ISBN10 or ISBN13. +func (c *Client) GetByISBN(k string) (*BookDetails, error) { + key := "ISBN:" + k + return c.fetch(key) +} + +// GetByOCLC fetches details for a book based on its WorldCat OCLC number. +func (c *Client) GetByOCLC(k string) (*BookDetails, error) { + key := "OCLC:" + k + return c.fetch(key) +} + +// GetByLCCN fetches details for a book based on its Library of Congress Control Number. +func (c *Client) GetByLCCN(k string) (*BookDetails, error) { + key := "LCCN:" + k + return c.fetch(key) +} + +// GetByOLID fetches details for a book based on its OpenLibrary ID number. +func (c *Client) GetByOLID(k string) (*BookDetails, error) { + key := "OLID:" + k + return c.fetch(key) +} + +// GetByRawKey fetches details for a book based on an arbitrary key; this method is provided +// as a future-proof fallback in case the OpenLibrary API adds more "bibkey" request types in the future. +// Most users should use one of the other lookup functions. +func (c *Client) GetByRawKey(k string) (*BookDetails, error) { + return c.fetch(k) +} + +func (c *Client) fetch(key string) (*BookDetails, error) { + path := c.apipath + if path == "" { + path = defaultAPI + } + + resp, err := c.httpClient.Get(path + "?bibkeys=%s&jscmd=data&format=json") + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("received non-200 status code: %d", resp.StatusCode) + } + + var result response + b, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if err = json.Unmarshal(b, &result); err != nil { + return nil, err + } + + details, ok := result[key] + if !ok { + return nil, fmt.Errorf("book not found") + } + + return &details, nil +} diff --git a/client/response.go b/client/response.go new file mode 100644 index 0000000..f944da6 --- /dev/null +++ b/client/response.go @@ -0,0 +1,71 @@ +package client + +type response map[string]BookDetails + +type BookDetails struct { + Authors []Authors `json:"authors"` + Classifications Classifications `json:"classifications"` + Cover Cover `json:"cover"` + Excerpts []Excerpts `json:"excerpts"` + Identifiers Identifiers `json:"identifiers"` + Links []Links `json:"links"` + NumberOfPages int `json:"number_of_pages"` + PublishDate string `json:"publish_date"` + Publishers []Publishers `json:"publishers"` + PublishPlaces []PublishPlaces `json:"publish_places"` + Subjects []Subjects `json:"subjects"` + Title string `json:"title"` + URL string `json:"url"` + Weight string `json:"weight"` +} + +type Authors struct { + Name string `json:"name"` + URL string `json:"url"` +} + +type Classifications struct { + DeweyDecimalClass []string `json:"dewey_decimal_class"` + LcClassifications []string `json:"lc_classifications"` +} + +type Cover struct { + Small string `json:"small"` + Medium string `json:"medium"` + Large string `json:"large"` +} + +type Excerpts struct { + Text string `json:"text"` + Comment string `json:"comment"` +} + +type Identifiers struct { + Amazon []string `json:"amazon"` + Goodreads []string `json:"goodreads"` + Google []string `json:"google"` + Isbn10 []string `json:"isbn_10"` + Isbn13 []string `json:"isbn_13"` + Lccn []string `json:"lccn"` + Librarything []string `json:"librarything"` + Oclc []string `json:"oclc"` + ProjectGutenberg []string `json:"project_gutenberg"` +} + +type Links struct { + Title string `json:"title"` + URL string `json:"url"` +} + +type Publishers struct { + Name string `json:"name"` +} + +type PublishPlaces struct { + Name string `json:"name"` +} + +type Subjects struct { + Name string `json:"name"` + URL string `json:"url"` +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a9fd6e2 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.yetaga.in/alazyreader/go-openlibrary + +go 1.16