Browse Source

Communication is working, showing generic error, showing simple preview

MarcinZiabek 3 years ago
parent
commit
57e68e293d

+ 54 - 0
QuestPDF.UnitTests/PreviewerTests.cs

@@ -0,0 +1,54 @@
+
+
+using System;
+using NUnit.Framework;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+using QuestPDF.Previewer;
+
+namespace QuestPDF.UnitTests
+{
+    public class PreviewerTests
+    {
+        [Test]
+        public void SendError()
+        {
+            Document
+                .Create(container =>
+                {
+                    container.Page(page =>
+                    {
+                        page.Size(PageSizes.A4);
+                        page.Margin(2, Unit.Centimetre);
+                        page.PageColor(Colors.White);
+                        page.DefaultTextStyle(x => x.FontSize(20));
+                        
+                        page.Header()
+                            .Text("Hello PDF!")
+                            .SemiBold().FontSize(36).FontColor(Colors.Blue.Medium);
+                        
+                        page.Content()
+                            .PaddingVertical(1, Unit.Centimetre)
+                            .Column(x =>
+                            {
+                                x.Spacing(20);
+                                
+                                x.Item().Text(Placeholders.LoremIpsum());
+                                x.Item().Image(Placeholders.Image(200, 100));
+                            });
+                        
+                        page.Footer()
+                            .AlignCenter()
+                            .Text(x =>
+                            {
+                                //throw new Exception("Dupa!");
+                                x.Span("Page ");
+                                x.CurrentPageNumber();
+                            });
+                    });
+                })
+                .ShowInPreviewer();
+        }
+    }
+}

+ 0 - 108
QuestPDF/Previewer/ExceptionDocument.cs

@@ -1,108 +0,0 @@
-using System;
-using QuestPDF.Drawing;
-using QuestPDF.Fluent;
-using QuestPDF.Helpers;
-using QuestPDF.Infrastructure;
-using SkiaSharp;
-
-namespace QuestPDF.Previewer
-{
-    public class ExceptionDocument : IDocument
-    {
-        private Exception Exception { get; }
-    
-        public ExceptionDocument(Exception exception)
-        {
-            Exception = exception;
-        }
-    
-        public DocumentMetadata GetMetadata()
-        {
-            return DocumentMetadata.Default;
-        }
-
-        public void Compose(IDocumentContainer document)
-        {
-            document.Page(page =>
-            {
-                page.ContinuousSize(PageSizes.A4.Width);
-                page.Margin(50);
-                page.DefaultTextStyle(x => x.FontSize(16));
-
-                page.Foreground().PaddingTop(5).Border(10).BorderColor(Colors.Red.Medium);
-                
-                page.Header()
-                    .ShowOnce()
-                    .PaddingBottom(5)
-                    .Row(row =>
-                    {
-                        row.Spacing(15);
-                        
-                        row.AutoItem()
-                            .PaddingTop(15)
-                            .Width(48)
-                            .AspectRatio(1)
-                            .Canvas((canvas, size) =>
-                            {
-                                const float iconSize = 24;
-                                using var iconPath = SKPath.ParseSvgPathData("M23,12L20.56,14.78L20.9,18.46L17.29,19.28L15.4,22.46L12,21L8.6,22.47L6.71,19.29L3.1,18.47L3.44,14.78L1,12L3.44,9.21L3.1,5.53L6.71,4.72L8.6,1.54L12,3L15.4,1.54L17.29,4.72L20.9,5.54L20.56,9.22L23,12M20.33,12L18.5,9.89L18.74,7.1L16,6.5L14.58,4.07L12,5.18L9.42,4.07L8,6.5L5.26,7.09L5.5,9.88L3.67,12L5.5,14.1L5.26,16.9L8,17.5L9.42,19.93L12,18.81L14.58,19.92L16,17.5L18.74,16.89L18.5,14.1L20.33,12M11,15H13V17H11V15M11,7H13V13H11V7");
-                                
-                                using var paint = new SKPaint()
-                                { 
-                                    Color = SKColors.Red,
-                                    IsAntialias = true
-                                };
-                                
-                                canvas.Scale(size.Width / iconSize);
-                                canvas.DrawPath(iconPath, paint);
-                            });
-                        
-                        row.RelativeItem()
-                            .Column(column =>
-                            {
-                                column.Item().Text("Exception").FontSize(36).FontColor(Colors.Red.Medium).Bold();
-                                column.Item().PaddingTop(-10).Text("Don't panic! Just analyze what's happened...").FontSize(18).FontColor(Colors.Red.Medium).Bold();
-                            }); 
-                    });
-
-                page.Content().PaddingVertical(20).Column(column =>
-                {
-                    var currentException = Exception;
-                    
-                    while (currentException != null)
-                    {
-                        column.Item()
-                            .PaddingTop(25)
-                            .PaddingBottom(15)
-                            
-                            .Padding(-10)
-                            .Background(Colors.Grey.Lighten3)
-                            .Padding(10)
-                            
-                            .Text(text =>
-                            {
-                                text.DefaultTextStyle(x => x.FontSize(16));
-
-                                text.Span(currentException.GetType().Name + ": ").Bold();
-                                text.Span(currentException.Message);
-                            });
-                        
-                        foreach (var trace in currentException.StackTrace.Split('\n'))
-                        {
-                            column
-                                .Item()
-                                .ShowEntire()
-                                .BorderBottom(1)
-                                .BorderColor(Colors.Grey.Lighten2)
-                                .PaddingVertical(5)
-                                .Text(trace.Trim())
-                                .FontSize(12);
-                        }
-                        
-                        currentException = currentException.InnerException;
-                    }
-                });
-            });
-        }
-    }   
-}

