using System.Runtime.Serialization;
using System.Text.Json;
using Akka.Actor;
using Akka.Serialization;
using ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.Protocol;
namespace ZB.MOM.WW.Secrets.Replicator.AkkaDotNet;
///
/// Explicit System.Text.Json serializer for the secret-replication wire protocol.
///
///
///
/// Registering this is optional but recommended. The protocol DTOs are all primitives, so
/// Akka's default JSON serializer round-trips them correctly if an application never wires this up —
/// but relying on the default means secret ciphertext is serialized by whatever the cluster's
/// fallback happens to be, which is not a decision worth leaving implicit for this payload. Binding
/// it explicitly also pins the manifest strings below as the versioning boundary: a future protocol
/// change adds a manifest, so a mixed-version cluster fails loudly on an unknown manifest instead of
/// silently mis-deserializing a secret.
///
///
/// Wire it up by merging into the
/// application's Akka configuration — see that member for the HOCON.
///
///
/// The actor system this serializer belongs to.
public sealed class SecretReplicationSerializer(ExtendedActorSystem system) : SerializerWithStringManifest(system)
{
/// Stable serializer id. Must not collide with other serializers in the cluster.
public const int SerializerId = 7710;
private const string RowsManifest = "zbs-rows-v1";
private const string ManifestAnnounceManifest = "zbs-manifest-v1";
private const string PullRequestManifest = "zbs-pull-v1";
private static readonly JsonSerializerOptions JsonOptions = new()
{
// Explicitly NOT indented and NOT camel-cased: the wire form should be compact and stable,
// not pretty. Property names are the C# names, pinned by the manifest version.
WriteIndented = false,
};
///
public override int Identifier => SerializerId;
///
public override string Manifest(object o) => o switch
{
SecretRowsMessage => RowsManifest,
SecretManifestAnnounce => ManifestAnnounceManifest,
SecretPullRequest => PullRequestManifest,
_ => throw new ArgumentException(
$"{nameof(SecretReplicationSerializer)} cannot serialize {o?.GetType().FullName ?? "null"}.",
nameof(o)),
};
///
public override byte[] ToBinary(object obj) => obj switch
{
SecretRowsMessage rows => JsonSerializer.SerializeToUtf8Bytes(rows, JsonOptions),
SecretManifestAnnounce announce => JsonSerializer.SerializeToUtf8Bytes(announce, JsonOptions),
SecretPullRequest pull => JsonSerializer.SerializeToUtf8Bytes(pull, JsonOptions),
_ => throw new ArgumentException(
$"{nameof(SecretReplicationSerializer)} cannot serialize {obj?.GetType().FullName ?? "null"}.",
nameof(obj)),
};
///
public override object FromBinary(byte[] bytes, string manifest) => manifest switch
{
RowsManifest => Deserialize(bytes, manifest),
ManifestAnnounceManifest => Deserialize(bytes, manifest),
PullRequestManifest => Deserialize(bytes, manifest),
_ => throw new SerializationException(
$"Unknown secret-replication manifest '{manifest}'. This usually means a peer is running a " +
"newer protocol version than this node."),
};
private static T Deserialize(byte[] bytes, string manifest) =>
JsonSerializer.Deserialize(bytes, JsonOptions)
?? throw new SerializationException(
$"Secret-replication payload with manifest '{manifest}' deserialized to null.");
}