Parallelize invoice image fetch, add telemetry, generalize HtmlConverter
- InvoiceService.ResolveInlineImagesAsync now fetches an invoice's distinct whitelisted images concurrently (SemaphoreSlim-capped at 4) instead of one at a time. It ran inline on every invoice render, including repeated draft-preview renders, so N sequential mfr-portal round-trips could otherwise add N x latency to a user-facing request. - Add fuchs.invoice.inlineimages.fetches (counter, tagged by outcome: success/http_error/bad_content_type/oversized/exception) and fuchs.invoice.inlineimages.resolve.duration (histogram) to FuchsTelemetry, matching the project's observability convention for outbound-HTTP operations. - MigraDocExtensions.HtmlConverter's pre-resolved-image parameter is renamed inlineImages -> resolvedImages with generic docs: the converter fetches nothing and knows nothing about invoices — which images to resolve, and any whitelist/network policy, is entirely the caller's concern (InvoiceService, ADR 0018 today; any future caller tomorrow). - Add tests asserting fetches actually overlap (not just faster) and that the new counter/histogram are recorded. - Update ADR 0018 to describe the concurrent fetch, telemetry, and the generalized converter parameter. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,13 +26,14 @@ namespace MigraDoc.Extensions
|
||||
|
||||
public static Section AddHtml(this Section section, string html)
|
||||
=> section.Add(html, new MigraDoc.Extensions.Html.HtmlConverter());
|
||||
/// <param name="inlineImages">Maps an <img> element's original <c>src</c> to a MigraDoc
|
||||
/// image source ("base64:...") for images already fetched/vetted by the caller (see
|
||||
/// InvoiceService.ResolveInlineImagesAsync / ADR 0018). Src values not present here are
|
||||
/// dropped, matching the converter's long-standing behaviour for unsupported elements.</param>
|
||||
/// <param name="resolvedImages">Maps an <img> element's original <c>src</c> to a MigraDoc
|
||||
/// image source ("base64:...") for images the caller has already fetched/vetted — this converter
|
||||
/// never fetches anything itself. A src not present here is dropped, matching the converter's
|
||||
/// long-standing behaviour for unsupported elements. (One caller: InvoiceService resolves
|
||||
/// whitelisted invoice-text image URLs this way — see ADR 0018.)</param>
|
||||
public static Cell AddHtml(this Cell cell, string html,
|
||||
IReadOnlyDictionary<string, string>? inlineImages = null)
|
||||
=> cell.Add(html, new MigraDoc.Extensions.Html.HtmlConverter(inlineImages));
|
||||
IReadOnlyDictionary<string, string>? resolvedImages = null)
|
||||
=> cell.Add(html, new MigraDoc.Extensions.Html.HtmlConverter(resolvedImages));
|
||||
public static TextFrame AddHtml(this TextFrame frame, string html)
|
||||
=> frame.Add(html, new MigraDoc.Extensions.Html.HtmlConverter());
|
||||
|
||||
@@ -62,15 +63,20 @@ namespace MigraDoc.Extensions
|
||||
|
||||
namespace MigraDoc.Extensions.Html
|
||||
{
|
||||
/// <summary>Converts a subset of HTML to MigraDoc document objects.</summary>
|
||||
/// <summary>
|
||||
/// Converts a subset of HTML to MigraDoc document objects. Fetches nothing itself: an
|
||||
/// <img> only renders when its <c>src</c> is a key in the <c>resolvedImages</c> map the
|
||||
/// caller supplies — any network/whitelist policy for resolving those images lives entirely
|
||||
/// with the caller (e.g. <c>InvoiceService.ResolveInlineImagesAsync</c>, ADR 0018).
|
||||
/// </summary>
|
||||
public class HtmlConverter : MigraDoc.Extensions.IConverter
|
||||
{
|
||||
private readonly IDictionary<string, Func<HtmlNode, DocumentObject, DocumentObject>> _handlers;
|
||||
private readonly IReadOnlyDictionary<string, string>? _inlineImages;
|
||||
private readonly IReadOnlyDictionary<string, string>? _resolvedImages;
|
||||
|
||||
public HtmlConverter(IReadOnlyDictionary<string, string>? inlineImages = null)
|
||||
public HtmlConverter(IReadOnlyDictionary<string, string>? resolvedImages = null)
|
||||
{
|
||||
_inlineImages = inlineImages;
|
||||
_resolvedImages = resolvedImages;
|
||||
_handlers = new Dictionary<string, Func<HtmlNode, DocumentObject, DocumentObject>>();
|
||||
AddDefaultHandlers();
|
||||
}
|
||||
@@ -120,7 +126,7 @@ namespace MigraDoc.Extensions.Html
|
||||
_handlers["img"] = (n, p) =>
|
||||
{
|
||||
string src = n.GetAttributeValue("src", "");
|
||||
if (src.Length == 0 || _inlineImages == null || !_inlineImages.TryGetValue(src, out var source))
|
||||
if (src.Length == 0 || _resolvedImages == null || !_resolvedImages.TryGetValue(src, out var source))
|
||||
return p;
|
||||
var para = GetPara(p);
|
||||
if (para == null) return p;
|
||||
|
||||
Reference in New Issue
Block a user