mirror of
https://github.com/dstotijn/go-notion.git
synced 2025-06-15 00:05:04 +02:00
Initial commit
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 David Stotijn
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
43
README.md
Normal file
43
README.md
Normal file
@ -0,0 +1,43 @@
|
||||
# go-notion
|
||||
|
||||
Go client for the [Notion API](https://developers.notion.com/reference).
|
||||
|
||||
## Status
|
||||
|
||||
🐣 Early development
|
||||
|
||||
## API endpoints
|
||||
|
||||
### Databases
|
||||
|
||||
- [x] [Retrieve a database](database.go)
|
||||
- [ ] Query a database
|
||||
- [ ] List databases
|
||||
|
||||
## Pages
|
||||
|
||||
- [ ] Retrieve a page
|
||||
- [ ] Create a page
|
||||
- [ ] Update page properties
|
||||
|
||||
### Blocks
|
||||
|
||||
- [ ] Retrieve block children
|
||||
- [ ] Append block children
|
||||
|
||||
### Users
|
||||
|
||||
- [ ] Retrieve a user
|
||||
- [ ] List all users
|
||||
|
||||
### Search
|
||||
|
||||
- [ ] Search
|
||||
|
||||
## License
|
||||
|
||||
[MIT License](LICENSE)
|
||||
|
||||
---
|
||||
|
||||
© 2021 David Stotijn — [Twitter](https://twitter.com/dstotijn), [Email](mailto:dstotijn@gmail.com)
|
54
client.go
Normal file
54
client.go
Normal file
@ -0,0 +1,54 @@
|
||||
package notion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
baseURL = "https://api.notion.com/v1"
|
||||
apiVersion = "2021-05-13"
|
||||
)
|
||||
|
||||
// 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(method, url string, body io.Reader) (*http.Request, error) {
|
||||
req, err := http.NewRequest(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)
|
||||
|
||||
return req, nil
|
||||
}
|
113
database.go
Normal file
113
database.go
Normal file
@ -0,0 +1,113 @@
|
||||
package notion
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Database is a resource on the Notion platform.
|
||||
// See: https://developers.notion.com/reference/database
|
||||
type Database struct {
|
||||
ID string `json:"id"`
|
||||
CreatedTime time.Time `json:"created_time"`
|
||||
LastEditedTime time.Time `json:"last_edited_time"`
|
||||
Title []RichText `json:"title"`
|
||||
Properties DatabaseProperties `json:"properties"`
|
||||
}
|
||||
|
||||
type DatabaseProperties map[string]interface{}
|
||||
|
||||
// Database property types.
|
||||
type (
|
||||
TitleDatabaseProperty struct{}
|
||||
RichTextDatabaseProperty struct{}
|
||||
DateDatabaseProperty struct{}
|
||||
PeopleDatabaseProperty struct{}
|
||||
FileDatabaseProperty struct{}
|
||||
CheckboxDatabaseProperty struct{}
|
||||
URLDatabaseProperty struct{}
|
||||
EmailDatabaseProperty struct{}
|
||||
PhoneNumberDatabaseProperty struct{}
|
||||
CreatedTimeDatabaseProperty struct{}
|
||||
CreatedByDatabaseProperty struct{}
|
||||
LastEditedTimeDatabaseProperty struct{}
|
||||
LastEditedByDatabaseProperty struct{}
|
||||
|
||||
NumberDatabaseProperty struct {
|
||||
Format string `json:"format"`
|
||||
}
|
||||
SelectDatabaseProperty struct {
|
||||
Options []SelectOptions `json:"options"`
|
||||
}
|
||||
FormulaDatabaseProperty struct {
|
||||
Expression string `json:"expression"`
|
||||
}
|
||||
RelationDatabaseProperty struct {
|
||||
DatabaseID string `json:"database_id"`
|
||||
SyncedPropName *string `json:"synced_property_name"`
|
||||
SyncedPropID *string `json:"synced_property_id"`
|
||||
}
|
||||
RollupDatabaseProperty struct {
|
||||
RelationPropName string `json:"relation_property_name"`
|
||||
RelationPropID string `json:"relation_property_id"`
|
||||
RollupPropName string `json:"rollup_property_name"`
|
||||
RollupPropID string `json:"rollup_property_id"`
|
||||
Function string `json:"function"`
|
||||
}
|
||||
)
|
||||
|
||||
type SelectOptions struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
type DatabaseProperty struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
|
||||
Title *TitleDatabaseProperty `json:"title"`
|
||||
RichText *RichTextDatabaseProperty `json:"rich_text"`
|
||||
Number *NumberDatabaseProperty `json:"number"`
|
||||
Select *SelectDatabaseProperty `json:"select"`
|
||||
MultiSelect *SelectDatabaseProperty `json:"multi_select"`
|
||||
Date *DateDatabaseProperty `json:"date"`
|
||||
People *PeopleDatabaseProperty `json:"people"`
|
||||
File *FileDatabaseProperty `json:"file"`
|
||||
Checkbox *CheckboxDatabaseProperty `json:"checkbox"`
|
||||
URL *URLDatabaseProperty `json:"url"`
|
||||
Email *EmailDatabaseProperty `json:"email"`
|
||||
PhoneNumber *PhoneNumberDatabaseProperty `json:"phone_number"`
|
||||
Formula *FormulaDatabaseProperty `json:"formula"`
|
||||
Relation *RelationDatabaseProperty `json:"relation"`
|
||||
Rollup *RollupDatabaseProperty `json:"rollup"`
|
||||
CreatedTime *CreatedTimeDatabaseProperty `json:"created_time"`
|
||||
CreatedBy *CreatedByDatabaseProperty `json:"created_by"`
|
||||
LastEditedTime *LastEditedTimeDatabaseProperty `json:"last_edited_time"`
|
||||
LastEditedBy *LastEditedByDatabaseProperty `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
func (c *Client) FindDatabaseByID(id string) (db Database, err error) {
|
||||
req, err := c.newRequest("GET", "/databases/"+id, nil)
|
||||
if err != nil {
|
||||
return Database{}, fmt.Errorf("invalid URL: %w", err)
|
||||
}
|
||||
|
||||
res, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return Database{}, fmt.Errorf("failed to make HTTP request: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
return Database{}, fmt.Errorf("notion: failed to get database: %w", parseErrorResponse(res))
|
||||
}
|
||||
|
||||
err = json.NewDecoder(res.Body).Decode(&db)
|
||||
if err != nil {
|
||||
return Database{}, fmt.Errorf("failed to parse HTTP response: %w", err)
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
75
error.go
Normal file
75
error.go
Normal file
@ -0,0 +1,75 @@
|
||||
package notion
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// See: https://developers.notion.com/reference/errors.
|
||||
var (
|
||||
ErrInvalidJSON = errors.New("notion: request body could not be decoded as JSON")
|
||||
ErrInvalidRequestURL = errors.New("notion: request URL is not valid")
|
||||
ErrInvalidRequest = errors.New("notion: request is not supported")
|
||||
ErrValidation = errors.New("notion: request body does not match the schema for the expected parameters")
|
||||
ErrUnauthorized = errors.New("notion: bearer token is not valid")
|
||||
ErrRestrictedResource = errors.New("notion: client doesn't have permission to perform this operation")
|
||||
ErrObjectNotFound = errors.New("notion: the resource does not exist")
|
||||
ErrConflict = errors.New("notion: the transaction could not be completed, potentially due to a data collision")
|
||||
ErrRateLimited = errors.New("notion: this request exceeds the number of requests allowed")
|
||||
ErrInternalServer = errors.New("notion: an unexpected error occurred")
|
||||
ErrServiceUnavailable = errors.New("notion: service is unavailable")
|
||||
)
|
||||
|
||||
type APIError struct {
|
||||
Object string `json:"object"`
|
||||
Status int `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Error implements `error`.
|
||||
func (err *APIError) Error() string {
|
||||
return fmt.Sprintf("%v (code: %v, status: %v)", err.Message, err.Code, err.Status)
|
||||
}
|
||||
|
||||
func (err *APIError) Unwrap() error {
|
||||
switch err.Code {
|
||||
case "invalid_json":
|
||||
return ErrInvalidJSON
|
||||
case "invalid_request_url":
|
||||
return ErrInvalidRequestURL
|
||||
case "invalid_request":
|
||||
return ErrInvalidRequest
|
||||
case "validation_error":
|
||||
return ErrValidation
|
||||
case "unauthorized":
|
||||
return ErrUnauthorized
|
||||
case "restricted_resource":
|
||||
return ErrRestrictedResource
|
||||
case "object_not_found":
|
||||
return ErrObjectNotFound
|
||||
case "conflict_error":
|
||||
return ErrConflict
|
||||
case "rate_limited":
|
||||
return ErrRateLimited
|
||||
case "internal_server_error":
|
||||
return ErrInternalServer
|
||||
case "service_unavailable":
|
||||
return ErrServiceUnavailable
|
||||
default:
|
||||
return fmt.Errorf("notion: unknown error (%v)", err.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func parseErrorResponse(res *http.Response) error {
|
||||
var apiErr APIError
|
||||
|
||||
err := json.NewDecoder(res.Body).Decode(&apiErr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse error from HTTP response: %w", err)
|
||||
}
|
||||
|
||||
return &apiErr
|
||||
}
|
58
rich_text.go
Normal file
58
rich_text.go
Normal file
@ -0,0 +1,58 @@
|
||||
package notion
|
||||
|
||||
import "time"
|
||||
|
||||
type RichText struct {
|
||||
PlainText string `json:"plain_text"`
|
||||
HRef *string `json:"href"`
|
||||
Annotations Annotations `json:"annotations"`
|
||||
Type string `json:"type"`
|
||||
|
||||
Text *Text `json:"text"`
|
||||
Mention *Mention `json:"mention"`
|
||||
Equation *Equation `json:"equation"`
|
||||
}
|
||||
|
||||
type Equation struct {
|
||||
Expression string `json:"expression"`
|
||||
}
|
||||
|
||||
type Annotations struct {
|
||||
Bold bool `json:"bold"`
|
||||
Italic bool `json:"italic"`
|
||||
Strikethrough bool `json:"strikethrough"`
|
||||
Underline bool `json:"underline"`
|
||||
Code bool `json:"code"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
type Mention struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
User *User `json:"user"`
|
||||
Page *PageMention `json:"page"`
|
||||
Database *DatabaseMention `json:"database"`
|
||||
Date *Date `json:"date"`
|
||||
}
|
||||
|
||||
type Date struct {
|
||||
Start time.Time `json:"start"`
|
||||
End *time.Time `json:"end"`
|
||||
}
|
||||
|
||||
type Text struct {
|
||||
Content string `json:"content"`
|
||||
Link *Link `json:"link"`
|
||||
}
|
||||
|
||||
type Link struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type PageMention struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type DatabaseMention struct {
|
||||
ID string `json:"id"`
|
||||
}
|
17
user.go
Normal file
17
user.go
Normal file
@ -0,0 +1,17 @@
|
||||
package notion
|
||||
|
||||
type Person struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type Bot struct{}
|
||||
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
AvatarURL *string `json:"avatar_url"`
|
||||
|
||||
Person *Person `json:"person"`
|
||||
Bot *Bot `json:"bot"`
|
||||
}
|
Reference in New Issue
Block a user