|
|
@@ -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)
|
|
|
+ using var cancellationTokenSource = new CancellationTokenSource();
|
|
|
+ cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
|
|
|
+
|
|
|
+ var cancellationToken = cancellationTokenSource.Token;
|
|
|
+
|
|
|
+ while (true)
|
|
|
{
|
|
|
- //StartPreviewer();
|
|
|
- //await WaitForConnection();
|
|
|
+ // 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;
|
|
|
}
|
|
|
-
|
|
|
- //var previewerVersion = await GetPreviewerVersion();
|
|
|
- //CheckVersionCompatibility(previewerVersion);
|
|
|
-
|
|
|
- StartNotifyPresenceTask();
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Checking compatibility
|
|
|
+
|
|
|
+ private async Task<Version> GetPreviewerVersion()
|
|
|
+ {
|
|
|
+ using var result = await HttpClient.GetAsync("/version");
|
|
|
+ return await result.Content.ReadFromJsonAsync<Version>();
|
|
|
}
|
|
|
|
|
|
- private async Task<bool> IsPreviewerAvailable()
|
|
|
+ private async Task CheckVersionCompatibility(Version version)
|
|
|
{
|
|
|
- try
|
|
|
- {
|
|
|
- using var result = await HttpClient.GetAsync("/ping");
|
|
|
- return result.IsSuccessStatusCode;
|
|
|
- }
|
|
|
- catch
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ 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}'");
|
|
|
}
|
|
|
|
|
|
- public void StartNotifyPresenceTask()
|
|
|
+ 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
|
|
|
}
|
|
|
}
|
|
|
|