feat: introduce models to represent atproto id
This commit is contained in:
1 parent
e3b4194653
commit
716d79739c
2 files changed
+151
No files matched your search
@@ -0,0 +1,132 @@
|
|||||||
|
namespace StevanFreeborn.Atproto.IdentityResolver;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public abstract class AtprotoId
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string Value { get; protected set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static AtprotoId From(string atprotoId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(atprotoId))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Cannot be null or empty", nameof(atprotoId));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsDomain(atprotoId))
|
||||||
|
{
|
||||||
|
return Handle.From(atprotoId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Did.From(atprotoId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsDomain(string did)
|
||||||
|
{
|
||||||
|
var hostType = Uri.CheckHostName(did);
|
||||||
|
return hostType == UriHostNameType.Dns && did.Contains('.', StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class Did : AtprotoId
|
||||||
|
{
|
||||||
|
private static readonly string[] ValidPrefixes = ["did:web:", "did:plc:"];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string Method { get; private set; }
|
||||||
|
|
||||||
|
private Did(string did)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(did))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Cannot be null or empty", nameof(did));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryParseWithoutPrefix(did, out var prefix, out var didWithoutPrefix) is false)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Must be did with one of these methods: {string.Join(',', ValidPrefixes)}", nameof(did));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(didWithoutPrefix))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Must not be null or empty without did method prefix", nameof(did));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method should probably be a strongly
|
||||||
|
// typed object
|
||||||
|
Method = prefix.Contains("web", StringComparison.OrdinalIgnoreCase) ? "web" : "plc";
|
||||||
|
Value = did;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static new Did From(string did)
|
||||||
|
{
|
||||||
|
return new(did);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryParseWithoutPrefix(
|
||||||
|
string did,
|
||||||
|
out string prefix,
|
||||||
|
out string didWithoutPrefix
|
||||||
|
)
|
||||||
|
{
|
||||||
|
prefix = string.Empty;
|
||||||
|
didWithoutPrefix = string.Empty;
|
||||||
|
|
||||||
|
foreach (var validPrefix in ValidPrefixes)
|
||||||
|
{
|
||||||
|
if (did.StartsWith(validPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
prefix = did[..validPrefix.Length];
|
||||||
|
didWithoutPrefix = did[validPrefix.Length..];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class Handle : AtprotoId
|
||||||
|
{
|
||||||
|
private Handle(string handle)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(handle))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Cannot be null or empty", nameof(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle.StartsWith('@'))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Cannot start with @", nameof(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
Value = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static new Handle From(string handle)
|
||||||
|
{
|
||||||
|
return new(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
namespace StevanFreeborn.Atproto.IdentityResolver.Tests;
|
||||||
|
|
||||||
|
public class AtprotoIdTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("")]
|
||||||
|
[InlineData("localhost")]
|
||||||
|
[InlineData("did:web:")]
|
||||||
|
[InlineData("did:plc:")]
|
||||||
|
[InlineData("did:mu:dxbmien5xsrkov5yhywgc4zu")]
|
||||||
|
[InlineData("@stevanfreeborn.com")]
|
||||||
|
[InlineData(null)]
|
||||||
|
public void From_WhenCalledWithInvalidDid_ItShouldThrowInvalidArgumentException(string? atprotoId)
|
||||||
|
{
|
||||||
|
var act = () => AtprotoId.From(atprotoId!);
|
||||||
|
|
||||||
|
act.Should().Throw<ArgumentException>();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user