diff --git a/audio_block_test.go b/audio_block_test.go new file mode 100644 index 0000000..e05bbb4 --- /dev/null +++ b/audio_block_test.go @@ -0,0 +1,93 @@ +package notionapi_test + +import ( + "github.com/jomei/notionapi" + "testing" + "time" +) + +func TestAudioBlock_GetURL(t *testing.T) { + tests := []struct { + name string + block *notionapi.AudioBlock + want string + }{ + { + name: "returns internal file URL", + block: ¬ionapi.AudioBlock{ + Audio: notionapi.Audio{ + File: ¬ionapi.FileObject{ + URL: "https://example.com/internal.mp3", + }, + }, + }, + want: "https://example.com/internal.mp3", + }, + { + name: "returns external file URL", + block: ¬ionapi.AudioBlock{ + Audio: notionapi.Audio{ + External: ¬ionapi.FileObject{ + URL: "https://example.com/external.mp3", + }, + }, + }, + want: "https://example.com/external.mp3", + }, + { + name: "returns empty string when no URL", + block: ¬ionapi.AudioBlock{}, + want: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.block.GetURL(); got != tt.want { + t.Errorf("AudioBlock.GetURL() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAudioBlock_GetExpiryTime(t *testing.T) { + now := time.Now() + tests := []struct { + name string + block *notionapi.AudioBlock + want *time.Time + }{ + { + name: "returns expiry time for internal file", + block: ¬ionapi.AudioBlock{ + Audio: notionapi.Audio{ + File: ¬ionapi.FileObject{ + ExpiryTime: &now, + }, + }, + }, + want: &now, + }, + { + name: "returns nil for external file", + block: ¬ionapi.AudioBlock{ + Audio: notionapi.Audio{ + External: ¬ionapi.FileObject{}, + }, + }, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.block.GetExpiryTime() + if got != tt.want { + t.Errorf("AudioBlock.GetExpiryTime() = %v, want %v", got, tt.want) + } + }) + } +} + +// Compile-time interface check +var _ notionapi.DownloadableFileBlock = (*notionapi.AudioBlock)(nil) diff --git a/block.go b/block.go index 50e65d0..f1c4201 100644 --- a/block.go +++ b/block.go @@ -172,6 +172,7 @@ type BlockUpdateRequest struct { Equation *Equation `json:"equation,omitempty"` Quote *Quote `json:"quote,omitempty"` TableRow *TableRow `json:"table_row,omitempty"` + Audio *Audio `json:"audio,omitempty"` } // Sets a Block object, including page blocks, to archived: true using the ID @@ -776,6 +777,8 @@ func (r *AppendBlockChildrenResponse) UnmarshalJSON(data []byte) error { func decodeBlock(raw map[string]interface{}) (Block, error) { var b Block switch BlockType(raw["type"].(string)) { + case BlockTypeAudio: + b = &AudioBlock{} case BlockTypeParagraph: b = &ParagraphBlock{} case BlockTypeHeading1: diff --git a/const.go b/const.go index 3bd7b79..b9aba37 100644 --- a/const.go +++ b/const.go @@ -220,6 +220,7 @@ const ( BlockTypeEmbed BlockType = "embed" BlockTypeImage BlockType = "image" + BlockTypeAudio BlockType = "audio" BlockTypeVideo BlockType = "video" BlockTypeFile BlockType = "file" BlockTypePdf BlockType = "pdf" diff --git a/downloadable_interface.go b/downloadable_interface.go new file mode 100644 index 0000000..7277e4f --- /dev/null +++ b/downloadable_interface.go @@ -0,0 +1,86 @@ +package notionapi + +import "time" + +// DownloadableFileBlock is an interface for blocks that can be downloaded +// such as Pdf, FileBlock, and Image +type DownloadableFileBlock interface { + Block + GetURL() string + GetExpiryTime() *time.Time +} + +// GetURL implements DownloadableFileBlock interface for PdfBlock +func (b *PdfBlock) GetURL() string { + if b.Pdf.File != nil { + return b.Pdf.File.URL + } + if b.Pdf.External != nil { + return b.Pdf.External.URL + } + return "" +} + +// GetURL implements DownloadableFileBlock interface for AudioBlock by returning +// the URL of the audio file. For internal files, this will be a Notion URL with +// an expiry time. For external files, this will be the external URL. +func (b *AudioBlock) GetURL() string { + return b.Audio.GetURL() +} + +// GetExpiryTime implements DownloadableFileBlock interface for AudioBlock by returning +// the expiry time of the internal audio file. Returns nil for external files or if no +// expiry time is set. +func (b *AudioBlock) GetExpiryTime() *time.Time { + if b.Audio.File != nil { + return b.Audio.File.ExpiryTime + } + return nil +} + +// GetExpiryTime implements DownloadableFileBlock interface for PdfBlock +func (b *PdfBlock) GetExpiryTime() *time.Time { + if b.Pdf.File != nil { + return b.Pdf.File.ExpiryTime + } + return nil +} + +// GetURL implements DownloadableFileBlock interface for FileBlock +func (b *FileBlock) GetURL() string { + if b.File.File != nil { + return b.File.File.URL + } + if b.File.External != nil { + return b.File.External.URL + } + return "" +} + +// GetExpiryTime implements DownloadableFileBlock interface for FileBlock +func (b *FileBlock) GetExpiryTime() *time.Time { + if b.File.File != nil { + return b.File.File.ExpiryTime + } + return nil +} + +// GetURL implements DownloadableFileBlock interface for ImageBlock +func (b *ImageBlock) GetURL() string { + return b.Image.GetURL() +} + +// GetExpiryTime implements DownloadableFileBlock interface for ImageBlock +func (b *ImageBlock) GetExpiryTime() *time.Time { + if b.Image.File != nil { + return b.Image.File.ExpiryTime + } + return nil +} + +// Verify that types implement DownloadableFileBlock interface +var ( + _ DownloadableFileBlock = (*PdfBlock)(nil) + _ DownloadableFileBlock = (*FileBlock)(nil) + _ DownloadableFileBlock = (*ImageBlock)(nil) +) diff --git a/downloadable_interface_test.go b/downloadable_interface_test.go new file mode 100644 index 0000000..50622c9 --- /dev/null +++ b/downloadable_interface_test.go @@ -0,0 +1,126 @@ +package notionapi + +import ( + "testing" + "time" +) + +func TestPdfBlockImplementsDownloadableFileBlock(t *testing.T) { + // Test setup + now := time.Now() + pdfBlock := &PdfBlock{ + Pdf: Pdf{ + File: &FileObject{ + URL: "https://example.com/file.pdf", + ExpiryTime: &now, + }, + }, + } + + // Test GetURL + if url := pdfBlock.GetURL(); url != "https://example.com/file.pdf" { + t.Errorf("Expected URL to be 'https://example.com/file.pdf', got %s", url) + } + + // Test GetExpiryTime + if expiry := pdfBlock.GetExpiryTime(); expiry != &now { + t.Errorf("Expected expiry time to be %v, got %v", now, expiry) + } +} + +func TestFileBlockImplementsDownloadableFileBlock(t *testing.T) { + // Test setup + now := time.Now() + fileBlock := &FileBlock{ + File: BlockFile{ + File: &FileObject{ + URL: "https://example.com/file.txt", + ExpiryTime: &now, + }, + }, + } + + // Test GetURL + if url := fileBlock.GetURL(); url != "https://example.com/file.txt" { + t.Errorf("Expected URL to be 'https://example.com/file.txt', got %s", url) + } + + // Test GetExpiryTime + if expiry := fileBlock.GetExpiryTime(); expiry != &now { + t.Errorf("Expected expiry time to be %v, got %v", now, expiry) + } +} + +func TestImageBlockImplementsDownloadableFileBlock(t *testing.T) { + // Test setup + now := time.Now() + imageBlock := &ImageBlock{ + Image: Image{ + File: &FileObject{ + URL: "https://example.com/image.jpg", + ExpiryTime: &now, + }, + }, + } + + // Test GetURL + if url := imageBlock.GetURL(); url != "https://example.com/image.jpg" { + t.Errorf("Expected URL to be 'https://example.com/image.jpg', got %s", url) + } + + // Test GetExpiryTime + if expiry := imageBlock.GetExpiryTime(); expiry != &now { + t.Errorf("Expected expiry time to be %v, got %v", now, expiry) + } +} + +func TestExternalURLCases(t *testing.T) { + // Test External URLs for each block type + testCases := []struct { + name string + block DownloadableFileBlock + expected string + }{ + { + name: "PDF with external URL", + block: &PdfBlock{ + Pdf: Pdf{ + External: &FileObject{ + URL: "https://external.com/file.pdf", + }, + }, + }, + expected: "https://external.com/file.pdf", + }, + { + name: "File with external URL", + block: &FileBlock{ + File: BlockFile{ + External: &FileObject{ + URL: "https://external.com/file.txt", + }, + }, + }, + expected: "https://external.com/file.txt", + }, + { + name: "Image with external URL", + block: &ImageBlock{ + Image: Image{ + External: &FileObject{ + URL: "https://external.com/image.jpg", + }, + }, + }, + expected: "https://external.com/image.jpg", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if url := tc.block.GetURL(); url != tc.expected { + t.Errorf("Expected URL to be '%s', got '%s'", tc.expected, url) + } + }) + } +}