Skip to content

Commit

Permalink
New streaming method.
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianStehle committed Mar 1, 2023
1 parent 4d59f0f commit 99e5788
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 11 deletions.
2 changes: 1 addition & 1 deletion csharp/Squidex.ClientLibrary/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<PackageTags>Squidex HeadlessCMS</PackageTags>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Version>14.1.0</Version>
<Version>14.2.0</Version>
</PropertyGroup>

<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,68 @@ public async Task GetAllAsync(Func<TEntity, Task> callback, int batchSize = 200,
while (!ct.IsCancellationRequested);
}

/// <inheritdoc/>
public async Task StreamAllAsync(Func<TEntity, Task> callback, int skip = 0, QueryContext? context = null,
CancellationToken ct = default)
{
Guard.Between(skip, 0, int.MaxValue, nameof(skip));
Guard.NotNull(callback, nameof(callback));

context = (context ?? QueryContext.Default).WithoutTotal();

var httpClient = HttpClientProvider.Get();
try
{
using (var request = BuildRequest(HttpMethod.Get, BuildUrl($"stream?skip={skip}", false), null, context))
{
using (var response = await httpClient.SendAsync(request, ct))
{
await EnsureResponseIsValidAsync(response);

#if NETSTANDARD2_0 || NETCOREAPP3_1
using (var reader = new StreamReader(await response.Content.ReadAsStreamAsync()))
#else
using (var reader = new StreamReader(await response.Content.ReadAsStreamAsync(ct)))
#endif
{
string? line;
#if NET7_0_OR_GREATER
while ((line = await reader.ReadLineAsync(ct)) != null)
#else
while ((line = await reader.ReadLineAsync()) != null)
#endif
{
ct.ThrowIfCancellationRequested();

if (string.IsNullOrWhiteSpace(line))
{
continue;
}

const string Prefix = "data: ";

if (!line.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase))
{
throw new SquidexException("Line does not start with data prefix.", 0, null);
}

#pragma warning disable IDE0057 // Use range operator
var contentJson = line.Substring(Prefix.Length);
#pragma warning restore IDE0057 // Use range operator
var contentItem = contentJson.FromJson<TEntity>();

await callback(contentItem);
}
}
}
}
}
finally
{
HttpClientProvider.Return(httpClient);
}
}

/// <inheritdoc/>
public Task<ContentsResult<TEntity, TData>> GetAsync(ContentQuery? query = null, QueryContext? context = null,
CancellationToken ct = default)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ Task<List<BulkResult>> BulkUpdateAsync(BulkUpdate update,
/// <summary>
/// Gets all content items in batches.
/// </summary>
/// <param name="callback">The callbac that is invoked for each content item.</param>
/// <param name="callback">The callback that is invoked for each content item.</param>
/// <param name="batchSize">Size of each batch.</param>
/// <param name="context">The context object to add additonal headers to the request and change the behavior of the API when querying content items.</param>
/// <param name="ct">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
Expand All @@ -282,6 +282,20 @@ Task<List<BulkResult>> BulkUpdateAsync(BulkUpdate update,
Task GetAllAsync(Func<TEntity, Task> callback, int batchSize = 200, QueryContext? context = null,
CancellationToken ct = default);

/// <summary>
/// Gets all content items in batches.
/// </summary>
/// <param name="callback">The callback that is invoked for each content item.</param>
/// <param name="skip">The items to skip.</param>
/// <param name="context">The context object to add additonal headers to the request and change the behavior of the API when querying content items.</param>
/// <param name="ct">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>
/// The task for completion.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="callback"/> is null.</exception>
Task StreamAllAsync(Func<TEntity, Task> callback, int skip = 0, QueryContext? context = null,
CancellationToken ct = default);

/// <summary>
/// Gets a content item by ID.
/// </summary>
Expand Down
25 changes: 25 additions & 0 deletions csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/QueryContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ public sealed class QueryContext
/// </value>
public bool IsUnpublished { get; private set; }

/// <summary>
/// Gets a value indicating whether scripting will be skipped.
/// </summary>
/// <value>
/// <c>true</c> if scripting will be skipped; otherwise, <c>false</c>.
/// </value>
public bool IsNotScripting { get; private set; }

/// <summary>
/// Gets a value indicating whether the Content CDN will not be used.
/// </summary>
Expand Down Expand Up @@ -128,6 +136,18 @@ public QueryContext WithoutCDN(bool value = true)
return Clone(c => c.IsNotUsingCDN = value);
}

/// <summary>
/// Creates a new copy of the context object and defines whether scripting will be skipped.
/// </summary>
/// <param name="value">if set to <c>true</c> scripting will be skipped.</param>
/// <returns>
/// The new query context.
/// </returns>
public QueryContext WithoutScripting(bool value = true)
{
return Clone(c => c.IsNotScripting = value);
}

/// <summary>
/// Creates a new copy of the context object and defines whether the total number of results should not be returned.
/// </summary>
Expand Down Expand Up @@ -216,6 +236,11 @@ internal void AddToHeaders(HttpRequestHeaders headers)
headers.TryAddWithoutValidation("X-NoSlowTotal", "true");
}

