1
0
mirror of https://github.com/dstotijn/go-notion.git synced 2025-06-08 23:46:12 +02:00

Convert Block from a struct to an interface (#27)

This commit is contained in:
David Stotijn 2022-08-12 19:49:36 +02:00 committed by GitHub
parent 1bd13b04cf
commit 8c9f519e73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 1118 additions and 263 deletions

875
block.go
View File

@ -2,13 +2,22 @@ package notion
import ( import (
"encoding/json" "encoding/json"
"fmt"
"time" "time"
) )
// Block represents content on the Notion platform. // Block represents content on the Notion platform.
// See: https://developers.notion.com/reference/block // See: https://developers.notion.com/reference/block
type Block struct { type Block interface {
Object string `json:"object"` ID() string
CreatedTime() time.Time
LastEditedTime() time.Time
HasChildren() bool
Archived() bool
json.Marshaler
}
type blockDTO struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Type BlockType `json:"type,omitempty"` Type BlockType `json:"type,omitempty"`
CreatedTime *time.Time `json:"created_time,omitempty"` CreatedTime *time.Time `json:"created_time,omitempty"`
@ -16,113 +25,640 @@ type Block struct {
HasChildren bool `json:"has_children,omitempty"` HasChildren bool `json:"has_children,omitempty"`
Archived *bool `json:"archived,omitempty"` Archived *bool `json:"archived,omitempty"`
Paragraph *RichTextBlock `json:"paragraph,omitempty"` Paragraph *ParagraphBlock `json:"paragraph,omitempty"`
Heading1 *Heading `json:"heading_1,omitempty"` Heading1 *Heading1Block `json:"heading_1,omitempty"`
Heading2 *Heading `json:"heading_2,omitempty"` Heading2 *Heading2Block `json:"heading_2,omitempty"`
Heading3 *Heading `json:"heading_3,omitempty"` Heading3 *Heading3Block `json:"heading_3,omitempty"`
BulletedListItem *RichTextBlock `json:"bulleted_list_item,omitempty"` BulletedListItem *BulletedListItemBlock `json:"bulleted_list_item,omitempty"`
NumberedListItem *RichTextBlock `json:"numbered_list_item,omitempty"` NumberedListItem *NumberedListItemBlock `json:"numbered_list_item,omitempty"`
ToDo *ToDo `json:"to_do,omitempty"` ToDo *ToDoBlock `json:"to_do,omitempty"`
Toggle *RichTextBlock `json:"toggle,omitempty"` Toggle *ToggleBlock `json:"toggle,omitempty"`
ChildPage *ChildPage `json:"child_page,omitempty"` ChildPage *ChildPageBlock `json:"child_page,omitempty"`
ChildDatabase *ChildDatabase `json:"child_database,omitempty"` ChildDatabase *ChildDatabaseBlock `json:"child_database,omitempty"`
Callout *Callout `json:"callout,omitempty"` Callout *CalloutBlock `json:"callout,omitempty"`
Quote *RichTextBlock `json:"quote,omitempty"` Quote *QuoteBlock `json:"quote,omitempty"`
Code *Code `json:"code,omitempty"` Code *CodeBlock `json:"code,omitempty"`
Embed *Embed `json:"embed,omitempty"` Embed *EmbedBlock `json:"embed,omitempty"`
Image *FileBlock `json:"image,omitempty"` Image *ImageBlock `json:"image,omitempty"`
Video *FileBlock `json:"video,omitempty"` Video *VideoBlock `json:"video,omitempty"`
File *FileBlock `json:"file,omitempty"` File *FileBlock `json:"file,omitempty"`
PDF *FileBlock `json:"pdf,omitempty"` PDF *PDFBlock `json:"pdf,omitempty"`
Bookmark *Bookmark `json:"bookmark,omitempty"` Bookmark *BookmarkBlock `json:"bookmark,omitempty"`
Equation *Equation `json:"equation,omitempty"` Equation *EquationBlock `json:"equation,omitempty"`
Divider *Divider `json:"divider,omitempty"` Divider *DividerBlock `json:"divider,omitempty"`
TableOfContents *TableOfContents `json:"table_of_contents,omitempty"` TableOfContents *TableOfContentsBlock `json:"table_of_contents,omitempty"`
Breadcrumb *Breadcrumb `json:"breadcrumb,omitempty"` Breadcrumb *BreadcrumbBlock `json:"breadcrumb,omitempty"`
ColumnList *ColumnList `json:"column_list,omitempty"` ColumnList *ColumnListBlock `json:"column_list,omitempty"`
Column *Column `json:"column,omitempty"` Column *ColumnBlock `json:"column,omitempty"`
Table *Table `json:"table,omitempty"` Table *TableBlock `json:"table,omitempty"`
TableRow *TableRow `json:"table_row,omitempty"` TableRow *TableRowBlock `json:"table_row,omitempty"`
LinkPreview *LinkPreview `json:"link_preview,omitempty"` LinkPreview *LinkPreviewBlock `json:"link_preview,omitempty"`
LinkToPage *LinkToPage `json:"link_to_page,omitempty"` LinkToPage *LinkToPageBlock `json:"link_to_page,omitempty"`
SyncedBlock *SyncedBlock `json:"synced_block,omitempty"` SyncedBlock *SyncedBlock `json:"synced_block,omitempty"`
Template *RichTextBlock `json:"template,omitempty"` Template *TemplateBlock `json:"template,omitempty"`
} }
type RichTextBlock struct { type baseBlock struct {
id string
createdTime time.Time
lastEditedTime time.Time
hasChildren bool
archived bool
}
// ID returns the identifier (UUIDv4) for the block.
func (b baseBlock) ID() string {
return b.id
}
func (b baseBlock) CreatedTime() time.Time {
return b.createdTime
}
func (b baseBlock) LastEditedTime() time.Time {
return b.lastEditedTime
}
func (b baseBlock) HasChildren() bool {
return b.hasChildren
}
func (b baseBlock) Archived() bool {
return b.archived
}
type ParagraphBlock struct {
baseBlock
Text []RichText `json:"text"` Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"` Children []Block `json:"children,omitempty"`
} }
type Heading struct { // MarshalJSON implements json.Marshaler.
Text []RichText `json:"text"` func (b ParagraphBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias ParagraphBlock
dto struct {
Paragraph blockAlias `json:"paragraph"`
}
)
return json.Marshal(dto{
Paragraph: blockAlias(b),
})
} }
type ToDo struct { type BulletedListItemBlock struct {
RichTextBlock baseBlock
Checked *bool `json:"checked,omitempty"`
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
} }
type ChildPage struct { // MarshalJSON implements json.Marshaler.
func (b BulletedListItemBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias BulletedListItemBlock
dto struct {
BulletedListItem blockAlias `json:"bulleted_list_item"`
}
)
return json.Marshal(dto{
BulletedListItem: blockAlias(b),
})
}
type NumberedListItemBlock struct {
baseBlock
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b NumberedListItemBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias NumberedListItemBlock
dto struct {
NumberedListItem blockAlias `json:"numbered_list_item"`
}
)
return json.Marshal(dto{
NumberedListItem: blockAlias(b),
})
}
type QuoteBlock struct {
baseBlock
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b QuoteBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias QuoteBlock
dto struct {
Quote blockAlias `json:"quote"`
}
)
return json.Marshal(dto{
Quote: blockAlias(b),
})
}
type ToggleBlock struct {
baseBlock
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b ToggleBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias ToggleBlock
dto struct {
Toggle blockAlias `json:"toggle"`
}
)
return json.Marshal(dto{
Toggle: blockAlias(b),
})
}
type TemplateBlock struct {
baseBlock
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b TemplateBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias TemplateBlock
dto struct {
Template blockAlias `json:"template"`
}
)
return json.Marshal(dto{
Template: blockAlias(b),
})
}
type Heading1Block struct {
baseBlock
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b Heading1Block) MarshalJSON() ([]byte, error) {
type (
blockAlias Heading1Block
dto struct {
Heading1 blockAlias `json:"heading_1"`
}
)
return json.Marshal(dto{
Heading1: blockAlias(b),
})
}
type Heading2Block struct {
baseBlock
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b Heading2Block) MarshalJSON() ([]byte, error) {
type (
blockAlias Heading2Block
dto struct {
Heading2 blockAlias `json:"heading_2"`
}
)
return json.Marshal(dto{
Heading2: blockAlias(b),
})
}
type Heading3Block struct {
baseBlock
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b Heading3Block) MarshalJSON() ([]byte, error) {
type (
blockAlias Heading3Block
dto struct {
Heading3 blockAlias `json:"heading_3"`
}
)
return json.Marshal(dto{
Heading3: blockAlias(b),
})
}
type ToDoBlock struct {
baseBlock
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
Checked *bool `json:"checked,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b ToDoBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias ToDoBlock
dto struct {
ToDo blockAlias `json:"to_do"`
}
)
return json.Marshal(dto{
ToDo: blockAlias(b),
})
}
type ChildPageBlock struct {
baseBlock
Title string `json:"title"` Title string `json:"title"`
} }
type ChildDatabase struct { // MarshalJSON implements json.Marshaler.
func (b ChildPageBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias ChildPageBlock
dto struct {
ChildPage blockAlias `json:"child_page"`
}
)
return json.Marshal(dto{
ChildPage: blockAlias(b),
})
}
type ChildDatabaseBlock struct {
baseBlock
Title string `json:"title"` Title string `json:"title"`
} }
type Callout struct { // MarshalJSON implements json.Marshaler.
RichTextBlock func (b ChildDatabaseBlock) MarshalJSON() ([]byte, error) {
Icon *Icon `json:"icon,omitempty"` type (
blockAlias ChildDatabaseBlock
dto struct {
ChildDatabase blockAlias `json:"child_database"`
}
)
return json.Marshal(dto{
ChildDatabase: blockAlias(b),
})
} }
type Code struct { type CalloutBlock struct {
RichTextBlock baseBlock
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
Icon *Icon `json:"icon,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b CalloutBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias CalloutBlock
dto struct {
Callout blockAlias `json:"callout"`
}
)
return json.Marshal(dto{
Callout: blockAlias(b),
})
}
type CodeBlock struct {
baseBlock
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
Caption []RichText `json:"caption,omitempty"` Caption []RichText `json:"caption,omitempty"`
Language *string `json:"language,omitempty"` Language *string `json:"language,omitempty"`
} }
type Embed struct { // MarshalJSON implements json.Marshaler.
func (b CodeBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias CodeBlock
dto struct {
Code blockAlias `json:"code"`
}
)
return json.Marshal(dto{
Code: blockAlias(b),
})
}
type EmbedBlock struct {
baseBlock
URL string `json:"url"` URL string `json:"url"`
} }
type FileBlock struct { // MarshalJSON implements json.Marshaler.
Type FileType `json:"type"` func (b EmbedBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias EmbedBlock
dto struct {
Embed blockAlias `json:"embed"`
}
)
return json.Marshal(dto{
Embed: blockAlias(b),
})
}
type ImageBlock struct {
baseBlock
Type FileType `json:"type"`
File *FileFile `json:"file,omitempty"` File *FileFile `json:"file,omitempty"`
External *FileExternal `json:"external,omitempty"` External *FileExternal `json:"external,omitempty"`
Caption []RichText `json:"caption,omitempty"` Caption []RichText `json:"caption,omitempty"`
} }
type Bookmark struct { // MarshalJSON implements json.Marshaler.
func (b ImageBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias ImageBlock
dto struct {
Image blockAlias `json:"image"`
}
)
return json.Marshal(dto{
Image: blockAlias(b),
})
}
type VideoBlock struct {
baseBlock
Type FileType `json:"type"`
File *FileFile `json:"file,omitempty"`
External *FileExternal `json:"external,omitempty"`
Caption []RichText `json:"caption,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b VideoBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias VideoBlock
dto struct {
Video blockAlias `json:"video"`
}
)
return json.Marshal(dto{
Video: blockAlias(b),
})
}
type FileBlock struct {
baseBlock
Type FileType `json:"type"`
File *FileFile `json:"file,omitempty"`
External *FileExternal `json:"external,omitempty"`
Caption []RichText `json:"caption,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b FileBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias FileBlock
dto struct {
File blockAlias `json:"file"`
}
)
return json.Marshal(dto{
File: blockAlias(b),
})
}
type PDFBlock struct {
baseBlock
Type FileType `json:"type"`
File *FileFile `json:"file,omitempty"`
External *FileExternal `json:"external,omitempty"`
Caption []RichText `json:"caption,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b PDFBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias PDFBlock
dto struct {
PDF blockAlias `json:"pdf"`
}
)
return json.Marshal(dto{
PDF: blockAlias(b),
})
}
type BookmarkBlock struct {
baseBlock
URL string `json:"url"` URL string `json:"url"`
Caption []RichText `json:"caption,omitempty"` Caption []RichText `json:"caption,omitempty"`
} }
type ColumnList struct { // MarshalJSON implements json.Marshaler.
func (b BookmarkBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias BookmarkBlock
dto struct {
Bookmark blockAlias `json:"bookmark"`
}
)
return json.Marshal(dto{
Bookmark: blockAlias(b),
})
}
type EquationBlock struct {
baseBlock
Expression string `json:"expression"`
}
// MarshalJSON implements json.Marshaler.
func (b EquationBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias EquationBlock
dto struct {
Equation blockAlias `json:"equation"`
}
)
return json.Marshal(dto{
Equation: blockAlias(b),
})
}
type ColumnListBlock struct {
baseBlock
Children []Block `json:"children,omitempty"` Children []Block `json:"children,omitempty"`
} }
type Column struct { // MarshalJSON implements json.Marshaler.
func (b ColumnListBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias ColumnListBlock
dto struct {
ColumnList blockAlias `json:"column_list"`
}
)
return json.Marshal(dto{
ColumnList: blockAlias(b),
})
}
type ColumnBlock struct {
baseBlock
Children []Block `json:"children,omitempty"` Children []Block `json:"children,omitempty"`
} }
type Table struct { // MarshalJSON implements json.Marshaler.
func (b ColumnBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias ColumnBlock
dto struct {
Column blockAlias `json:"column"`
}
)
return json.Marshal(dto{
Column: blockAlias(b),
})
}
type TableBlock struct {
baseBlock
TableWidth int `json:"table_width"` TableWidth int `json:"table_width"`
HasColumnHeader bool `json:"has_column_header"` HasColumnHeader bool `json:"has_column_header"`
HasRowHeader bool `json:"has_row_header"` HasRowHeader bool `json:"has_row_header"`
Children []Block `json:"children,omitempty"` Children []Block `json:"children,omitempty"`
} }
type TableRow struct { // MarshalJSON implements json.Marshaler.
func (b TableBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias TableBlock
dto struct {
Table blockAlias `json:"table"`
}
)
return json.Marshal(dto{
Table: blockAlias(b),
})
}
type TableRowBlock struct {
baseBlock
Cells [][]RichText `json:"cells"` Cells [][]RichText `json:"cells"`
} }
type LinkToPage struct { // MarshalJSON implements json.Marshaler.
Type LinkToPageType `json:"type"` func (b TableRowBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias TableRowBlock
dto struct {
TableRow blockAlias `json:"table_row"`
}
)
PageID string `json:"page_id,omitempty"` return json.Marshal(dto{
DatabaseID string `json:"database_id,omitempty"` TableRow: blockAlias(b),
})
}
type LinkPreviewBlock struct {
baseBlock
URL string `json:"url"`
}
// MarshalJSON implements json.Marshaler.
func (b LinkPreviewBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias LinkPreviewBlock
dto struct {
LinkPreview blockAlias `json:"link_preview"`
}
)
return json.Marshal(dto{
LinkPreview: blockAlias(b),
})
}
type LinkToPageBlock struct {
baseBlock
Type LinkToPageType `json:"type"`
PageID string `json:"page_id,omitempty"`
DatabaseID string `json:"database_id,omitempty"`
}
// MarshalJSON implements json.Marshaler.
func (b LinkToPageBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias LinkToPageBlock
dto struct {
LinkToPage blockAlias `json:"link_to_page"`
}
)
return json.Marshal(dto{
LinkToPage: blockAlias(b),
})
} }
type LinkToPageType string type LinkToPageType string
@ -133,10 +669,26 @@ const (
) )
type SyncedBlock struct { type SyncedBlock struct {
baseBlock
SyncedFrom *SyncedFrom `json:"synced_from"` SyncedFrom *SyncedFrom `json:"synced_from"`
Children []Block `json:"children,omitempty"` Children []Block `json:"children,omitempty"`
} }
// MarshalJSON implements json.Marshaler.
func (b SyncedBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias SyncedBlock
dto struct {
SyncedBlock blockAlias `json:"synced_block"`
}
)
return json.Marshal(dto{
SyncedBlock: blockAlias(b),
})
}
type SyncedFrom struct { type SyncedFrom struct {
Type SyncedFromType `json:"type"` Type SyncedFromType `json:"type"`
BlockID string `json:"block_id"` BlockID string `json:"block_id"`
@ -146,11 +698,59 @@ type SyncedFromType string
const SyncedFromTypeBlockID SyncedFromType = "block_id" const SyncedFromTypeBlockID SyncedFromType = "block_id"
type ( type DividerBlock struct {
Divider struct{} baseBlock
TableOfContents struct{} }
Breadcrumb struct{}
) // MarshalJSON implements json.Marshaler.
func (b DividerBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias DividerBlock
dto struct {
Divider blockAlias `json:"divider"`
}
)
return json.Marshal(dto{
Divider: blockAlias(b),
})
}
type TableOfContentsBlock struct {
baseBlock
}
// MarshalJSON implements json.Marshaler.
func (b TableOfContentsBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias TableOfContentsBlock
dto struct {
TableOfContents blockAlias `json:"table_of_contents"`
}
)
return json.Marshal(dto{
TableOfContents: blockAlias(b),
})
}
type BreadcrumbBlock struct {
baseBlock
}
// MarshalJSON implements json.Marshaler.
func (b BreadcrumbBlock) MarshalJSON() ([]byte, error) {
type (
blockAlias BreadcrumbBlock
dto struct {
Breadcrumb blockAlias `json:"breadcrumb"`
}
)
return json.Marshal(dto{
Breadcrumb: blockAlias(b),
})
}
type BlockType string type BlockType string
@ -196,17 +796,148 @@ type PaginationQuery struct {
// BlockChildrenResponse contains results (block children) and pagination data returned from a find request. // BlockChildrenResponse contains results (block children) and pagination data returned from a find request.
type BlockChildrenResponse struct { type BlockChildrenResponse struct {
Results []Block `json:"results"` Results []Block
HasMore bool `json:"has_more"` HasMore bool
NextCursor *string `json:"next_cursor"` NextCursor *string
} }
// MarshalJSON implements json.Marshaler. func (resp *BlockChildrenResponse) UnmarshalJSON(b []byte) error {
func (b Block) MarshalJSON() ([]byte, error) { type responseDTO struct {
type blockAlias Block Results []blockDTO `json:"results"`
HasMore bool `json:"has_more"`
NextCursor *string `json:"next_cursor"`
}
alias := blockAlias(b) var dto responseDTO
alias.Object = "block"
return json.Marshal(alias) if err := json.Unmarshal(b, &dto); err != nil {
return err
}
resp.HasMore = dto.HasMore
resp.NextCursor = dto.NextCursor
resp.Results = make([]Block, len(dto.Results))
for i, blockDTO := range dto.Results {
resp.Results[i] = blockDTO.Block()
}
return nil
}
func (dto blockDTO) Block() Block {
baseBlock := baseBlock{
id: dto.ID,
hasChildren: dto.HasChildren,
}
if dto.CreatedTime != nil {
baseBlock.createdTime = *dto.CreatedTime
}
if dto.LastEditedTime != nil {
baseBlock.lastEditedTime = *dto.LastEditedTime
}
if dto.Archived != nil {
baseBlock.archived = *dto.Archived
}
switch dto.Type {
case BlockTypeParagraph:
dto.Paragraph.baseBlock = baseBlock
return dto.Paragraph
case BlockTypeHeading1:
dto.Heading1.baseBlock = baseBlock
return dto.Heading1
case BlockTypeHeading2:
dto.Heading2.baseBlock = baseBlock
return dto.Heading2
case BlockTypeHeading3:
dto.Heading3.baseBlock = baseBlock
return dto.Heading3
case BlockTypeBulletedListItem:
dto.BulletedListItem.baseBlock = baseBlock
return dto.BulletedListItem
case BlockTypeNumberedListItem:
dto.NumberedListItem.baseBlock = baseBlock
return dto.NumberedListItem
case BlockTypeToDo:
dto.ToDo.baseBlock = baseBlock
return dto.ToDo
case BlockTypeToggle:
dto.Toggle.baseBlock = baseBlock
return dto.Toggle
case BlockTypeChildPage:
dto.ChildPage.baseBlock = baseBlock
return dto.ChildPage
case BlockTypeChildDatabase:
dto.ChildDatabase.baseBlock = baseBlock
return dto.ChildDatabase
case BlockTypeCallout:
dto.Callout.baseBlock = baseBlock
return dto.Callout
case BlockTypeQuote:
dto.Quote.baseBlock = baseBlock
return dto.Quote
case BlockTypeCode:
dto.Code.baseBlock = baseBlock
return dto.Code
case BlockTypeEmbed:
dto.Embed.baseBlock = baseBlock
return dto.Embed
case BlockTypeImage:
dto.Image.baseBlock = baseBlock
return dto.Image
case BlockTypeVideo:
dto.Video.baseBlock = baseBlock
return dto.Video
case BlockTypeFile:
dto.File.baseBlock = baseBlock
return dto.File
case BlockTypePDF:
dto.PDF.baseBlock = baseBlock
return dto.PDF
case BlockTypeBookmark:
dto.Bookmark.baseBlock = baseBlock
return dto.Bookmark
case BlockTypeEquation:
dto.Equation.baseBlock = baseBlock
return dto.Equation
case BlockTypeDivider:
dto.Divider.baseBlock = baseBlock
return dto.Divider
case BlockTypeTableOfContents:
dto.TableOfContents.baseBlock = baseBlock
return dto.TableOfContents
case BlockTypeBreadCrumb:
dto.Breadcrumb.baseBlock = baseBlock
return dto.Breadcrumb
case BlockTypeColumnList:
dto.ColumnList.baseBlock = baseBlock
return dto.ColumnList
case BlockTypeColumn:
dto.Column.baseBlock = baseBlock
return dto.Column
case BlockTypeTable:
dto.Table.baseBlock = baseBlock
return dto.Table
case BlockTypeTableRow:
dto.TableRow.baseBlock = baseBlock
return dto.TableRow
case BlockTypeLinkPreview:
dto.LinkPreview.baseBlock = baseBlock
return dto.LinkPreview
case BlockTypeLinkToPage:
dto.LinkToPage.baseBlock = baseBlock
return dto.LinkToPage
case BlockTypeSyncedBlock:
dto.SyncedBlock.baseBlock = baseBlock
return dto.SyncedBlock
case BlockTypeTemplate:
dto.Template.baseBlock = baseBlock
return dto.Template
default:
panic(fmt.Sprintf("type %q is unsupported", dto.Type))
}
} }

View File

@ -413,87 +413,93 @@ func (c *Client) AppendBlockChildren(ctx context.Context, blockID string, childr
// FindBlockByID returns a single of block for a given block ID. // FindBlockByID returns a single of block for a given block ID.
// See: https://developers.notion.com/reference/retrieve-a-block // See: https://developers.notion.com/reference/retrieve-a-block
func (c *Client) FindBlockByID(ctx context.Context, blockID string) (block Block, err error) { func (c *Client) FindBlockByID(ctx context.Context, blockID string) (Block, error) {
req, err := c.newRequest(ctx, http.MethodGet, fmt.Sprintf("/blocks/%v", blockID), nil) req, err := c.newRequest(ctx, http.MethodGet, fmt.Sprintf("/blocks/%v", blockID), nil)
if err != nil { if err != nil {
return Block{}, fmt.Errorf("notion: invalid request: %w", err) return nil, fmt.Errorf("notion: invalid request: %w", err)
} }
res, err := c.httpClient.Do(req) res, err := c.httpClient.Do(req)
if err != nil { if err != nil {
return Block{}, fmt.Errorf("notion: failed to make HTTP request: %w", err) return nil, fmt.Errorf("notion: failed to make HTTP request: %w", err)
} }
defer res.Body.Close() defer res.Body.Close()
if res.StatusCode != http.StatusOK { if res.StatusCode != http.StatusOK {
return Block{}, fmt.Errorf("notion: failed to find block: %w", parseErrorResponse(res)) return nil, fmt.Errorf("notion: failed to find block: %w", parseErrorResponse(res))
} }
err = json.NewDecoder(res.Body).Decode(&block) var dto blockDTO
err = json.NewDecoder(res.Body).Decode(&dto)
if err != nil { if err != nil {
return Block{}, fmt.Errorf("notion: failed to parse HTTP response: %w", err) return nil, fmt.Errorf("notion: failed to parse HTTP response: %w", err)
} }
return block, nil return dto.Block(), nil
} }
// UpdateBlock updates a block. // UpdateBlock updates a block.
// See: https://developers.notion.com/reference/update-a-block // See: https://developers.notion.com/reference/update-a-block
func (c *Client) UpdateBlock(ctx context.Context, blockID string, block Block) (updatedBlock Block, err error) { func (c *Client) UpdateBlock(ctx context.Context, blockID string, block Block) (Block, error) {
body := &bytes.Buffer{} body := &bytes.Buffer{}
err = json.NewEncoder(body).Encode(block) err := json.NewEncoder(body).Encode(block)
if err != nil { if err != nil {
return Block{}, fmt.Errorf("notion: failed to encode body params to JSON: %w", err) return nil, fmt.Errorf("notion: failed to encode body params to JSON: %w", err)
} }
req, err := c.newRequest(ctx, http.MethodPatch, "/blocks/"+blockID, body) req, err := c.newRequest(ctx, http.MethodPatch, "/blocks/"+blockID, body)
if err != nil { if err != nil {
return Block{}, fmt.Errorf("notion: invalid request: %w", err) return nil, fmt.Errorf("notion: invalid request: %w", err)
} }
res, err := c.httpClient.Do(req) res, err := c.httpClient.Do(req)
if err != nil { if err != nil {
return Block{}, fmt.Errorf("notion: failed to make HTTP request: %w", err) return nil, fmt.Errorf("notion: failed to make HTTP request: %w", err)
} }
defer res.Body.Close() defer res.Body.Close()
if res.StatusCode != http.StatusOK { if res.StatusCode != http.StatusOK {
return Block{}, fmt.Errorf("notion: failed to update block: %w", parseErrorResponse(res)) return nil, fmt.Errorf("notion: failed to update block: %w", parseErrorResponse(res))
} }
err = json.NewDecoder(res.Body).Decode(&updatedBlock) var dto blockDTO
err = json.NewDecoder(res.Body).Decode(&dto)
if err != nil { if err != nil {
return Block{}, fmt.Errorf("notion: failed to parse HTTP response: %w", err) return nil, fmt.Errorf("notion: failed to parse HTTP response: %w", err)
} }
return updatedBlock, nil return dto.Block(), nil
} }
// DeleteBlock sets `archived: true` on a (page) block object. // DeleteBlock sets `archived: true` on a (page) block object.
// See: https://developers.notion.com/reference/delete-a-block // See: https://developers.notion.com/reference/delete-a-block
func (c *Client) DeleteBlock(ctx context.Context, blockID string) (deletedBlock Block, err error) { func (c *Client) DeleteBlock(ctx context.Context, blockID string) (Block, error) {
req, err := c.newRequest(ctx, http.MethodDelete, "/blocks/"+blockID, nil) req, err := c.newRequest(ctx, http.MethodDelete, "/blocks/"+blockID, nil)
if err != nil { if err != nil {
return Block{}, fmt.Errorf("notion: invalid request: %w", err) return nil, fmt.Errorf("notion: invalid request: %w", err)
} }
res, err := c.httpClient.Do(req) res, err := c.httpClient.Do(req)
if err != nil { if err != nil {
return Block{}, fmt.Errorf("notion: failed to make HTTP request: %w", err) return nil, fmt.Errorf("notion: failed to make HTTP request: %w", err)
} }
defer res.Body.Close() defer res.Body.Close()
if res.StatusCode != http.StatusOK { if res.StatusCode != http.StatusOK {
return Block{}, fmt.Errorf("notion: failed to delete block: %w", parseErrorResponse(res)) return nil, fmt.Errorf("notion: failed to delete block: %w", parseErrorResponse(res))
} }
err = json.NewDecoder(res.Body).Decode(&deletedBlock) var dto blockDTO
err = json.NewDecoder(res.Body).Decode(&dto)
if err != nil { if err != nil {
return Block{}, fmt.Errorf("notion: failed to parse HTTP response: %w", err) return nil, fmt.Errorf("notion: failed to parse HTTP response: %w", err)
} }
return deletedBlock, nil return dto.Block(), nil
} }
// FindUserByID fetches a user by ID. // FindUserByID fetches a user by ID.

View File

@ -14,6 +14,7 @@ import (
"github.com/dstotijn/go-notion" "github.com/dstotijn/go-notion"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
) )
type mockRoundtripper struct { type mockRoundtripper struct {
@ -1667,15 +1668,11 @@ func TestCreatePage(t *testing.T) {
}, },
}, },
Children: []notion.Block{ Children: []notion.Block{
{ &notion.ParagraphBlock{
Object: "block", Text: []notion.RichText{
Type: notion.BlockTypeParagraph, {
Paragraph: &notion.RichTextBlock{ Text: &notion.Text{
Text: []notion.RichText{ Content: "Lorem ipsum dolor sit amet.",
{
Text: &notion.Text{
Content: "Lorem ipsum dolor sit amet.",
},
}, },
}, },
}, },
@ -1763,8 +1760,6 @@ func TestCreatePage(t *testing.T) {
}, },
"children": []interface{}{ "children": []interface{}{
map[string]interface{}{ map[string]interface{}{
"object": "block",
"type": "paragraph",
"paragraph": map[string]interface{}{ "paragraph": map[string]interface{}{
"text": []interface{}{ "text": []interface{}{
map[string]interface{}{ map[string]interface{}{
@ -1846,15 +1841,11 @@ func TestCreatePage(t *testing.T) {
}, },
}, },
Children: []notion.Block{ Children: []notion.Block{
{ &notion.ParagraphBlock{
Object: "block", Text: []notion.RichText{
Type: notion.BlockTypeParagraph, {
Paragraph: &notion.RichTextBlock{ Text: &notion.Text{
Text: []notion.RichText{ Content: "Lorem ipsum dolor sit amet.",
{
Text: &notion.Text{
Content: "Lorem ipsum dolor sit amet.",
},
}, },
}, },
}, },
@ -1916,8 +1907,6 @@ func TestCreatePage(t *testing.T) {
}, },
"children": []interface{}{ "children": []interface{}{
map[string]interface{}{ map[string]interface{}{
"object": "block",
"type": "paragraph",
"paragraph": map[string]interface{}{ "paragraph": map[string]interface{}{
"text": []interface{}{ "text": []interface{}{
map[string]interface{}{ map[string]interface{}{
@ -2877,6 +2866,14 @@ func TestFindPagePropertyByID(t *testing.T) {
func TestFindBlockChildrenById(t *testing.T) { func TestFindBlockChildrenById(t *testing.T) {
t.Parallel() t.Parallel()
type blockFields struct {
id string
createdTime time.Time
lastEditedTime time.Time
hasChildren bool
archived bool
}
tests := []struct { tests := []struct {
name string name string
query *notion.PaginationQuery query *notion.PaginationQuery
@ -2884,6 +2881,7 @@ func TestFindBlockChildrenById(t *testing.T) {
respStatusCode int respStatusCode int
expQueryParams url.Values expQueryParams url.Values
expResponse notion.BlockChildrenResponse expResponse notion.BlockChildrenResponse
expBlockFields []blockFields
expError error expError error
}{ }{
{ {
@ -2939,24 +2937,17 @@ func TestFindBlockChildrenById(t *testing.T) {
}, },
expResponse: notion.BlockChildrenResponse{ expResponse: notion.BlockChildrenResponse{
Results: []notion.Block{ Results: []notion.Block{
{ &notion.ParagraphBlock{
Object: "block", Text: []notion.RichText{
ID: "ae9c9a31-1c1e-4ae2-a5ee-c539a2d43113", {
CreatedTime: notion.TimePtr(mustParseTime(time.RFC3339Nano, "2021-05-14T09:15:00.000Z")), Type: notion.RichTextTypeText,
LastEditedTime: notion.TimePtr(mustParseTime(time.RFC3339Nano, "2021-05-14T09:15:00.000Z")), Text: &notion.Text{
Type: notion.BlockTypeParagraph, Content: "Lorem ipsum dolor sit amet.",
Paragraph: &notion.RichTextBlock{
Text: []notion.RichText{
{
Type: notion.RichTextTypeText,
Text: &notion.Text{
Content: "Lorem ipsum dolor sit amet.",
},
Annotations: &notion.Annotations{
Color: notion.ColorDefault,
},
PlainText: "Lorem ipsum dolor sit amet.",
}, },
Annotations: &notion.Annotations{
Color: notion.ColorDefault,
},
PlainText: "Lorem ipsum dolor sit amet.",
}, },
}, },
}, },
@ -2964,6 +2955,15 @@ func TestFindBlockChildrenById(t *testing.T) {
HasMore: true, HasMore: true,
NextCursor: notion.StringPtr("A^hd"), NextCursor: notion.StringPtr("A^hd"),
}, },
expBlockFields: []blockFields{
{
id: "ae9c9a31-1c1e-4ae2-a5ee-c539a2d43113",
createdTime: mustParseTime(time.RFC3339, "2021-05-14T09:15:00.000Z"),
lastEditedTime: mustParseTime(time.RFC3339, "2021-05-14T09:15:00.000Z"),
hasChildren: false,
archived: false,
},
},
expError: nil, expError: nil,
}, },
{ {
@ -3049,9 +3049,35 @@ func TestFindBlockChildrenById(t *testing.T) {
t.Fatalf("error not equal (expected: %v, got: %v)", tt.expError, err) t.Fatalf("error not equal (expected: %v, got: %v)", tt.expError, err)
} }
if diff := cmp.Diff(tt.expResponse, resp); diff != "" { if diff := cmp.Diff(tt.expResponse, resp, cmpopts.IgnoreUnexported(notion.ParagraphBlock{})); diff != "" {
t.Fatalf("response not equal (-exp, +got):\n%v", diff) t.Fatalf("response not equal (-exp, +got):\n%v", diff)
} }
if len(tt.expBlockFields) != len(resp.Results) {
t.Fatalf("expected %v result(s), got %v", len(tt.expBlockFields), len(resp.Results))
}
for i, exp := range tt.expBlockFields {
if exp.id != resp.Results[i].ID() {
t.Fatalf("id not equal (expected: %v, got: %v)", exp.id, resp.Results[i].ID())
}
if exp.createdTime != resp.Results[i].CreatedTime() {
t.Fatalf("createdTime not equal (expected: %v, got: %v)", exp.createdTime, resp.Results[i].CreatedTime())
}
if exp.lastEditedTime != resp.Results[i].LastEditedTime() {
t.Fatalf("lastEditedTime not equal (expected: %v, got: %v)", exp.lastEditedTime, resp.Results[i].LastEditedTime())
}
if exp.hasChildren != resp.Results[i].HasChildren() {
t.Fatalf("hasChildren not equal (expected: %v, got: %v)", exp.hasChildren, resp.Results[i].HasChildren())
}
if exp.archived != resp.Results[i].Archived() {
t.Fatalf("archived not equal (expected: %v, got: %v)", exp.archived, resp.Results[i].Archived())
}
}
}) })
} }
} }
@ -3059,6 +3085,14 @@ func TestFindBlockChildrenById(t *testing.T) {
func TestAppendBlockChildren(t *testing.T) { func TestAppendBlockChildren(t *testing.T) {
t.Parallel() t.Parallel()
type blockFields struct {
id string
createdTime time.Time
lastEditedTime time.Time
hasChildren bool
archived bool
}
tests := []struct { tests := []struct {
name string name string
children []notion.Block children []notion.Block
@ -3066,19 +3100,17 @@ func TestAppendBlockChildren(t *testing.T) {
respStatusCode int respStatusCode int
expPostBody map[string]interface{} expPostBody map[string]interface{}
expResponse notion.BlockChildrenResponse expResponse notion.BlockChildrenResponse
expBlockFields []blockFields
expError error expError error
}{ }{
{ {
name: "successful response", name: "successful response",
children: []notion.Block{ children: []notion.Block{
{ &notion.ParagraphBlock{
Type: notion.BlockTypeParagraph, Text: []notion.RichText{
Paragraph: &notion.RichTextBlock{ {
Text: []notion.RichText{ Text: &notion.Text{
{ Content: "Lorem ipsum dolor sit amet.",
Text: &notion.Text{
Content: "Lorem ipsum dolor sit amet.",
},
}, },
}, },
}, },
@ -3128,8 +3160,6 @@ func TestAppendBlockChildren(t *testing.T) {
expPostBody: map[string]interface{}{ expPostBody: map[string]interface{}{
"children": []interface{}{ "children": []interface{}{
map[string]interface{}{ map[string]interface{}{
"object": "block",
"type": "paragraph",
"paragraph": map[string]interface{}{ "paragraph": map[string]interface{}{
"text": []interface{}{ "text": []interface{}{
map[string]interface{}{ map[string]interface{}{
@ -3144,24 +3174,17 @@ func TestAppendBlockChildren(t *testing.T) {
}, },
expResponse: notion.BlockChildrenResponse{ expResponse: notion.BlockChildrenResponse{
Results: []notion.Block{ Results: []notion.Block{
{ &notion.ParagraphBlock{
Object: "block", Text: []notion.RichText{
ID: "ae9c9a31-1c1e-4ae2-a5ee-c539a2d43113", {
CreatedTime: notion.TimePtr(mustParseTime(time.RFC3339Nano, "2021-05-14T09:15:00.000Z")), Type: notion.RichTextTypeText,
LastEditedTime: notion.TimePtr(mustParseTime(time.RFC3339Nano, "2021-05-14T09:15:00.000Z")), Text: &notion.Text{
Type: notion.BlockTypeParagraph, Content: "Lorem ipsum dolor sit amet.",
Paragraph: &notion.RichTextBlock{
Text: []notion.RichText{
{
Type: notion.RichTextTypeText,
Text: &notion.Text{
Content: "Lorem ipsum dolor sit amet.",
},
Annotations: &notion.Annotations{
Color: notion.ColorDefault,
},
PlainText: "Lorem ipsum dolor sit amet.",
}, },
Annotations: &notion.Annotations{
Color: notion.ColorDefault,
},
PlainText: "Lorem ipsum dolor sit amet.",
}, },
}, },
}, },
@ -3169,19 +3192,25 @@ func TestAppendBlockChildren(t *testing.T) {
HasMore: true, HasMore: true,
NextCursor: notion.StringPtr("A^hd"), NextCursor: notion.StringPtr("A^hd"),
}, },
expBlockFields: []blockFields{
{
id: "ae9c9a31-1c1e-4ae2-a5ee-c539a2d43113",
createdTime: mustParseTime(time.RFC3339, "2021-05-14T09:15:00.000Z"),
lastEditedTime: mustParseTime(time.RFC3339, "2021-05-14T09:15:00.000Z"),
hasChildren: false,
archived: false,
},
},
expError: nil, expError: nil,
}, },
{ {
name: "error response", name: "error response",
children: []notion.Block{ children: []notion.Block{
{ &notion.ParagraphBlock{
Type: notion.BlockTypeParagraph, Text: []notion.RichText{
Paragraph: &notion.RichTextBlock{ {
Text: []notion.RichText{ Text: &notion.Text{
{ Content: "Lorem ipsum dolor sit amet.",
Text: &notion.Text{
Content: "Lorem ipsum dolor sit amet.",
},
}, },
}, },
}, },
@ -3201,8 +3230,6 @@ func TestAppendBlockChildren(t *testing.T) {
expPostBody: map[string]interface{}{ expPostBody: map[string]interface{}{
"children": []interface{}{ "children": []interface{}{
map[string]interface{}{ map[string]interface{}{
"object": "block",
"type": "paragraph",
"paragraph": map[string]interface{}{ "paragraph": map[string]interface{}{
"text": []interface{}{ "text": []interface{}{
map[string]interface{}{ map[string]interface{}{
@ -3268,9 +3295,35 @@ func TestAppendBlockChildren(t *testing.T) {
t.Fatalf("error not equal (expected: %v, got: %v)", tt.expError, err) t.Fatalf("error not equal (expected: %v, got: %v)", tt.expError, err)
} }
if diff := cmp.Diff(tt.expResponse, resp); diff != "" { if diff := cmp.Diff(tt.expResponse, resp, cmpopts.IgnoreUnexported(notion.ParagraphBlock{})); diff != "" {
t.Fatalf("response not equal (-exp, +got):\n%v", diff) t.Fatalf("response not equal (-exp, +got):\n%v", diff)
} }
if len(tt.expBlockFields) != len(resp.Results) {
t.Fatalf("expected %v result(s), got %v", len(tt.expBlockFields), len(resp.Results))
}
for i, exp := range tt.expBlockFields {
if exp.id != resp.Results[i].ID() {
t.Fatalf("id not equal (expected: %v, got: %v)", exp.id, resp.Results[i].ID())
}
if exp.createdTime != resp.Results[i].CreatedTime() {
t.Fatalf("createdTime not equal (expected: %v, got: %v)", exp.createdTime, resp.Results[i].CreatedTime())
}
if exp.lastEditedTime != resp.Results[i].LastEditedTime() {
t.Fatalf("lastEditedTime not equal (expected: %v, got: %v)", exp.lastEditedTime, resp.Results[i].LastEditedTime())
}
if exp.hasChildren != resp.Results[i].HasChildren() {
t.Fatalf("hasChildren not equal (expected: %v, got: %v)", exp.hasChildren, resp.Results[i].HasChildren())
}
if exp.archived != resp.Results[i].Archived() {
t.Fatalf("archived not equal (expected: %v, got: %v)", exp.archived, resp.Results[i].Archived())
}
}
}) })
} }
} }
@ -3939,12 +3992,17 @@ func TestFindBlockByID(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
name string name string
blockID string blockID string
respBody func(r *http.Request) io.Reader respBody func(r *http.Request) io.Reader
respStatusCode int respStatusCode int
expBlock notion.Block expBlock notion.Block
expError error expID string
expCreatedTime time.Time
expLastEditedTime time.Time
expHasChildren bool
expArchived bool
expError error
}{ }{
{ {
name: "successful response", name: "successful response",
@ -3966,17 +4024,15 @@ func TestFindBlockByID(t *testing.T) {
) )
}, },
respStatusCode: http.StatusOK, respStatusCode: http.StatusOK,
expBlock: notion.Block{ expBlock: &notion.ChildPageBlock{
Object: "block", Title: "test title",
ID: "048e165e-352d-4119-8128-e46c3527d95c",
Type: "child_page",
CreatedTime: mustParseTimePointer(time.RFC3339, "2021-10-02T06:09:00Z"),
LastEditedTime: mustParseTimePointer(time.RFC3339, "2021-10-02T06:31:00Z"),
HasChildren: true,
ChildPage: &notion.ChildPage{Title: "test title"},
Archived: notion.BoolPtr(false),
}, },
expError: nil, expID: "048e165e-352d-4119-8128-e46c3527d95c",
expCreatedTime: mustParseTime(time.RFC3339, "2021-10-02T06:09:00Z"),
expLastEditedTime: mustParseTime(time.RFC3339, "2021-10-02T06:31:00Z"),
expHasChildren: true,
expArchived: false,
expError: nil,
}, },
{ {
name: "error response not found", name: "error response not found",
@ -3991,7 +4047,7 @@ func TestFindBlockByID(t *testing.T) {
) )
}, },
respStatusCode: http.StatusNotFound, respStatusCode: http.StatusNotFound,
expBlock: notion.Block{}, expBlock: nil,
expError: errors.New("notion: failed to find block: Could not find block with ID: test id. (code: object_not_found, status: 404)"), expError: errors.New("notion: failed to find block: Could not find block with ID: test id. (code: object_not_found, status: 404)"),
}, },
} }
@ -4023,9 +4079,31 @@ func TestFindBlockByID(t *testing.T) {
t.Fatalf("error not equal (expected: %v, got: %v)", tt.expError, err) t.Fatalf("error not equal (expected: %v, got: %v)", tt.expError, err)
} }
if diff := cmp.Diff(tt.expBlock, block); diff != "" { if diff := cmp.Diff(tt.expBlock, block, cmpopts.IgnoreUnexported(notion.ChildPageBlock{})); diff != "" {
t.Fatalf("user not equal (-exp, +got):\n%v", diff) t.Fatalf("user not equal (-exp, +got):\n%v", diff)
} }
if block != nil {
if tt.expID != block.ID() {
t.Fatalf("id not equal (expected: %v, got: %v)", tt.expID, block.ID())
}
if tt.expCreatedTime != block.CreatedTime() {
t.Fatalf("createdTime not equal (expected: %v, got: %v)", tt.expCreatedTime, block.CreatedTime())
}
if tt.expLastEditedTime != block.LastEditedTime() {
t.Fatalf("lastEditedTime not equal (expected: %v, got: %v)", tt.expLastEditedTime, block.LastEditedTime())
}
if tt.expHasChildren != block.HasChildren() {
t.Fatalf("hasChildren not equal (expected: %v, got: %v)", tt.expHasChildren, block.HasChildren())
}
if tt.expArchived != block.Archived() {
t.Fatalf("archived not equal (expected: %v, got: %v)", tt.expArchived, block.Archived())
}
}
}) })
} }
} }
@ -4034,23 +4112,26 @@ func TestUpdateBlock(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
name string name string
block notion.Block block notion.Block
respBody func(r *http.Request) io.Reader respBody func(r *http.Request) io.Reader
respStatusCode int respStatusCode int
expPostBody map[string]interface{} expPostBody map[string]interface{}
expResponse notion.Block expResponse notion.Block
expError error expID string
expCreatedTime time.Time
expLastEditedTime time.Time
expHasChildren bool
expArchived bool
expError error
}{ }{
{ {
name: "successful response", name: "successful response",
block: notion.Block{ block: &notion.ParagraphBlock{
Paragraph: &notion.RichTextBlock{ Text: []notion.RichText{
Text: []notion.RichText{ {
{ Text: &notion.Text{
Text: &notion.Text{ Content: "Foobar",
Content: "Foobar",
},
}, },
}, },
}, },
@ -4091,7 +4172,6 @@ func TestUpdateBlock(t *testing.T) {
}, },
respStatusCode: http.StatusOK, respStatusCode: http.StatusOK,
expPostBody: map[string]interface{}{ expPostBody: map[string]interface{}{
"object": "block",
"paragraph": map[string]interface{}{ "paragraph": map[string]interface{}{
"text": []interface{}{ "text": []interface{}{
map[string]interface{}{ map[string]interface{}{
@ -4102,40 +4182,34 @@ func TestUpdateBlock(t *testing.T) {
}, },
}, },
}, },
expResponse: notion.Block{ expResponse: &notion.ParagraphBlock{
Object: "block", Text: []notion.RichText{
ID: "048e165e-352d-4119-8128-e46c3527d95c", {
Type: notion.BlockTypeParagraph, Type: notion.RichTextTypeText,
CreatedTime: mustParseTimePointer(time.RFC3339, "2021-10-02T06:09:00Z"), Text: &notion.Text{
LastEditedTime: mustParseTimePointer(time.RFC3339, "2021-10-02T06:31:00Z"), Content: "Foobar",
HasChildren: true, },
Paragraph: &notion.RichTextBlock{ PlainText: "Foobar",
Text: []notion.RichText{ Annotations: &notion.Annotations{
{ Color: notion.ColorDefault,
Type: notion.RichTextTypeText,
Text: &notion.Text{
Content: "Foobar",
},
PlainText: "Foobar",
Annotations: &notion.Annotations{
Color: notion.ColorDefault,
},
}, },
}, },
}, },
Archived: notion.BoolPtr(false),
}, },
expError: nil, expID: "048e165e-352d-4119-8128-e46c3527d95c",
expCreatedTime: mustParseTime(time.RFC3339, "2021-10-02T06:09:00Z"),
expLastEditedTime: mustParseTime(time.RFC3339, "2021-10-02T06:31:00Z"),
expHasChildren: true,
expArchived: false,
expError: nil,
}, },
{ {
name: "error response", name: "error response",
block: notion.Block{ block: &notion.ParagraphBlock{
Paragraph: &notion.RichTextBlock{ Text: []notion.RichText{
Text: []notion.RichText{ {
{ Text: &notion.Text{
Text: &notion.Text{ Content: "Foobar",
Content: "Foobar",
},
}, },
}, },
}, },
@ -4152,7 +4226,6 @@ func TestUpdateBlock(t *testing.T) {
}, },
respStatusCode: http.StatusBadRequest, respStatusCode: http.StatusBadRequest,
expPostBody: map[string]interface{}{ expPostBody: map[string]interface{}{
"object": "block",
"paragraph": map[string]interface{}{ "paragraph": map[string]interface{}{
"text": []interface{}{ "text": []interface{}{
map[string]interface{}{ map[string]interface{}{
@ -4163,7 +4236,7 @@ func TestUpdateBlock(t *testing.T) {
}, },
}, },
}, },
expResponse: notion.Block{}, expResponse: nil,
expError: errors.New("notion: failed to update block: foobar (code: validation_error, status: 400)"), expError: errors.New("notion: failed to update block: foobar (code: validation_error, status: 400)"),
}, },
} }
@ -4216,9 +4289,31 @@ func TestUpdateBlock(t *testing.T) {
t.Fatalf("error not equal (expected: %v, got: %v)", tt.expError, err) t.Fatalf("error not equal (expected: %v, got: %v)", tt.expError, err)
} }
if diff := cmp.Diff(tt.expResponse, updatedBlock); diff != "" { if diff := cmp.Diff(tt.expResponse, updatedBlock, cmpopts.IgnoreUnexported(notion.ParagraphBlock{})); diff != "" {
t.Fatalf("response not equal (-exp, +got):\n%v", diff) t.Fatalf("response not equal (-exp, +got):\n%v", diff)
} }
if updatedBlock != nil {
if tt.expID != updatedBlock.ID() {
t.Fatalf("id not equal (expected: %v, got: %v)", tt.expID, updatedBlock.ID())
}
if tt.expCreatedTime != updatedBlock.CreatedTime() {
t.Fatalf("createdTime not equal (expected: %v, got: %v)", tt.expCreatedTime, updatedBlock.CreatedTime())
}
if tt.expLastEditedTime != updatedBlock.LastEditedTime() {
t.Fatalf("lastEditedTime not equal (expected: %v, got: %v)", tt.expLastEditedTime, updatedBlock.LastEditedTime())
}
if tt.expHasChildren != updatedBlock.HasChildren() {
t.Fatalf("hasChildren not equal (expected: %v, got: %v)", tt.expHasChildren, updatedBlock.HasChildren())
}
if tt.expArchived != updatedBlock.Archived() {
t.Fatalf("archived not equal (expected: %v, got: %v)", tt.expArchived, updatedBlock.Archived())
}
}
}) })
} }
} }
@ -4227,11 +4322,16 @@ func TestDeleteBlock(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
name string name string
respBody func(r *http.Request) io.Reader respBody func(r *http.Request) io.Reader
respStatusCode int respStatusCode int
expResponse notion.Block expResponse notion.Block
expError error expID string
expCreatedTime time.Time
expLastEditedTime time.Time
expHasChildren bool
expArchived bool
expError error
}{ }{
{ {
name: "successful response", name: "successful response",
@ -4270,30 +4370,26 @@ func TestDeleteBlock(t *testing.T) {
) )
}, },
respStatusCode: http.StatusOK, respStatusCode: http.StatusOK,
expResponse: notion.Block{ expResponse: &notion.ParagraphBlock{
Object: "block", Text: []notion.RichText{
ID: "048e165e-352d-4119-8128-e46c3527d95c", {
Type: notion.BlockTypeParagraph, Type: notion.RichTextTypeText,
CreatedTime: mustParseTimePointer(time.RFC3339, "2021-10-02T06:09:00Z"), Text: &notion.Text{
LastEditedTime: mustParseTimePointer(time.RFC3339, "2021-10-02T06:31:00Z"), Content: "Foobar",
HasChildren: true, },
Paragraph: &notion.RichTextBlock{ PlainText: "Foobar",
Text: []notion.RichText{ Annotations: &notion.Annotations{
{ Color: notion.ColorDefault,
Type: notion.RichTextTypeText,
Text: &notion.Text{
Content: "Foobar",
},
PlainText: "Foobar",
Annotations: &notion.Annotations{
Color: notion.ColorDefault,
},
}, },
}, },
}, },
Archived: notion.BoolPtr(true),
}, },
expError: nil, expID: "048e165e-352d-4119-8128-e46c3527d95c",
expCreatedTime: mustParseTime(time.RFC3339, "2021-10-02T06:09:00Z"),
expLastEditedTime: mustParseTime(time.RFC3339, "2021-10-02T06:31:00Z"),
expHasChildren: true,
expArchived: true,
expError: nil,
}, },
{ {
name: "error response", name: "error response",
@ -4308,7 +4404,7 @@ func TestDeleteBlock(t *testing.T) {
) )
}, },
respStatusCode: http.StatusBadRequest, respStatusCode: http.StatusBadRequest,
expResponse: notion.Block{}, expResponse: nil,
expError: errors.New("notion: failed to delete block: foobar (code: validation_error, status: 400)"), expError: errors.New("notion: failed to delete block: foobar (code: validation_error, status: 400)"),
}, },
} }
@ -4340,9 +4436,31 @@ func TestDeleteBlock(t *testing.T) {
t.Fatalf("error not equal (expected: %v, got: %v)", tt.expError, err) t.Fatalf("error not equal (expected: %v, got: %v)", tt.expError, err)
} }
if diff := cmp.Diff(tt.expResponse, deletedBlock); diff != "" { if diff := cmp.Diff(tt.expResponse, deletedBlock, cmpopts.IgnoreUnexported(notion.ParagraphBlock{})); diff != "" {
t.Fatalf("response not equal (-exp, +got):\n%v", diff) t.Fatalf("response not equal (-exp, +got):\n%v", diff)
} }
if deletedBlock != nil {
if tt.expID != deletedBlock.ID() {
t.Fatalf("id not equal (expected: %v, got: %v)", tt.expID, deletedBlock.ID())
}
if tt.expCreatedTime != deletedBlock.CreatedTime() {
t.Fatalf("createdTime not equal (expected: %v, got: %v)", tt.expCreatedTime, deletedBlock.CreatedTime())
}
if tt.expLastEditedTime != deletedBlock.LastEditedTime() {
t.Fatalf("lastEditedTime not equal (expected: %v, got: %v)", tt.expLastEditedTime, deletedBlock.LastEditedTime())
}
if tt.expHasChildren != deletedBlock.HasChildren() {
t.Fatalf("hasChildren not equal (expected: %v, got: %v)", tt.expHasChildren, deletedBlock.HasChildren())
}
if tt.expArchived != deletedBlock.Archived() {
t.Fatalf("archived not equal (expected: %v, got: %v)", tt.expArchived, deletedBlock.Archived())
}
}
}) })
} }
} }