1
0
mirror of https://github.com/dstotijn/go-notion.git synced 2024-11-24 08:42:26 +02:00
go-notion/client.go
2021-05-15 18:06:34 +02:00

223 lines
6.3 KiB
Go

package notion
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
)
const (
baseURL = "https://api.notion.com/v1"
apiVersion = "2021-05-13"
clientVersion = "0.0.0"
)
// Client is used for HTTP requests to the Notion API.
type Client struct {
apiKey string
httpClient *http.Client
}
// ClientOption is used to override default client behavior.
type ClientOption func(*Client)
// NewClient returns a new Client.
func NewClient(apiKey string, opts ...ClientOption) *Client {
c := &Client{
apiKey: apiKey,
httpClient: http.DefaultClient,
}
for _, opt := range opts {
opt(c)
}
return c
}
// WithHTTPClient overrides the default http.Client.
func WithHTTPClient(httpClient *http.Client) ClientOption {
return func(c *Client) {
c.httpClient = httpClient
}
}
func (c *Client) newRequest(ctx context.Context, method, url string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, method, baseURL+url, body)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", c.apiKey))
req.Header.Set("Notion-Version", apiVersion)
req.Header.Set("User-Agent", "go-notion/"+clientVersion)
if method == http.MethodPost || method == http.MethodPatch {
req.Header.Set("Content-Type", "application/json")
}
return req, nil
}
// FindDatabaseByID fetches a database by ID.
// See: https://developers.notion.com/reference/get-database
func (c *Client) FindDatabaseByID(ctx context.Context, id string) (db Database, err error) {
req, err := c.newRequest(ctx, http.MethodGet, "/databases/"+id, nil)
if err != nil {
return Database{}, fmt.Errorf("notion: invalid request: %w", err)
}
res, err := c.httpClient.Do(req)
if err != nil {
return Database{}, fmt.Errorf("notion: failed to make HTTP request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Database{}, fmt.Errorf("notion: failed to find database: %w", parseErrorResponse(res))
}
err = json.NewDecoder(res.Body).Decode(&db)
if err != nil {
return Database{}, fmt.Errorf("notion: failed to parse HTTP response: %w", err)
}
return db, nil
}
// QueryDatabase returns database contents, with optional filters, sorts and pagination.
// See: https://developers.notion.com/reference/post-database-query
func (c *Client) QueryDatabase(ctx context.Context, id string, query DatabaseQuery) (result DatabaseQueryResponse, err error) {
body := &bytes.Buffer{}
err = json.NewEncoder(body).Encode(query)
if err != nil {
return DatabaseQueryResponse{}, fmt.Errorf("notion: failed to encode filter to JSON: %w", err)
}
req, err := c.newRequest(ctx, http.MethodPost, fmt.Sprintf("/databases/%v/query", id), body)
if err != nil {
return DatabaseQueryResponse{}, fmt.Errorf("notion: invalid request: %w", err)
}
res, err := c.httpClient.Do(req)
if err != nil {
return DatabaseQueryResponse{}, fmt.Errorf("notion: failed to make HTTP request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return DatabaseQueryResponse{}, fmt.Errorf("notion: failed to find database: %w", parseErrorResponse(res))
}
err = json.NewDecoder(res.Body).Decode(&result)
if err != nil {
return DatabaseQueryResponse{}, fmt.Errorf("notion: failed to parse HTTP response: %w", err)
}
return result, nil
}
// FindPageByID fetches a page by ID.
// See: https://developers.notion.com/reference/get-page
func (c *Client) FindPageByID(ctx context.Context, id string) (page Page, err error) {
req, err := c.newRequest(ctx, http.MethodGet, "/pages/"+id, nil)
if err != nil {
return Page{}, fmt.Errorf("notion: invalid request: %w", err)
}
res, err := c.httpClient.Do(req)
if err != nil {
return Page{}, fmt.Errorf("notion: failed to make HTTP request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Page{}, fmt.Errorf("notion: failed to find page: %w", parseErrorResponse(res))
}
err = json.NewDecoder(res.Body).Decode(&page)
if err != nil {
return Page{}, fmt.Errorf("notion: failed to parse HTTP response: %w", err)
}
return page, nil
}
// CreatePage creates a new page in the specified database or as a child of an existing page.
// See: https://developers.notion.com/reference/post-page
func (c *Client) CreatePage(ctx context.Context, params CreatePageParams) (page Page, err error) {
if err := params.Validate(); err != nil {
return Page{}, fmt.Errorf("notion: invalid page params: %w", err)
}
body := &bytes.Buffer{}
err = json.NewEncoder(body).Encode(params)
if err != nil {
return Page{}, fmt.Errorf("notion: failed to encode body params to JSON: %w", err)
}
req, err := c.newRequest(ctx, http.MethodPost, "/pages", body)
if err != nil {
return Page{}, fmt.Errorf("notion: invalid request: %w", err)
}
res, err := c.httpClient.Do(req)
if err != nil {
return Page{}, fmt.Errorf("notion: failed to make HTTP request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Page{}, fmt.Errorf("notion: failed to create page: %w", parseErrorResponse(res))
}
err = json.NewDecoder(res.Body).Decode(&page)
if err != nil {
return Page{}, fmt.Errorf("notion: failed to parse HTTP response: %w", err)
}
return page, nil
}
// UpdatePageProps updates page property values for a page.
// See: https://developers.notion.com/reference/patch-page
func (c *Client) UpdatePageProps(ctx context.Context, pageID string, params UpdatePageParams) (page Page, err error) {
if err := params.Validate(); err != nil {
return Page{}, fmt.Errorf("notion: invalid page params: %w", err)
}
body := &bytes.Buffer{}
err = json.NewEncoder(body).Encode(params)
if err != nil {
return Page{}, fmt.Errorf("notion: failed to encode body params to JSON: %w", err)
}
req, err := c.newRequest(ctx, http.MethodPatch, "/pages/"+pageID, body)
if err != nil {
return Page{}, fmt.Errorf("notion: invalid request: %w", err)
}
res, err := c.httpClient.Do(req)
if err != nil {
return Page{}, fmt.Errorf("notion: failed to make HTTP request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Page{}, fmt.Errorf("notion: failed to update page properties: %w", parseErrorResponse(res))
}
err = json.NewDecoder(res.Body).Decode(&page)
if err != nil {
return Page{}, fmt.Errorf("notion: failed to parse HTTP response: %w", err)
}
return page, nil
}