- List removal: GetOngoingEstimatesAsProvider filters on ProviderValidationDate so a provider's signed estimate leaves their list; PostIt refreshes the list on navigation pop-back. - Title renames: "Devis en attente de signature fournisseur/client" on both the provider and client sides (HomePage, VM, tests). - Move the estimate/bill TeX+PDF generation flow from Yavsc.Org into Yavsc.Api (TeXHelpers, PdfGenerationViewModel, BillViewComponent, Bill/Estimate templates, _ViewImports). Program.cs wires AddControllersWithViews + SiteSettings; FrontOfficeApiController injects IRazorViewEngine (cast to IViewEngine) with optional deps and exposes estimate.tex/estimate.pdf endpoints keyed by query id. - PDF generation drives through the Estimate_pdf view (side-effect render) and compiles via the system lualatex, fed TeX on stdin (no intermediate .tex), PDF to the bills dir, aux artifacts cleaned so only the .pdf persists. Resolve the bills dir to an absolute path so lualatex's -output-directory and working directory agree regardless of the host cwd (fixes "I can't write on file estimate-1.log"). - PostIt: DownloadAsync on the client + GetEstimateTex/PdfAsync; Télécharger PDF/TeX buttons on the validation and command-detail pages via a StorageProvider save helper. Co-Authored-By: Claude Code <noreply@anthropic.com>
95 lines
3.6 KiB
C#
95 lines
3.6 KiB
C#
using System;
|
|
using System.Net.Http;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Yavsc.Api.Client;
|
|
|
|
/// <summary>
|
|
/// Transport surface that the high-level clients
|
|
/// (<see cref="BlogApiClient"/>, <see cref="CircleApiClient"/>,
|
|
/// <see cref="BlogAclApiClient"/>) need to do their work.
|
|
///
|
|
/// <para>This is intentionally a thin, transport-only contract. It
|
|
/// does not include the OIDC login / refresh / logout surface —
|
|
/// that lives on the concrete <c>YavscApiClient</c> in the
|
|
/// consuming application and is wired by the application
|
|
/// composition root. Splitting the two keeps <c>Yavsc.Api.Client</c>
|
|
/// usable from any host (a CLI, a unit test, a future iOS
|
|
/// client) without dragging OIDC, identity, and a <c>Settings</c>
|
|
/// POMVO everywhere.</para>
|
|
///
|
|
/// <para>Implementations are expected to:</para>
|
|
/// <list type="bullet">
|
|
/// <item>Attach a Bearer access token to every outbound request.</item>
|
|
/// <item>Silently refresh the token on a 401 and retry once.</item>
|
|
/// <item>Serialise the request body as JSON and deserialise the
|
|
/// response body with case-insensitive property matching.</item>
|
|
/// </list>
|
|
///
|
|
/// The exception contract on non-2xx responses is
|
|
/// <see cref="HttpRequestException"/> with a message that includes
|
|
/// the response body (capped), so callers can surface the
|
|
/// server-side validation problem to the UI without losing
|
|
/// context.
|
|
/// </summary>
|
|
public interface IYavscApiClient : IAsyncDisposable
|
|
{
|
|
/// <summary>
|
|
/// The configured <see cref="HttpClient"/>. Clients set its
|
|
/// <c>BaseAddress</c> in their constructors to point at the
|
|
/// API host they target.
|
|
/// </summary>
|
|
HttpClient Http { get; }
|
|
|
|
/// <summary>Call a JSON endpoint with a typed return value.</summary>
|
|
/// <param name="method">HTTP verb.</param>
|
|
/// <param name="path">Path relative to <see cref="HttpClient.BaseAddress"/>.</param>
|
|
/// <param name="body">Optional request body, serialised as JSON.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
Task<T> CallAsync<T>(
|
|
HttpMethod method,
|
|
string path,
|
|
object? body = null,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>Call a multipart endpoint with a typed return value.</summary>
|
|
Task<T> CallAsync<T>(
|
|
HttpMethod method,
|
|
string path,
|
|
Func<HttpContent> contentFactory,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>Call a JSON endpoint that returns no useful body (DELETE, 204, etc.).</summary>
|
|
Task CallAsync(
|
|
HttpMethod method,
|
|
string path,
|
|
object? body = null,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>Call a multipart endpoint that returns no useful body.</summary>
|
|
Task CallAsync(
|
|
HttpMethod method,
|
|
string path,
|
|
Func<HttpContent> contentFactory,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Download a binary resource (PDF, TeX, image, …). Unlike
|
|
/// <see cref="CallAsync{T}(HttpMethod,string,object?,CancellationToken)"/>,
|
|
/// which JSON-decodes the response, this returns the raw response
|
|
/// bytes. The same Bearer-token attach + 401-refresh-retry-once
|
|
/// contract applies.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// A default interface implementation is provided so that test
|
|
/// fakes that do not exercise binary download still compile without
|
|
/// declaring the member; the real <see cref="YavscApiClient"/>
|
|
/// overrides it with the authenticated transport.
|
|
/// </remarks>
|
|
Task<byte[]> DownloadAsync(
|
|
HttpMethod method,
|
|
string path,
|
|
CancellationToken ct = default)
|
|
=> Task.FromResult(Array.Empty<byte>());
|
|
}
|