From 7c96ee37939f3c2d433a7f22b881dd3391da7837 Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Tue, 22 Jul 2025 18:18:34 -0400 Subject: [PATCH 1/5] Get-BloggerPosts now includes -All parameter --- README.md | 30 +++++++++++++-- src/public/Get-BloggerPost.ps1 | 5 ++- src/public/Get-BloggerPosts.ps1 | 57 +++++++++++++++++----------- src/tests/Get-BloggerPost.Tests.ps1 | 4 +- src/tests/Get-BloggerPosts.Tests.ps1 | 46 ++++++++++++++++++++++ 5 files changed, 113 insertions(+), 29 deletions(-) create mode 100644 src/tests/Get-BloggerPosts.Tests.ps1 diff --git a/README.md b/README.md index a037056..79e8038 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,30 @@ When publishing, all images are converted to standard markdown format with Googl - Find-MarkdownImages: scans the markdown file to locate images in both standard and Obsidian formats - Update-MarkdownImages: updates the content in the markdown file with updated urls, converting all formats to standard markdown -## Future - -- Download existing posts to your machine in markdown +## Examples + +## Download existing posts to your machine in markdown + +```powershell +### Initialize the auth settings for your google account +Initialize-Blogger -ClientId -ClientSecret + +### Fetch the available blogs and set the first blog as the default +$blogs = Get-BloggerBlogs +$blogId = $blogs[0].id +Set-BloggerConfig -BlogId $blogId + +### Fetch posts +$posts = Get-BloggerPosts -All + +### Download Posts +$total = $posts.count +$count = 0 +foreach($post in $posts) { + $complete = $count++/$total * 100 + Write-Progress -Activity "Downloading..." -PercentComplete $complete -Status "$complete% ($count of $total)" + $post = Get-BloggerPost -PostId $post.id -Format Markdown -FolderDateFormat "yyyy\\MM" -OutDirectory ".\Posts" + Write-Host "Downloaded: $($post.title) - $($post.published)" +} +Write-Progress -Activity "Downloading..." -Complete +``` \ No newline at end of file diff --git a/src/public/Get-BloggerPost.ps1 b/src/public/Get-BloggerPost.ps1 index a7758c7..da19289 100644 --- a/src/public/Get-BloggerPost.ps1 +++ b/src/public/Get-BloggerPost.ps1 @@ -71,9 +71,10 @@ Function Get-BloggerPost { # Construct a subfolder based on the published date if ($FolderDateFormat -and $result.published) { - $date = [datetime]::Parse($result.published) - $formattedDate = $date.ToString($FolderDateFormat) + $formattedDate = $result.published.ToString($FolderDateFormat) + Write-Verbose "Using published date '$formattedDate' for folder structure." $OutDirectory = Join-Path -Path $OutDirectory -ChildPath $formattedDate + Write-Verbose "Output directory set to: $OutDirectory" } # Ensure the output directory exists diff --git a/src/public/Get-BloggerPosts.ps1 b/src/public/Get-BloggerPosts.ps1 index 587871f..e65f2bf 100644 --- a/src/public/Get-BloggerPosts.ps1 +++ b/src/public/Get-BloggerPosts.ps1 @@ -8,32 +8,45 @@ .PARAMETER Status The status of the posts to retrieve. Valid values are "draft", "live", and "scheduled" #> -Function Get-BloggerPosts -{ - [CmdletBinding()] - param( - [string]$BlogId, +Function Get-BloggerPosts { + [CmdletBinding()] + param( + [Parameter(Mandatory = $false)] + [string]$BlogId, - [ValidateSet("draft","live","scheduled")] - [string]$Status = "live" - ) + [Parameter(Mandatory = $false)] + [ValidateSet("draft", "live", "scheduled")] + [string]$Status = "live", - if (!$PSBoundParameters.ContainsKey("BlogId")) - { - $BlogId = $BloggerSession.BlogId - if (0 -eq $BlogId) { - throw "BlogId not specified." - } + [Parameter(Mandatory = $false)] + [switch]$All + ) + + if (!$PSBoundParameters.ContainsKey("BlogId")) { + $BlogId = $BloggerSession.BlogId + if (0 -eq $BlogId) { + throw "BlogId not specified." } + } - try { - $uri = "https://www.googleapis.com/blogger/v3/blogs/$BlogId/posts?status=$status" - - $result = Invoke-GApi -uri $uri + try { + $done = $false + $pageToken = $null + while (!$done) { + $uri = "https://www.googleapis.com/blogger/v3/blogs/$BlogId/posts?status=$status" + if ($pageToken) { + $uri += "&pageToken=$pageToken" + } + $result = Invoke-GApi -uri $uri - $result.items - } - catch { - Write-Error $_.ToString() -ErrorAction Stop + $result.items + + # loop if pageToken is present and -All switch is set + $pageToken = $result.nextPageToken + $done = $All.IsPresent -and $All -and [string]::IsNullOrEmpty($pageToken) } + } + catch { + Write-Error $_.ToString() -ErrorAction Stop + } } \ No newline at end of file diff --git a/src/tests/Get-BloggerPost.Tests.ps1 b/src/tests/Get-BloggerPost.Tests.ps1 index b1855f3..801961d 100644 --- a/src/tests/Get-BloggerPost.Tests.ps1 +++ b/src/tests/Get-BloggerPost.Tests.ps1 @@ -182,7 +182,7 @@ Describe "Get-BloggerPost" { return @{ id = $postId title = "Test Post" - published = "2023-10-01T12:00:00Z" + published = "10/01/2023 12:00:00" content = "