if (IsNotScripting)
{
headers.TryAddWithoutValidation("X-NoScripting", "true");
}

if (Languages != null)
{
var languages = string.Join(", ", Languages.Where(x => !string.IsNullOrWhiteSpace(x)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ namespace Squidex.ClientLibrary.Utils;
/// <seealso cref="IDisposable" />
public abstract class SquidexClientBase
{
private readonly IHttpClientProvider httpClientProvider;

/// <summary>
/// Gets the name of the App.
/// </summary>
Expand All @@ -34,6 +32,11 @@ public abstract class SquidexClientBase
/// </value>
public SquidexOptions Options { get; }

/// <summary>
/// The http client provider.
/// </summary>
protected IHttpClientProvider HttpClientProvider { get; }

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
protected internal SquidexClientBase(SquidexOptions options, string appName, IHttpClientProvider httpClientProvider)
{
Expand All @@ -47,13 +50,13 @@ protected internal SquidexClientBase(SquidexOptions options, string appName, IHt
// Just pass in the options to have direct access to them.
Options = options;

this.httpClientProvider = httpClientProvider;
HttpClientProvider = httpClientProvider;
}

protected internal async Task RequestAsync(HttpMethod method, string path, HttpContent? content, QueryContext? context,
CancellationToken ct)
{
var httpClient = httpClientProvider.Get();
var httpClient = HttpClientProvider.Get();
try
{
using (var request = BuildRequest(method, path, content, context))
Expand All @@ -66,14 +69,14 @@ protected internal async Task RequestAsync(HttpMethod method, string path, HttpC
}
finally
{
httpClientProvider.Return(httpClient);
HttpClientProvider.Return(httpClient);
}
}

protected internal async Task<Stream> RequestStreamAsync(HttpMethod method, string path, HttpContent? content, QueryContext? context,
CancellationToken ct)
{
var httpClient = httpClientProvider.Get();
var httpClient = HttpClientProvider.Get();
try
{
using (var request = BuildRequest(method, path, content, context))
Expand All @@ -90,14 +93,14 @@ protected internal async Task<Stream> RequestStreamAsync(HttpMethod method, stri
}
finally
{
httpClientProvider.Return(httpClient);
HttpClientProvider.Return(httpClient);
}
}

protected internal async Task<T> RequestJsonAsync<T>(HttpMethod method, string path, HttpContent? content, QueryContext? context,
CancellationToken ct)
{
var httpClient = httpClientProvider.Get();
var httpClient = HttpClientProvider.Get();
try
{
using (var request = BuildRequest(method, path, content, context))
Expand All @@ -112,7 +115,7 @@ protected internal async Task<T> RequestJsonAsync<T>(HttpMethod method, string p
}
finally
{
httpClientProvider.Return(httpClient);
HttpClientProvider.Return(httpClient);
}
}

Expand Down

0 comments on commit 99e5788

Please sign in to comment.