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
|
||||
|
||||
- [x] [Retrieve a page](client.go)
|
||||
- [ ] Create a page
|
||||
- [x] [Create a page](client.go)
|
||||
- [ ] Update page properties
|
||||
|
||||
### 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"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -146,3 +147,42 @@ func (c *Client) FindPageByID(ctx context.Context, id string) (page Page, err er
|
||||
|
||||
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 {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
ID string `json:"id"`
|
||||
Type DatabasePropertyType `json:"type"`
|
||||
|
||||
Number *NumberMetadata `json:"number"`
|
||||
Select *SelectMetadata `json:"select"`
|
||||
@ -136,19 +136,19 @@ type MultiSelectDatabaseQueryFilter struct {
|
||||
}
|
||||
|
||||
type DateDatabaseQueryFilter struct {
|
||||
Equals time.Time `json:"equals,omitempty"`
|
||||
Before time.Time `json:"before,omitempty"`
|
||||
After time.Time `json:"after,omitempty"`
|
||||
OnOrBefore time.Time `json:"on_or_before,omitempty"`
|
||||
OnOrAfter time.Time `json:"on_or_after,omitempty"`
|
||||
IsEmpty bool `json:"is_empty,omitempty"`
|
||||
IsNotEmpty bool `json:"is_not_empty,omitempty"`
|
||||
PastWeek *struct{} `json:"past_week,omitempty"`
|
||||
PastMonth *struct{} `json:"past_month,omitempty"`
|
||||
PastYear *struct{} `json:"past_year,omitempty"`
|
||||
NextWeek *struct{} `json:"next_week,omitempty"`
|
||||
NextMonth *struct{} `json:"next_month,omitempty"`
|
||||
NextYear *struct{} `json:"next_year,omitempty"`
|
||||
Equals *time.Time `json:"equals,omitempty"`
|
||||
Before *time.Time `json:"before,omitempty"`
|
||||
After *time.Time `json:"after,omitempty"`
|
||||
OnOrBefore *time.Time `json:"on_or_before,omitempty"`
|
||||
OnOrAfter *time.Time `json:"on_or_after,omitempty"`
|
||||
IsEmpty bool `json:"is_empty,omitempty"`
|
||||
IsNotEmpty bool `json:"is_not_empty,omitempty"`
|
||||
PastWeek *struct{} `json:"past_week,omitempty"`
|
||||
PastMonth *struct{} `json:"past_month,omitempty"`
|
||||
PastYear *struct{} `json:"past_year,omitempty"`
|
||||
NextWeek *struct{} `json:"next_week,omitempty"`
|
||||
NextMonth *struct{} `json:"next_month,omitempty"`
|
||||
NextYear *struct{} `json:"next_year,omitempty"`
|
||||
}
|
||||
|
||||
type PeopleDatabaseQueryFilter struct {
|
||||
@ -184,11 +184,33 @@ type DatabaseQuerySort struct {
|
||||
}
|
||||
|
||||
type (
|
||||
SortTimestamp string
|
||||
SortDirection string
|
||||
DatabasePropertyType string
|
||||
SortTimestamp string
|
||||
SortDirection string
|
||||
)
|
||||
|
||||
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.
|
||||
SortTimeStampCreatedTime SortTimestamp = "created_time"
|
||||
SortTimeStampLastEditedTime SortTimestamp = "last_edited_time"
|
||||
|
83
page.go
83
page.go
@ -2,6 +2,7 @@ package notion
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
@ -24,15 +25,17 @@ type Page struct {
|
||||
type PageParent struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
PageID *string `json:"page_id"`
|
||||
DatabaseID *string `json:"database_id"`
|
||||
PageID *string `json:"page_id,omitempty"`
|
||||
DatabaseID *string `json:"database_id,omitempty"`
|
||||
}
|
||||
|
||||
// PageProperties are properties of a page whose parent is a page or a workspace.
|
||||
type PageProperties struct {
|
||||
Title struct {
|
||||
Title []RichText `json:"title"`
|
||||
} `json:"title"`
|
||||
Title PageTitle `json:"title"`
|
||||
}
|
||||
|
||||
type PageTitle struct {
|
||||
Title []RichText `json:"title"`
|
||||
}
|
||||
|
||||
// DatabasePageProperties are properties of a page whose parent is a database.
|
||||
@ -45,6 +48,76 @@ type DatabasePageProperty struct {
|
||||
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.
|
||||
//
|
||||
// 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"
|
||||
|
||||
type RichText struct {
|
||||
PlainText string `json:"plain_text"`
|
||||
HRef *string `json:"href"`
|
||||
Annotations Annotations `json:"annotations"`
|
||||
Type string `json:"type"`
|
||||
Type RichTextType `json:"type"`
|
||||
Annotations *Annotations `json:"annotations,omitempty"`
|
||||
|
||||
Text *Text `json:"text"`
|
||||
Mention *Mention `json:"mention"`
|
||||
Equation *Equation `json:"equation"`
|
||||
PlainText string `json:"plain_text,omitempty"`
|
||||
HRef *string `json:"href,omitempty"`
|
||||
Text *Text `json:"text,omitempty"`
|
||||
Mention *Mention `json:"mention,omitempty"`
|
||||
Equation *Equation `json:"equation,omitempty"`
|
||||
}
|
||||
|
||||
type Equation struct {
|
||||
@ -18,41 +18,78 @@ type Equation struct {
|
||||
}
|
||||
|
||||
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"`
|
||||
Bold bool `json:"bold,omitempty"`
|
||||
Italic bool `json:"italic,omitempty"`
|
||||
Strikethrough bool `json:"strikethrough,omitempty"`
|
||||
Underline bool `json:"underline,omitempty"`
|
||||
Code bool `json:"code,omitempty"`
|
||||
Color Color `json:"color,omitempty"`
|
||||
}
|
||||
|
||||
type Mention struct {
|
||||
Type string `json:"type"`
|
||||
Type MentionType `json:"type"`
|
||||
|
||||
User *User `json:"user"`
|
||||
Page *PageMention `json:"page"`
|
||||
Database *DatabaseMention `json:"database"`
|
||||
Date *Date `json:"date"`
|
||||
User *User `json:"user,omitempty"`
|
||||
Page *ID `json:"page,omitempty"`
|
||||
Database *ID `json:"database,omitempty"`
|
||||
Date *Date `json:"date,omitempty"`
|
||||
}
|
||||
|
||||
type Date struct {
|
||||
Start time.Time `json:"start"`
|
||||
End *time.Time `json:"end"`
|
||||
End *time.Time `json:"end,omitempty"`
|
||||
}
|
||||
|
||||
type Text struct {
|
||||
Content string `json:"content"`
|
||||
Link *Link `json:"link"`
|
||||
Link *Link `json:"link,omitempty"`
|
||||
}
|
||||
|
||||
type Link struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type PageMention struct {
|
||||
type ID struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type DatabaseMention struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
type (
|
||||
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