Previewer ConnectionService refactoring

This commit is contained in:
MarcinZiabek
2022-12-06 20:54:12 +01:00
parent 42c6c75862
commit 423c630a47
9 changed files with 190 additions and 158 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
@@ -83,6 +83,8 @@ namespace QuestPDF.ReportSample.Layouts
.Range(0, model.PhotoCount)
.ToList()
.ForEach(x => grid.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>());
//grid.Item().AspectRatio(4 / 3f).Width(100000).Component<ImagePlaceholder>();
});
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ namespace QuestPDF.ReportSample
[Test]
public void GenerateAndShowPdf()
{
Report.ShowInPreviewer();
Report.ShowInPreviewer(5000);
//ImagePlaceholder.Solid = true;
//
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
+1 -1
View File
@@ -10,7 +10,7 @@ namespace QuestPDF.Drawing.Proxy;
internal static class Helpers
{
public static IEnumerable<DocumentElementProperty> GetElementConfiguration(this IElement element)
public static IReadOnlyCollection<DocumentElementProperty> GetElementConfiguration(this IElement element)
{
return element
.GetType()
+44
View File
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using QuestPDF.Previewer.Inspection;
namespace QuestPDF.Previewer;
internal sealed class IncompatibleVersionApiRequest
{
}
internal sealed class NotifyPresenceApiRequest
{
public string Id { get; set; }
public string LibraryVersion { get; set; }
public bool IsDotnet6OrBeyond { get; set; }
public bool IsDotnet3OrBeyond { get; set; }
public bool IsExecutedInUnitTest { get; set; }
}
internal sealed class DocumentPreviewApiRequest
{
public ICollection<PageSnapshot> PageSnapshots { get; set; }
public InspectionElement DocumentHierarchy { get; set; }
public class PageSnapshot
{
public string ResourceId { get; } = Guid.NewGuid().ToString("N");
public int PageNumber { get; set; }
public float Width { get; set; }
public float Height { get; set; }
}
}
internal sealed class GenericErrorApiRequest
{
public GenericError? Error { get; set; }
}
internal sealed class LayoutErrorApiRequest
{
public LayoutRenderingTrace? Trace { get; set; }
}
+17 -1
View File
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
using QuestPDF.Previewer.Inspection;
namespace QuestPDF.Previewer;
@@ -32,3 +34,17 @@ internal sealed class LayoutRenderingTrace
public SpacePlan SpacePlan { get; set; }
public IReadOnlyCollection<LayoutRenderingTrace> Children { get; set; }
}
internal sealed class GenericError
{
public string ExceptionType { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }
public GenericError? InnerException { get; set; }
}
+7 -8
View File
@@ -12,19 +12,19 @@ namespace QuestPDF.Previewer
{
public static class Extensions
{
public static void ShowInPreviewer(this IDocument document, int port = 5000)
public static void ShowInPreviewer(this IDocument document, int port = 12500)
{
document.ShowInPreviewerAsync(port).ConfigureAwait(true).GetAwaiter().GetResult();
}
public static async Task ShowInPreviewerAsync(this IDocument document, int port = 5000, CancellationToken cancellationToken = default)
public static async Task ShowInPreviewerAsync(this IDocument document, int port = 12500, CancellationToken cancellationToken = default)
{
QuestPDF.Settings.EnableDebugging = true;
var previewerService = new PreviewerService(port);
using var cancellationTokenSource = new CancellationTokenSource();
previewerService.OnPreviewerStopped += () => cancellationTokenSource.Cancel();
previewerService.OnLostConnection += () => cancellationTokenSource.Cancel();
await previewerService.Connect();
await RefreshPreview();
@@ -35,14 +35,13 @@ namespace QuestPDF.Previewer
while (true)
{
//if (cancellationToken.IsCancellationRequested)
// break;
if (cancellationToken.IsCancellationRequested)
break;
// ReSharper disable once MethodSupportsCancellation
await Task.Delay(TimeSpan.FromMilliseconds(1000));
}
//await previewerService.Disconnect();
Task RefreshPreview()
{
try
+116 -145
View File
@@ -4,72 +4,29 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using QuestPDF.Drawing;
using QuestPDF.Drawing.Exceptions;
using QuestPDF.Previewer.Inspection;
namespace QuestPDF.Previewer
{
internal sealed class NotifyPresenceRequest
{
public string Id { get; set; }
public string LibraryVersion { get; set; }
public bool IsDotnet6OrBeyond { get; set; }
public bool IsDotnet3OrBeyond { get; set; }
public bool IsExecutedInUnitTest { get; set; }
}
internal sealed class GenericError
{
public string ExceptionType { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }
public GenericError? InnerException { get; set; }
}
internal sealed class ShowGenericErrorApiRequest
{
public GenericError? Error { get; set; }
}
internal sealed class ShowLayoutErrorApiRequest
{
public LayoutRenderingTrace? Trace { get; set; }
}
internal sealed class UpdateDocumentPreviewApiRequest
{
public ICollection<PageSnapshot> PageSnapshots { get; set; }
public InspectionElement DocumentHierarchy { get; set; }
public class PageSnapshot
{
public string ResourceId { get; } = Guid.NewGuid().ToString("N");
public int PageNumber { get; set; }
public float Width { get; set; }
public float Height { get; set; }
}
}
internal class PreviewerService
{
private string ClientId { get; } = Guid.NewGuid().ToString("D");
private string LibraryVersion { get; } = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version?.ToString();
private int Port { get; }
private HttpClient HttpClient { get; }
public event Action? OnPreviewerStopped;
private const int RequiredPreviewerVersionMajor = 2022;
private const int RequiredPreviewerVersionMinor = 11;
public event Action? OnLostConnection;
private const int RequiredPreviewerVersionMajor = 2023;
private const int RequiredPreviewerVersionMinor = 1;
public PreviewerService(int port)
{
@@ -80,7 +37,48 @@ namespace QuestPDF.Previewer
Timeout = TimeSpan.FromSeconds(1)
};
}
internal static readonly JsonSerializerOptions JsonSerializerOptions = new()
{
PropertyNameCaseInsensitive = true,
MaxDepth = 256,
Converters =
{
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
}
};
#region Connection
public async Task Connect()
{
var isAvailable = await IsPreviewerAvailable();
if (!isAvailable)
{
StartPreviewer();
await WaitForConnection();
}
var previewerVersion = await GetPreviewerVersion();
CheckVersionCompatibility(previewerVersion);
StartNotifyPresenceTask();
}
private async Task<bool> IsPreviewerAvailable()
{
try
{
using var result = await HttpClient.GetAsync("/ping");
return result.IsSuccessStatusCode;
}
catch
{
return false;
}
}
private void StartPreviewer()
{
try
@@ -105,72 +103,99 @@ namespace QuestPDF.Previewer
}
}
public async Task Connect()
private async Task WaitForConnection()
{
var isAvailable = await IsPreviewerAvailable();
if (!isAvailable)
{
//StartPreviewer();
//await WaitForConnection();
}
using var cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
//var previewerVersion = await GetPreviewerVersion();
//CheckVersionCompatibility(previewerVersion);
StartNotifyPresenceTask();
var cancellationToken = cancellationTokenSource.Token;
while (true)
{
// ReSharper disable once MethodSupportsCancellation
await Task.Delay(TimeSpan.FromMilliseconds(250));
if (cancellationToken.IsCancellationRequested)
throw new Exception($"Cannot connect to the QuestPDF Previewer tool. Please make sure that your Operating System does not block HTTP connections on port {Port}.");
var isConnected = await IsPreviewerAvailable();
if (isConnected)
break;
}
}
private async Task<bool> IsPreviewerAvailable()
#endregion
#region Checking compatibility
private async Task<Version> GetPreviewerVersion()
{
try
{
using var result = await HttpClient.GetAsync("/ping");
return result.IsSuccessStatusCode;
}
catch
{
return false;
}
using var result = await HttpClient.GetAsync("/version");
return await result.Content.ReadFromJsonAsync<Version>();
}
public void StartNotifyPresenceTask()
private async Task CheckVersionCompatibility(Version version)
{
if (version.Major == RequiredPreviewerVersionMajor && version.Minor == RequiredPreviewerVersionMinor)
return;
await ShowIncompatibleVersion();
throw new Exception($"Previewer version is not compatible. Possible solutions: " +
$"1) Update the QuestPDF library to newer version. " +
$"2) Update the QuestPDF previewer tool using the following command: 'dotnet tool update --global QuestPDF.Previewer --version {RequiredPreviewerVersionMajor}.{RequiredPreviewerVersionMinor}'");
}
private async Task ShowIncompatibleVersion()
{
var payload = new IncompatibleVersionApiRequest();
await HttpClient.PostAsJsonAsync("/update/incompatibleVersion", payload, JsonSerializerOptions);
}
#endregion
#region Reporting presence
private void StartNotifyPresenceTask()
{
Task.Run(async () =>
{
while (true)
{
NotifyPresence();
await NotifyPresence();
await Task.Delay(1000);
}
});
}
public async Task NotifyPresence()
private async Task NotifyPresence()
{
var payload = new NotifyPresenceRequest
var payload = new NotifyPresenceApiRequest
{
Id = ClientId,
LibraryVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version?.ToString(),
LibraryVersion = LibraryVersion,
IsDotnet6OrBeyond = RuntimeDetector.IsNet6OrGreater,
IsDotnet3OrBeyond = RuntimeDetector.IsNet3OrGreater,
IsExecutedInUnitTest = UnitTestDetector.RunningInUnitTest
};
await HttpClient.PostAsJsonAsync("/v1/notify/presence", payload);
await HttpClient.PostAsJsonAsync("/update/notifyPresence", payload);
}
#endregion
#region Updating state
public async Task ShowDocumentPreview(DocumentPreviewResult documentPreviewResult)
{
using var multipartContent = new MultipartFormDataContent();
var pages = new List<UpdateDocumentPreviewApiRequest.PageSnapshot>();
var pages = new List<DocumentPreviewApiRequest.PageSnapshot>();
foreach (var snapshot in documentPreviewResult.PageSnapshots)
{
var page = new UpdateDocumentPreviewApiRequest.PageSnapshot
var page = new DocumentPreviewApiRequest.PageSnapshot
{
PageNumber = documentPreviewResult.PageSnapshots.IndexOf(snapshot) + 1,
Width = snapshot.Size.Width,
@@ -183,17 +208,17 @@ namespace QuestPDF.Previewer
multipartContent.Add(new StreamContent(pictureStream), "snapshots", page.ResourceId);
}
var request = new UpdateDocumentPreviewApiRequest
var request = new DocumentPreviewApiRequest
{
PageSnapshots = pages,
PageSnapshots = pages,
DocumentHierarchy = documentPreviewResult.DocumentHierarchy
};
var json = JsonSerializer.Serialize(request);
var json = JsonSerializer.Serialize(request, JsonSerializerOptions);
var jsonContent = new StringContent(json, Encoding.UTF8, "application/json");
multipartContent.Add(jsonContent, "request");
using var response = await HttpClient.PostAsync("/v1/update/preview/document", multipartContent);
using var response = await HttpClient.PostAsync("/update/documentPreview", multipartContent);
response.EnsureSuccessStatusCode();
foreach (var picture in documentPreviewResult.PageSnapshots)
@@ -202,12 +227,12 @@ namespace QuestPDF.Previewer
public async Task ShowGenericError(Exception exception)
{
var payload = new ShowGenericErrorApiRequest
var payload = new GenericErrorApiRequest
{
Error = MapException(exception)
};
await HttpClient.PostAsJsonAsync("/v1/update/error/generic", payload);
await HttpClient.PostAsJsonAsync("/update/genericError", payload, JsonSerializerOptions);
static GenericError? MapException(Exception? exception)
{
@@ -226,69 +251,15 @@ namespace QuestPDF.Previewer
public async Task ShowLayoutError(DocumentLayoutException exception)
{
var payload = new ShowLayoutErrorApiRequest
var payload = new LayoutErrorApiRequest
{
Trace = exception.ElementTrace
};
var jsonSerializerOptions = new JsonSerializerOptions
{
MaxDepth = 256,
Converters =
{
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
}
};
await HttpClient.PostAsJsonAsync("/v1/update/error/layout", payload, jsonSerializerOptions);
await HttpClient.PostAsJsonAsync("/update/layoutError", payload, JsonSerializerOptions);
}
//
// private async Task<Version> GetPreviewerVersion()
// {
// using var result = await HttpClient.GetAsync("/version");
// return await result.Content.ReadFromJsonAsync<Version>();
// }
//
//
// private void CheckVersionCompatibility(Version version)
// {
// if (version.Major == RequiredPreviewerVersionMajor && version.Minor == RequiredPreviewerVersionMinor)
// return;
//
// throw new Exception($"Previewer version is not compatible. Possible solutions: " +
// $"1) Update the QuestPDF library to newer version. " +
// $"2) Update the QuestPDF previewer tool using the following command: 'dotnet tool update --global QuestPDF.Previewer --version {RequiredPreviewerVersionMajor}.{RequiredPreviewerVersionMinor}'");
// }
//
// private async Task WaitForConnection()
// {
// using var cancellationTokenSource = new CancellationTokenSource();
// cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
//
// var cancellationToken = cancellationTokenSource.Token;
//
// while (true)
// {
// await Task.Delay(TimeSpan.FromMilliseconds(250));
//
// if (cancellationToken.IsCancellationRequested)
// throw new Exception($"Cannot connect to the QuestPDF Previewer tool. Please make sure that your Operating System does not block HTTP connections on port {Port}.");
//
// var isConnected = await IsPreviewerAvailable();
//
// if (isConnected)
// break;
// }
// }
#endregion
}
}