Skip to content

Commit

Permalink
Azure function app for outbound call using calling server SDK and sen…
Browse files Browse the repository at this point in the history
…d SMS (#24)

* Azure function for outbound call using calling server SDK and send SMS

* Adding lincense info
  • Loading branch information
ravithanneeru authored Nov 2, 2021
1 parent 19169bb commit 1c88634
Show file tree
Hide file tree
Showing 11 changed files with 592 additions and 0 deletions.
25 changes: 25 additions & 0 deletions OutboundCallReminder/OutboundFunction/OutboundFunction.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31729.503
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OutboundFunction", "OutboundFunction\OutboundFunction.csproj", "{5377DED5-CE39-4905-9FA5-697EF7B4E32B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5377DED5-CE39-4905-9FA5-697EF7B4E32B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5377DED5-CE39-4905-9FA5-697EF7B4E32B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5377DED5-CE39-4905-9FA5-697EF7B4E32B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5377DED5-CE39-4905-9FA5-697EF7B4E32B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {474F22B7-B653-46F8-9D37-A3E7CD747370}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace OutboundFunction
{
using Microsoft.AspNetCore.Http;
using System;
using System.Web;

public class EventAuthHandler
{
private static readonly string SecretKey = "secret";
private static readonly string SecretValue;

static EventAuthHandler()
{
SecretValue = Environment.GetEnvironmentVariable("SecretPlaceholder") ?? "h3llowW0rld";
}

public static bool Authorize(HttpRequest request)
{
string secretKeyValue = request.Query[SecretKey].ToString();
return !string.IsNullOrEmpty(secretKeyValue) && secretKeyValue.Equals(SecretValue);
}

public static string GetSecretQuerystring => $"{SecretKey}={HttpUtility.UrlEncode(SecretValue)}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace OutboundFunction
{
using Azure.Communication.CallingServer;
using Azure.Messaging;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class EventDispatcher
{
public static readonly EventDispatcher Instance;
private static readonly ConcurrentDictionary<string, NotificationCallback> NotificationCallback;
private readonly object SubscriptionLock = new object();

static EventDispatcher()
{
Instance = new EventDispatcher();
NotificationCallback = new ConcurrentDictionary<string, NotificationCallback>(StringComparer.OrdinalIgnoreCase);
}

public bool Subscribe(string eventType, string eventKey, NotificationCallback notificationCallback)
{
string eventId = BuildEventKey(eventType, eventKey);

lock (this.SubscriptionLock)
{
return NotificationCallback.TryAdd(eventId, notificationCallback);
}
}

public void Unsubscribe(string eventType, string eventKey)
{
string eventId = BuildEventKey(eventType, eventKey);

lock (this.SubscriptionLock)
{
NotificationCallback.TryRemove(eventId, out _);
}
}

public void ProcessNotification(string request)
{
var callEvent = this.ExtractEvent(request);

if (callEvent != null)
{
lock (SubscriptionLock)
{
if (NotificationCallback.TryGetValue(GetEventKey(callEvent), out NotificationCallback notificationCallback))
{
if (notificationCallback != null)
{
Task.Run(() =>
{
notificationCallback.Callback.Invoke(callEvent);
});
}
}
}
}
}

public string GetEventKey(CallingServerEventBase callEventBase)
{
if (callEventBase is CallConnectionStateChangedEvent)
{
var callLegId = ((CallConnectionStateChangedEvent)callEventBase).CallConnectionId;
return BuildEventKey(CallingServerEventType.CallConnectionStateChangedEvent.ToString(), callLegId);;
}

return null;
}

public string BuildEventKey(string eventType, string eventKey)
{
return $"{eventType}-{eventKey}";
}

/// <summary>
/// Extracting event from the json.
/// </summary>
/// <param name="content"></param>
/// <returns></returns>
public CallingServerEventBase ExtractEvent(string content)
{
CloudEvent cloudEvent = CloudEvent.Parse(BinaryData.FromString(content));

if (cloudEvent != null && cloudEvent.Data != null)
{
if (cloudEvent.Type.Equals(CallingServerEventType.CallConnectionStateChangedEvent.ToString()))
{
return CallConnectionStateChangedEvent.Deserialize(cloudEvent.Data.ToString());
}
}

return null;
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace OutboundFunction
{
using Azure.Communication.CallingServer;
using System;

public class NotificationCallback
{
public Action<CallingServerEventBase> Callback { get; set; }

public NotificationCallback(Action<CallingServerEventBase> callBack)
{
this.Callback = callBack;
}
}
}
35 changes: 35 additions & 0 deletions OutboundCallReminder/OutboundFunction/OutboundFunction/Logger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.Logging;

namespace OutboundFunction
{
//Caution: Logging should be removed/disabled if you want to use this sample in production to avoid exposing sensitive information
public static class Logger
{
public enum MessageType
{
INFORMATION,
ERROR
}

public static ILogger logger = null;

/// <summary>
/// Log message to console
/// </summary>
/// <param name="messageType">Type of the message: Information or Error</param>
/// <param name="message">Message string</param>
public static void LogMessage(MessageType messageType, string message)
{
if(messageType == MessageType.ERROR)
logger.LogError(message);
else
logger.LogInformation(message);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace OutboundFunction
{
public static class OutboundController
{
[FunctionName("OutboundController")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, new string[] { "get", "post" }, Route = "outboundcall/callback")] HttpRequest req,
ILogger log)
{
try
{
if (EventAuthHandler.Authorize(req))
{
// handling callbacks
var data = new StreamReader(req.Body).ReadToEndAsync().Result;

if (!string.IsNullOrEmpty(data))
{
log.LogInformation($"Getting callback with req data --> {data}");
Task.Run(() =>
{
EventDispatcher.Instance.ProcessNotification(data);
});
}
}
}
catch (Exception ex)
{
log.LogError($"Send Notification failed with exception --> {ex.Message}");
}
return new OkObjectResult("OK");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Communication.CallingServer" Version="1.0.0-beta.3" />
<PackageReference Include="Azure.Communication.Identity" Version="1.0.1" />
<PackageReference Include="Azure.Communication.Sms" Version="1.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.30" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.12" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.11" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
Loading

0 comments on commit 1c88634

Please sign in to comment.