Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Dapplo.Confluence.Tests/JsonTestFiles/search.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"size": 1,
"_links": {
"self": "https://greenshot.atlassian.net/wiki/rest/api/content/search?cql=text%20~%20%22greenshot%22",
"next": "/rest/api/search?cql=text%20~%20%22greenshot%22&limit=25&cursor=raNDoMsTRiNg",
"base": "https://greenshot.atlassian.net/wiki",
"context": "/wiki"
}
Expand Down
18 changes: 15 additions & 3 deletions src/Dapplo.Confluence/ContentExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,11 +262,12 @@ public static async Task<History> GetHistoryAsync(this IContentDomain confluence
/// the execution context for CQL functions, provides current space key and content id. If this is
/// not provided some CQL functions will not be available.
/// </param>
/// <param name="cursor">Cursor identifier to get the next pages of the results</param>
/// <param name="pagingInformation">PagingInformation</param>
/// <param name="expandSearch">The expand value for the search, when null the value from the ConfluenceClientConfig.ExpandSearch is taken</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>Result with content items</returns>
public static Task<Result<Content>> SearchAsync(this IContentDomain confluenceClient, IFinalClause cqlClause, string cqlContext = null, PagingInformation pagingInformation = null, IEnumerable<string> expandSearch = null,
public static Task<CursorBasedResult<Content>> SearchAsync(this IContentDomain confluenceClient, IFinalClause cqlClause, string cqlContext = null, string cursor = null, PagingInformation pagingInformation = null, IEnumerable<string> expandSearch = null,
CancellationToken cancellationToken = default)
{
var searchDetails = new SearchDetails(cqlClause)
Expand All @@ -283,6 +284,11 @@ public static Task<Result<Content>> SearchAsync(this IContentDomain confluenceCl
{
searchDetails.ExpandSearch = expandSearch;
}
if (cursor != null)
{
searchDetails.Cursor = cursor;
}

return confluenceClient.SearchAsync(searchDetails, cancellationToken);
}

Expand All @@ -295,7 +301,7 @@ public static Task<Result<Content>> SearchAsync(this IContentDomain confluenceCl
/// <param name="searchDetails">All the details needed for a search</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns>Result with content items</returns>
public static async Task<Result<Content>> SearchAsync(this IContentDomain confluenceClient, SearchDetails searchDetails, CancellationToken cancellationToken = default)
public static async Task<CursorBasedResult<Content>> SearchAsync(this IContentDomain confluenceClient, SearchDetails searchDetails, CancellationToken cancellationToken = default)
{
if (searchDetails == null) throw new ArgumentNullException(nameof(searchDetails));

Expand All @@ -311,6 +317,12 @@ public static async Task<Result<Content>> SearchAsync(this IContentDomain conflu
{
searchUri = searchUri.ExtendQuery("start", searchDetails.Start);
}
if (!string.IsNullOrEmpty(searchDetails.Cursor))
{
searchUri = searchUri.ExtendQuery("cursor", searchDetails.Cursor);
searchUri = searchUri.ExtendQuery("next", "true");
}


var expand = string.Join(",", searchDetails.ExpandSearch ?? ConfluenceClientConfig.ExpandSearch ?? Enumerable.Empty<string>());
if (!string.IsNullOrEmpty(expand))
Expand All @@ -323,7 +335,7 @@ public static async Task<Result<Content>> SearchAsync(this IContentDomain conflu
searchUri = searchUri.ExtendQuery("cqlcontext", searchDetails.CqlContext);
}

var response = await searchUri.GetAsAsync<HttpResponse<Result<Content>, Error>>(cancellationToken).ConfigureAwait(false);
var response = await searchUri.GetAsAsync<HttpResponse<CursorBasedResult<Content>, Error>>(cancellationToken).ConfigureAwait(false);
return response.HandleErrors();
}

Expand Down
35 changes: 35 additions & 0 deletions src/Dapplo.Confluence/Entities/CursorBasedResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;

namespace Dapplo.Confluence.Entities
{
/// <summary>
/// A container to store pageable results that need a cursor to be paged.
/// See: https://developer.atlassian.com/cloud/confluence/rest/v1/api-group-search/
/// </summary>
/// <typeparam name="TResult"></typeparam>
public class CursorBasedResult<TResult> : Result<TResult>
{
private string cursor = null;

/// <summary>
/// Cursor needed to page trought the results.
/// </summary>
[JsonIgnore]
public string Cursor
{
get
{
if (!HasNext) return null;
if (cursor == null)
{
var querystring = Links.Next.OriginalString.Substring(Links.Next.OriginalString.IndexOf('?'));
cursor = UriParseExtensions.QueryStringToDictionary(querystring)?["cursor"];
}
return cursor;
}
}
}
}
5 changes: 5 additions & 0 deletions src/Dapplo.Confluence/Entities/SearchDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ public SearchDetails(IFinalClause cql)
/// Specify the search expand values, default is what is specified in the ConfluenceClientConfig.ExpandSearch
/// </summary>
public IEnumerable<string> ExpandSearch { get; set; } = ConfluenceClientConfig.ExpandSearch;

/// <summary>
/// Cursor used to page trought search results.
/// </summary>
public string Cursor { get; set; }
}