JasonShave.Azure.Communication.Service.EventHandler.CallingServer 1.0.0-alpha-2022-07-31.1

Suggested Alternatives

JasonShave.Azure.Communication.Service.EventHandler.CallAutomation

Additional Details

Product name change. Please use updated package.

This is a prerelease version of JasonShave.Azure.Communication.Service.EventHandler.CallingServer.
The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package JasonShave.Azure.Communication.Service.EventHandler.CallingServer --version 1.0.0-alpha-2022-07-31.1
NuGet\Install-Package JasonShave.Azure.Communication.Service.EventHandler.CallingServer -Version 1.0.0-alpha-2022-07-31.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="JasonShave.Azure.Communication.Service.EventHandler.CallingServer" Version="1.0.0-alpha-2022-07-31.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add JasonShave.Azure.Communication.Service.EventHandler.CallingServer --version 1.0.0-alpha-2022-07-31.1
#r "nuget: JasonShave.Azure.Communication.Service.EventHandler.CallingServer, 1.0.0-alpha-2022-07-31.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install JasonShave.Azure.Communication.Service.EventHandler.CallingServer as a Cake Addin
#addin nuget:?package=JasonShave.Azure.Communication.Service.EventHandler.CallingServer&version=1.0.0-alpha-2022-07-31.1&prerelease

// Install JasonShave.Azure.Communication.Service.EventHandler.CallingServer as a Cake Tool
#tool nuget:?package=JasonShave.Azure.Communication.Service.EventHandler.CallingServer&version=1.0.0-alpha-2022-07-31.1&prerelease

Azure Communication Services Event Handler Library

.NET

This repository contains libraries which act as a set of convenience layer services to the Azure.Communication.Service.CallingServer and Azure.Communication.Service.JobRouter libraries currently in preview.

Problem statement

A common task developers must undertake with an event-driven platform is to deal with a common event payload which wraps a variation of models often denoted with a type identifier. Consider the following event, CallConnected which is 'wrapped' in an Azure.Messaging.CloudEvent type:

Azure.Messaging.CloudEvent

{
    "id": "7dec6eed-129c-43f3-a2bf-134ac1978168",
    "source": "calling/callConnections/441f1200-fd54-422e-9566-a867d187dca7/callState",
    "type": "Microsoft.Communication.CallConnected",
    "data": {
        "callConnectionId": "441f1200-fd54-422e-9566-a867d187dca7",
        "serverCallId": "e42f9a50-c36d-493a-9cc0-2fc1cdc7b708",
        "correlationId": "7a659f41-bd0f-4bae-8ac0-6af79283e1bc"
    },
    "time": "2022-06-24T15:12:41.5556858+00:00",
    "specversion": "1.0",
    "datacontenttype": "application/json",
    "subject": "calling/callConnections/441f1200-fd54-422e-9566-a867d187dca7/callState"
}

Since this event is triggered by a web hook callback, the API controller consuming this CloudEvent type must extract the body by reading the HttpRequest object, typically asynchronously, then deserialize it to it's proper type. The CloudEvent open specification provides a type property to aid developers in understanding the data payload type. The following example illustrates the cumbersome nature of handling different data payloads in a single CloudEvent envelope:

// get body from HttpRequest
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

// deserialize into an array of CloudEvent types
CloudEvent[] cloudEvents = JsonSerializer.Deserialize<CloudEvent[]>(requestBody);

foreach(var cloudEvent in cloudEvents)
{
    // conditional logic for every possible event type
    if (cloudEvent.Type == "Microsoft.Communication.CallConnected")
    {
        var @event = JsonSerializer.Deserialize<CallConnected>(cloudEvent.Data);
        // now you can invoke your action based on this event    
    }
}

This conditional logic handling needs to be done by every customer for every possible event type which turns the focus of the developer away from their business problem and concerns them with the non-functional challenges.

CallingServer and/or JobRouter Configuration

The following NuGet packages are available depending on if you want to handle CallingServer events, JobRouter events, or both.

Package Latest Details
EventHandler.CallingServer Nuget Used with ACS ServerCalling SDK
EventHandler.JobRouter Nuget Used with ACS JobRouter SDK

For a typical .NET 6 web application, the following configuration can be made to wire up the publishers, event catalog, dispatcher, and allow you to subscribe to events from either platform.

  1. Add the following to your .NET 6 or higher Program.cs file:

    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddEventHandlerServices() //<--adds common event handling services
        .AddCallingServerEventHandling() //<--adds support for CallingServer SDK events
        .AddJobRouterEventHandling(); //<--adds support for JobRouter SDK events
    
    var app = builder.Build();
    app.Run();
    

Publishing CloudEvents and EventGridEvents

Leveraging .NET's dependency injection framework, add the IEventPublisher<Calling> or IEventPublisher<Router> to push Azure.Messaging.CloudEvent and Azure.Messaging.EventGrid messages into the services where their types are automatically cast, deserialized, and the correct event handler is invoked.

// .NET 6 'minimal API' to handle JobRouter Event Grid HTTP web hook subscription
app.MapPost("/api/jobRouter", (
    [FromBody] EventGridEvent[] eventGridEvents,
    [FromServices] IEventPublisher<Router> publisher) =>
{
    foreach (var eventGridEvent in eventGridEvents)
    {
        publisher.Publish(eventGridEvent.Data.ToString(), eventGridEvent.EventType);
    }

    return Results.Ok();
}).Produces(StatusCodes.Status200OK);

// .NET 6 'minimal API' to handle mid-call web hook callbacks from CallingServer
app.MapPost("/api/calls/{contextId}", (
    [FromBody] CloudEvent[] cloudEvent,
    [FromRoute] string contextId,
    [FromServices] IEventPublisher<Calling> publisher) =>
{
    foreach (var @event in cloudEvent)
    {
        publisher.Publish(@event.Data.ToString(), @event.Type, contextId);
    }

    return Results.Ok();
}).Produces(StatusCodes.Status200OK);

Subscribing to CallingServer and JobRouter events

As mentioned above, the CloudEvent or EventGridEvent is pushed and the corresponding C# event is invoked and subscribed to. For the Calling Server SDK, incoming calls are delivered using Event Grid and mid-call events are delivered through web hook callbacks. Job Router uses Event Grid to deliver all events. The example below shows a .NET background service wiring up the event handler as follows:

public class MyService : BackgroundService
{
    public MyService(
        ICallingServerEventSubscriber callingServerSubscriber,
        IJobRouterEventSubscriber jobRouterSubscriber)
    {
        // subscribe to Calling Server and Job Router events
        callingServerSubscriber.OnCallConnected += HandleCallConnected;
        jobRouterSubscriber.OnJobQueued += HandleJobQueued;
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            Task.Delay(1000, cancellationToken);
        }
    }

    private async Task HandleCallConnected(CallConnected callConnected, string contextId) =>
        _logger.LogInformation($"Call connection ID: {callConnected.CallConnectionId} | Context: {contextId}");

    private Task HandleJobQueued(RouterJobQueued jobQueued, string contextId) =>
        _logger.LogInformation($"Job {jobQueued.JobId} in queue {jobQueued.QueueId}")
}

License

This project is licensed under the MIT License - see the LICENSE.md file for details.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated

Initial release.