merge: external-system retry policy end-to-end (register row 27) — MaxRetries/RetryDelay now travel artifact→gRPC→site schema→S&F enqueue, CLI --max-retries/--retry-delay-seconds, ArtifactDiff compares them

This commit is contained in:
Joseph Doherty
2026-08-15 17:56:21 -04:00
28 changed files with 1036 additions and 453 deletions
@@ -1,9 +1,41 @@
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Artifacts;
/// <summary>
/// A single external HTTP system, flattened for deployment to a site.
/// </summary>
/// <param name="Name">Unique system name — the key scripts call it by.</param>
/// <param name="EndpointUrl">Base URL of the system's HTTP endpoint.</param>
/// <param name="AuthType">Authentication type identifier (e.g. <c>ApiKey</c>, <c>BasicAuth</c>).</param>
/// <param name="AuthConfiguration">JSON-serialized authentication configuration for <paramref name="AuthType"/>.</param>
/// <param name="MethodDefinitionsJson">JSON-serialized method list, or <c>null</c> when the system defines none.</param>
/// <param name="TimeoutSeconds">
/// Per-system HTTP round-trip timeout in seconds. <c>0</c> = unset — the gateway's configured
/// <c>DefaultHttpTimeout</c> applies.
/// </param>
/// <param name="MaxRetries">
/// Store-and-Forward retry budget for this system's cached calls. <c>0</c> = unset — the engine's
/// <c>DefaultMaxRetries</c> applies.
/// <para>
/// <b>0 means "unset", not "no limit", at THIS layer</b> — the inverse of
/// <c>StoreAndForwardMessage.MaxRetries</c>, where a stored 0 means unbounded retry. The two
/// meanings are reconciled in <c>ExternalSystemClient.CachedCallAsync</c>, which passes a 0
/// through as <c>null</c> so the bounded engine default applies rather than an unbounded retry
/// loop. Keeping "unset" as 0 here (rather than a nullable) matches
/// <see cref="DatabaseConnectionArtifact"/>, the non-nullable
/// <c>ExternalSystemDefinition.MaxRetries</c> entity property this is projected from, and the
/// proto3 scalar it is carried on.
/// </para>
/// </param>
/// <param name="RetryDelay">
/// Fixed interval between Store-and-Forward retry sweeps for this system's cached calls.
/// <see cref="TimeSpan.Zero"/> = unset — the engine's <c>DefaultRetryInterval</c> applies.
/// </param>
public record ExternalSystemArtifact(
string Name,
string EndpointUrl,
string AuthType,
string? AuthConfiguration,
string? MethodDefinitionsJson,
int TimeoutSeconds = 0);
int TimeoutSeconds = 0,
int MaxRetries = 0,
TimeSpan RetryDelay = default);
@@ -2,8 +2,23 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
public record ListExternalSystemsCommand;
public record GetExternalSystemCommand(int ExternalSystemId);
public record CreateExternalSystemCommand(string Name, string EndpointUrl, string AuthType, string? AuthConfiguration, int? TimeoutSeconds = null);
public record UpdateExternalSystemCommand(int ExternalSystemId, string Name, string EndpointUrl, string AuthType, string? AuthConfiguration, int? TimeoutSeconds = null);
/// <summary>
/// Creates an external HTTP system.
/// </summary>
/// <remarks>
/// <paramref name="MaxRetries"/> and <paramref name="RetryDelay"/> are the Store-and-Forward
/// retry policy applied to this system's <c>ExternalSystem.CachedCall</c> buffering. Both are
/// nullable on the wire so "omit" is distinguishable from "set to 0"; a null (or an explicit 0)
/// stores the entity's unset value, which the site gateway translates into the engine's
/// <c>DefaultMaxRetries</c>/<c>DefaultRetryInterval</c>.
/// </remarks>
public record CreateExternalSystemCommand(string Name, string EndpointUrl, string AuthType, string? AuthConfiguration, int? TimeoutSeconds = null, int? MaxRetries = null, TimeSpan? RetryDelay = null);
/// <summary>
/// Updates an external HTTP system. Nullable members follow the "omit to preserve" convention —
/// a null leaves the stored value untouched.
/// </summary>
public record UpdateExternalSystemCommand(int ExternalSystemId, string Name, string EndpointUrl, string AuthType, string? AuthConfiguration, int? TimeoutSeconds = null, int? MaxRetries = null, TimeSpan? RetryDelay = null);
public record DeleteExternalSystemCommand(int ExternalSystemId);
// External System Methods