1
0
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:
David Stotijn 2021-05-15 13:42:01 +02:00
parent 955d10b769
commit b2af8b7d0b
7 changed files with 299 additions and 47 deletions

View File

@ -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
View 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"
)

View File

@ -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
}

View File

@ -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
View File

@ -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.

View File

@ -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
View 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
}