Previewer ConnectionService refactoring
This commit is contained in:
@@ -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>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace QuestPDF.ReportSample
|
||||
[Test]
|
||||
public void GenerateAndShowPdf()
|
||||
{
|
||||
Report.ShowInPreviewer();
|
||||
Report.ShowInPreviewer(5000);
|
||||
|
||||
//ImagePlaceholder.Solid = true;
|
||||
//
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user