From 1aff526b088fe5dade2522c455d00f7ceef80b7e Mon Sep 17 00:00:00 2001 From: Andrew Peacock Date: Fri, 14 May 2021 16:29:49 -0700 Subject: [PATCH] Adding the Azure.Communication.NetworkTraversal SDK --- .../CHANGELOG.md | 13 ++ .../README.md | 110 ++++++++++++++ ...ication.NetworkTraversal.netstandard2.0.cs | 34 +++++ ...zure.Communication.NetworkTraversal.csproj | 38 +++++ .../src/CommunicationRelayClient.cs | 143 ++++++++++++++++++ .../src/CommunicationRelayClientOptions.cs | 46 ++++++ ...CommunicationNetworkTraversalRestClient.cs | 118 +++++++++++++++ .../CommunicationError.Serialization.cs | 69 +++++++++ .../Generated/Models/CommunicationError.cs | 63 ++++++++ ...ommunicationErrorResponse.Serialization.cs | 29 ++++ .../Models/CommunicationErrorResponse.cs | 31 ++++ ...icationRelayConfiguration.Serialization.cs | 42 +++++ .../Models/CommunicationRelayConfiguration.cs | 46 ++++++ .../CommunicationTurnServer.Serialization.cs | 47 ++++++ .../Models/CommunicationTurnServer.cs | 60 ++++++++ .../src/autorest.md | 44 ++++++ ...ommunication.NetworkTraversal.Tests.csproj | 27 ++++ .../CommunicationRelayClientLiveTests.cs | 62 ++++++++ .../CommunicationRelayClientLiveTestBase.cs | 70 +++++++++ ...icationRelayClientRecordedTestSanitizer.cs | 11 ++ ...CommunicationRelayClientTestEnvironment.cs | 14 ++ ...ngTurnCredentialsWithConnectionString.json | 91 +++++++++++ ...nCredentialsWithConnectionStringAsync.json | 91 +++++++++++ ...ttingTurnCredentialsWithKeyCredential.json | 91 +++++++++++ ...TurnCredentialsWithKeyCredentialAsync.json | 91 +++++++++++ ...ingTurnCredentialsWithTokenCredential.json | 89 +++++++++++ ...rnCredentialsWithTokenCredentialAsync.json | 89 +++++++++++ .../GetRelayConfiguration.json | 6 + .../GetRelayConfigurationAsyncAsync.json | 6 + .../Sample1_CommunicationRelayClient.cs | 105 +++++++++++++ sdk/communication/Azure.Communication.sln | 12 ++ sdk/communication/ci.yml | 4 +- 32 files changed, 1791 insertions(+), 1 deletion(-) create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/CHANGELOG.md create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/README.md create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/api/Azure.Communication.NetworkTraversal.netstandard2.0.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/Azure.Communication.NetworkTraversal.csproj create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/CommunicationRelayClient.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/CommunicationRelayClientOptions.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/CommunicationNetworkTraversalRestClient.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationError.Serialization.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationError.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationErrorResponse.Serialization.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationErrorResponse.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationRelayConfiguration.Serialization.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationRelayConfiguration.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationTurnServer.Serialization.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationTurnServer.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/src/autorest.md create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/Azure.Communication.NetworkTraversal.Tests.csproj create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/CommunicationRelayClient/CommunicationRelayClientLiveTests.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientLiveTestBase.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientRecordedTestSanitizer.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientTestEnvironment.cs create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithConnectionString.json create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithConnectionStringAsync.json create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithKeyCredential.json create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithKeyCredentialAsync.json create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithTokenCredential.json create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithTokenCredentialAsync.json create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/Sample1_CommunicationRelayClient/GetRelayConfiguration.json create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/Sample1_CommunicationRelayClient/GetRelayConfigurationAsyncAsync.json create mode 100644 sdk/communication/Azure.Communication.NetworkTraversal/tests/samples/Sample1_CommunicationRelayClient.cs diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/CHANGELOG.md b/sdk/communication/Azure.Communication.NetworkTraversal/CHANGELOG.md new file mode 100644 index 0000000000000..e9ab6ca171516 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/CHANGELOG.md @@ -0,0 +1,13 @@ +# Release History + +## 1.0.0-beta.1 (2021-02-22-preview1) + +### Added +- Added CommunicationRelayClient in preview. +- Added CommunicationRelayClient.GetRelayConfiguration in preview. +- Added CommunicationRelayClient.GetRelayConfigurationAsync in preview. + +### Breaking + + +[read_me]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/communication/Azure.Communication.NetworkTraversal/README.md diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/README.md b/sdk/communication/Azure.Communication.NetworkTraversal/README.md new file mode 100644 index 0000000000000..1813d44aab263 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/README.md @@ -0,0 +1,110 @@ +# Azure Communication Network Traversal client library for .NET + +Azure Communication Network Traversal is managing tokens for Azure Communication Services. + +[Source code][source] | [Product documentation][product_docs] | [Samples][source_samples] + +## Getting started + +### Install the package + +Install the Azure Communication Network Traversal client library for .NET with [NuGet][nuget]: + +```Powershell +dotnet add package Azure.Communication.NetworkTraversal --version 1.0.0-beta.1 +``` + +### Prerequisites + +You need an [Azure subscription][azure_sub] and a [Communication Service Resource][communication_resource_docs] to use this package. + +To create a new Communication Service, you can use the [Azure Portal][communication_resource_create_portal], the [Azure PowerShell][communication_resource_create_power_shell], or the [.NET management client library][communication_resource_create_net]. + +### Authenticate the client + +The networking client can be authenticated using a connection string acquired from an Azure Communication Resources in the [Azure Portal][azure_portal]. + +```C# Snippet:CreateCommunicationRelayClient +// Get a connection string to our Azure Communication resource. +var connectionString = ""; +var client = new CommunicationRelayClient(connectionString); +``` + +Or alternatively using the endpoint and access key acquired from an Azure Communication Resources in the [Azure Portal][azure_portal]. + +```C# Snippet:CreateCommunicationNetworkingFromAccessKey +var endpoint = new Uri("https://my-resource.communication.azure.com"); +var accessKey = ""; +var client = new CommunicationRelayClient(endpoint, new AzureKeyCredential(accessKey)); +``` + +Clients also have the option to authenticate using a valid Active Directory token. + +```C# Snippet:CreateCommunicationNetworkingFromToken +var endpoint = new Uri("https://my-resource.communication.azure.com"); +TokenCredential tokenCredential = new DefaultAzureCredential(); +var client = new CommunicationRelayClient(endpoint, tokenCredential); +``` + +### Key concepts + +`CommunicationRelayClient` provides the functionalities to gain STUN/TURN server URLs and credentials for access. + +### Thread safety +We guarantee that all client instance methods are thread-safe and independent of each other ([guideline](https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-service-methods-thread-safety)). This ensures that the recommendation of reusing client instances is always safe, even across threads. + +### Additional concepts + +[Client options](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/README.md#configuring-service-clients-using-clientoptions) | +[Accessing the response](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/README.md#accessing-http-response-details-using-responset) | +[Long-running operations](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/README.md#consuming-long-running-operations-using-operationt) | +[Handling failures](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/README.md#reporting-errors-requestfailedexception) | +[Diagnostics](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/samples/Diagnostics.md) | +[Mocking](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/README.md#mocking) | +[Client lifetime](https://devblogs.microsoft.com/azure-sdk/lifetime-management-and-thread-safety-guarantees-of-azure-sdk-net-clients/) + + +## Examples + +## Generating TURN credentials for a user + +```C# Snippet:CreateTURNTokenAsync +Response turnTokenResponse = await client.GetRelayConfigurationAsync(user); +DateTimeOffset turnTokenExpiresOn = turnTokenResponse.Value.ExpiresOn; +IReadOnlyList turnServers = turnTokenResponse.Value.TurnServers; +Console.WriteLine($"Expires On: {turnTokenExpiresOn}"); +foreach (CommunicationTurnServer turnServer in turnServers) +{ + foreach (string url in turnServer.Urls) + { + Console.WriteLine($"TURN Url: {url}"); + } + Console.WriteLine($"TURN Username: {turnServer.Username}"); + Console.WriteLine($"TURN Credential: {turnServer.Credential}"); +} +``` + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla]. + +This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. + + + +[azure_sub]: https://azure.microsoft.com/free/ +[azure_portal]: https://portal.azure.com +[source]: https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/communication/Azure.Communication.NetworkTraversal/src +[source_samples]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/communication/Azure.Communication.NetworkTraversal/samples +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com + +[product_docs]: https://docs.microsoft.com/azure/communication-services/overview +[nuget]: https://www.nuget.org/ +[user_access_token]: https://docs.microsoft.com/azure/communication-services/quickstarts/access-tokens?pivots=programming-language-csharp +[communication_resource_docs]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[communication_resource_create_portal]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp +[communication_resource_create_power_shell]: https://docs.microsoft.com/powershell/module/az.communication/new-azcommunicationservice +[communication_resource_create_net]: https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-net diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/api/Azure.Communication.NetworkTraversal.netstandard2.0.cs b/sdk/communication/Azure.Communication.NetworkTraversal/api/Azure.Communication.NetworkTraversal.netstandard2.0.cs new file mode 100644 index 0000000000000..bc54fc0667f66 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/api/Azure.Communication.NetworkTraversal.netstandard2.0.cs @@ -0,0 +1,34 @@ +namespace Azure.Communication.NetworkTraversal +{ + public partial class CommunicationRelayClient + { + protected CommunicationRelayClient() { } + public CommunicationRelayClient(string connectionString) { } + public CommunicationRelayClient(string connectionString, Azure.Communication.NetworkTraversal.CommunicationRelayClientOptions options) { } + public CommunicationRelayClient(System.Uri endpoint, Azure.AzureKeyCredential keyCredential, Azure.Communication.NetworkTraversal.CommunicationRelayClientOptions options = null) { } + public CommunicationRelayClient(System.Uri endpoint, Azure.Core.TokenCredential tokenCredential, Azure.Communication.NetworkTraversal.CommunicationRelayClientOptions options = null) { } + public virtual Azure.Response GetRelayConfiguration(Azure.Communication.CommunicationUserIdentifier communicationUser, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task> GetRelayConfigurationAsync(Azure.Communication.CommunicationUserIdentifier communicationUser, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + } + public partial class CommunicationRelayClientOptions : Azure.Core.ClientOptions + { + public CommunicationRelayClientOptions(Azure.Communication.NetworkTraversal.CommunicationRelayClientOptions.ServiceVersion version = Azure.Communication.Networking.CommunicationRelayClientOptions.ServiceVersion.V2021_02_22_preview1) { } + public enum ServiceVersion + { + V2021_02_22_preview1 = 0, + } + } + public partial class CommunicationRelayConfiguration + { + internal CommunicationRelayConfiguration() { } + public System.DateTimeOffset ExpiresOn { get { throw null; } } + public System.Collections.Generic.IReadOnlyList TurnServers { get { throw null; } } + } + public partial class CommunicationTurnServer + { + internal CommunicationTurnServer() { } + public string Credential { get { throw null; } } + public System.Collections.Generic.IReadOnlyList Urls { get { throw null; } } + public string Username { get { throw null; } } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/Azure.Communication.NetworkTraversal.csproj b/sdk/communication/Azure.Communication.NetworkTraversal/src/Azure.Communication.NetworkTraversal.csproj new file mode 100644 index 0000000000000..8f93b1c04b29c --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/Azure.Communication.NetworkTraversal.csproj @@ -0,0 +1,38 @@ + + + + This client library enables working with the Microsoft Azure Communication Network Traversal service. + For this release, see notes - https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/communication/Azure.Communication.NetworkTraversal/README.md and https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/communication/Azure.Communication.NetworkTraversal/CHANGELOG.md. + + Azure Communication Network Traversal Service + 1.0.0-beta.1 + Microsoft Azure Communication Network Traversal Service;Microsoft;Azure;Azure Communication Service;Azure Communication Network Traversal Service;Network Traversal;Communication;$(PackageCommonTags) + $(RequiredTargetFrameworks) + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/CommunicationRelayClient.cs b/sdk/communication/Azure.Communication.NetworkTraversal/src/CommunicationRelayClient.cs new file mode 100644 index 0000000000000..7bdf6f1a370b3 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/CommunicationRelayClient.cs @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Azure.Communication.Pipeline; +using Azure.Core; +using Azure.Core.Pipeline; + +namespace Azure.Communication.NetworkTraversal +{ + /// + /// The Azure Communication Services Networking client. + /// + public class CommunicationRelayClient + { + private readonly ClientDiagnostics _clientDiagnostics; + internal CommunicationNetworkTraversalRestClient RestClient { get; } + + #region public constructors - all argument need null check + + /// Initializes a new instance of . + /// Connection string acquired from the Azure Communication Services resource. + public CommunicationRelayClient(string connectionString) + : this( + ConnectionString.Parse(AssertNotNullOrEmpty(connectionString, nameof(connectionString))), + new CommunicationRelayClientOptions()) + { } + + /// Initializes a new instance of . + /// Connection string acquired from the Azure Communication Services resource. + /// Client option exposing , , , etc. + public CommunicationRelayClient(string connectionString, CommunicationRelayClientOptions options) + : this( + ConnectionString.Parse(AssertNotNullOrEmpty(connectionString, nameof(connectionString))), + options ?? new CommunicationRelayClientOptions()) + { } + + /// Initializes a new instance of . + /// The URI of the Azure Communication Services resource. + /// The used to authenticate requests. + /// Client option exposing , , , etc. + public CommunicationRelayClient(Uri endpoint, AzureKeyCredential keyCredential, CommunicationRelayClientOptions options = default) + : this( + AssertNotNull(endpoint, nameof(endpoint)).AbsoluteUri, + AssertNotNull(keyCredential, nameof(keyCredential)), + options ?? new CommunicationRelayClientOptions()) + { } + + /// Initializes a new instance of . + /// The URI of the Azure Communication Services resource. + /// The used to authenticate requests, such as DefaultAzureCredential. + /// Client option exposing , , , etc. + public CommunicationRelayClient(Uri endpoint, TokenCredential tokenCredential, CommunicationRelayClientOptions options = default) + : this( + AssertNotNull(endpoint, nameof(endpoint)).AbsoluteUri, + AssertNotNull(tokenCredential, nameof(tokenCredential)), + options ?? new CommunicationRelayClientOptions()) + { } + + #endregion + + #region private constructors + + private CommunicationRelayClient(ConnectionString connectionString, CommunicationRelayClientOptions options) + : this(connectionString.GetRequired("endpoint"), options.BuildHttpPipeline(connectionString), options) + { } + + private CommunicationRelayClient(string endpoint, AzureKeyCredential keyCredential, CommunicationRelayClientOptions options) + : this(endpoint, options.BuildHttpPipeline(keyCredential), options) + { } + + private CommunicationRelayClient(string endpoint, TokenCredential tokenCredential, CommunicationRelayClientOptions options) + : this(endpoint, options.BuildHttpPipeline(tokenCredential), options) + { } + + private CommunicationRelayClient(string endpoint, HttpPipeline httpPipeline, CommunicationRelayClientOptions options) + { + _clientDiagnostics = new ClientDiagnostics(options); + RestClient = new CommunicationNetworkTraversalRestClient(_clientDiagnostics, httpPipeline, endpoint, options.ApiVersion); + } + + #endregion + + /// Initializes a new instance of for mocking. + protected CommunicationRelayClient() + { + _clientDiagnostics = null; + RestClient = null; + } + + /// Gets a TURN credential for a . + /// The for whom to issue a token. + /// The cancellation token to use. + /// The server returned an error. + public virtual Response GetRelayConfiguration(CommunicationUserIdentifier communicationUser, CancellationToken cancellationToken = default) + { + using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(CommunicationRelayClient)}.{nameof(GetRelayConfiguration)}"); + scope.Start(); + try + { + return RestClient.IssueTurnCredentials(communicationUser.Id, cancellationToken); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + } + + /// Asynchronously gets a TURN credential for a . + /// The for whom to issue a token. + /// The cancellation token to use. + public virtual async Task> GetRelayConfigurationAsync(CommunicationUserIdentifier communicationUser, CancellationToken cancellationToken = default) + { + using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(CommunicationRelayClient)}.{nameof(GetRelayConfiguration)}"); + scope.Start(); + try + { + return await RestClient.IssueTurnCredentialsAsync(communicationUser.Id, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + } + + private static T AssertNotNull(T argument, string argumentName) + where T : class + { + Argument.AssertNotNull(argument, argumentName); + return argument; + } + + private static string AssertNotNullOrEmpty(string argument, string argumentName) + { + Argument.AssertNotNullOrEmpty(argument, argumentName); + return argument; + } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/CommunicationRelayClientOptions.cs b/sdk/communication/Azure.Communication.NetworkTraversal/src/CommunicationRelayClientOptions.cs new file mode 100644 index 0000000000000..dcf620cbeb164 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/CommunicationRelayClientOptions.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Azure.Core; + +namespace Azure.Communication.NetworkTraversal +{ + /// + /// The options for communication . + /// + public class CommunicationRelayClientOptions : ClientOptions + { + /// + /// The latest version of the networking service. + /// + internal const ServiceVersion LatestVersion = ServiceVersion.V2021_02_22_preview1; + + internal string ApiVersion { get; } + + /// + /// Initializes a new instance of the . + /// + public CommunicationRelayClientOptions(ServiceVersion version = LatestVersion) + { + ApiVersion = version switch + { + ServiceVersion.V2021_02_22_preview1 => "2021-02-22-preview1", + _ => throw new ArgumentOutOfRangeException(nameof(version)), + }; + } + + /// + /// The token service version. + /// + public enum ServiceVersion + { +#pragma warning disable CA1707 // Identifiers should not contain underscores + /// + /// The V2021_02_22_preview1 of the networking service. + /// + V2021_02_22_preview1 = 1, +#pragma warning restore CA1707 // Identifiers should not contain underscores + } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/CommunicationNetworkTraversalRestClient.cs b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/CommunicationNetworkTraversalRestClient.cs new file mode 100644 index 0000000000000..8dddf4b2515b6 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/CommunicationNetworkTraversalRestClient.cs @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Azure; +using Azure.Core; +using Azure.Core.Pipeline; + +namespace Azure.Communication.NetworkTraversal +{ + internal partial class CommunicationNetworkTraversalRestClient + { + private string endpoint; + private string apiVersion; + private ClientDiagnostics _clientDiagnostics; + private HttpPipeline _pipeline; + + /// Initializes a new instance of CommunicationNetworkTraversalRestClient. + /// The handler for diagnostic messaging in the client. + /// The HTTP pipeline for sending and receiving REST requests and responses. + /// The communication resource, for example https://my-resource.communication.azure.com. + /// Api Version. + /// or is null. + public CommunicationNetworkTraversalRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string endpoint, string apiVersion = "2021-02-22-preview1") + { + if (endpoint == null) + { + throw new ArgumentNullException(nameof(endpoint)); + } + if (apiVersion == null) + { + throw new ArgumentNullException(nameof(apiVersion)); + } + + this.endpoint = endpoint; + this.apiVersion = apiVersion; + _clientDiagnostics = clientDiagnostics; + _pipeline = pipeline; + } + + internal HttpMessage CreateIssueTurnCredentialsRequest(string id) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Post; + var uri = new RawRequestUriBuilder(); + uri.AppendRaw(endpoint, false); + uri.AppendPath("/turn/", false); + uri.AppendPath(id, true); + uri.AppendPath("/:issueCredentials", false); + uri.AppendQuery("api-version", apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// Issue TURN credentials for an existing identity. + /// Identifier of the existing identity to issue credentials for. + /// The cancellation token to use. + /// is null. + public async Task> IssueTurnCredentialsAsync(string id, CancellationToken cancellationToken = default) + { + if (id == null) + { + throw new ArgumentNullException(nameof(id)); + } + + using var message = CreateIssueTurnCredentialsRequest(id); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + CommunicationRelayConfiguration value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = CommunicationRelayConfiguration.DeserializeCommunicationRelayConfiguration(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// Issue TURN credentials for an existing identity. + /// Identifier of the existing identity to issue credentials for. + /// The cancellation token to use. + /// is null. + public Response IssueTurnCredentials(string id, CancellationToken cancellationToken = default) + { + if (id == null) + { + throw new ArgumentNullException(nameof(id)); + } + + using var message = CreateIssueTurnCredentialsRequest(id); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + CommunicationRelayConfiguration value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = CommunicationRelayConfiguration.DeserializeCommunicationRelayConfiguration(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationError.Serialization.cs b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationError.Serialization.cs new file mode 100644 index 0000000000000..ac32efe0f44b1 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationError.Serialization.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using System.Text.Json; +using Azure.Core; + +namespace Azure.Communication.NetworkTraversal +{ + internal partial class CommunicationError + { + internal static CommunicationError DeserializeCommunicationError(JsonElement element) + { + string code = default; + string message = default; + Optional target = default; + Optional> details = default; + Optional innererror = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("code")) + { + code = property.Value.GetString(); + continue; + } + if (property.NameEquals("message")) + { + message = property.Value.GetString(); + continue; + } + if (property.NameEquals("target")) + { + target = property.Value.GetString(); + continue; + } + if (property.NameEquals("details")) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + property.ThrowNonNullablePropertyIsNull(); + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(DeserializeCommunicationError(item)); + } + details = array; + continue; + } + if (property.NameEquals("innererror")) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + property.ThrowNonNullablePropertyIsNull(); + continue; + } + innererror = DeserializeCommunicationError(property.Value); + continue; + } + } + return new CommunicationError(code, message, target.Value, Optional.ToList(details), innererror.Value); + } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationError.cs b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationError.cs new file mode 100644 index 0000000000000..3e18f2f1446a1 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationError.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using Azure.Core; + +namespace Azure.Communication.NetworkTraversal +{ + /// The Communication Services error. + internal partial class CommunicationError + { + /// Initializes a new instance of CommunicationError. + /// The error code. + /// The error message. + /// or is null. + internal CommunicationError(string code, string message) + { + if (code == null) + { + throw new ArgumentNullException(nameof(code)); + } + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } + + Code = code; + Message = message; + Details = new ChangeTrackingList(); + } + + /// Initializes a new instance of CommunicationError. + /// The error code. + /// The error message. + /// The error target. + /// Further details about specific errors that led to this error. + /// The inner error if any. + internal CommunicationError(string code, string message, string target, IReadOnlyList details, CommunicationError innerError) + { + Code = code; + Message = message; + Target = target; + Details = details; + InnerError = innerError; + } + + /// The error code. + public string Code { get; } + /// The error message. + public string Message { get; } + /// The error target. + public string Target { get; } + /// Further details about specific errors that led to this error. + public IReadOnlyList Details { get; } + /// The inner error if any. + public CommunicationError InnerError { get; } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationErrorResponse.Serialization.cs b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationErrorResponse.Serialization.cs new file mode 100644 index 0000000000000..10550b70c8fb8 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationErrorResponse.Serialization.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Text.Json; +using Azure.Core; + +namespace Azure.Communication.NetworkTraversal +{ + internal partial class CommunicationErrorResponse + { + internal static CommunicationErrorResponse DeserializeCommunicationErrorResponse(JsonElement element) + { + CommunicationError error = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("error")) + { + error = CommunicationError.DeserializeCommunicationError(property.Value); + continue; + } + } + return new CommunicationErrorResponse(error); + } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationErrorResponse.cs b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationErrorResponse.cs new file mode 100644 index 0000000000000..11e24c41d4fdc --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationErrorResponse.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; + +namespace Azure.Communication.NetworkTraversal +{ + /// The Communication Services error. + internal partial class CommunicationErrorResponse + { + /// Initializes a new instance of CommunicationErrorResponse. + /// The Communication Services error. + /// is null. + internal CommunicationErrorResponse(CommunicationError error) + { + if (error == null) + { + throw new ArgumentNullException(nameof(error)); + } + + Error = error; + } + + /// The Communication Services error. + public CommunicationError Error { get; } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationRelayConfiguration.Serialization.cs b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationRelayConfiguration.Serialization.cs new file mode 100644 index 0000000000000..b03742e428f6d --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationRelayConfiguration.Serialization.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Text.Json; +using Azure.Core; + +namespace Azure.Communication.NetworkTraversal +{ + public partial class CommunicationRelayConfiguration + { + internal static CommunicationRelayConfiguration DeserializeCommunicationRelayConfiguration(JsonElement element) + { + DateTimeOffset expiresOn = default; + IReadOnlyList turnServers = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("expiresOn")) + { + expiresOn = property.Value.GetDateTimeOffset("O"); + continue; + } + if (property.NameEquals("turnServers")) + { + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(CommunicationTurnServer.DeserializeCommunicationTurnServer(item)); + } + turnServers = array; + continue; + } + } + return new CommunicationRelayConfiguration(expiresOn, turnServers); + } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationRelayConfiguration.cs b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationRelayConfiguration.cs new file mode 100644 index 0000000000000..8043e9c0aa5ce --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationRelayConfiguration.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Azure.Communication.NetworkTraversal +{ + /// A TURN credentials response. + public partial class CommunicationRelayConfiguration + { + /// Initializes a new instance of CommunicationRelayConfiguration. + /// The date for which the username and credentials are not longer valid. + /// An array representing the credentials and the TURN server URL. + /// is null. + internal CommunicationRelayConfiguration(DateTimeOffset expiresOn, IEnumerable turnServers) + { + if (turnServers == null) + { + throw new ArgumentNullException(nameof(turnServers)); + } + + ExpiresOn = expiresOn; + TurnServers = turnServers.ToList(); + } + + /// Initializes a new instance of CommunicationRelayConfiguration. + /// The date for which the username and credentials are not longer valid. + /// An array representing the credentials and the TURN server URL. + internal CommunicationRelayConfiguration(DateTimeOffset expiresOn, IReadOnlyList turnServers) + { + ExpiresOn = expiresOn; + TurnServers = turnServers; + } + + /// The date for which the username and credentials are not longer valid. + public DateTimeOffset ExpiresOn { get; } + /// An array representing the credentials and the TURN server URL. + public IReadOnlyList TurnServers { get; } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationTurnServer.Serialization.cs b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationTurnServer.Serialization.cs new file mode 100644 index 0000000000000..5d6e61a8353ee --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationTurnServer.Serialization.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using System.Text.Json; +using Azure.Core; + +namespace Azure.Communication.NetworkTraversal +{ + public partial class CommunicationTurnServer + { + internal static CommunicationTurnServer DeserializeCommunicationTurnServer(JsonElement element) + { + IReadOnlyList urls = default; + string username = default; + string credential = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("urls")) + { + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(item.GetString()); + } + urls = array; + continue; + } + if (property.NameEquals("username")) + { + username = property.Value.GetString(); + continue; + } + if (property.NameEquals("credential")) + { + credential = property.Value.GetString(); + continue; + } + } + return new CommunicationTurnServer(urls, username, credential); + } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationTurnServer.cs b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationTurnServer.cs new file mode 100644 index 0000000000000..16d321d2b1e7c --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/Generated/Models/CommunicationTurnServer.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Azure.Communication.NetworkTraversal +{ + /// An instance of a TURN server with credentials. + public partial class CommunicationTurnServer + { + /// Initializes a new instance of CommunicationTurnServer. + /// List of TURN server URLs. + /// User account name which uniquely identifies the credentials. + /// Credential for the server. + /// , , or is null. + internal CommunicationTurnServer(IEnumerable urls, string username, string credential) + { + if (urls == null) + { + throw new ArgumentNullException(nameof(urls)); + } + if (username == null) + { + throw new ArgumentNullException(nameof(username)); + } + if (credential == null) + { + throw new ArgumentNullException(nameof(credential)); + } + + Urls = urls.ToList(); + Username = username; + Credential = credential; + } + + /// Initializes a new instance of CommunicationTurnServer. + /// List of TURN server URLs. + /// User account name which uniquely identifies the credentials. + /// Credential for the server. + internal CommunicationTurnServer(IReadOnlyList urls, string username, string credential) + { + Urls = urls; + Username = username; + Credential = credential; + } + + /// List of TURN server URLs. + public IReadOnlyList Urls { get; } + /// User account name which uniquely identifies the credentials. + public string Username { get; } + /// Credential for the server. + public string Credential { get; } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/src/autorest.md b/sdk/communication/Azure.Communication.NetworkTraversal/src/autorest.md new file mode 100644 index 0000000000000..966eb33031494 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/src/autorest.md @@ -0,0 +1,44 @@ +# Azure.Communication.NetworkTraversal + +Run `dotnet msbuild /t:GenerateCode` to generate code. + +### AutoRest Configuration +> see https://aka.ms/autorest + +``` yaml +tag: package-2021-02-22-preview1 +require: + - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/60be518b4fa1a9fb011a0cb69ae7ca3e1cee06b1/specification/communication/data-plane/Microsoft.CommunicationServicesTurn/readme.md +payload-flattening-threshold: 3 +``` + + +### Rename OperationId to for RestClient rename + +```yaml +directive: + from: swagger-document + where: '$.paths["/turn/{id}/:issueCredentials"].post' + transform: > + $["operationId"] = "CommunicationNetworkTraversal_IssueTurnCredentials"; +``` + +### Directive renaming "CommunicationTurnCredentialsResponse" model to "CommunicationRelayConfiguration" + +```yaml +directive: + from: swagger-document + where: $.definitions.CommunicationTurnCredentialsResponse + transform: > + $["x-ms-client-name"] = "CommunicationRelayConfiguration"; +``` + +### Move all the models to the main namespace + +```yaml +directive: + from: swagger-document + where: $.definitions.* + transform: > + $["x-namespace"] = "Azure.Communication.NetworkTraversal" +``` diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/Azure.Communication.NetworkTraversal.Tests.csproj b/sdk/communication/Azure.Communication.NetworkTraversal/tests/Azure.Communication.NetworkTraversal.Tests.csproj new file mode 100644 index 0000000000000..a2f263db54425 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/Azure.Communication.NetworkTraversal.Tests.csproj @@ -0,0 +1,27 @@ + + + $(RequiredTargetFrameworks) + enable + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/CommunicationRelayClient/CommunicationRelayClientLiveTests.cs b/sdk/communication/Azure.Communication.NetworkTraversal/tests/CommunicationRelayClient/CommunicationRelayClientLiveTests.cs new file mode 100644 index 0000000000000..60d8fe1a999ad --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/CommunicationRelayClient/CommunicationRelayClientLiveTests.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading.Tasks; +using Azure.Communication.Tests; +using Azure.Communication.Identity; +using NUnit.Framework; + +namespace Azure.Communication.NetworkTraversal.Tests +{ + /// + /// The suite of tests for the class. + /// + /// + /// These tests have a dependency on live Azure services and may incur costs for the associated + /// Azure subscription. + /// + public class CommunicationRelayClientLiveTests : CommunicationRelayClientLiveTestBase + { + /// + /// Initializes a new instance of the class. + /// + /// A flag used by the Azure Core Test Framework to differentiate between tests for asynchronous and synchronous methods. + public CommunicationRelayClientLiveTests(bool isAsync) : base(isAsync) + { + } + + [Test] + [TestCase(AuthMethod.ConnectionString, TestName = "GettingTurnCredentialsWithConnectionString")] + [TestCase(AuthMethod.KeyCredential, TestName = "GettingTurnCredentialsWithKeyCredential")] + [TestCase(AuthMethod.TokenCredential, TestName = "GettingTurnCredentialsWithTokenCredential")] + public async Task GettingTurnCredentialsGeneratesTurnCredentials(AuthMethod authMethod, params string[] scopes) + { + CommunicationRelayClient client = authMethod switch + { + AuthMethod.ConnectionString => CreateClientWithConnectionString(), + AuthMethod.KeyCredential => CreateClientWithAzureKeyCredential(), + AuthMethod.TokenCredential => CreateClientWithTokenCredential(), + _ => throw new ArgumentOutOfRangeException(nameof(authMethod)), + }; + + CommunicationIdentityClient communicationIdentityClient = CreateInstrumentedCommunicationIdentityClient(); + + Response userResponse = await communicationIdentityClient.CreateUserAsync(); + Response turnCredentialsResponse = await client.GetRelayConfigurationAsync(userResponse.Value); + + Assert.IsNotNull(turnCredentialsResponse.Value); + Assert.IsNotNull(turnCredentialsResponse.Value.ExpiresOn); + Assert.IsNotNull(turnCredentialsResponse.Value.TurnServers); + foreach (CommunicationTurnServer serverCredential in turnCredentialsResponse.Value.TurnServers) + { + foreach (string url in serverCredential.Urls) + { + Assert.IsFalse(string.IsNullOrWhiteSpace(url)); + } + Assert.IsFalse(string.IsNullOrWhiteSpace(serverCredential.Username)); + Assert.IsFalse(string.IsNullOrWhiteSpace(serverCredential.Credential)); + } + } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientLiveTestBase.cs b/sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientLiveTestBase.cs new file mode 100644 index 0000000000000..22f1eb2db1c73 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientLiveTestBase.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Communication.Identity; +using Azure.Communication.Pipeline; +using Azure.Core.TestFramework; +using Azure.Identity; +using NUnit.Framework; + +namespace Azure.Communication.NetworkTraversal.Tests +{ + public class CommunicationRelayClientLiveTestBase : RecordedTestBase + { + public CommunicationRelayClientLiveTestBase(bool isAsync) : base(isAsync) + => Sanitizer = new CommunicationRelayClientRecordedTestSanitizer(); + + [OneTimeSetUp] + public void Setup() + { + if (TestEnvironment.ShouldIgnoreTests) + { + Assert.Ignore("Networking tests are skipped " + + "because networking package is not included in the TEST_PACKAGES_ENABLED variable"); + } + } + + /// + /// Creates a with the connectionstring via environment + /// variables and instruments it to make use of the Azure Core Test Framework functionalities. + /// + /// The instrumented . + protected CommunicationRelayClient CreateClientWithConnectionString() + => InstrumentClient( + new CommunicationRelayClient( + TestEnvironment.ConnectionString, + CreateNetworkingClientOptionsWithCorrelationVectorLogs())); + + protected CommunicationRelayClient CreateClientWithAzureKeyCredential() + => InstrumentClient( + new CommunicationRelayClient( + TestEnvironment.Endpoint, + new AzureKeyCredential(TestEnvironment.AccessKey), + CreateNetworkingClientOptionsWithCorrelationVectorLogs())); + + protected CommunicationRelayClient CreateClientWithTokenCredential() + => InstrumentClient( + new CommunicationRelayClient( + TestEnvironment.Endpoint, + (Mode == RecordedTestMode.Playback) ? new MockCredential() : new DefaultAzureCredential(), + CreateNetworkingClientOptionsWithCorrelationVectorLogs())); + + /// + /// Creates a with the connectionstring via environment + /// variables and instruments it to make use of the Azure Core Test Framework functionalities. + /// + /// The instrumented . + protected CommunicationIdentityClient CreateInstrumentedCommunicationIdentityClient() + => InstrumentClient( + new CommunicationIdentityClient( + TestEnvironment.ConnectionString, + InstrumentClientOptions(new CommunicationIdentityClientOptions()))); + + private CommunicationRelayClientOptions CreateNetworkingClientOptionsWithCorrelationVectorLogs() + { + CommunicationRelayClientOptions communicationNetworkingClientOptions = new CommunicationRelayClientOptions(); + communicationNetworkingClientOptions.Diagnostics.LoggedHeaderNames.Add("MS-CV"); + return InstrumentClientOptions(communicationNetworkingClientOptions); + } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientRecordedTestSanitizer.cs b/sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientRecordedTestSanitizer.cs new file mode 100644 index 0000000000000..9ad72ddde39e8 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientRecordedTestSanitizer.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.Communication.Pipeline +{ + public class CommunicationRelayClientRecordedTestSanitizer : CommunicationRecordedTestSanitizer + { + public CommunicationRelayClientRecordedTestSanitizer() + => AddJsonPathSanitizer("$..token"); + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientTestEnvironment.cs b/sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientTestEnvironment.cs new file mode 100644 index 0000000000000..27466203f6255 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/Infrastructure/CommunicationRelayClientTestEnvironment.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Communication.Tests; + +namespace Azure.Communication.NetworkTraversal.Tests +{ + public class CommunicationRelayClientTestEnvironment : CommunicationTestEnvironment + { + // please find the allowed package value in tests.yml + private const string NetworkTraversalPackagesEnabled = "networktraversal"; + public override string ExpectedTestPackagesEnabled { get { return NetworkTraversalPackagesEnabled; } } + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithConnectionString.json b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithConnectionString.json new file mode 100644 index 0000000000000..51f9134c7343f --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithConnectionString.json @@ -0,0 +1,91 @@ +{ + "Entries": [ + { + "RequestUri": "https://anpearesource1.communication.azure.com/identities?api-version=2021-03-07", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "2", + "Content-Type": "application/json", + "Date": "Fri, 14 May 2021 22:59:42 GMT", + "traceparent": "00-d508d3920c8a43418bd3c67cceb34e70-41f1eb8edc6fb046-00", + "User-Agent": [ + "azsdk-net-Communication.Identity/1.1.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "6871f17a0b73943f24d7bf1130628130", + "x-ms-content-sha256": "Sanitized", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": {}, + "StatusCode": 201, + "ResponseHeaders": { + "api-supported-versions": "2020-07-20-preview2, 2021-02-22-preview1, 2021-03-07, 2021-03-31-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:42 GMT", + "MS-CV": "i6al0T\u002BSxk2p/O8nvseXyA.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "03gCfYAAAAACJAd1OjCQeSqx\u002BPBx3ohkBV1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "6871f17a0b73943f24d7bf1130628130", + "X-Processing-Time": "71ms" + }, + "ResponseBody": { + "identity": { + "id": "8:acs:e1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-082a-47b4-a43a0d006da0" + } + } + }, + { + "RequestUri": "https://anpearesource1.communication.azure.com/turn/8%3Aacs%3Ae1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-082a-47b4-a43a0d006da0/:issueCredentials?api-version=2021-02-22-preview1", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Date": "Fri, 14 May 2021 22:59:42 GMT", + "traceparent": "00-dbc6cc38269ab94a90342490b072e7a1-9cabb521ff382c40-00", + "User-Agent": [ + "azsdk-net-Communication.NetworkTraversal/1.0.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "770284ac2cd08e315f4ded0eb88f5dcd", + "x-ms-content-sha256": "Sanitized", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": null, + "StatusCode": 200, + "ResponseHeaders": { + "api-supported-versions": "2021-02-22-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:42 GMT", + "MS-CV": "Sf6Gj1GvpECw0Lv2\u002BH4tNA.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "03gCfYAAAAAD0Od6USTdcTYIziEkw3iCuV1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "770284ac2cd08e315f4ded0eb88f5dcd", + "X-Processing-Time": "160ms" + }, + "ResponseBody": { + "expiresOn": "2021-05-14T22:59:42.5331101\u002B00:00", + "turnServers": [ + { + "urls": [ + "turn:worldaz.turn.skype.com:3478" + ], + "username": "BQAANig1RVAB10qnuoFcuVOn4A1cQ3BnpyguwLS54FoAAAAMARDhtQreHmhCb7YC/dpf2n6xaCdyQrwVlJo\u002BdUQQ6s9ZfXrpYVg=", + "credential": "Sanitized" + } + ] + } + } + ], + "Variables": { + "COMMUNICATION_CONNECTION_STRING": "endpoint=https://anpearesource1.communication.azure.com/;accesskey=Kg==", + "RandomSeed": "660110768" + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithConnectionStringAsync.json b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithConnectionStringAsync.json new file mode 100644 index 0000000000000..dee6f27300885 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithConnectionStringAsync.json @@ -0,0 +1,91 @@ +{ + "Entries": [ + { + "RequestUri": "https://anpearesource1.communication.azure.com/identities?api-version=2021-03-07", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "2", + "Content-Type": "application/json", + "Date": "Fri, 14 May 2021 22:59:44 GMT", + "traceparent": "00-ee03d6ce6df3cb4bb4a4e25d6c7970a4-6993d12f68ebdc4c-00", + "User-Agent": [ + "azsdk-net-Communication.Identity/1.1.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "05a7b0cec23b04e849833833fd4ea375", + "x-ms-content-sha256": "Sanitized", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": {}, + "StatusCode": 201, + "ResponseHeaders": { + "api-supported-versions": "2020-07-20-preview2, 2021-02-22-preview1, 2021-03-07, 2021-03-31-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:44 GMT", + "MS-CV": "jQ07upZD30iApX5xUeWxrA.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "04ACfYAAAAABVFV4NZQ1OTJoWprXQXN3aV1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "05a7b0cec23b04e849833833fd4ea375", + "X-Processing-Time": "58ms" + }, + "ResponseBody": { + "identity": { + "id": "8:acs:e1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-0fc7-47b4-a43a0d006da4" + } + } + }, + { + "RequestUri": "https://anpearesource1.communication.azure.com/turn/8%3Aacs%3Ae1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-0fc7-47b4-a43a0d006da4/:issueCredentials?api-version=2021-02-22-preview1", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Date": "Fri, 14 May 2021 22:59:44 GMT", + "traceparent": "00-1858e2a38f2285409293c9edf5d3fa54-62b6e636cb34254d-00", + "User-Agent": [ + "azsdk-net-Communication.NetworkTraversal/1.0.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "d9736c859a78e237edaa417bcb69bcc7", + "x-ms-content-sha256": "Sanitized", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": null, + "StatusCode": 200, + "ResponseHeaders": { + "api-supported-versions": "2021-02-22-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:44 GMT", + "MS-CV": "GybidwkVqkGrqcPXNHNVCQ.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "04ACfYAAAAAAq/CX3ocCzTb5i/oaJxf44V1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "d9736c859a78e237edaa417bcb69bcc7", + "X-Processing-Time": "180ms" + }, + "ResponseBody": { + "expiresOn": "2021-05-14T22:59:44.4782884\u002B00:00", + "turnServers": [ + { + "urls": [ + "turn:worldaz.turn.skype.com:3478" + ], + "username": "BQAANildRwUB10qnAopvurKQWzzZD0KIOQOI\u002BqkcruMAAAAMARDhtQreHmhCb7YC/dpf2n6x2d8sH61X4FOc3Z2pWZR74iAG2qA=", + "credential": "Sanitized" + } + ] + } + } + ], + "Variables": { + "COMMUNICATION_CONNECTION_STRING": "endpoint=https://anpearesource1.communication.azure.com/;accesskey=Kg==", + "RandomSeed": "107621483" + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithKeyCredential.json b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithKeyCredential.json new file mode 100644 index 0000000000000..527c77c1d56a1 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithKeyCredential.json @@ -0,0 +1,91 @@ +{ + "Entries": [ + { + "RequestUri": "https://anpearesource1.communication.azure.com/identities?api-version=2021-03-07", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "2", + "Content-Type": "application/json", + "Date": "Fri, 14 May 2021 22:59:42 GMT", + "traceparent": "00-4289ae00ecd3dd4ab10c8f9e3752b167-fd8696e51cc7cf4f-00", + "User-Agent": [ + "azsdk-net-Communication.Identity/1.1.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "6fe595f9798c2c2d7f3038dab4ee44fa", + "x-ms-content-sha256": "Sanitized", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": {}, + "StatusCode": 201, + "ResponseHeaders": { + "api-supported-versions": "2020-07-20-preview2, 2021-02-22-preview1, 2021-03-07, 2021-03-31-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:42 GMT", + "MS-CV": "aT54hqeqzUqFh4khvRX8NA.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "03gCfYAAAAABxJKkFecKZRpEDeWEpcVp/V1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "6fe595f9798c2c2d7f3038dab4ee44fa", + "X-Processing-Time": "59ms" + }, + "ResponseBody": { + "identity": { + "id": "8:acs:e1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-09a1-47b4-a43a0d006da1" + } + } + }, + { + "RequestUri": "https://anpearesource1.communication.azure.com/turn/8%3Aacs%3Ae1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-09a1-47b4-a43a0d006da1/:issueCredentials?api-version=2021-02-22-preview1", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Date": "Fri, 14 May 2021 22:59:42 GMT", + "traceparent": "00-b89491288c7a3e4daeceea6fc92bf383-868ae1ff7af58e44-00", + "User-Agent": [ + "azsdk-net-Communication.NetworkTraversal/1.0.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "97b36126b895b22427811ed1a0236708", + "x-ms-content-sha256": "Sanitized", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": null, + "StatusCode": 200, + "ResponseHeaders": { + "api-supported-versions": "2021-02-22-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:42 GMT", + "MS-CV": "LmJTQYRycUCYa3mLTUnLWA.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "03gCfYAAAAAAUK/qrc5YqQJ9bZ\u002BVKHlx4V1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "97b36126b895b22427811ed1a0236708", + "X-Processing-Time": "171ms" + }, + "ResponseBody": { + "expiresOn": "2021-05-14T22:59:42.9131316\u002B00:00", + "turnServers": [ + { + "urls": [ + "turn:worldaz.turn.skype.com:3478" + ], + "username": "BQAANihtqekB10qncDEecy9rsBY3oRj\u002BIQLg9DsTSuUAAAAMARDhtQreHmhCb7YC/dpf2n6xHaI1qFZS8RnYjhiYNCNH7P\u002BtzJs=", + "credential": "Sanitized" + } + ] + } + } + ], + "Variables": { + "COMMUNICATION_CONNECTION_STRING": "endpoint=https://anpearesource1.communication.azure.com/;accesskey=Kg==", + "RandomSeed": "1266706672" + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithKeyCredentialAsync.json b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithKeyCredentialAsync.json new file mode 100644 index 0000000000000..0ee2032f01025 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithKeyCredentialAsync.json @@ -0,0 +1,91 @@ +{ + "Entries": [ + { + "RequestUri": "https://anpearesource1.communication.azure.com/identities?api-version=2021-03-07", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "2", + "Content-Type": "application/json", + "Date": "Fri, 14 May 2021 22:59:44 GMT", + "traceparent": "00-e46e321e41552943922508ce0aa4554e-5f136e696e33cc42-00", + "User-Agent": [ + "azsdk-net-Communication.Identity/1.1.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "904233410bb9e287278628d15e995d09", + "x-ms-content-sha256": "Sanitized", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": {}, + "StatusCode": 201, + "ResponseHeaders": { + "api-supported-versions": "2020-07-20-preview2, 2021-02-22-preview1, 2021-03-07, 2021-03-31-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:44 GMT", + "MS-CV": "j8ngQQjP9ECvXKM4ltu/ow.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "04ACfYAAAAAAVwzd02weIS5G1k0sSAlnHV1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "904233410bb9e287278628d15e995d09", + "X-Processing-Time": "58ms" + }, + "ResponseBody": { + "identity": { + "id": "8:acs:e1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-1123-47b4-a43a0d006da7" + } + } + }, + { + "RequestUri": "https://anpearesource1.communication.azure.com/turn/8%3Aacs%3Ae1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-1123-47b4-a43a0d006da7/:issueCredentials?api-version=2021-02-22-preview1", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Date": "Fri, 14 May 2021 22:59:44 GMT", + "traceparent": "00-9eed74d8653af6439ee65e12638f8bee-fc8d5958ac147548-00", + "User-Agent": [ + "azsdk-net-Communication.NetworkTraversal/1.0.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "117faa9682ab681e5fc160daf529d60c", + "x-ms-content-sha256": "Sanitized", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": null, + "StatusCode": 200, + "ResponseHeaders": { + "api-supported-versions": "2021-02-22-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:44 GMT", + "MS-CV": "hkkZlqfkSk6FChLdlXE90w.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "04ACfYAAAAAAGvRKe7/40QJXl/k8m/vmZV1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "117faa9682ab681e5fc160daf529d60c", + "X-Processing-Time": "183ms" + }, + "ResponseBody": { + "expiresOn": "2021-05-14T22:59:44.8261839\u002B00:00", + "turnServers": [ + { + "urls": [ + "turn:worldaz.turn.skype.com:3478" + ], + "username": "BQAANimQ6z0B10qnhAxMoT3pn5NdoHaiL/kuSXbgavcAAAAMARDhtQreHmhCb7YC/dpf2n6xqiocCLYvJtJ1VXEp7yjvgVuNETQ=", + "credential": "Sanitized" + } + ] + } + } + ], + "Variables": { + "COMMUNICATION_CONNECTION_STRING": "endpoint=https://anpearesource1.communication.azure.com/;accesskey=Kg==", + "RandomSeed": "983455153" + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithTokenCredential.json b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithTokenCredential.json new file mode 100644 index 0000000000000..d0a520ee962a7 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithTokenCredential.json @@ -0,0 +1,89 @@ +{ + "Entries": [ + { + "RequestUri": "https://anpearesource1.communication.azure.com/identities?api-version=2021-03-07", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "2", + "Content-Type": "application/json", + "Date": "Fri, 14 May 2021 22:59:42 GMT", + "traceparent": "00-f685ff25bfa4f94683d1044984e275b1-6d7fbb5c80c2bc48-00", + "User-Agent": [ + "azsdk-net-Communication.Identity/1.1.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "c8da0877372dda7b1d54c338aa032636", + "x-ms-content-sha256": "Sanitized", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": {}, + "StatusCode": 201, + "ResponseHeaders": { + "api-supported-versions": "2020-07-20-preview2, 2021-02-22-preview1, 2021-03-07, 2021-03-31-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:42 GMT", + "MS-CV": "MUAqcjh5mkCqKnff9swL7w.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "03wCfYAAAAADBx2TbaF7OTq5eGPktvd3MV1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "c8da0877372dda7b1d54c338aa032636", + "X-Processing-Time": "61ms" + }, + "ResponseBody": { + "identity": { + "id": "8:acs:e1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-0b5a-47b4-a43a0d006da3" + } + } + }, + { + "RequestUri": "https://anpearesource1.communication.azure.com/turn/8%3Aacs%3Ae1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-0b5a-47b4-a43a0d006da3/:issueCredentials?api-version=2021-02-22-preview1", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "traceparent": "00-bb0dd4e04999224a9155e98d2a04d479-3d6cf75b441c284b-00", + "User-Agent": [ + "azsdk-net-Communication.NetworkTraversal/1.0.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "2887dea8f70a6f0830ec722b533da1e2", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": null, + "StatusCode": 200, + "ResponseHeaders": { + "api-supported-versions": "2021-02-22-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:43 GMT", + "MS-CV": "EaGTQ1B8mUG77B6jUtjSqA.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "03wCfYAAAAADctUkrZOthQY7nN6YQRyR1V1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "2887dea8f70a6f0830ec722b533da1e2", + "X-Processing-Time": "182ms" + }, + "ResponseBody": { + "expiresOn": "2021-05-14T22:59:44.116941\u002B00:00", + "turnServers": [ + { + "urls": [ + "turn:worldaz.turn.skype.com:3478" + ], + "username": "BQAANiklQBAB10qnTNYgSAEGqnisOToG297tC0i/vNoAAAAMARDhtQreHmhCb7YC/dpf2n6xCDhCpY7Mx3jS5zAeIFExxz6SmjU=", + "credential": "Sanitized" + } + ] + } + } + ], + "Variables": { + "COMMUNICATION_CONNECTION_STRING": "endpoint=https://anpearesource1.communication.azure.com/;accesskey=Kg==", + "RandomSeed": "1842871913" + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithTokenCredentialAsync.json b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithTokenCredentialAsync.json new file mode 100644 index 0000000000000..dc4a85b93866f --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/CommunicationRelayClientLiveTests/GettingTurnCredentialsWithTokenCredentialAsync.json @@ -0,0 +1,89 @@ +{ + "Entries": [ + { + "RequestUri": "https://anpearesource1.communication.azure.com/identities?api-version=2021-03-07", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "2", + "Content-Type": "application/json", + "Date": "Fri, 14 May 2021 22:59:44 GMT", + "traceparent": "00-9c9d852307064e4fa13649f81e683418-d3e8c5a458ec7344-00", + "User-Agent": [ + "azsdk-net-Communication.Identity/1.1.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "4d3587be19c7ff4831850f0d2948f0c5", + "x-ms-content-sha256": "Sanitized", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": {}, + "StatusCode": 201, + "ResponseHeaders": { + "api-supported-versions": "2020-07-20-preview2, 2021-02-22-preview1, 2021-03-07, 2021-03-31-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:44 GMT", + "MS-CV": "3Vyg3yaaoEWdPZunJWw4HQ.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "04ACfYAAAAACq/NvEKfaOT6adUbTBSQEBV1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "4d3587be19c7ff4831850f0d2948f0c5", + "X-Processing-Time": "68ms" + }, + "ResponseBody": { + "identity": { + "id": "8:acs:e1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-127f-47b4-a43a0d006da9" + } + } + }, + { + "RequestUri": "https://anpearesource1.communication.azure.com/turn/8%3Aacs%3Ae1b50ade-1e68-426f-b602-fdda5fda7eb1_0000000a-0cfd-127f-47b4-a43a0d006da9/:issueCredentials?api-version=2021-02-22-preview1", + "RequestMethod": "POST", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "traceparent": "00-f9fd6455c3b850448a7f439545f3e8a8-f394ff97563ff344-00", + "User-Agent": [ + "azsdk-net-Communication.NetworkTraversal/1.0.0-alpha.20210514.1", + "(.NET 5.0.6; Microsoft Windows 10.0.19042)" + ], + "x-ms-client-request-id": "a08a5477d84c4a87ffb15003af5743d3", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": null, + "StatusCode": 200, + "ResponseHeaders": { + "api-supported-versions": "2021-02-22-preview1", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 14 May 2021 22:59:44 GMT", + "MS-CV": "DiIBIY9uwUW2tx5KLKrhJw.0", + "Request-Context": "appId=", + "Strict-Transport-Security": "max-age=2592000", + "Transfer-Encoding": "chunked", + "X-Azure-Ref": "04ACfYAAAAADO52mnQpz/T6mr5olClymnV1NURURHRTA4MTAAOWZjN2I1MTktYThjYy00Zjg5LTkzNWUtYzkxNDhhZTA5ZTgx", + "X-Cache": "CONFIG_NOCACHE", + "x-ms-client-request-id": "a08a5477d84c4a87ffb15003af5743d3", + "X-Processing-Time": "157ms" + }, + "ResponseBody": { + "expiresOn": "2021-05-14T22:59:45.1652963\u002B00:00", + "turnServers": [ + { + "urls": [ + "turn:worldaz.turn.skype.com:3478" + ], + "username": "BQAANinGLn4B10qn1BQq6Mbt8S9abKp\u002Bmx9xA\u002B7QLlcAAAAMARDhtQreHmhCb7YC/dpf2n6xZmwPiXSw1h08DnZrdHvdul5W6i4=", + "credential": "Sanitized" + } + ] + } + } + ], + "Variables": { + "COMMUNICATION_CONNECTION_STRING": "endpoint=https://anpearesource1.communication.azure.com/;accesskey=Kg==", + "RandomSeed": "1627404540" + } +} diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/Sample1_CommunicationRelayClient/GetRelayConfiguration.json b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/Sample1_CommunicationRelayClient/GetRelayConfiguration.json new file mode 100644 index 0000000000000..98f9085433930 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/Sample1_CommunicationRelayClient/GetRelayConfiguration.json @@ -0,0 +1,6 @@ +{ + "Entries": [], + "Variables": { + "COMMUNICATION_CONNECTION_STRING": "endpoint=https://anpearesource1.communication.azure.com/;accesskey=Kg==" + } +} \ No newline at end of file diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/Sample1_CommunicationRelayClient/GetRelayConfigurationAsyncAsync.json b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/Sample1_CommunicationRelayClient/GetRelayConfigurationAsyncAsync.json new file mode 100644 index 0000000000000..98f9085433930 --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/SessionRecords/Sample1_CommunicationRelayClient/GetRelayConfigurationAsyncAsync.json @@ -0,0 +1,6 @@ +{ + "Entries": [], + "Variables": { + "COMMUNICATION_CONNECTION_STRING": "endpoint=https://anpearesource1.communication.azure.com/;accesskey=Kg==" + } +} \ No newline at end of file diff --git a/sdk/communication/Azure.Communication.NetworkTraversal/tests/samples/Sample1_CommunicationRelayClient.cs b/sdk/communication/Azure.Communication.NetworkTraversal/tests/samples/Sample1_CommunicationRelayClient.cs new file mode 100644 index 0000000000000..63c276a36337b --- /dev/null +++ b/sdk/communication/Azure.Communication.NetworkTraversal/tests/samples/Sample1_CommunicationRelayClient.cs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Azure.Communication.Identity; +using Azure.Communication.NetworkTraversal.Tests; +using Azure.Core.TestFramework; +using NUnit.Framework; + +#pragma warning disable IDE0059 // Unnecessary assignment of a value + +namespace Azure.Communication.NetworkTraversal.Samples +{ + /// + /// Basic Azure Communication.NetworkTraversal samples. + /// + public partial class Sample1_CommunicationRelayClient : CommunicationRelayClientLiveTestBase + { + public Sample1_CommunicationRelayClient(bool isAsync) : base(isAsync) + { + } + + [Test] + [AsyncOnly] + public async Task GetRelayConfigurationAsync() + { + var connectionString = TestEnvironment.ConnectionString; + #region Snippet:CreateCommunicationIdentityClient + // Get a connection string to our Azure Communication resource. + //@@var connectionString = ""; + var communicationIdentityClient = new CommunicationIdentityClient(connectionString); + #endregion Snippet:CreateCommunicationIdentityClient + + #region Snippet:CreateCommunicationRelayClientAsync + // Get a connection string to our Azure Communication resource. + //@@var connectionString = ""; + var client = new CommunicationRelayClient(connectionString); + #endregion Snippet:CreateCommunicationRelayClientAsync + + #region Snippet:CreateCommunicationUserAsync + Response userResponse = await communicationIdentityClient.CreateUserAsync(); + CommunicationUserIdentifier user = userResponse.Value; + Console.WriteLine($"User id: {user.Id}"); + #endregion Snippet:CreateCommunicationUserAsync + + #region Snippet:CreateTURNTokenAsync + Response turnTokenResponse = await client.GetRelayConfigurationAsync(user); + DateTimeOffset turnTokenExpiresOn = turnTokenResponse.Value.ExpiresOn; + IReadOnlyList turnServers = turnTokenResponse.Value.TurnServers; + Console.WriteLine($"Expires On: {turnTokenExpiresOn}"); + foreach (CommunicationTurnServer turnServer in turnServers) + { + foreach (string url in turnServer.Urls) + { + Console.WriteLine($"TURN Url: {url}"); + } + Console.WriteLine($"TURN Username: {turnServer.Username}"); + Console.WriteLine($"TURN Credential: {turnServer.Credential}"); + } + #endregion Snippet:CreateTURNTokenAsync 1~ + } + + [Test] + [SyncOnly] + public void GetRelayConfiguration() + { + var connectionString = TestEnvironment.ConnectionString; + #region Snippet:CreateCommunicationIdentityClient + // Get a connection string to our Azure Communication resource. + //@@var connectionString = ""; + var communicationIdentityClient = new CommunicationIdentityClient(connectionString); + #endregion Snippet:CreateCommunicationIdentityClient + + #region Snippet:CreateCommunicationRelayClientAsync + // Get a connection string to our Azure Communication resource. + //@@var connectionString = ""; + var client = new CommunicationRelayClient(connectionString); + #endregion Snippet:CreateCommunicationRelayClientAsync + + #region Snippet:CreateCommunicationUser + Response userResponse = communicationIdentityClient.CreateUser(); + CommunicationUserIdentifier user = userResponse.Value; + Console.WriteLine($"User id: {user.Id}"); + #endregion Snippet:CreateCommunicationUser + + #region Snippet:CreateTURNToken + Response turnTokenResponse = client.GetRelayConfiguration(user); + DateTimeOffset turnTokenExpiresOn = turnTokenResponse.Value.ExpiresOn; + IReadOnlyList turnServers = turnTokenResponse.Value.TurnServers; + Console.WriteLine($"Expires On: {turnTokenExpiresOn}"); + foreach (CommunicationTurnServer turnServer in turnServers) + { + foreach (string url in turnServer.Urls) + { + Console.WriteLine($"TURN Url: {url}"); + } + Console.WriteLine($"TURN Username: {turnServer.Username}"); + Console.WriteLine($"TURN Credential: {turnServer.Credential}"); + } + #endregion Snippet:CreateTURNToken + } + } +} diff --git a/sdk/communication/Azure.Communication.sln b/sdk/communication/Azure.Communication.sln index e3cec90443838..a3c7c8459dee5 100644 --- a/sdk/communication/Azure.Communication.sln +++ b/sdk/communication/Azure.Communication.sln @@ -33,6 +33,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Communication.Identit EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Communication.Identity.Tests", "Azure.Communication.Identity\tests\Azure.Communication.Identity.Tests.csproj", "{EB52F268-C8C0-4395-9925-E914923D586E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Communication.NetworkTraversal", "Azure.Communication.NetworkTraversal\src\Azure.Communication.NetworkTraversal.csproj", "{38284EE4-B29B-48B2-8CCB-1E2469BE6E2E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Communication.NetworkTraversal.Tests", "Azure.Communication.NetworkTraversal\tests\Azure.Communication.NetworkTraversal.Tests.csproj", "{AE51BEF4-AAA3-4EAB-BC11-9BFA1182A3A5}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Communication.PhoneNumbers", "Azure.Communication.PhoneNumbers\src\Azure.Communication.PhoneNumbers.csproj", "{F10D25F9-A415-43EE-8A62-76A38E7B7D5D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Communication.PhoneNumbers.Tests", "Azure.Communication.PhoneNumbers\tests\Azure.Communication.PhoneNumbers.Tests.csproj", "{7A17601B-FDEC-49E7-BFD1-7AFE9C0B6A89}" @@ -87,6 +91,14 @@ Global {EB52F268-C8C0-4395-9925-E914923D586E}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB52F268-C8C0-4395-9925-E914923D586E}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB52F268-C8C0-4395-9925-E914923D586E}.Release|Any CPU.Build.0 = Release|Any CPU + {38284EE4-B29B-48B2-8CCB-1E2469BE6E2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {38284EE4-B29B-48B2-8CCB-1E2469BE6E2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {38284EE4-B29B-48B2-8CCB-1E2469BE6E2E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {38284EE4-B29B-48B2-8CCB-1E2469BE6E2E}.Release|Any CPU.Build.0 = Release|Any CPU + {AE51BEF4-AAA3-4EAB-BC11-9BFA1182A3A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE51BEF4-AAA3-4EAB-BC11-9BFA1182A3A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE51BEF4-AAA3-4EAB-BC11-9BFA1182A3A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE51BEF4-AAA3-4EAB-BC11-9BFA1182A3A5}.Release|Any CPU.Build.0 = Release|Any CPU {F10D25F9-A415-43EE-8A62-76A38E7B7D5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F10D25F9-A415-43EE-8A62-76A38E7B7D5D}.Debug|Any CPU.Build.0 = Debug|Any CPU {F10D25F9-A415-43EE-8A62-76A38E7B7D5D}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/sdk/communication/ci.yml b/sdk/communication/ci.yml index 3e7a0d2e7710b..09346bf56d765 100644 --- a/sdk/communication/ci.yml +++ b/sdk/communication/ci.yml @@ -36,7 +36,9 @@ extends: safeName: AzureCommunicationSms - name: Azure.Communication.Identity safeName: AzureCommunicationIdentity + - name: Azure.Communication.NetworkTraversal + safeName: AzureCommunicationNetworkTraversal - name: Azure.Communication.PhoneNumbers safeName: AzureCommunicationPhoneNumbers - name: Azure.ResourceManager.Communication - safeName: AzureResourceManagerCommunication \ No newline at end of file + safeName: AzureResourceManagerCommunication