Hello World

This is a post.

" } } @@ -225,7 +225,7 @@ Describe "Get-BloggerPost" { return @{ id = $postId title = "Test Post" - published = "2023-10-01T12:00:00Z" + published = "10/01/2023 12:00:00" content = "

Hello World

This is a post.

" } } diff --git a/src/tests/Get-BloggerPosts.Tests.ps1 b/src/tests/Get-BloggerPosts.Tests.ps1 new file mode 100644 index 0000000..8cdc73f --- /dev/null +++ b/src/tests/Get-BloggerPosts.Tests.ps1 @@ -0,0 +1,46 @@ +Describe "Get-BloggerPosts" { + + BeforeEach { + Import-Module $PSScriptRoot\..\PSBlogger.psm1 -Force + } + + Context "Retrieve All Posts" { + BeforeEach { + InModuleScope PSBlogger { + + $BloggerSession.BlogId = "123456789" + + # Setup initial get-bloggerposts + Mock Invoke-GApi { + return @{ + items = @( + @{ id = "1"; title = "Post 1"; published = "2023-10-01T00:00:00Z" }, + @{ id = "2"; title = "Post 2"; published = "2023-10-02T00:00:00Z" } + ) + nextPageToken = "1" + } + } -ParameterFilter { $uri -notlike "*pageToken*" } + + + # setup second call + Mock Invoke-GApi { + @{ + items = @( + @{ id = "3"; title = "Post 3"; published = "2023-10-03T00:00:00Z" } + ) + nextPageToken = $null + } + } -ParameterFilter { $uri -like "*pageToken*" } + } + } + + It "Should fetch all posts when -All switch is used" { + + # act + $result = Get-BloggerPosts -All + + # assert + $result.Count | Should -Be 3 + } + } +} \ No newline at end of file From 0ff1e4b67215ffb89713a762eb2858112c28b2af Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Tue, 22 Jul 2025 18:21:03 -0400 Subject: [PATCH 2/5] updated readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79e8038..47e5d91 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ Initialize-Blogger -ClientId -ClientSecret ### Fetch the available blogs and set the first blog as the default $blogs = Get-BloggerBlogs $blogId = $blogs[0].id -Set-BloggerConfig -BlogId $blogId +Set-BloggerConfig -Name BlogId $blogId ### Fetch posts $posts = Get-BloggerPosts -All From 0b735711efd789304356c024e2a709b17bbe5933 Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Tue, 22 Jul 2025 18:44:45 -0400 Subject: [PATCH 3/5] updated docs --- src/readme.md | 90 ++++++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/src/readme.md b/src/readme.md index 592b6f7..50028b0 100644 --- a/src/readme.md +++ b/src/readme.md @@ -5,15 +5,11 @@ - Setup the google access-token + refresh token, save it to disk ``` - Init-Blogger -clientId -clientSecret -redirectUri -code + Initialize-Blogger -clientId -clientSecret -redirectUri -code ``` And if we could host a weblistener, we could launch the browser and wait for the auth-flow - ``` - Connect-Blogger - ``` - We want configuration settings to store default values. - Get configuration settings. This returns an object with settings to use. @@ -25,7 +21,7 @@ We want configuration settings to store default values. - Set Configuration settings. You can pass individual settings. Set to "" to erase ``` - Set-BloggerConfig + Set-BloggerConfig -Name -Value ``` @@ -37,54 +33,75 @@ We want configuration settings to store default values. Get-BloggerBlogs ``` -- Get a list of blogger posts and save to disk +- Get a list of blogger posts ``` - Get-BloggerPosts -blogid -directory + Get-BloggerPosts -BlogId -All ``` - Publish to Blogger. We also need to consider 'publishing' or scheduling a publish ``` - Publish-BloggerPost -blogid -content -title -draft - Publish-BloggerPost -blogid -postid -title + Publish-BloggerPost -blogid -content -title "First Post" -draft + Publish-BloggerPost -blogid -postid -content -title "First Post" ``` ## Google Drive We need the ability to upload images to google drive. The upload process would read the images from the markdown and if the backing URL isn't Google Drive, it uploads to google and then updates the markdown. You should be able to publish the images independently of the blog. -- Test if an image exists in GDrive +- Get all items from Google Drive + + ``` + Get-GoogleDriveItems -ResultType All + ``` + +- Find if an image exists in Google Drive + + ``` + $folder = Get-GoogleDriveItems -ResultType Folders -Title "PSBlogger" + Get-GoogleDriveItem -ResultType Files -Title image.jpg -Folder $folder.id + ``` + +- Upload an image to Google Drive ``` - Test-GDriveImage + Add-GoogleDriveFile -FilePath "c:\image.jpg" -TargetFolderName "PSBlogger" ``` -- Upload an image to GDrive +- Make an image publicly accessible to the internet ``` - Send-GDriveImage -file + $folder = Get-GoogleDriveItems -ResultType Folders -Title "PSBlogger" + $file = Get-GoogleDriveItem -ResultType Files -Title image.jpg -Folder $folder.id + $permission = New-GoogleDriveFilePermission -role "reader" -type "anyone" + Set-GoogleDriveFilePermission -FileId $file.id -PermissionData $permission ``` ## Markdown -- Related to finding images in the markdown and uploading, we want: +- Related to finding images in the markdown, we want: -``` -Get-MarkdownLinks -file -``` + ``` + $imageMappings = Find-MarkdownImages -File .\file.md + ``` -and +- After uploading these images, we update the mappings and then update the file: -``` -Update-MarkdownLink -file -path -url -``` + ``` + Update-MarkdownImages -File .\file.md -ImageMappings $imageMappings + ``` - We'll also need to get the meta-data from the markdown ``` - Show-MarkdownFrontMatter + Get-MarkdownFrontMatter -File .\file.md + ``` + +- And the ability to update the meta-data + ``` + Set-MarkdownFrontMatter -file .\file.md -Update [ordered]@{ postid = "123" } ## Pandoc @@ -96,39 +113,30 @@ The conversion of markdown to HTML will use pandoc with some custom extensions t ConvertTo-HtmlFromMarkdown -file something.md ``` + ...which is equivalent to: + ``` pandoc test.md -f markdown -t html -o test.html ``` - Upload the images in the markdown to Google Drive. This involves: - - Find all the images in the markdown that need to be uploaded - - Upload the files in the markdown to google drive - - Update the markdown with the values - - ``` - Publish-GDriveImages -file something.md - ``` - -- Update the local files for a post if they've been updated. This involves: - - - find all the referenced images in the markdown - - get the modified date from the local file - - get the modified date from google drive - - upload newer files to google drive + - Find all the images in the markdown that need to be uploaded `Find-MarkdownImages` + - Upload the files in the markdown to google drive `Add-GoogleDriveFile` + - Update the markdown with the values `Update-MarkdownIamges` ``` - Update-GDriveImages -file something.md + Publish-MarkdownDriveImages -file something.md ``` - The "money shot" is performing the work of converting the markdown and publishing to blogger. Assuming this involves: - - Publish-GDriveImages - - Update-GDriveImages + - Publish-MarkdownDriveImages - ConvertTo-HtmlFromMarkDown - Get-MarkdownFrontMatter - Publish-BloggerPost + - Set-MarkdownFrontMatter ``` - Publish-MarkdownToBlog -file post.md + Publish-MarkdownBloggerPost -file post.md ``` \ No newline at end of file From bc9ad965fe7334c721bcd966209c89244ef0e397 Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:07:49 -0400 Subject: [PATCH 4/5] fix for broken tests --- src/tests/Get-BloggerPost.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/Get-BloggerPost.Tests.ps1 b/src/tests/Get-BloggerPost.Tests.ps1 index 801961d..c5abc49 100644 --- a/src/tests/Get-BloggerPost.Tests.ps1 +++ b/src/tests/Get-BloggerPost.Tests.ps1 @@ -225,7 +225,7 @@ Describe "Get-BloggerPost" { return @{ id = $postId title = "Test Post" - published = "10/01/2023 12:00:00" + published = [datetime]"10/01/2023 12:00:00" content = "

