diff --git a/src/managed/OpenLiveWriter.BlogClient/Clients/GoogleBloggerv3Client.cs b/src/managed/OpenLiveWriter.BlogClient/Clients/GoogleBloggerv3Client.cs
index 4027ebfc..907e44c1 100644
--- a/src/managed/OpenLiveWriter.BlogClient/Clients/GoogleBloggerv3Client.cs
+++ b/src/managed/OpenLiveWriter.BlogClient/Clients/GoogleBloggerv3Client.cs
@@ -37,6 +37,11 @@ namespace OpenLiveWriter.BlogClient.Clients
public static string BloggerServiceScope = BloggerService.Scope.Blogger;
public static char LabelDelimiter = ',';
+ ///
+ /// Maximum number of results the Google Blogger v3 API will return in one request.
+ ///
+ public static int MaxResultsPerRequest = 500;
+
public static Task GetOAuth2AuthorizationAsync(string blogId, CancellationToken taskCancellationToken)
{
// This async task will either find cached credentials in the IDataStore provided, or it will pop open a
@@ -337,8 +342,14 @@ namespace OpenLiveWriter.BlogClient.Clients
return new BlogPostKeyword[] { };
}
- private PostList ListRecentPosts(string blogId, int maxPosts, DateTime? now, PostsResource.ListRequest.StatusEnum status)
+ private PostList ListRecentPosts(string blogId, int maxPosts, DateTime? now, PostsResource.ListRequest.StatusEnum status, PostList previousPage)
{
+ if (previousPage != null && string.IsNullOrWhiteSpace(previousPage.NextPageToken))
+ {
+ // The previous page was also the last page, so do nothing and return an empty list.
+ return new PostList();
+ }
+
var recentPostsRequest = GetService().Posts.List(blogId);
if (now.HasValue)
{
@@ -348,21 +359,46 @@ namespace OpenLiveWriter.BlogClient.Clients
recentPostsRequest.MaxResults = maxPosts;
recentPostsRequest.OrderBy = PostsResource.ListRequest.OrderByEnum.Published;
recentPostsRequest.Status = status;
+ recentPostsRequest.PageToken = previousPage?.NextPageToken;
return recentPostsRequest.Execute();
}
public BlogPost[] GetRecentPosts(string blogId, int maxPosts, bool includeCategories, DateTime? now)
{
- var draftRecentPostsList = ListRecentPosts(blogId, maxPosts, now, PostsResource.ListRequest.StatusEnum.Draft);
- var liveRecentPostsList = ListRecentPosts(blogId, maxPosts, now, PostsResource.ListRequest.StatusEnum.Live);
- var scheduledRecentPostsList = ListRecentPosts(blogId, maxPosts, now, PostsResource.ListRequest.StatusEnum.Scheduled);
+ // Blogger requires separate API calls to get drafts vs. live vs. scheduled posts. We aggregate each
+ // type of post separately.
+ IList draftRecentPosts = new List();
+ IList liveRecentPosts = new List();
+ IList scheduledRecentPosts = new List();
+ IEnumerable allPosts = new List();
- var draftRecentPosts = draftRecentPostsList.Items ?? new List();
- var liveRecentPosts = liveRecentPostsList.Items ?? new List();
- var scheduledRecentPosts = scheduledRecentPostsList.Items ?? new List();
+ // We keep around the PostList returned by each request to support pagination.
+ PostList draftRecentPostsList = null;
+ PostList liveRecentPostsList = null;
+ PostList scheduledRecentPostsList = null;
- var allPosts = draftRecentPosts.Concat(liveRecentPosts).Concat(scheduledRecentPosts);
+ // Google has a per-request results limit on their API.
+ var maxResultsPerRequest = Math.Min(maxPosts, MaxResultsPerRequest);
+
+ // We break out of the following loop depending on which one of these two cases we hit:
+ // (a) the number of all blog posts ever posted to this blog is greater than maxPosts, so eventually
+ // allPosts.count() will exceed maxPosts and we can stop making requests.
+ // (b) the number of all blog posts ever posted to this blog is less than maxPosts, so eventually our
+ // calls to ListRecentPosts() will return 0 results and we need to stop making requests.
+ do
+ {
+ draftRecentPostsList = ListRecentPosts(blogId, maxResultsPerRequest, now, PostsResource.ListRequest.StatusEnum.Draft, draftRecentPostsList);
+ liveRecentPostsList = ListRecentPosts(blogId, maxResultsPerRequest, now, PostsResource.ListRequest.StatusEnum.Live, liveRecentPostsList);
+ scheduledRecentPostsList = ListRecentPosts(blogId, maxResultsPerRequest, now, PostsResource.ListRequest.StatusEnum.Scheduled, scheduledRecentPostsList);
+
+ draftRecentPosts = draftRecentPostsList?.Items ?? new List();
+ liveRecentPosts = liveRecentPostsList?.Items ?? new List();
+ scheduledRecentPosts = scheduledRecentPostsList?.Items ?? new List();
+ allPosts = allPosts.Concat(draftRecentPosts).Concat(liveRecentPosts).Concat(scheduledRecentPosts);
+
+ } while (allPosts.Count() < maxPosts && (draftRecentPosts.Count > 0 || liveRecentPosts.Count > 0 || scheduledRecentPosts.Count > 0));
+
return allPosts
.OrderByDescending(p => p.Published)
.Take(maxPosts)
@@ -430,22 +466,53 @@ namespace OpenLiveWriter.BlogClient.Clients
return ConvertToBlogPost(getPageRequest.Execute());
}
- private PageList ListPages(string blogId, int? maxPages, PagesResource.ListRequest.StatusEnum status)
+ private PageList ListPages(string blogId, int? maxPages, PagesResource.ListRequest.StatusEnum status, PageList previousPage)
{
+ if (previousPage != null && string.IsNullOrWhiteSpace(previousPage.NextPageToken))
+ {
+ // The previous page was also the last page, so do nothing and return an empty list.
+ return new PageList();
+ }
+
var getPagesRequest = GetService().Pages.List(blogId);
- getPagesRequest.MaxResults = maxPages;
+ if (maxPages.HasValue)
+ {
+ // Google has a per-request results limit on their API.
+ getPagesRequest.MaxResults = Math.Min(maxPages.Value, MaxResultsPerRequest);
+ }
getPagesRequest.Status = status;
return getPagesRequest.Execute();
}
private IEnumerable ListAllPages(string blogId, int? maxPages)
{
- var draftPageList = ListPages(blogId, maxPages, PagesResource.ListRequest.StatusEnum.Draft);
- var livePageList = ListPages(blogId, maxPages, PagesResource.ListRequest.StatusEnum.Live);
+ // Blogger requires separate API calls to get drafts vs. live vs. scheduled posts. We aggregate each
+ // type of post separately.
+ IList draftPages = new List();
+ IList livePages = new List();
+ IEnumerable allPages = new List();
- var draftPages = draftPageList.Items ?? new List();
- var livePages = livePageList.Items ?? new List();
- return draftPages.Concat(livePages);
+ // We keep around the PageList returned by each request to support pagination.
+ PageList draftPagesList = null;
+ PageList livePagesList = null;
+
+ // We break out of the following loop depending on which one of these two cases we hit:
+ // (a) the number of all blog pages ever posted to this blog is greater than maxPages, so eventually
+ // allPages.count() will exceed maxPages and we can stop making requests.
+ // (b) the number of all blog pages ever posted to this blog is less than maxPages, so eventually our
+ // calls to ListPages() will return 0 results and we need to stop making requests.
+ do
+ {
+ draftPagesList = ListPages(blogId, maxPages, PagesResource.ListRequest.StatusEnum.Draft, draftPagesList);
+ livePagesList = ListPages(blogId, maxPages, PagesResource.ListRequest.StatusEnum.Live, livePagesList);
+
+ draftPages = draftPagesList?.Items ?? new List();
+ livePages = livePagesList?.Items ?? new List();
+ allPages = allPages.Concat(draftPages).Concat(livePages);
+
+ } while (allPages.Count() < maxPages && (draftPages.Count > 0 || livePages.Count > 0));
+
+ return allPages;
}
public PageInfo[] GetPageList(string blogId)