Skip to content

Commit

Permalink
[Communication] - Azure.Communication.NetworkTraversal - Adding the A…
Browse files Browse the repository at this point in the history
…CS NetworkTraversal SDK (Azure#21094)

* Adding the Azure.Communication.NetworkTraversal SDK

* Adding the Azure.Communication.NetworkTraversal SDK - update
  • Loading branch information
ajpeacock0 authored May 19, 2021
1 parent 2103c38 commit 2032102
Show file tree
Hide file tree
Showing 37 changed files with 2,372 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Release History

## 1.0.0-beta.1 (2021-05-25)

This is the first release of Azure Communication Services for Network Traversal. For more information, please see the [README][read_me].

This is a Public Preview version, so breaking changes are possible in subsequent releases as we improve the product. To provide feedback, please submit an issue in our [Azure SDK for .NET GitHub repo](https://github.com/Azure/azure-sdk-for-net/issues).

<!-- LINKS -->
<!--[read_me]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/communication/Azure.Communication.NetworkTraversal/README.md-->
117 changes: 117 additions & 0 deletions sdk/communication/Azure.Communication.NetworkTraversal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Azure Communication Network Traversal client library for .NET

Azure Communication Network Traversal is managing tokens for Azure Communication Services.

[Source code][source] <!--| [Package (NuGet)][package]--> | [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 = "<connection_string>";
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:CreateCommunicationRelayFromAccessKey
var endpoint = new Uri("https://my-resource.communication.azure.com");
var accessKey = "<access_key>";
var client = new CommunicationRelayClient(endpoint, new AzureKeyCredential(accessKey));
```

Clients also have the option to authenticate using a valid Active Directory token.

```C# Snippet:CreateCommunicationRelayFromToken
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 COMMON BAR -->
[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/)
<!-- CLIENT COMMON BAR -->

## Examples

## Generating TURN credentials for a user

```C# Snippet:GetRelayConfigurationAsync
Response<CommunicationRelayConfiguration> turnTokenResponse = await client.GetRelayConfigurationAsync(user);
DateTimeOffset turnTokenExpiresOn = turnTokenResponse.Value.ExpiresOn;
IReadOnlyList<CommunicationTurnServer> 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}");
}
```

## Troubleshooting

> TODO
## Next steps

> TODO
## 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 [[email protected]][coc_contact] with any additional questions or comments.

<!-- LINKS -->

[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:[email protected]
<!--[package]: https://www.nuget.org/packages/Azure.Communication.NetworkTraversal-->
[product_docs]: https://docs.microsoft.com/azure/communication-services/overview
[nuget]: https://www.nuget.org/
[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
Original file line number Diff line number Diff line change
@@ -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<Azure.Communication.NetworkTraversal.CommunicationRelayConfiguration> GetRelayConfiguration(Azure.Communication.CommunicationUserIdentifier communicationUser, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task<Azure.Response<Azure.Communication.NetworkTraversal.CommunicationRelayConfiguration>> 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.NetworkTraversal.CommunicationRelayClientOptions.ServiceVersion.V2021_02_22_preview1) { }
public enum ServiceVersion
{
V2021_02_22_preview1 = 1,
}
}
public partial class CommunicationRelayConfiguration
{
internal CommunicationRelayConfiguration() { }
public System.DateTimeOffset ExpiresOn { get { throw null; } }
public System.Collections.Generic.IReadOnlyList<Azure.Communication.NetworkTraversal.CommunicationTurnServer> TurnServers { get { throw null; } }
}
public partial class CommunicationTurnServer
{
internal CommunicationTurnServer() { }
public string Credential { get { throw null; } }
public System.Collections.Generic.IReadOnlyList<string> Urls { get { throw null; } }
public string Username { get { throw null; } }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>
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.
</Description>
<AssemblyTitle>Azure Communication Network Traversal Service</AssemblyTitle>
<Version>1.0.0-beta.1</Version>
<PackageTags>Microsoft Azure Communication Network Traversal Service;Microsoft;Azure;Azure Communication Service;Azure Communication Network Traversal Service;Network Traversal;Communication;$(PackageCommonTags)</PackageTags>
<TargetFrameworks>$(RequiredTargetFrameworks)</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Core" />
<PackageReference Include="System.Text.Json" />
</ItemGroup>

<!-- Shared source from Azure.Core -->
<ItemGroup>
<Compile Include="..\..\Shared\src\ClientOptionsExtensions.cs" Link="Shared\Communication\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="..\..\Shared\src\HMACAuthenticationPolicy.cs" Link="Shared\Communication\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="..\..\Shared\Properties\CommunicationAssembyInfo.cs" Link="Shared\Communication\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)AzureResourceProviderNamespaceAttribute.cs" Link="Shared\Core\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)Argument.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)ArrayBufferWriter.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)ClientDiagnostics.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)ConnectionString.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)ContentTypeUtilities.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)DiagnosticScope.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)DiagnosticScopeFactory.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)HttpMessageSanitizer.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)TaskExtensions.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Communication.Common" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// The Azure Communication Services Networking client.
/// </summary>
public class CommunicationRelayClient
{
private readonly ClientDiagnostics _clientDiagnostics;
internal CommunicationNetworkTraversalRestClient RestClient { get; }

#region public constructors - all argument need null check

/// <summary> Initializes a new instance of <see cref="CommunicationRelayClient"/>.</summary>
/// <param name="connectionString">Connection string acquired from the Azure Communication Services resource.</param>
public CommunicationRelayClient(string connectionString)
: this(
ConnectionString.Parse(AssertNotNullOrEmpty(connectionString, nameof(connectionString))),
new CommunicationRelayClientOptions())
{ }

/// <summary> Initializes a new instance of <see cref="CommunicationRelayClient"/>.</summary>
/// <param name="connectionString">Connection string acquired from the Azure Communication Services resource.</param>
/// <param name="options">Client option exposing <see cref="ClientOptions.Diagnostics"/>, <see cref="ClientOptions.Retry"/>, <see cref="ClientOptions.Transport"/>, etc.</param>
public CommunicationRelayClient(string connectionString, CommunicationRelayClientOptions options)
: this(
ConnectionString.Parse(AssertNotNullOrEmpty(connectionString, nameof(connectionString))),
options ?? new CommunicationRelayClientOptions())
{ }

/// <summary> Initializes a new instance of <see cref="CommunicationRelayClient"/>.</summary>
/// <param name="endpoint">The URI of the Azure Communication Services resource.</param>
/// <param name="keyCredential">The <see cref="AzureKeyCredential"/> used to authenticate requests.</param>
/// <param name="options">Client option exposing <see cref="ClientOptions.Diagnostics"/>, <see cref="ClientOptions.Retry"/>, <see cref="ClientOptions.Transport"/>, etc.</param>
public CommunicationRelayClient(Uri endpoint, AzureKeyCredential keyCredential, CommunicationRelayClientOptions options = default)
: this(
AssertNotNull(endpoint, nameof(endpoint)).AbsoluteUri,
AssertNotNull(keyCredential, nameof(keyCredential)),
options ?? new CommunicationRelayClientOptions())
{ }

/// <summary> Initializes a new instance of <see cref="CommunicationRelayClient"/>.</summary>
/// <param name="endpoint">The URI of the Azure Communication Services resource.</param>
/// <param name="tokenCredential">The <see cref="TokenCredential"/> used to authenticate requests, such as DefaultAzureCredential.</param>
/// <param name="options">Client option exposing <see cref="ClientOptions.Diagnostics"/>, <see cref="ClientOptions.Retry"/>, <see cref="ClientOptions.Transport"/>, etc.</param>
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

/// <summary>Initializes a new instance of <see cref="CommunicationRelayClient"/> for mocking.</summary>
protected CommunicationRelayClient()
{
_clientDiagnostics = null;
RestClient = null;
}

/// <summary>Gets a TURN credential for a <see cref="CommunicationUserIdentifier"/>.</summary>
/// <param name="communicationUser">The <see cref="CommunicationUserIdentifier"/> for whom to issue a token.</param>
/// <param name="cancellationToken">The cancellation token to use.</param>
/// <exception cref="RequestFailedException">The server returned an error.</exception>
public virtual Response<CommunicationRelayConfiguration> 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;
}
}

/// <summary>Asynchronously gets a TURN credential for a <see cref="CommunicationUserIdentifier"/>.</summary>
/// <param name="communicationUser">The <see cref="CommunicationUserIdentifier"/> for whom to issue a token.</param>
/// <param name="cancellationToken">The cancellation token to use.</param>
public virtual async Task<Response<CommunicationRelayConfiguration>> 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>(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;
}
}
}
Loading

0 comments on commit 2032102

Please sign in to comment.