+ 19 - 0
QuestPDF/Previewer/Helpers.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Net.Http;
+using System.Text;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace QuestPDF.Previewer;
+
+internal static class Helpers
+{
+    public static Task<HttpResponseMessage> PostAsJsonAsync<TValue>(this HttpClient client, string? requestUri, TValue value, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default)
+    {
+        var json = JsonSerializer.Serialize(value);
+        var jsonContent = new StringContent(json, Encoding.UTF8, "application/json");
+        
+        return client.PostAsync(requestUri, jsonContent, cancellationToken);
+    }
+}

+ 23 - 28
QuestPDF/Previewer/PreviewerExtensions.cs

@@ -1,4 +1,4 @@
-#if NET6_0_OR_GREATER
+#if NETCOREAPP3_0_OR_GREATER
 
 using System;
 using System.Collections.Generic;
@@ -11,12 +11,12 @@ namespace QuestPDF.Previewer
 {
     public static class Extensions
     {
-        public static void ShowInPreviewer(this IDocument document, int port = 12500)
+        public static void ShowInPreviewer(this IDocument document, int port = 5000)
         {
             document.ShowInPreviewerAsync(port).ConfigureAwait(true).GetAwaiter().GetResult();
         }
         
-        public static async Task ShowInPreviewerAsync(this IDocument document, int port = 12500)
+        public static async Task ShowInPreviewerAsync(this IDocument document, int port = 5000)
         {
             var previewerService = new PreviewerService(port);
             
@@ -26,39 +26,34 @@ namespace QuestPDF.Previewer
             await previewerService.Connect();
             await RefreshPreview();
             
-            HotReloadManager.UpdateApplicationRequested += (_, _) => RefreshPreview();
-            
-            await WaitForPreviewerExit(cancellationTokenSource.Token);
+            //
+            // //HotReloadManager.UpdateApplicationRequested += (_, _) => RefreshPreview();
+            //
+            // await WaitForPreviewerExit(cancellationTokenSource.Token);
             
             Task RefreshPreview()
             {
-                var pictures = GetPictures();
-                return previewerService.RefreshPreview(pictures);
-                
-                ICollection<PreviewerPicture> GetPictures()
+                try
                 {
-                    try
-                    {
-                        return DocumentGenerator.GeneratePreviewerPictures(document);
-                    }
-                    catch (Exception exception)
-                    {
-                        var exceptionDocument = new ExceptionDocument(exception);
-                        return DocumentGenerator.GeneratePreviewerPictures(exceptionDocument);
-                    }
+                    var pictures = DocumentGenerator.GeneratePreviewerPictures(document);
+                    return previewerService.ShowDocumentPreview(pictures);
                 }
-            }
-
-            async Task WaitForPreviewerExit(CancellationToken cancellationToken)
-            {
-                while (true)
+                catch (Exception exception)
                 {
-                    if (cancellationToken.IsCancellationRequested)
-                        return;
-                
-                    await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
+                    return previewerService.ShowGenericError(exception);
                 }
             }
+            //
+            // async Task WaitForPreviewerExit(CancellationToken cancellationToken)
+            // {
+            //     while (true)
+            //     {
+            //         if (cancellationToken.IsCancellationRequested)
+            //             return;
+            //     
+            //         await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
+            //     }
+            // }
         }
     }
 }

+ 3 - 3
QuestPDF/Previewer/PreviewerRefreshCommand.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using QuestPDF.Infrastructure;
 
-#if NET6_0_OR_GREATER
+#if NETCOREAPP3_0_OR_GREATER
 
 namespace QuestPDF.Previewer
 {
@@ -14,8 +14,8 @@ namespace QuestPDF.Previewer
         {
             public string Id { get; } = Guid.NewGuid().ToString("N");
             
-            public float Width { get; init; }
-            public float Height { get; init; }
+            public float Width { get; set; }
+            public float Height { get; set; }
         }
     }
 }

