using ZB.MOM.WW.ScadaBridge.Commons.Entities.ExternalSystems;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Scripts;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
namespace ZB.MOM.WW.ScadaBridge.Transport.Serialization;
///
/// In-memory aggregate of all bundle-eligible Commons entities. Matches the
/// shape of but uses the real persistence-
/// ignorant POCO types — what consumes/produces
/// on the application side of the bundle boundary.
///
// ApiKeys is intentionally absent: inbound API keys are not transported between
// environments (re-arch C4). The aggregate only carries API methods.
public sealed record EntityAggregate(
IReadOnlyList TemplateFolders,
IReadOnlyList Templates,
IReadOnlyList SharedScripts,
IReadOnlyList ExternalSystems,
IReadOnlyList ExternalSystemMethods,
IReadOnlyList DatabaseConnections,
IReadOnlyList NotificationLists,
IReadOnlyList SmtpConfigurations,
IReadOnlyList ApiMethods)
{
// M8: site/instance-scoped entities. Init-only with empty-array defaults so
// existing positional `new EntityAggregate(...)` callers keep compiling and
// never see a null collection; new callers opt in via object-initializer.
public IReadOnlyList Sites { get; init; } = Array.Empty();
public IReadOnlyList DataConnections { get; init; } = Array.Empty();
public IReadOnlyList Instances { get; init; } = Array.Empty();
}
///
/// Top-level serializable bundle payload. Lists are sequenced in dependency
/// order so importers can apply them inline. Lists are never null on the wire
/// — empty arrays are preferred over nulls so JSON consumers can rely on each
/// property being present — except , which is intentionally
/// null-defaulted (see below).
///
/// is a legacy, read-only field retained purely for
/// backward-compatible deserialization of bundles produced before the
/// inbound-API-key re-architecture (C4). New exports never populate it (it stays
/// null and is dropped from the JSON by the serializer's
/// WhenWritingNull policy); the importer counts any keys present in an old
/// bundle, ignores them, and surfaces a note — keys are re-created per environment.
///
///
public sealed record BundleContentDto(
IReadOnlyList TemplateFolders,
IReadOnlyList Templates,
IReadOnlyList SharedScripts,
IReadOnlyList ExternalSystems,
IReadOnlyList DatabaseConnections,
IReadOnlyList NotificationLists,
IReadOnlyList SmtpConfigs,
IReadOnlyList ApiMethods,
IReadOnlyList? ApiKeys = null)
{
// M8: site/instance-scoped payloads. Modeled as init-only properties with
// empty-array defaults (rather than additional positional ctor params) for
// two reasons:
// 1. Forward-compat: deserializing an OLDER bundle whose content blob has
// no sites/dataConnections/instances fields leaves each property at its
// Array.Empty<>() initializer — so consumers always see an empty list,
// never null. (Contrast ApiKeys, which is a nullable legacy field.)
// 2. Source-compat: every existing `new BundleContentDto(...)` positional
// caller keeps compiling unchanged; new producers opt in via an
// object-initializer (`new BundleContentDto(...) { Sites = ... }`).
// These are written by the serializer (WhenWritingNull does not apply — they
// are non-null), so new bundles always carry the three arrays explicitly.
public IReadOnlyList Sites { get; init; } = Array.Empty();
public IReadOnlyList DataConnections { get; init; } = Array.Empty();
public IReadOnlyList Instances { get; init; } = Array.Empty();
}
///
/// Carved-off secret values for an entity. The outer DTO carries all non-
/// sensitive fields; secrets land here so a future "share without secrets"
/// export mode can drop this block without touching anything else.
///
public sealed record SecretsBlock(IReadOnlyDictionary Values);
public sealed record TemplateFolderDto(
string Name,
string? ParentName,
int SortOrder);
public sealed record TemplateDto(
string Name,
string? FolderName,
string? BaseTemplateName,
string? Description,
IReadOnlyList Attributes,
IReadOnlyList Alarms,
IReadOnlyList Scripts,
IReadOnlyList Compositions);
public sealed record TemplateAttributeDto(
string Name,
string? Value,
DataType DataType,
bool IsLocked,
string? Description,
string? DataSourceReference,
DataType? ElementDataType = null);
public sealed record TemplateAlarmDto(
string Name,
string? Description,
int PriorityLevel,
AlarmTriggerType TriggerType,
string? TriggerConfiguration,
bool IsLocked,
string? OnTriggerScriptName);
public sealed record TemplateScriptDto(
string Name,
string Code,
string? TriggerType,
string? TriggerConfiguration,
string? ParameterDefinitions,
string? ReturnDefinition,
bool IsLocked,
TimeSpan? MinTimeBetweenRuns,
// M2.5 (#9): per-script execution timeout (seconds). Additive trailing field;
// null on bundles written before this field existed.
int? ExecutionTimeoutSeconds = null);
public sealed record TemplateCompositionDto(
string InstanceName,
string ComposedTemplateName);
public sealed record SharedScriptDto(
string Name,
string Code,
string? ParameterDefinitions,
string? ReturnDefinition);
public sealed record ExternalSystemDto(
string Name,
string BaseUrl,
string AuthType,
int MaxRetries,
TimeSpan RetryDelay,
IReadOnlyList Methods,
SecretsBlock? Secrets);
public sealed record ExternalSystemMethodDto(
string Name,
string HttpMethod,
string Path,
string? ParameterDefinitions,
string? ReturnDefinition);
public sealed record DatabaseConnectionDto(
string Name,
int MaxRetries,
TimeSpan RetryDelay,
SecretsBlock? Secrets);
public sealed record NotificationListDto(
string Name,
NotificationType Type,
IReadOnlyList Recipients);
public sealed record NotificationRecipientDto(
string Name,
string EmailAddress);
public sealed record SmtpConfigDto(
string Host,
int Port,
string AuthType,
string FromAddress,
string? TlsMode,
int ConnectionTimeoutSeconds,
int MaxConcurrentConnections,
int MaxRetries,
TimeSpan RetryDelay,
SecretsBlock? Secrets);
// Legacy DTO: only deserialized from pre-C4 bundles so the importer can count and
// ignore the keys they contain. New exports never emit an ApiKeys array.
public sealed record ApiKeyDto(
string Name,
string KeyHash,
bool IsEnabled,
SecretsBlock? Secrets);
// ApprovedApiKeyIds is intentionally absent: it linked methods to keys that are no
// longer transported (re-arch C4). Scopes are re-granted per environment. The field
// in any old bundle is simply ignored on read.
public sealed record ApiMethodDto(
string Name,
string Script,
string? ParameterDefinitions,
string? ReturnDefinition,
int TimeoutSeconds);
// --- M8: site/instance-scoped transport DTOs --------------------------------
// These travel alongside the original central-config DTOs above. Sites and
// instances are referenced by their stable string identities (SiteIdentifier,
// UniqueName, TemplateName) rather than database ids so a bundle resolves
// correctly against the *target* environment's own surrogate keys.
///
/// A site definition. Addresses are carried verbatim; the importer decides
/// whether to keep or rewrite them for the target environment.
///
public sealed record SiteDto(
string SiteIdentifier,
string Name,
string? Description,
string? NodeAAddress,
string? NodeBAddress,
string? GrpcNodeAAddress,
string? GrpcNodeBAddress);
///
/// A site-scoped protocol connection (the Sites.DataConnection entity —
/// NOT the External-System ). The protocol-
/// specific Primary/Backup configuration JSON rides inside
/// so a "share without secrets" export can drop it as a unit.
///
public sealed record DataConnectionDto(
string SiteIdentifier,
string Name,
string Protocol,
int FailoverRetryCount,
SecretsBlock? Secrets);
public sealed record InstanceAttributeOverrideDto(
string AttributeName,
string? OverrideValue,
DataType? ElementDataType);
public sealed record InstanceAlarmOverrideDto(
string AlarmCanonicalName,
string? TriggerConfigurationOverride,
int? PriorityLevelOverride);
public sealed record InstanceNativeAlarmSourceOverrideDto(
string SourceCanonicalName,
string? ConnectionNameOverride,
string? SourceReferenceOverride,
string? ConditionFilterOverride);
public sealed record InstanceConnectionBindingDto(
string AttributeName,
string ConnectionName,
string? DataSourceReferenceOverride);
///
/// A deployable instance with all of its per-instance overrides and bindings.
/// References its template and site by name/identifier (not id).
///
public sealed record InstanceDto(
string UniqueName,
string TemplateName,
string SiteIdentifier,
string? AreaName,
InstanceState State,
IReadOnlyList AttributeOverrides,
IReadOnlyList AlarmOverrides,
IReadOnlyList NativeAlarmSourceOverrides,
IReadOnlyList ConnectionBindings);