diff --git a/.github/workflows/build-dotnet.yml b/.github/workflows/build-dotnet.yml index c281b3eb9..cb09b8a2b 100644 --- a/.github/workflows/build-dotnet.yml +++ b/.github/workflows/build-dotnet.yml @@ -34,7 +34,7 @@ jobs: - run: | dotnet workload install ios dotnet restore - dotnet build -c Release /p:IsBrowser=true ./Trinsic + dotnet build -c Release -f net6.0 -p:IsBrowser=true ./Trinsic dotnet build -c Release dotnet test -c Release -v n working-directory: ./dotnet diff --git a/dotnet/.editorconfig b/dotnet/.editorconfig index 796fc6597..30e12b719 100644 --- a/dotnet/.editorconfig +++ b/dotnet/.editorconfig @@ -1,4 +1,3 @@ - [*] charset = utf-8 end_of_line = lf @@ -10,7 +9,7 @@ indent_size = 4 # Microsoft .NET properties csharp_new_line_before_catch = false csharp_new_line_before_members_in_object_initializers = false -csharp_new_line_before_open_brace = accessors,control_blocks,events,indexers,properties,types +csharp_new_line_before_open_brace = accessors, control_blocks, events, indexers, properties, types csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async:suggestion csharp_style_var_elsewhere = true:suggestion csharp_style_var_for_built_in_types = true:suggestion diff --git a/dotnet/Directory.Build.props b/dotnet/Directory.Build.props index 5405a7d3a..b3fa60092 100644 --- a/dotnet/Directory.Build.props +++ b/dotnet/Directory.Build.props @@ -1,25 +1,25 @@ - - - Trinsic Engineering Team - Trinsic - Apache-2.0 - https://github.com/trinsic-id/sdk - Trinsic - Trinsic SDK for NET - https://github.com/trinsic-id/sdk.git - git - 1.0.0 - true - snupkg - - - - - full - latest - $(NoWarn);1591 - enable - true - + + + Trinsic Engineering Team + Trinsic + Apache-2.0 + https://github.com/trinsic-id/sdk + Trinsic + Trinsic SDK for NET + https://github.com/trinsic-id/sdk.git + git + 1.0.0 + true + snupkg + + + + + full + latest + $(NoWarn);1591 + enable + true + \ No newline at end of file diff --git a/dotnet/Tests/HostTests.cs b/dotnet/Tests/HostTests.cs new file mode 100644 index 000000000..45da8500c --- /dev/null +++ b/dotnet/Tests/HostTests.cs @@ -0,0 +1,69 @@ +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Trinsic; +using Trinsic.Services.Common.V1; +using Xunit; + +namespace Tests; + +public class HostTests +{ + [Fact(DisplayName = "Test default service host")] + public async Task TestGenericHost() { + var host = Host + .CreateDefaultBuilder() + .ConfigureServices(services => { + services.AddTrinsic(); + }).Build(); + + await host.StartAsync(); + + var providerService = host.Services.GetService(); + var accountService = host.Services.GetRequiredService(); + + providerService.Should().NotBeNull(); + accountService.Should().NotBeNull(); + + accountService.Options.ServerEndpoint.Should().Be(ServiceBase.DefaultServerEndpoint); + accountService.Options.ServerPort.Should().Be(ServiceBase.DefaultServerPort); + accountService.Options.ServerUseTls.Should().Be(ServiceBase.DefaultServerUseTls); + accountService.Options.DefaultEcosystem.Should().Be(ServiceBase.DefaultEcosystem); + accountService.Options.AuthToken.Should().Be(string.Empty); + accountService.TokenProvider.Should().BeOfType(); + + await host.StopAsync(); + } + + [Fact(DisplayName = "Test configured service host")] + public async Task TestConfiguredGenericHost() { + var host = Host + .CreateDefaultBuilder() + .ConfigureServices(services => { + services.AddTrinsic(options => { + options.AuthToken = "auth"; + options.DefaultEcosystem = "eco"; + options.ServerEndpoint = "example.com"; + options.ServerPort = 42; + options.ServerUseTls = true; + }); + }).Build(); + + await host.StartAsync(); + + var providerService = host.Services.GetService(); + var accountService = host.Services.GetRequiredService(); + + providerService.Should().NotBeNull(); + accountService.Should().NotBeNull(); + + accountService.Options.ServerEndpoint.Should().Be("example.com"); + accountService.Options.ServerPort.Should().Be(42); + accountService.Options.ServerUseTls.Should().BeTrue(); + accountService.Options.DefaultEcosystem.Should().Be("eco"); + accountService.Options.AuthToken.Should().Be("auth"); + + await host.StopAsync(); + } +} diff --git a/dotnet/Tests/Tests.cs b/dotnet/Tests/Tests.cs index f07f45e6e..6b145c189 100644 --- a/dotnet/Tests/Tests.cs +++ b/dotnet/Tests/Tests.cs @@ -20,6 +20,7 @@ using Trinsic.Services.VerifiableCredentials.V1; using FieldType = Trinsic.Services.VerifiableCredentials.Templates.V1.FieldType; using JsonSerializer = System.Text.Json.JsonSerializer; + #pragma warning disable CS0618 namespace Tests; @@ -32,9 +33,9 @@ public class Tests const int DefaultPort = 5000; const bool DefaultUseTls = false; #else - const string DefaultEndpoint = "staging-internal.trinsic.cloud"; - const int DefaultPort = 443; - const bool DefaultUseTls = true; + private const string DefaultEndpoint = "staging-internal.trinsic.cloud"; + private const int DefaultPort = 443; + private const bool DefaultUseTls = true; #endif private readonly ITestOutputHelper _testOutputHelper; @@ -67,9 +68,9 @@ public async Task TestWalletService() { // SETUP ACTORS // Create 3 different profiles for each participant in the scenario // setupActors() { - var allison = await accountService.SignInAsync(new SignInRequest {EcosystemId = ecosystemId}); - var clinic = await accountService.SignInAsync(new SignInRequest {EcosystemId = ecosystemId}); - var airline = await accountService.SignInAsync(new SignInRequest {EcosystemId = ecosystemId}); + var allison = await accountService.SignInAsync(new() {EcosystemId = ecosystemId}); + var clinic = await accountService.SignInAsync(new() {EcosystemId = ecosystemId}); + var airline = await accountService.SignInAsync(new() {EcosystemId = ecosystemId}); // } accountService.Options.AuthToken = clinic; @@ -251,7 +252,7 @@ public async Task TestInvitationIdSet() { invitationResponse.Should().NotBeNull(); invitationResponse.InvitationCode.Should().NotBeEmpty(); - await Assert.ThrowsAsync(async () => await providerService.InvitationStatusAsync(new InvitationStatusRequest())); + await Assert.ThrowsAsync(async () => await providerService.InvitationStatusAsync(new())); } [Fact(Skip = "Ecosystem support not complete yet")] @@ -263,7 +264,7 @@ public async Task TestInviteParticipant() { var response = await myProviderService.InviteParticipantAsync(invite); Assert.NotNull(response); - var statusResponse = await myProviderService.InvitationStatusAsync(new InvitationStatusRequest {InvitationId = response.InvitationId}); + var statusResponse = await myProviderService.InvitationStatusAsync(new() {InvitationId = response.InvitationId}); Assert.NotNull(statusResponse); } @@ -352,7 +353,6 @@ public async Task DemoTemplatesWithIssuance() { } } } - public static class Extensions { public static ServiceOptions CloneWithAuthToken(this ServiceOptions options, string authToken) { @@ -360,4 +360,4 @@ public static ServiceOptions CloneWithAuthToken(this ServiceOptions options, str cloned.AuthToken = authToken; return cloned; } -} \ No newline at end of file +} diff --git a/dotnet/Tests/Tests.csproj b/dotnet/Tests/Tests.csproj index f5e11665d..c47f1af61 100644 --- a/dotnet/Tests/Tests.csproj +++ b/dotnet/Tests/Tests.csproj @@ -10,9 +10,10 @@ 4 - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -27,16 +28,16 @@ - + - - TestData/vaccination-certificate-unsigned.jsonld - PreserveNewest - - - TestData/vaccination-certificate-frame.jsonld - PreserveNewest - + + TestData/vaccination-certificate-unsigned.jsonld + PreserveNewest + + + TestData/vaccination-certificate-frame.jsonld + PreserveNewest + \ No newline at end of file diff --git a/dotnet/Trinsic/AccountService.cs b/dotnet/Trinsic/AccountService.cs index b229c3f2c..1efd4e4e5 100644 --- a/dotnet/Trinsic/AccountService.cs +++ b/dotnet/Trinsic/AccountService.cs @@ -1,11 +1,11 @@ using System; using System.Threading.Tasks; using Google.Protobuf; +using Microsoft.Extensions.Options; using Okapi.Security; using Okapi.Security.V1; using Trinsic.Sdk.Options.V1; using Trinsic.Services.Account.V1; -using AccountServiceClient = Trinsic.Services.Account.V1.Account.AccountClient; namespace Trinsic; @@ -22,11 +22,20 @@ public AccountService(ServiceOptions options) public AccountService() { Client = new(Channel); } - + + internal AccountService(ITokenProvider tokenProvider) : base(new(), tokenProvider) { + Client = new(Channel); + } + + internal AccountService(ITokenProvider tokenProvider, IOptions options) + : base(options.Value, tokenProvider) { + Client = new(Channel); + } + /// /// Gets the underlying grpc client /// - public AccountServiceClient Client { get; } + private Account.AccountClient Client { get; } /// /// Perform a sign-in to obtain an account profile. If the are @@ -35,16 +44,12 @@ public AccountService() { /// /// public async Task SignInAsync(SignInRequest request) { - if (string.IsNullOrWhiteSpace(request.EcosystemId)) { - request.EcosystemId = Options.DefaultEcosystem; - } + if (string.IsNullOrWhiteSpace(request.EcosystemId)) request.EcosystemId = Options.DefaultEcosystem; var response = await Client.SignInAsync(request); var authToken = Convert.ToBase64String(response.Profile.ToByteArray()); - - if (!response.Profile.Protection?.Enabled ?? true) { - Options.AuthToken = authToken; - } + + if (!response.Profile.Protection?.Enabled ?? true) await TokenProvider.SaveAsync(authToken); return authToken; } @@ -55,13 +60,13 @@ public async Task SignInAsync(SignInRequest request) { /// /// public string SignIn(SignInRequest request) { - if (string.IsNullOrWhiteSpace(request.EcosystemId)) { - request.EcosystemId = Options.DefaultEcosystem; - } + if (string.IsNullOrWhiteSpace(request.EcosystemId)) request.EcosystemId = Options.DefaultEcosystem; var response = Client.SignIn(request); - - Options.AuthToken = Convert.ToBase64String(response.Profile.ToByteArray()); - return Options.AuthToken; + + var authToken = Convert.ToBase64String(response.Profile.ToByteArray()); + + if (!response.Profile.Protection?.Enabled ?? true) TokenProvider.Save(authToken); + return authToken; } /// @@ -150,4 +155,4 @@ public RevokeDeviceResponse RevokeDevice() { RevokeDeviceRequest request = new(); return Client.RevokeDevice(request, BuildMetadata(request)); } -} \ No newline at end of file +} diff --git a/dotnet/Trinsic/CredentialsService.cs b/dotnet/Trinsic/CredentialsService.cs index 1f1afac73..35aa581e1 100644 --- a/dotnet/Trinsic/CredentialsService.cs +++ b/dotnet/Trinsic/CredentialsService.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Microsoft.Extensions.Options; using Trinsic.Sdk.Options.V1; using Trinsic.Services.Common.V1; using Trinsic.Services.VerifiableCredentials.Templates.V1; @@ -18,6 +19,15 @@ public CredentialsService() { Client = new(Channel); } + internal CredentialsService(ITokenProvider tokenProvider) : base(new(), tokenProvider) { + Client = new(Channel); + } + + internal CredentialsService(ITokenProvider tokenProvider, IOptions options) + : base(options.Value, tokenProvider) { + Client = new(Channel); + } + private VerifiableCredential.VerifiableCredentialClient Client { get; } /// @@ -26,16 +36,12 @@ public CredentialsService() { /// /// public async Task IssueCredentialAsync(IssueRequest request) { - if (string.IsNullOrWhiteSpace(request.DocumentJson)) { - throw new ArgumentException("document json must not be empty"); - } + if (string.IsNullOrWhiteSpace(request.DocumentJson)) throw new ArgumentException("document json must not be empty"); return await Client.IssueAsync(request, await BuildMetadataAsync(request)); } public IssueResponse IssueCredential(IssueRequest request) { - if (string.IsNullOrWhiteSpace(request.DocumentJson)) { - throw new ArgumentException("document json must not be empty"); - } + if (string.IsNullOrWhiteSpace(request.DocumentJson)) throw new ArgumentException("document json must not be empty"); return Client.Issue(request, BuildMetadata(request)); } @@ -86,16 +92,16 @@ public CreateProofResponse CreateProof(CreateProofRequest request) { /// public async Task VerifyProofAsync(VerifyProofRequest request) { var response = await Client.VerifyProofAsync( - request: request, - headers: await BuildMetadataAsync(request)); + request, + await BuildMetadataAsync(request)); return response.IsValid; } public bool VerifyProof(VerifyProofRequest request) { var response = Client.VerifyProof( - request: request, - headers: BuildMetadata(request)); + request, + BuildMetadata(request)); return response.IsValid; } @@ -125,14 +131,14 @@ public async Task UpdateStatusAsync(string credentialStatusId, bool revoked) { UpdateStatusRequest request = new() {CredentialStatusId = credentialStatusId, Revoked = revoked}; var response = await Client.UpdateStatusAsync(request, await BuildMetadataAsync(request)); if (response.Status == ResponseStatus.Success) return; - throw new Exception($"Status not completely updated {response.Status}"); + throw new($"Status not completely updated {response.Status}"); } public void UpdateStatus(string credentialStatusId, bool revoked) { UpdateStatusRequest request = new() {CredentialStatusId = credentialStatusId, Revoked = revoked}; var response = Client.UpdateStatus(request, BuildMetadata(request)); if (response.Status == ResponseStatus.Success) return; - throw new Exception($"Status not completely updated {response.Status}"); + throw new($"Status not completely updated {response.Status}"); } @@ -143,13 +149,13 @@ public void UpdateStatus(string credentialStatusId, bool revoked) { /// public async Task SendAsync(SendRequest request) { var response = await Client.SendAsync( - request: request, - headers: await BuildMetadataAsync(request)); + request, + await BuildMetadataAsync(request)); } public void Send(SendRequest request) { var response = Client.Send( - request: request, - headers: BuildMetadata(request)); + request, + BuildMetadata(request)); } -} \ No newline at end of file +} diff --git a/dotnet/Trinsic/Extensions/MessageExtensions.cs b/dotnet/Trinsic/Extensions/MessageExtensions.cs index f3da768a8..3870ec49d 100644 --- a/dotnet/Trinsic/Extensions/MessageExtensions.cs +++ b/dotnet/Trinsic/Extensions/MessageExtensions.cs @@ -10,8 +10,7 @@ public static class ProtoMessageExtensions /// /// public static T As(this IMessage message) - where T : IMessage, new() - { + where T : IMessage, new() { var target = new T(); target.MergeFrom(message.ToByteString()); diff --git a/dotnet/Trinsic/Extensions/ServiceCollectionExtensions.cs b/dotnet/Trinsic/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..d0b859ce2 --- /dev/null +++ b/dotnet/Trinsic/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,59 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Options; +using Trinsic; +using Trinsic.Sdk.Options.V1; + +namespace Microsoft.Extensions.DependencyInjection; + +[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +[SuppressMessage("ReSharper", "UnusedType.Global")] +[SuppressMessage("ReSharper", "UnusedMember.Global")] +public static class ServiceCollectionExtensions +{ + /// + /// Registers Trinsic SDK services and dependencies + /// + /// + /// + /// + public static IServiceCollection AddTrinsic(this IServiceCollection services, Action configure) { + services.Configure(configure); + + return AddTrinsic(services); + } + + /// + /// Registers Trinsic SDK services and dependencies + /// + /// + /// + public static IServiceCollection AddTrinsic(this IServiceCollection services) { +#if __IOS__ + services.AddSingleton(); +#elif __BROWSER__ + services.AddSingleton(); +#else + services.AddSingleton(); +#endif + services.AddSingleton(provider => + new(provider.GetRequiredService(), + provider.GetRequiredService>())); + services.AddSingleton(provider => + new(provider.GetRequiredService(), + provider.GetRequiredService>())); + services.AddSingleton(provider => + new(provider.GetRequiredService(), + provider.GetRequiredService>())); + services.AddSingleton(provider => + new(provider.GetRequiredService(), + provider.GetRequiredService>())); + services.AddSingleton(provider => + new(provider.GetRequiredService(), + provider.GetRequiredService>())); + services.AddSingleton(provider => + new(provider.GetRequiredService(), + provider.GetRequiredService>())); + return services; + } +} diff --git a/dotnet/Trinsic/Extensions/ServiceOptionsExtensions.cs b/dotnet/Trinsic/Extensions/ServiceOptionsExtensions.cs index 2c033999a..d7854479d 100644 --- a/dotnet/Trinsic/Extensions/ServiceOptionsExtensions.cs +++ b/dotnet/Trinsic/Extensions/ServiceOptionsExtensions.cs @@ -4,6 +4,7 @@ namespace Trinsic.Services.Common.V1; public static class ServiceOptionsExtensions { - public static string FormatUrl(this ServiceOptions options) => - $"{(options.ServerUseTls ? "https" : "http")}://{options.ServerEndpoint}:{options.ServerPort}"; -} \ No newline at end of file + public static string FormatUrl(this ServiceOptions options) { + return $"{(options.ServerUseTls ? "https" : "http")}://{options.ServerEndpoint}:{options.ServerPort}"; + } +} diff --git a/dotnet/Trinsic/ProviderService.cs b/dotnet/Trinsic/ProviderService.cs index fa5597a6b..8f58ca2dc 100644 --- a/dotnet/Trinsic/ProviderService.cs +++ b/dotnet/Trinsic/ProviderService.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Google.Protobuf; +using Microsoft.Extensions.Options; using Trinsic.Sdk.Options.V1; using Trinsic.Services.Provider.V1; @@ -17,6 +18,15 @@ public ProviderService() { Client = new(Channel); } + internal ProviderService(ITokenProvider tokenProvider) : base(new(), tokenProvider) { + Client = new(Channel); + } + + internal ProviderService(ITokenProvider tokenProvider, IOptions options) + : base(options.Value, tokenProvider) { + Client = new(Channel); + } + private Provider.ProviderClient Client { get; } /// @@ -71,9 +81,7 @@ public InvitationStatusResponse InvitationStatus(InvitationStatusRequest request var authToken = Convert.ToBase64String(response.Profile.ToByteArray()); - if (!response.Profile.Protection?.Enabled ?? false) { - Options.AuthToken = authToken; - } + if (!response.Profile.Protection?.Enabled ?? false) Options.AuthToken = authToken; return (response.Ecosystem, authToken); } @@ -89,9 +97,7 @@ public InvitationStatusResponse InvitationStatus(InvitationStatusRequest request var response = Client.CreateEcosystem(request); var authToken = Convert.ToBase64String(response.Profile.ToByteArray()); - if (!response.Profile.Protection?.Enabled ?? true) { - Options.AuthToken = authToken; - } + if (!response.Profile.Protection?.Enabled ?? true) Options.AuthToken = authToken; return (response.Ecosystem, authToken); } @@ -118,4 +124,4 @@ public string GenerateToken(GenerateTokenRequest request) { return Convert.ToBase64String(response.ToByteArray()); } -} \ No newline at end of file +} diff --git a/dotnet/Trinsic/Security/OberonSecurityProvider.cs b/dotnet/Trinsic/Security/OberonSecurityProvider.cs index 13771d748..e502556b5 100644 --- a/dotnet/Trinsic/Security/OberonSecurityProvider.cs +++ b/dotnet/Trinsic/Security/OberonSecurityProvider.cs @@ -11,8 +11,7 @@ namespace Trinsic; internal class OberonSecurityProvider : ISecurityProvider { - public Task GetAuthHeaderAsync(AccountProfile accountProfile, IMessage message) - { + public Task GetAuthHeaderAsync(AccountProfile accountProfile, IMessage message) { return Task.FromResult(GetAuthHeader(accountProfile, message)); } @@ -21,42 +20,38 @@ public Task GetAuthHeaderAsync(AccountProfile accountProfile, IMessage m /// /// /// - private static string Base64UrlEncode(byte[] data) => Convert.ToBase64String(data) - .Replace('+', '-') - .Replace('/', '_') - .Trim('='); + private static string Base64UrlEncode(byte[] data) { + return Convert.ToBase64String(data) + .Replace('+', '-') + .Replace('/', '_') + .Trim('='); + } - public string GetAuthHeader(AccountProfile accountProfile, IMessage message) - { + public string GetAuthHeader(AccountProfile accountProfile, IMessage message) { if (accountProfile.Protection?.Enabled ?? false) throw new("The token must be unprotected before use."); // compute the hash of the request and capture current timestamp - byte[] requestBytes = message.ToByteArray(); - ByteString requestHash = ByteString.Empty; + var requestBytes = message.ToByteArray(); + var requestHash = ByteString.Empty; - if (requestBytes.Any()) - { - requestHash = Okapi.Hashing.Blake3.Hash(new() {Data = ByteString.CopyFrom(requestBytes)}).Digest; - } + if (requestBytes.Any()) requestHash = Okapi.Hashing.Blake3.Hash(new() {Data = ByteString.CopyFrom(requestBytes)}).Digest; - Nonce nonce = new() - { + Nonce nonce = new() { Timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(), RequestHash = requestHash }; - var proof = Oberon.CreateProof(new() - { + var proof = Oberon.CreateProof(new() { Token = accountProfile!.AuthToken, Data = accountProfile!.AuthData, Nonce = nonce.ToByteString() }); var header = "Oberon " + - $"ver={1}," + - $"proof={Base64UrlEncode(proof.Proof.ToByteArray())}," + - $"data={Base64UrlEncode(accountProfile.AuthData.ToByteArray())}," + - $"nonce={Base64UrlEncode(nonce.ToByteArray())}"; + $"ver={1}," + + $"proof={Base64UrlEncode(proof.Proof.ToByteArray())}," + + $"data={Base64UrlEncode(accountProfile.AuthData.ToByteArray())}," + + $"nonce={Base64UrlEncode(nonce.ToByteArray())}"; return header; } diff --git a/dotnet/Trinsic/ServiceBase.cs b/dotnet/Trinsic/ServiceBase.cs index f994daefe..ed74d4250 100644 --- a/dotnet/Trinsic/ServiceBase.cs +++ b/dotnet/Trinsic/ServiceBase.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using Grpc.Core; using Google.Protobuf; using Trinsic.Services.Common.V1; @@ -13,38 +14,39 @@ namespace Trinsic; +[SuppressMessage("Usage", "CA2211:Non-constant fields should not be visible")] public abstract class ServiceBase { - private const string DefaultEcosystem = "default"; - private const bool DefaultServerUseTls = true; - private const int DefaultServerPort = 443; - private const string DefaultServerEndpoint = "prod.trinsic.cloud"; - - protected internal ServiceBase() { - Options = new(); - EnsureOptionDefaults(); - Channel = CreateChannel(Options); - } + internal const string DefaultEcosystem = "default"; + internal const bool DefaultServerUseTls = true; + internal const int DefaultServerPort = 443; + internal const string DefaultServerEndpoint = "prod.trinsic.cloud"; + + protected internal readonly ITokenProvider TokenProvider; + + protected internal ServiceBase() : this(new()) { } protected internal ServiceBase(ServiceOptions options) { Options = options; EnsureOptionDefaults(); - Channel = CreateChannel(options); + Channel = CreateChannel(Options); + +#if __IOS__ + TokenProvider = KeyChainTokenProvider.StaticInstance; +#else + TokenProvider = FileTokenProvider.StaticInstance; +#endif + } + + protected internal ServiceBase(ServiceOptions options, ITokenProvider tokenProvider) : this(options) { + TokenProvider = tokenProvider; } private void EnsureOptionDefaults() { - if (string.IsNullOrWhiteSpace(Options.ServerEndpoint)) { - Options.ServerEndpoint = DefaultServerEndpoint; - } - if (Options.ServerPort == default) { - Options.ServerPort = DefaultServerPort; - } - if (Options.ServerPort == DefaultServerPort) { - Options.ServerUseTls = DefaultServerUseTls; - } - if (string.IsNullOrWhiteSpace(Options.DefaultEcosystem)) { - Options.DefaultEcosystem = DefaultEcosystem; - } + if (string.IsNullOrWhiteSpace(Options.ServerEndpoint)) Options.ServerEndpoint = DefaultServerEndpoint; + if (Options.ServerPort == default) Options.ServerPort = DefaultServerPort; + if (Options.ServerPort == DefaultServerPort) Options.ServerUseTls = DefaultServerUseTls; + if (string.IsNullOrWhiteSpace(Options.DefaultEcosystem)) Options.DefaultEcosystem = DefaultEcosystem; } private static GrpcChannel CreateChannel(ServiceOptions options) { @@ -64,18 +66,17 @@ private static GrpcChannel CreateChannel(ServiceOptions options) { /// Gets the gRPC channel used by this service. This channel can be reused /// by passing this instance to other service constructors. /// - public GrpcChannel Channel { get; set; } + protected GrpcChannel Channel { get; } /// /// Create call metadata by setting the required authentication headers /// /// protected async Task BuildMetadataAsync(IMessage request) { - if (Options is null || string.IsNullOrWhiteSpace(Options.AuthToken)) { - throw new("Cannot call authenticated endpoint: auth token must be set in service options"); - } + var authToken = string.IsNullOrWhiteSpace(Options.AuthToken) ? TokenProvider.Get() : Options.AuthToken; + if (authToken is null) throw new("Cannot call authenticated endpoint before signing in"); - var profile = AccountProfile.Parser.ParseFrom(Convert.FromBase64String(Options.AuthToken)); + var profile = AccountProfile.Parser.ParseFrom(Convert.FromBase64String(authToken)); return new() { {"Authorization", await _securityProvider.GetAuthHeaderAsync(profile, request)} @@ -87,14 +88,13 @@ protected async Task BuildMetadataAsync(IMessage request) { /// /// protected Metadata BuildMetadata(IMessage request) { - if (Options is null || string.IsNullOrWhiteSpace(Options.AuthToken)) { - throw new("Cannot call authenticated endpoint: auth token must be set in service options"); - } + var authToken = string.IsNullOrWhiteSpace(Options.AuthToken) ? TokenProvider.Get() : Options.AuthToken; + if (authToken is null) throw new("Cannot call authenticated endpoint before signing in"); - var profile = AccountProfile.Parser.ParseFrom(Convert.FromBase64String(Options.AuthToken)); + var profile = AccountProfile.Parser.ParseFrom(Convert.FromBase64String(authToken)); return new() { {"Authorization", _securityProvider.GetAuthHeader(profile, request)} }; } -} \ No newline at end of file +} diff --git a/dotnet/Trinsic/Storage/BrowserTokenProvider.cs b/dotnet/Trinsic/Storage/BrowserTokenProvider.cs new file mode 100644 index 000000000..89b1738a1 --- /dev/null +++ b/dotnet/Trinsic/Storage/BrowserTokenProvider.cs @@ -0,0 +1,35 @@ +#if __BROWSER__ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.JSInterop; +using Microsoft.JSInterop.WebAssembly; + +namespace Trinsic; + +public class BrowserTokenProvider : ITokenProvider +{ + private readonly IJSRuntime _jsRuntime; + private string? _cachedToken; + public BrowserTokenProvider(IJSRuntime jsRuntime) { + _jsRuntime = jsRuntime; + } + + public async Task GetAsync(string name = TokenDefaults.Name, CancellationToken cancellationToken = default) { + if (_cachedToken is not null) return _cachedToken; + + _cachedToken = await _jsRuntime.InvokeAsync("localStorage.getItem", cancellationToken, name); + return _cachedToken; + } + public string? Get(string name = TokenDefaults.Name) { + throw new NotImplementedException("sync method not supported, please use async overload instead"); + } + public async Task SaveAsync(string authToken, string name = TokenDefaults.Name, CancellationToken cancellationToken = default) { + _cachedToken = authToken; + await _jsRuntime.InvokeVoidAsync("localStorage.setItem", cancellationToken, name, authToken); + } + public void Save(string authToken, string name = TokenDefaults.Name) { + throw new NotImplementedException("sync method not supported, please use async overload instead"); + } +} +#endif diff --git a/dotnet/Trinsic/Storage/FileProfileProvider.cs b/dotnet/Trinsic/Storage/FileProfileProvider.cs deleted file mode 100644 index 8faad1fc2..000000000 --- a/dotnet/Trinsic/Storage/FileProfileProvider.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Google.Protobuf; -using Multiformats.Base; -using Trinsic.Services.Account.V1; - -namespace Trinsic; - -internal class FileProfileProvider : IProfileProvider -{ - public async Task GetAsync(string name, CancellationToken cancellationToken = default) - { - var rootPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - var filename = Path.Combine( - rootPath, - Multibase.Base58.Encode(SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(name)))); - - return AccountProfile.Parser.ParseFrom( - await File.ReadAllBytesAsync(filename, cancellationToken)); - } - - public Task SaveAsync(string name, AccountProfile accountProfile, CancellationToken cancellationToken = default) - { - var rootPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - var filename = Path.Combine( - rootPath, - Multibase.Base58.Encode(SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(name)))); - - return File.WriteAllBytesAsync(filename, accountProfile.ToByteArray(), cancellationToken); - } -} \ No newline at end of file diff --git a/dotnet/Trinsic/Storage/FileTokenProvider.cs b/dotnet/Trinsic/Storage/FileTokenProvider.cs new file mode 100644 index 000000000..f3ceb235e --- /dev/null +++ b/dotnet/Trinsic/Storage/FileTokenProvider.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Trinsic; + +internal class FileTokenProvider : ITokenProvider +{ + private const string Vendor = "Trinsic"; + + public static FileTokenProvider StaticInstance => new(); + + private string? _cachedToken; + + public async Task GetAsync(string name = TokenDefaults.Name, CancellationToken cancellationToken = default) { + if (_cachedToken is not null) return _cachedToken; + + var rootPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var filename = Path.Combine(rootPath, Vendor, name); + + try { + _cachedToken = await File.ReadAllTextAsync(filename, cancellationToken); + } catch { + // ignored + } + return _cachedToken; + } + public string? Get(string name = TokenDefaults.Name) { + if (_cachedToken is not null) return _cachedToken; + + var rootPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var filename = Path.Combine(rootPath, Vendor, name); + + try { + _cachedToken = File.ReadAllText(filename); + } catch { + // ignored + } + return _cachedToken; + } + + public Task SaveAsync(string authToken, string name = TokenDefaults.Name, CancellationToken cancellationToken = default) { + var rootPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var filename = Path.Combine(rootPath, Vendor, name); + + Directory.CreateDirectory(Path.Combine(rootPath, Vendor)); + + _cachedToken = authToken; + + return File.WriteAllTextAsync(filename, authToken, cancellationToken); + } + public void Save(string authToken, string name = TokenDefaults.Name) { + var rootPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var filename = Path.Combine(rootPath, Vendor, name); + + Directory.CreateDirectory(Path.Combine(rootPath, Vendor)); + + _cachedToken = authToken; + + File.WriteAllText(filename, authToken); + } +} diff --git a/dotnet/Trinsic/Storage/IProfileProvider.cs b/dotnet/Trinsic/Storage/IProfileProvider.cs deleted file mode 100644 index 890a0ed75..000000000 --- a/dotnet/Trinsic/Storage/IProfileProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Trinsic.Services.Account.V1; - -namespace Trinsic; - -public interface IProfileProvider -{ - Task GetAsync(string name, CancellationToken cancellationToken = default); - - Task SaveAsync(string name, AccountProfile accountProfile, CancellationToken cancellationToken = default); -} diff --git a/dotnet/Trinsic/Storage/ITokenProvider.cs b/dotnet/Trinsic/Storage/ITokenProvider.cs new file mode 100644 index 000000000..0339387c1 --- /dev/null +++ b/dotnet/Trinsic/Storage/ITokenProvider.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; + +namespace Trinsic; + +[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] +internal class TokenDefaults +{ + internal const string Name = "dotnet.authtoken"; +} +public interface ITokenProvider +{ + Task GetAsync(string name = TokenDefaults.Name, CancellationToken cancellationToken = default); + + string? Get(string name = TokenDefaults.Name); + + Task SaveAsync(string authToken, string name = TokenDefaults.Name, CancellationToken cancellationToken = default); + + void Save(string authToken, string name = TokenDefaults.Name); +} diff --git a/dotnet/Trinsic/Storage/KeyChainTokenProvider.cs b/dotnet/Trinsic/Storage/KeyChainTokenProvider.cs new file mode 100644 index 000000000..6e4887e43 --- /dev/null +++ b/dotnet/Trinsic/Storage/KeyChainTokenProvider.cs @@ -0,0 +1,58 @@ +#if __IOS__ + +using System.Threading.Tasks; +using Google.Protobuf; +using Trinsic.Services.Account.V1; +using Foundation; +using Security; +using System.Threading; +using Intents; + +namespace Trinsic; + +internal class KeyChainTokenProvider : ITokenProvider +{ + public static KeyChainTokenProvider StaticInstance => new(); + + public Task GetAsync(string name, CancellationToken cancellationToken = default) { + return Task.Run(() => Get(name), cancellationToken); + } + public string? Get(string name) { + var rec = new SecRecord(SecKind.GenericPassword) { + Generic = NSData.FromString(name) + }; + + var @record = SecKeyChain.QueryAsRecord(rec, out var res); + return res != SecStatusCode.Success ? null : @record!.ValueData!.ToString(); + } + + public Task SaveAsync(string authToken, string name = "default", CancellationToken cancellationToken = default) { + return Task.Run(() => Save(authToken, name), cancellationToken); + } + + public void Save(string authToken, string name = "default") { + var rec = new SecRecord(SecKind.GenericPassword) { + Generic = NSData.FromString(name) + }; + _ = SecKeyChain.QueryAsRecord(rec, out var res); + if (res != SecStatusCode.Success) { + var s = new SecRecord(SecKind.GenericPassword) { + Label = $"Account Profile: {name}", + Description = "Oberon Account Profile", + Account = "Account", + Service = "Service", + Comment = "Your comment here", + ValueData = NSData.FromString(authToken), + Generic = NSData.FromString(name) + }; + + var err = SecKeyChain.Add(s); + if (err != SecStatusCode.Success) throw new($"error saving token: {err:G}"); + } + else { + throw new("Profile already exists"); + } + } +} + +#endif diff --git a/dotnet/Trinsic/Storage/KeychainProfileProvider.cs b/dotnet/Trinsic/Storage/KeychainProfileProvider.cs deleted file mode 100644 index a2dada480..000000000 --- a/dotnet/Trinsic/Storage/KeychainProfileProvider.cs +++ /dev/null @@ -1,70 +0,0 @@ - -#if __IOS__ - -using System.Threading.Tasks; -using Google.Protobuf; -using Trinsic.Services.Account.V1; -using Foundation; -using Security; -using System.Threading; -using System; -using Multiformats.Base; -using System.Security.Cryptography; -using System.Text; - -namespace Trinsic; - -internal class KeychainProfileProvider : IProfileProvider -{ - public Task GetAsync(string name, CancellationToken cancellationToken = default) - { - var nameSerialized = Multibase.Base58.Encode(SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(name))); - - var rec = new SecRecord(SecKind.GenericPassword) - { - Generic = NSData.FromString(nameSerialized) - }; - - var match = SecKeyChain.QueryAsRecord(rec, out SecStatusCode res); - if (res != SecStatusCode.Success) - { - throw new("Profile not found"); - } - - return Task.FromResult(AccountProfile.Parser.ParseFrom(rec.ValueData!.ToArray())); - } - - public Task SaveAsync(string name, AccountProfile accountProfile, CancellationToken cancellationToken = default) - { - var nameSerialized = Multibase.Base58.Encode(SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(name))); - - var rec = new SecRecord(SecKind.GenericPassword) - { - Generic = NSData.FromString(nameSerialized) - }; - _ = SecKeyChain.QueryAsRecord(rec, out SecStatusCode res); - if (res != SecStatusCode.Success) - { - var s = new SecRecord(SecKind.GenericPassword) - { - Label = $"Account Profile: {name}", - Description = "Oberon Account Profile", - Account = "Account", - Service = "Service", - Comment = "Your comment here", - ValueData = NSData.FromArray(accountProfile.ToByteArray()), - Generic = NSData.FromString(nameSerialized) - }; - - var err = SecKeyChain.Add(s); - } - else - { - throw new("Profile already exists"); - } - - return Task.CompletedTask; - } -} - -#endif diff --git a/dotnet/Trinsic/TemplateService.cs b/dotnet/Trinsic/TemplateService.cs index 1360fd285..9c568e3b3 100644 --- a/dotnet/Trinsic/TemplateService.cs +++ b/dotnet/Trinsic/TemplateService.cs @@ -1,8 +1,6 @@ using System.Threading.Tasks; -using Grpc.Net.Client; +using Microsoft.Extensions.Options; using Trinsic.Sdk.Options.V1; -using Trinsic.Services.Account.V1; -using Trinsic.Services.Common.V1; using Trinsic.Services.VerifiableCredentials.Templates.V1; namespace Trinsic; @@ -21,7 +19,16 @@ public TemplateService() { Client = new(Channel); } - private CredentialTemplates.CredentialTemplatesClient Client { get; set; } + internal TemplateService(ITokenProvider tokenProvider) : base(new(), tokenProvider) { + Client = new(Channel); + } + + internal TemplateService(ITokenProvider tokenProvider, IOptions options) + : base(options.Value, tokenProvider) { + Client = new(Channel); + } + + private CredentialTemplates.CredentialTemplatesClient Client { get; } /// /// Create new credential template with the given parameters @@ -48,7 +55,7 @@ public async Task GetAsync(GetCredentialTemplateR public GetCredentialTemplateResponse Get(GetCredentialTemplateRequest request) { return Client.Get(request, BuildMetadata(request)); } - + /// /// List the available templates for the given ecosystem. /// Results can be customized using a SQL query. @@ -64,7 +71,7 @@ public GetCredentialTemplateResponse Get(GetCredentialTemplateRequest request) { public async Task ListAsync(ListCredentialTemplatesRequest request) { return await Client.ListAsync(request, await BuildMetadataAsync(request)); } - + public ListCredentialTemplatesResponse List(ListCredentialTemplatesRequest request) { return Client.List(request, BuildMetadata(request)); } @@ -96,4 +103,4 @@ public async Task DeleteAsync(DeleteCredential public DeleteCredentialTemplateResponse Delete(DeleteCredentialTemplateRequest request) { return Client.Delete(request, BuildMetadata(request)); } -} \ No newline at end of file +} diff --git a/dotnet/Trinsic/Trinsic.csproj b/dotnet/Trinsic/Trinsic.csproj index 7d4cefd8d..1db5451c5 100644 --- a/dotnet/Trinsic/Trinsic.csproj +++ b/dotnet/Trinsic/Trinsic.csproj @@ -13,9 +13,10 @@ - + + - + - <_BuiltProjectOutputGroupOutputIntermediate Remove="$(OutDir)$(_DeploymentTargetApplicationManifestFileName)" /> - + <_BuiltProjectOutputGroupOutputIntermediate Remove="$(OutDir)$(_DeploymentTargetApplicationManifestFileName)"/> + - - + - + + + none @@ -42,23 +44,26 @@ <_Parameter1>Tests + + <_Parameter1>Trinsic.Browser + - - - - - + + + + + - - + + - + diff --git a/dotnet/Trinsic/Trinsic.xml b/dotnet/Trinsic/Trinsic.xml index 43a5efd23..534a3ca5f 100644 --- a/dotnet/Trinsic/Trinsic.xml +++ b/dotnet/Trinsic/Trinsic.xml @@ -2843,5 +2843,20 @@ + + + Registers Trinsic SDK services and dependencies + + + + + + + + Registers Trinsic SDK services and dependencies + + + + diff --git a/dotnet/Trinsic/TrustRegistryService.cs b/dotnet/Trinsic/TrustRegistryService.cs index a3a61f81b..7998dcc8c 100644 --- a/dotnet/Trinsic/TrustRegistryService.cs +++ b/dotnet/Trinsic/TrustRegistryService.cs @@ -1,9 +1,8 @@ using System; using System.Threading.Tasks; using Grpc.Core; -using Grpc.Net.Client; +using Microsoft.Extensions.Options; using Trinsic.Sdk.Options.V1; -using Trinsic.Services.Account.V1; using Trinsic.Services.Common.V1; using Trinsic.Services.TrustRegistry.V1; @@ -20,6 +19,14 @@ public TrustRegistryService() { Client = new(Channel); } + internal TrustRegistryService(ITokenProvider tokenProvider) + : this(tokenProvider, Microsoft.Extensions.Options.Options.Create(new ServiceOptions())) { } + + internal TrustRegistryService(ITokenProvider tokenProvider, IOptions options) + : base(options.Value, tokenProvider) { + Client = new(Channel); + } + private TrustRegistry.TrustRegistryClient Client { get; } /// @@ -32,9 +39,7 @@ public TrustRegistryService() { /// The framework description /// public async Task RegisterGovernanceFrameworkAsync(string governanceFramework, string description) { - if (!Uri.TryCreate(governanceFramework, UriKind.Absolute, out _)) { - throw new("Invalid URI string"); - } + if (!Uri.TryCreate(governanceFramework, UriKind.Absolute, out _)) throw new("Invalid URI string"); AddFrameworkRequest request = new() { GovernanceFramework = new() { @@ -46,9 +51,7 @@ public async Task RegisterGovernanceFrameworkAsync(string governanceFramework, s } public void RegisterGovernanceFramework(string governanceFramework, string description) { - if (!Uri.TryCreate(governanceFramework, UriKind.Absolute, out _)) { - throw new("Invalid URI string"); - } + if (!Uri.TryCreate(governanceFramework, UriKind.Absolute, out _)) throw new("Invalid URI string"); AddFrameworkRequest request = new() { GovernanceFramework = new() { GovernanceFrameworkUri = governanceFramework, @@ -73,16 +76,12 @@ public RemoveFrameworkResponse RemoveGovernanceFramework(RemoveFrameworkRequest /// public async Task RegisterIssuerAsync(RegisterIssuerRequest request) { var response = await Client.RegisterIssuerAsync(request, await BuildMetadataAsync(request)); - if (response.Status != ResponseStatus.Success) { - throw new($"cannot register issuer: code {response.Status}"); - } + if (response.Status != ResponseStatus.Success) throw new($"cannot register issuer: code {response.Status}"); } public void RegisterIssuer(RegisterIssuerRequest request) { var response = Client.RegisterIssuer(request, BuildMetadata(request)); - if (response.Status != ResponseStatus.Success) { - throw new($"cannot register issuer: code {response.Status}"); - } + if (response.Status != ResponseStatus.Success) throw new($"cannot register issuer: code {response.Status}"); } public async Task UnregisterIssuerAsync(UnregisterIssuerRequest request) { @@ -100,16 +99,12 @@ public UnregisterIssuerResponse UnregisterIssuer(UnregisterIssuerRequest request /// public async Task RegisterVerifierAsync(RegisterVerifierRequest request) { var response = await Client.RegisterVerifierAsync(request, await BuildMetadataAsync(request)); - if (response.Status != ResponseStatus.Success) { - throw new($"cannot register verifier: code {response.Status}"); - } + if (response.Status != ResponseStatus.Success) throw new($"cannot register verifier: code {response.Status}"); } public void RegisterVerifier(RegisterVerifierRequest request) { var response = Client.RegisterVerifier(request, BuildMetadata(request)); - if (response.Status != ResponseStatus.Success) { - throw new($"cannot register verifier: code {response.Status}"); - } + if (response.Status != ResponseStatus.Success) throw new($"cannot register verifier: code {response.Status}"); } public async Task UnregisterVerifierAsync(UnregisterVerifierRequest request) { @@ -144,7 +139,7 @@ public async Task CheckVerifierStatusAsync(CheckVerifierStat return response.Status; } - + public RegistrationStatus CheckVerifierStatus(CheckVerifierStatusRequest request) { return Client.CheckVerifierStatus(request, BuildMetadata(request)).Status; } @@ -175,4 +170,4 @@ public SearchRegistryResponse SearchRegistry(string query = "SELECT * FROM c", s public IAsyncStreamReader FetchData(FetchDataRequest request) { return Client.FetchData(request, BuildMetadata(request)).ResponseStream; } -} \ No newline at end of file +} diff --git a/dotnet/Trinsic/WalletService.cs b/dotnet/Trinsic/WalletService.cs index aa187a94d..7f018b125 100644 --- a/dotnet/Trinsic/WalletService.cs +++ b/dotnet/Trinsic/WalletService.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Microsoft.Extensions.Options; using Trinsic.Services.UniversalWallet.V1; using Trinsic.Sdk.Options.V1; @@ -15,6 +16,14 @@ public WalletService() { Client = new(Channel); } + internal WalletService(ITokenProvider tokenProvider) + : this(tokenProvider, Microsoft.Extensions.Options.Options.Create(new ServiceOptions())) { } + + internal WalletService(ITokenProvider tokenProvider, IOptions options) + : base(options.Value, tokenProvider) { + Client = new(Channel); + } + private UniversalWallet.UniversalWalletClient Client { get; } /// @@ -25,9 +34,8 @@ public WalletService() { /// See https://docs.microsoft.com/en-us/azure/cosmos-db/sql-query-select /// /// - public async Task SearchAsync(string query = "SELECT * FROM c") - { - SearchRequest request = new() { Query = query }; + public async Task SearchAsync(string query = "SELECT * FROM c") { + SearchRequest request = new() {Query = query}; var response = await Client.SearchAsync(request, await BuildMetadataAsync(request)); return response; } @@ -40,9 +48,8 @@ public async Task SearchAsync(string query = "SELECT * FROM c") /// See https://docs.microsoft.com/en-us/azure/cosmos-db/sql-query-select /// /// - public SearchResponse Search(string query = "SELECT * FROM c") - { - SearchRequest request = new() { Query = query }; + public SearchResponse Search(string query = "SELECT * FROM c") { + SearchRequest request = new() {Query = query}; var response = Client.Search(request, BuildMetadata(request)); return response; } @@ -54,8 +61,8 @@ public SearchResponse Search(string query = "SELECT * FROM c") /// public async Task InsertItemAsync(InsertItemRequest request) { var response = await Client.InsertItemAsync( - request: request, - headers: await BuildMetadataAsync(request)); + request, + await BuildMetadataAsync(request)); return response.ItemId; } @@ -66,8 +73,8 @@ public async Task InsertItemAsync(InsertItemRequest request) { /// public string InsertItem(InsertItemRequest request) { var response = Client.InsertItem( - request: request, - headers: BuildMetadata(request)); + request, + BuildMetadata(request)); return response.ItemId; } diff --git a/node/.env b/node/.env index 3ef4fdec6..dc8bf2f84 100644 --- a/node/.env +++ b/node/.env @@ -1,3 +1,3 @@ -TEST_SERVER_ENDPOINT=dev-internal.trinsic.cloud +TEST_SERVER_ENDPOINT=staging-internal.trinsic.cloud TEST_SERVER_PORT=443 TEST_SERVER_USE_TLS=true \ No newline at end of file diff --git a/node/test/TestData.ts b/node/test/TestData.ts index 4d3facdfa..bb1261bcd 100644 --- a/node/test/TestData.ts +++ b/node/test/TestData.ts @@ -21,7 +21,7 @@ export function getVaccineCertUnsignedJSON(): any { } export function getTestServerOptions(): ServiceOptions { - const endpoint = process.env.TEST_SERVER_ENDPOINT || "dev-internal.trinsic.cloud"; + const endpoint = process.env.TEST_SERVER_ENDPOINT || "staging-internal.trinsic.cloud"; const port = process.env.TEST_SERVER_PORT || "443"; const useTls = (process.env.TEST_SERVER_USE_TLS || "true") != "false"; diff --git a/web/test/env.js b/web/test/env.js index aac195f12..f179f113a 100644 --- a/web/test/env.js +++ b/web/test/env.js @@ -1,6 +1,7 @@ const { ServiceOptions } = require("../lib/proto"); export const options = new ServiceOptions() - .setServerEndpoint("dev-internal.trinsic.cloud") + .setServerEndpoint("staging-internal.trinsic.cloud") .setServerPort(443) - .setServerUseTls(true); + .setServerUseTls(true) + .setDefaultEcosystem("default");