+ 198 - 107
QuestPDF/Previewer/PreviewerService.cs

@@ -1,16 +1,47 @@
-#if NET6_0_OR_GREATER
+#if NETCOREAPP3_0_OR_GREATER
 
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Net.Http;
-using System.Net.Http.Json;
-using System.Threading;
+using System.Text;
+using System.Text.Json;
 using System.Threading.Tasks;
 using QuestPDF.Drawing;
 
 namespace QuestPDF.Previewer
 {
+    
+    
+    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 ShowLayoutErrorApiRequestVersion
+    {
+        public GenericError? Error { get; set; }
+    }
+
+
+    internal sealed class UpdateDocumentPreviewApiRequest
+    {
+        public ICollection<PageSnapshot> PageSnapshots { 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 int Port { get; }
@@ -31,133 +62,193 @@ namespace QuestPDF.Previewer
             };
         }
 
-        public async Task Connect()
+        public async Task ShowDocumentPreview(ICollection<PreviewerPicture> pictures)
         {
-            var isAvailable = await IsPreviewerAvailable();
-
-            if (!isAvailable)
-            {
-                StartPreviewer();
-                await WaitForConnection();
-            }
+            using var multipartContent = new MultipartFormDataContent();
             
-            var previewerVersion = await GetPreviewerVersion();
-            CheckVersionCompatibility(previewerVersion);
-        }
-
-        private async Task<bool> IsPreviewerAvailable()
-        {
-            try
+            var pages = new List<UpdateDocumentPreviewApiRequest.PageSnapshot>();
+            
+            foreach (var picture in pictures)
             {
-                using var result = await HttpClient.GetAsync("/ping");
-                return result.IsSuccessStatusCode;
+                var page = new UpdateDocumentPreviewApiRequest.PageSnapshot
+                {
+                    Width = picture.Size.Width,
+                    Height = picture.Size.Height
+                };
+                
+                pages.Add(page);
+            
+                var pictureStream = picture.Picture.Serialize().AsStream();
+                multipartContent.Add(new StreamContent(pictureStream), "snapshots", page.ResourceId);
             }
-            catch
+            
+            var request = new UpdateDocumentPreviewApiRequest
             {
-                return false;
-            }
-        }
-        
-        private async Task<Version> GetPreviewerVersion()
-        {
-            using var result = await HttpClient.GetAsync("/version");
-            return await result.Content.ReadFromJsonAsync<Version>();
+                PageSnapshots = pages
+            };
+            
+            var json = JsonSerializer.Serialize(request);
+            var jsonContent = new StringContent(json, Encoding.UTF8, "application/json");
+            multipartContent.Add(jsonContent, "request");
+            
+            using var response = await HttpClient.PostAsync("/v1/update/preview/document", multipartContent);
+            response.EnsureSuccessStatusCode();
+            
+            foreach (var picture in pictures)
+                picture.Picture.Dispose();
         }
         
-        private void StartPreviewer()
+        public async Task ShowGenericError(Exception exception)
         {
-            try
+            var payload = new ShowLayoutErrorApiRequestVersion
             {
-                var process = new Process
-                {
-                    StartInfo = new()
-                    {
-                        FileName = "questpdf-previewer",
-                        Arguments = $"{Port}",
-                        UseShellExecute = false,
-                        CreateNoWindow = true
-                    }
-                };
-                
-                process.Start();
+                Error = MapException(exception)
+            };
 
-                Task.Run(async () =>
-                {
-                    await process.WaitForExitAsync();
-                    process.Dispose();
-                    OnPreviewerStopped?.Invoke();
-                });
-            }
-            catch
+            await HttpClient.PostAsJsonAsync("/v1/update/error/generic", payload);
+            
+            static GenericError? MapException(Exception? exception)
             {
-                throw new Exception("Cannot start the QuestPDF Previewer tool. " +
-                                    "Please install it by executing in terminal the following command: 'dotnet tool install --global QuestPDF.Previewer'.");
+                if (exception == null)
+                    return null;
+                        
+                return new GenericError
+                {
+                    ExceptionType = exception.GetType().FullName, 
+                    Message = exception.Message, 
+                    StackTrace = exception.StackTrace, 
+                    InnerException = MapException(exception.InnerException)
+                };
             }
         }
