mirror of
https://github.com/dstotijn/go-notion.git
synced 2025-06-08 23:46:12 +02:00
Add "create a page" endpoint
This commit is contained in:
parent
955d10b769
commit
b2af8b7d0b
@ -16,7 +16,7 @@ Go client for the [Notion API](https://developers.notion.com/reference).
|
|||||||
## Pages
|
## Pages
|
||||||
|
|
||||||
- [x] [Retrieve a page](client.go)
|
- [x] [Retrieve a page](client.go)
|
||||||
- [ ] Create a page
|
- [x] [Create a page](client.go)
|
||||||
- [ ] Update page properties
|
- [ ] Update page properties
|
||||||
|
|
||||||
### Blocks
|
### Blocks
|
||||||
|
57
block.go
Normal file
57
block.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package notion
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Block represents content on the Notion platform.
|
||||||
|
// See: https://developers.notion.com/reference/block
|
||||||
|
type Block struct {
|
||||||
|
Object string `json:"object"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Type BlockType `json:"type"`
|
||||||
|
CreatedTime *time.Time `json:"created_time,omitempty"`
|
||||||
|
LastEditedTime *time.Time `json:"last_edited_time,omitempty"`
|
||||||
|
HasChildren bool `json:"has_children"`
|
||||||
|
|
||||||
|
Paragraph *RichTextBlock `json:"paragraph,omitempty"`
|
||||||
|
Heading1 *Heading `json:"heading_1,omitempty"`
|
||||||
|
Heading2 *Heading `json:"heading_2,omitempty"`
|
||||||
|
Heading3 *Heading `json:"heading_3,omitempty"`
|
||||||
|
BulletedListItem *RichTextBlock `json:"bulleted_list_item,omitempty"`
|
||||||
|
NumberedListItem *RichText `json:"numbered_list_item,omitempty"`
|
||||||
|
ToDo *ToDo `json:"to_do,omitempty"`
|
||||||
|
Toggle *RichTextBlock `json:"toggle,omitempty"`
|
||||||
|
ChildPage *ChildPage `json:"rich_text,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RichTextBlock struct {
|
||||||
|
Text []RichText `json:"text"`
|
||||||
|
Children []Block `json:"children,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Heading struct {
|
||||||
|
Text []RichText `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToDo struct {
|
||||||
|
RichTextBlock
|
||||||
|
Checked *bool `json:"checked,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChildPage struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlockType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
BlockTypeParagraph BlockType = "paragraph"
|
||||||
|
BlockTypeHeading1 BlockType = "heading_1"
|
||||||
|
BlockTypeHeading2 BlockType = "heading_2"
|
||||||
|
BlockTypeHeading3 BlockType = "heading_3"
|
||||||
|
BlockTypeBulletedListItem BlockType = "bulleted_list_item"
|
||||||
|
BlockTypeNumberedListItem BlockType = "numbered_list_item"
|
||||||
|
BlockTypeToDo BlockType = "to_do"
|
||||||
|
BlockTypeToggle BlockType = "toggle"
|
||||||
|
BlockTypeChildPage BlockType = "child_page"
|
||||||
|
BlockTypeUnsupported BlockType = "unsupported"
|
||||||
|
)
|
40
client.go
40
client.go
@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -146,3 +147,42 @@ func (c *Client) FindPageByID(ctx context.Context, id string) (page Page, err er
|
|||||||
|
|
||||||
return page, nil
|
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{}
|
||||||
|
|
||||||
|
_ = json.NewEncoder(os.Stderr).Encode(params)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
56
database.go
56
database.go
@ -49,8 +49,8 @@ type SelectOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DatabaseProperty struct {
|
type DatabaseProperty struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Type string `json:"type"`
|
Type DatabasePropertyType `json:"type"`
|
||||||
|
|
||||||
Number *NumberMetadata `json:"number"`
|
Number *NumberMetadata `json:"number"`
|
||||||
Select *SelectMetadata `json:"select"`
|
Select *SelectMetadata `json:"select"`
|
||||||
@ -136,19 +136,19 @@ type MultiSelectDatabaseQueryFilter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DateDatabaseQueryFilter struct {
|
type DateDatabaseQueryFilter struct {
|
||||||
Equals time.Time `json:"equals,omitempty"`
|
Equals *time.Time `json:"equals,omitempty"`
|
||||||
Before time.Time `json:"before,omitempty"`
|
Before *time.Time `json:"before,omitempty"`
|
||||||
After time.Time `json:"after,omitempty"`
|
After *time.Time `json:"after,omitempty"`
|
||||||
OnOrBefore time.Time `json:"on_or_before,omitempty"`
|
OnOrBefore *time.Time `json:"on_or_before,omitempty"`
|
||||||
OnOrAfter time.Time `json:"on_or_after,omitempty"`
|
OnOrAfter *time.Time `json:"on_or_after,omitempty"`
|
||||||
IsEmpty bool `json:"is_empty,omitempty"`
|
IsEmpty bool `json:"is_empty,omitempty"`
|
||||||
IsNotEmpty bool `json:"is_not_empty,omitempty"`
|
IsNotEmpty bool `json:"is_not_empty,omitempty"`
|
||||||
PastWeek *struct{} `json:"past_week,omitempty"`
|
PastWeek *struct{} `json:"past_week,omitempty"`
|
||||||
PastMonth *struct{} `json:"past_month,omitempty"`
|
PastMonth *struct{} `json:"past_month,omitempty"`
|
||||||
PastYear *struct{} `json:"past_year,omitempty"`
|
PastYear *struct{} `json:"past_year,omitempty"`
|
||||||
NextWeek *struct{} `json:"next_week,omitempty"`
|
NextWeek *struct{} `json:"next_week,omitempty"`
|
||||||
NextMonth *struct{} `json:"next_month,omitempty"`
|
NextMonth *struct{} `json:"next_month,omitempty"`
|
||||||
NextYear *struct{} `json:"next_year,omitempty"`
|
NextYear *struct{} `json:"next_year,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PeopleDatabaseQueryFilter struct {
|
type PeopleDatabaseQueryFilter struct {
|
||||||
@ -184,11 +184,33 @@ type DatabaseQuerySort struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
SortTimestamp string
|
DatabasePropertyType string
|
||||||
SortDirection string
|
SortTimestamp string
|
||||||
|
SortDirection string
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// Database property type enums.
|
||||||
|
DBPropTypeTitle DatabasePropertyType = "title"
|
||||||
|
DBPropTypeRichText DatabasePropertyType = "rich_text"
|
||||||
|
DBPropTypeNumber DatabasePropertyType = "number"
|
||||||
|
DBPropTypeSelect DatabasePropertyType = "select"
|
||||||
|
DBPropTypeMultiSelect DatabasePropertyType = "multi_select"
|
||||||
|
DBPropTypeDate DatabasePropertyType = "date"
|
||||||
|
DBPropTypePeople DatabasePropertyType = "people"
|
||||||
|
DBPropTypeFile DatabasePropertyType = "file"
|
||||||
|
DBPropTypeCheckbox DatabasePropertyType = "checkbox"
|
||||||
|
DBPropTypeURL DatabasePropertyType = "url"
|
||||||
|
DBPropTypeEmail DatabasePropertyType = "email"
|
||||||
|
DBPropTypePhoneNumber DatabasePropertyType = "phone_number"
|
||||||
|
DBPropTypeFormula DatabasePropertyType = "formula"
|
||||||
|
DBPropTypeRelation DatabasePropertyType = "relation"
|
||||||
|
DBPropTypeRollup DatabasePropertyType = "rollup"
|
||||||
|
DBPropTypeCreatedTime DatabasePropertyType = "created_time"
|
||||||
|
DBPropTypeCreatedBy DatabasePropertyType = "created_by"
|
||||||
|
DBPropTypeLastEditedTime DatabasePropertyType = "last_edited_time"
|
||||||
|
DBPropTypeLastEditedBy DatabasePropertyType = "last_edited_by"
|
||||||
|
|
||||||
// Sort timestamp enums.
|
// Sort timestamp enums.
|
||||||
SortTimeStampCreatedTime SortTimestamp = "created_time"
|
SortTimeStampCreatedTime SortTimestamp = "created_time"
|
||||||
SortTimeStampLastEditedTime SortTimestamp = "last_edited_time"
|
SortTimeStampLastEditedTime SortTimestamp = "last_edited_time"
|
||||||
|
83
page.go
83
page.go
@ -2,6 +2,7 @@ package notion
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -24,15 +25,17 @@ type Page struct {
|
|||||||
type PageParent struct {
|
type PageParent struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
|
||||||
PageID *string `json:"page_id"`
|
PageID *string `json:"page_id,omitempty"`
|
||||||
DatabaseID *string `json:"database_id"`
|
DatabaseID *string `json:"database_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PageProperties are properties of a page whose parent is a page or a workspace.
|
// PageProperties are properties of a page whose parent is a page or a workspace.
|
||||||
type PageProperties struct {
|
type PageProperties struct {
|
||||||
Title struct {
|
Title PageTitle `json:"title"`
|
||||||
Title []RichText `json:"title"`
|
}
|
||||||
} `json:"title"`
|
|
||||||
|
type PageTitle struct {
|
||||||
|
Title []RichText `json:"title"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DatabasePageProperties are properties of a page whose parent is a database.
|
// DatabasePageProperties are properties of a page whose parent is a database.
|
||||||
@ -45,6 +48,76 @@ type DatabasePageProperty struct {
|
|||||||
MultiSelect []SelectOptions `json:"multi_select"`
|
MultiSelect []SelectOptions `json:"multi_select"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreatePageParams are the params used for creating a page.
|
||||||
|
type CreatePageParams struct {
|
||||||
|
ParentType ParentType
|
||||||
|
ParentID string
|
||||||
|
|
||||||
|
// Either DatabasePageProperties or Title must be not nil.
|
||||||
|
DatabasePageProperties *DatabasePageProperties
|
||||||
|
Title []RichText
|
||||||
|
|
||||||
|
// Optionally, children blocks are added to the page.
|
||||||
|
Children []Block
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParentType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ParentTypeDatabase ParentType = "database_id"
|
||||||
|
ParentTypePage ParentType = "page_id"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p CreatePageParams) Validate() error {
|
||||||
|
if p.ParentType == "" {
|
||||||
|
return errors.New("parent type is required")
|
||||||
|
}
|
||||||
|
if p.ParentID == "" {
|
||||||
|
return errors.New("parent ID is required")
|
||||||
|
}
|
||||||
|
if p.ParentType == ParentTypeDatabase && p.DatabasePageProperties == nil {
|
||||||
|
return errors.New("database page properties is required when parent type is database")
|
||||||
|
}
|
||||||
|
if p.ParentType == ParentTypePage && p.Title == nil {
|
||||||
|
return errors.New("title is required when parent type is page")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p CreatePageParams) MarshalJSON() ([]byte, error) {
|
||||||
|
type CreatePageParamsDTO struct {
|
||||||
|
Parent PageParent `json:"parent"`
|
||||||
|
Properties interface{} `json:"properties"`
|
||||||
|
Children []Block `json:"children,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var parent PageParent
|
||||||
|
|
||||||
|
if p.DatabasePageProperties != nil {
|
||||||
|
parent.Type = "database_id"
|
||||||
|
parent.DatabaseID = StringPtr(p.ParentID)
|
||||||
|
} else if p.Title != nil {
|
||||||
|
parent.Type = "page_id"
|
||||||
|
parent.PageID = StringPtr(p.ParentID)
|
||||||
|
}
|
||||||
|
|
||||||
|
dto := CreatePageParamsDTO{
|
||||||
|
Parent: parent,
|
||||||
|
Children: p.Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.DatabasePageProperties != nil {
|
||||||
|
dto.Properties = p.DatabasePageProperties
|
||||||
|
} else if p.Title != nil {
|
||||||
|
dto.Properties = PageTitle{
|
||||||
|
Title: p.Title,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(dto)
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
//
|
//
|
||||||
// Pages get a different Properties type based on the parent of the page.
|
// Pages get a different Properties type based on the parent of the page.
|
||||||
|
85
rich_text.go
85
rich_text.go
@ -3,14 +3,14 @@ package notion
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type RichText struct {
|
type RichText struct {
|
||||||
PlainText string `json:"plain_text"`
|
Type RichTextType `json:"type"`
|
||||||
HRef *string `json:"href"`
|
Annotations *Annotations `json:"annotations,omitempty"`
|
||||||
Annotations Annotations `json:"annotations"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
|
|
||||||
Text *Text `json:"text"`
|
PlainText string `json:"plain_text,omitempty"`
|
||||||
Mention *Mention `json:"mention"`
|
HRef *string `json:"href,omitempty"`
|
||||||
Equation *Equation `json:"equation"`
|
Text *Text `json:"text,omitempty"`
|
||||||
|
Mention *Mention `json:"mention,omitempty"`
|
||||||
|
Equation *Equation `json:"equation,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Equation struct {
|
type Equation struct {
|
||||||
@ -18,41 +18,78 @@ type Equation struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Annotations struct {
|
type Annotations struct {
|
||||||
Bold bool `json:"bold"`
|
Bold bool `json:"bold,omitempty"`
|
||||||
Italic bool `json:"italic"`
|
Italic bool `json:"italic,omitempty"`
|
||||||
Strikethrough bool `json:"strikethrough"`
|
Strikethrough bool `json:"strikethrough,omitempty"`
|
||||||
Underline bool `json:"underline"`
|
Underline bool `json:"underline,omitempty"`
|
||||||
Code bool `json:"code"`
|
Code bool `json:"code,omitempty"`
|
||||||
Color string `json:"color"`
|
Color Color `json:"color,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mention struct {
|
type Mention struct {
|
||||||
Type string `json:"type"`
|
Type MentionType `json:"type"`
|
||||||
|
|
||||||
User *User `json:"user"`
|
User *User `json:"user,omitempty"`
|
||||||
Page *PageMention `json:"page"`
|
Page *ID `json:"page,omitempty"`
|
||||||
Database *DatabaseMention `json:"database"`
|
Database *ID `json:"database,omitempty"`
|
||||||
Date *Date `json:"date"`
|
Date *Date `json:"date,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Date struct {
|
type Date struct {
|
||||||
Start time.Time `json:"start"`
|
Start time.Time `json:"start"`
|
||||||
End *time.Time `json:"end"`
|
End *time.Time `json:"end,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Text struct {
|
type Text struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Link *Link `json:"link"`
|
Link *Link `json:"link,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Link struct {
|
type Link struct {
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PageMention struct {
|
type ID struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DatabaseMention struct {
|
type (
|
||||||
ID string `json:"id"`
|
RichTextType string
|
||||||
}
|
MentionType string
|
||||||
|
Color string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RichTextTypeText RichTextType = "text"
|
||||||
|
RichTextTypeMention RichTextType = "mention"
|
||||||
|
RichTextTypeEquation RichTextType = "equation"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MentionTypeUser MentionType = "user"
|
||||||
|
MentionTypePage MentionType = "page"
|
||||||
|
MentionTypeDatabase MentionType = "database"
|
||||||
|
MentionTypeDate MentionType = "date"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ColorDefault Color = "default"
|
||||||
|
ColorGray Color = "gray"
|
||||||
|
ColorBrown Color = "brown"
|
||||||
|
ColorOrange Color = "orange"
|
||||||
|
ColorYellow Color = "yellow"
|
||||||
|
ColorGreen Color = "green"
|
||||||
|
ColorBlue Color = "blue"
|
||||||
|
ColorPurple Color = "purple"
|
||||||
|
ColorPink Color = "pink"
|
||||||
|
ColorRed Color = "red"
|
||||||
|
ColorGrayBg Color = "gray_background"
|
||||||
|
ColorBrownBg Color = "brown_background"
|
||||||
|
ColorOrangeBg Color = "orange_background"
|
||||||
|
ColorYellowBg Color = "yellow_background"
|
||||||
|
ColorGreenBg Color = "green_background"
|
||||||
|
ColorBlueBg Color = "blue_background"
|
||||||
|
ColorPurpleBg Color = "purple_background"
|
||||||
|
ColorPinkBg Color = "pink_background"
|
||||||
|
ColorRedBg Color = "red_background"
|
||||||
|
)
|
||||||
|
23
util.go
Normal file
23
util.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package notion
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// StringPtr returns the pointer of a string value.
|
||||||
|
func StringPtr(s string) *string {
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntPtr returns the pointer of an int value.
|
||||||
|
func IntPtr(i int) *int {
|
||||||
|
return &i
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimePtr returns the pointer of a time.Time value.
|
||||||
|
func BoolPtr(b bool) *bool {
|
||||||
|
return &b
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimePtr returns the pointer of a time.Time value.
|
||||||
|
func TimePtr(t time.Time) *time.Time {
|
||||||
|
return &t
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user