Hello World

This is a post.

" } } From 0c97840fed3fc86d2c960303adabe8c483d858df Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:09:23 -0400 Subject: [PATCH 5/5] Get-BloggerPosts supports -Since date filter --- src/public/Get-BloggerPosts.ps1 | 18 ++++++- src/tests/Get-BloggerPosts.Tests.ps1 | 79 ++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/public/Get-BloggerPosts.ps1 b/src/public/Get-BloggerPosts.ps1 index e65f2bf..1d2e88e 100644 --- a/src/public/Get-BloggerPosts.ps1 +++ b/src/public/Get-BloggerPosts.ps1 @@ -7,6 +7,12 @@ .PARAMETER Status The status of the posts to retrieve. Valid values are "draft", "live", and "scheduled" + +.PARAMETER Since + Filter the results to only return posts that are older than the supplied date. + +.PARAMETER All + Flag to indicate if all paginated results should be returned. #> Function Get-BloggerPosts { [CmdletBinding()] @@ -18,6 +24,9 @@ Function Get-BloggerPosts { [ValidateSet("draft", "live", "scheduled")] [string]$Status = "live", + [Parameter(Mandatory = $false)] + [datetime]$Since, + [Parameter(Mandatory = $false)] [switch]$All ) @@ -28,22 +37,27 @@ Function Get-BloggerPosts { throw "BlogId not specified." } } + $includeDateFilter = $PSBoundParameters.ContainsKey("Since") try { $done = $false $pageToken = $null while (!$done) { - $uri = "https://www.googleapis.com/blogger/v3/blogs/$BlogId/posts?status=$status" + $uri = "https://www.googleapis.com/blogger/v3/blogs/$BlogId/posts?status=$status&fetchBodies=false" + if ($pageToken) { $uri += "&pageToken=$pageToken" } + if ($includeDateFilter) { + $uri += "&since=" + $Since.ToString("yyyy-MM-ddTHH:mm:ssZ") + } $result = Invoke-GApi -uri $uri $result.items # loop if pageToken is present and -All switch is set $pageToken = $result.nextPageToken - $done = $All.IsPresent -and $All -and [string]::IsNullOrEmpty($pageToken) + $done = ($All.IsPresent -and $All -and [string]::IsNullOrEmpty($pageToken)) -or !$All.IsPresent } } catch { diff --git a/src/tests/Get-BloggerPosts.Tests.ps1 b/src/tests/Get-BloggerPosts.Tests.ps1 index 8cdc73f..6d913f5 100644 --- a/src/tests/Get-BloggerPosts.Tests.ps1 +++ b/src/tests/Get-BloggerPosts.Tests.ps1 @@ -42,5 +42,84 @@ Describe "Get-BloggerPosts" { # assert $result.Count | Should -Be 3 } + + It "Should only fetch initial set if -All switch is not used" { + # arrange + InModuleScope PSBlogger { + # Setup initial get-bloggerposts + Mock Invoke-GApi { + return @{ + items = @( + @{ id = "1"; title = "Post 1"; published = "2023-10-01T00:00:00Z" }, + @{ id = "2"; title = "Post 2"; published = "2023-10-02T00:00:00Z" } + ) + nextPageToken = $null + } + } -ParameterFilter { $uri -notlike "*pageToken*" } + } + + # act + $result = Get-BloggerPosts -All + + # assert + $result.Count | Should -Be 2 + } + } + + Context "Retrieve Posts with Date Filter" { + BeforeEach { + InModuleScope PSBlogger { + + $BloggerSession.BlogId = "123456789" + } + } + + It "Should filter posts based on Since parameter" { + # arrange + InModuleScope PSBlogger { + + # Mock the API call to return posts after a specific date + Mock Invoke-GApi { + return @{ + items = @( + @{ id = "1"; title = "Post 1"; published = "2023-10-01T00:00:00Z" }, + @{ id = "2"; title = "Post 2"; published = "2023-10-02T00:00:00Z" } + ) + nextPageToken = $null + } + } -ParameterFilter { $uri -like "*since=2023-09-30*" } + } + + # act + $result = Get-BloggerPosts -Since (Get-Date "2023-09-30") + + # assert + $result.Count | Should -Be 2 + $result[0].title | Should -Be "Post 1" + $result[1].title | Should -Be "Post 2" + } + + It "Should not constrain by date if Since parameter is not provided" { + # arrange + InModuleScope PSBlogger { + + # Mock the API call to return all posts + Mock Invoke-GApi { + return @{ + items = @( + @{ id = "1"; title = "Post 1"; published = "2023-10-01T00:00:00Z" }, + @{ id = "2"; title = "Post 2"; published = "2023-10-02T00:00:00Z" } + ) + nextPageToken = $null + } + } -ParameterFilter { $uri -notlike "*since*" } + } + + # act + $result = Get-BloggerPosts + + # assert + $result.Count | Should -Be 2 + } } } \ No newline at end of file