-
-        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()
+        public async Task Connect()
         {
-            using var cancellationTokenSource = new CancellationTokenSource();
-            cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
-            
-            var cancellationToken = cancellationTokenSource.Token; 
-            
-            while (true)
+            var isAvailable = await IsPreviewerAvailable();
+        
+            if (!isAvailable)
             {
-                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;
+                //StartPreviewer();
+                //await WaitForConnection();
             }
+            
+            //var previewerVersion = await GetPreviewerVersion();
+            //CheckVersionCompatibility(previewerVersion);
         }
         
-        public async Task RefreshPreview(ICollection<PreviewerPicture> pictures)
+        private async Task<bool> IsPreviewerAvailable()
         {
-            using var multipartContent = new MultipartFormDataContent();
-
-            var pages = new List<PreviewerRefreshCommand.Page>();
-            
-            foreach (var picture in pictures)
+            try
             {
-                var page = new PreviewerRefreshCommand.Page
-                {
-                    Width = picture.Size.Width,
-                    Height = picture.Size.Height
-                };
-                
-                pages.Add(page);
-
-                var pictureStream = picture.Picture.Serialize().AsStream();
-                multipartContent.Add(new StreamContent(pictureStream), page.Id, page.Id);
+                using var result = await HttpClient.GetAsync("/ping");
+                return result.IsSuccessStatusCode;
             }
-
-            var command = new PreviewerRefreshCommand
+            catch
             {
-                Pages = pages
-            };
-            
-            multipartContent.Add(JsonContent.Create(command), "command");
-
-            using var _ = await HttpClient.PostAsync("/update/preview", multipartContent);
-
-            foreach (var picture in pictures)
-                picture.Picture.Dispose();
+                return false;
+            }
         }
+        //
+        // private async Task<Version> GetPreviewerVersion()
+        // {
+        //     using var result = await HttpClient.GetAsync("/version");
+        //     return await result.Content.ReadFromJsonAsync<Version>();
+        // }
+        //
+        // private void StartPreviewer()
+        // {
+        //     try
+        //     {
+        //         var process = new Process
+        //         {
+        //             StartInfo = new()
+        //             {
+        //                 FileName = "questpdf-previewer",
+        //                 Arguments = $"{Port}",
+        //                 UseShellExecute = false,
+        //                 CreateNoWindow = true
+        //             }
+        //         };
+        //         
+        //         process.Start();
+        //
+        //         Task.Run(async () =>
+        //         {
+        //             await process.WaitForExitAsync();
+        //             process.Dispose();
+        //             OnPreviewerStopped?.Invoke();
+        //         });
+        //     }
+        //     catch
+        //     {
+        //         throw new Exception("Cannot start the QuestPDF Previewer tool. " +
+        //                             "Please install it by executing in terminal the following command: 'dotnet tool install --global QuestPDF.Previewer'.");
+        //     }
+        // }
+        //
+        // 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;
+        //     }
+        // }
+        //
+        // public async Task RefreshPreview(ICollection<PreviewerPicture> pictures)
+        // {
+        //     using var multipartContent = new MultipartFormDataContent();
+        //
+        //     var pages = new List<PreviewerRefreshCommand.Page>();
+        //     
+        //     foreach (var picture in pictures)
+        //     {
+        //         var page = new PreviewerRefreshCommand.Page
+        //         {
+        //             Width = picture.Size.Width,
+        //             Height = picture.Size.Height
+        //         };
+        //         
+        //         pages.Add(page);
+        //
+        //         var pictureStream = picture.Picture.Serialize().AsStream();
+        //         multipartContent.Add(new StreamContent(pictureStream), page.Id, page.Id);
+        //     }
+        //
+        //     var command = new PreviewerRefreshCommand
+        //     {
+        //         Pages = pages
+        //     };
+        //     
+        //     multipartContent.Add(JsonContent.Create(command), "command");
+        //
+        //     using var _ = await HttpClient.PostAsync("/update/preview", multipartContent);
+        //
+        //     foreach (var picture in pictures)
+        //         picture.Picture.Dispose();
+        // }
     }
 }
 

+ 1 - 4
QuestPDF/QuestPDF.csproj

@@ -18,6 +18,7 @@
         <PackageLicenseExpression>MIT</PackageLicenseExpression>
         <Nullable>enable</Nullable>
         <TargetFrameworks>net462;netstandard2.0;netcoreapp2.0;netcoreapp3.0;net6.0</TargetFrameworks>
+        <TargetFrameworks>netcoreapp3.0;</TargetFrameworks>
         <IncludeSymbols>true</IncludeSymbols>
         <SymbolPackageFormat>snupkg</SymbolPackageFormat>
     </PropertyGroup>
@@ -69,8 +70,4 @@
     <ItemGroup>
       <Compile Remove="Previewer\Models\GenericError.cs" />
     </ItemGroup>
-
-    <ItemGroup>
-      <Folder Include="Previewer\Models" />
-    </ItemGroup>
 </Project>