src/WebJobs.Extensions.Twilio/Config/TwilioExtensionConfigProvider.cs (153 lines of code) (raw):
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Microsoft.Azure.WebJobs.Description;
using Microsoft.Azure.WebJobs.Extensions.Bindings;
using Microsoft.Azure.WebJobs.Host.Config;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using Twilio.Clients;
using Twilio.Rest.Api.V2010.Account;
using Twilio.Types;
namespace Microsoft.Azure.WebJobs.Extensions.Twilio
{
[Extension("Twilio")]
internal class TwilioExtensionConfigProvider : IExtensionConfigProvider
{
internal const string AzureWebJobsTwilioAccountSidKeyName = "AzureWebJobsTwilioAccountSid";
internal const string AzureWebJobsTwilioAccountAuthTokenName = "AzureWebJobsTwilioAuthToken";
private readonly IOptions<TwilioSmsOptions> _options;
private readonly ConcurrentDictionary<Tuple<string, string>, TwilioRestClient> _twilioClientCache =
new ConcurrentDictionary<Tuple<string, string>, TwilioRestClient>();
public TwilioExtensionConfigProvider(IOptions<TwilioSmsOptions> options)
{
_options = options;
}
public void Initialize(ExtensionConfigContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
context.AddConverter<JObject, CreateMessageOptions>(CreateMessageOptions);
var rule = context.AddBindingRule<TwilioSmsAttribute>();
rule.AddValidator(ValidateBinding);
rule.BindToCollector<CreateMessageOptions>((attr) =>
{
return new TwilioSmsMessageAsyncCollector(CreateContext(attr));
});
}
private void ValidateBinding(TwilioSmsAttribute attribute, Type type)
{
string accountSid = Utility.FirstOrDefault(attribute.AccountSidSetting, _options.Value.AccountSid);
string authToken = Utility.FirstOrDefault(attribute.AuthTokenSetting, _options.Value.AuthToken);
if (string.IsNullOrEmpty(accountSid))
{
ThrowMissingSettingException("AccountSID", AzureWebJobsTwilioAccountSidKeyName, "AccountSID");
}
if (string.IsNullOrEmpty(authToken))
{
ThrowMissingSettingException("AuthToken", AzureWebJobsTwilioAccountAuthTokenName, "AuthToken");
}
}
private TwilioSmsContext CreateContext(TwilioSmsAttribute attribute)
{
string accountSid = Utility.FirstOrDefault(attribute.AccountSidSetting, _options.Value.AccountSid);
string authToken = Utility.FirstOrDefault(attribute.AuthTokenSetting, _options.Value.AuthToken);
TwilioRestClient client = _twilioClientCache.GetOrAdd(new Tuple<string, string>(accountSid, authToken),
t => new TwilioRestClient(t.Item1, t.Item2));
var context = new TwilioSmsContext
{
Client = client,
Body = Utility.FirstOrDefault(attribute.Body, _options.Value.Body),
From = Utility.FirstOrDefault(attribute.From, _options.Value.From),
To = Utility.FirstOrDefault(attribute.To, _options.Value.To)
};
return context;
}
internal static CreateMessageOptions CreateMessageOptions(JObject messageOptions)
{
var options = new CreateMessageOptions(new PhoneNumber(GetValueOrDefault<string>(messageOptions, "to")))
{
Body = GetValueOrDefault<string>(messageOptions, "body"),
ForceDelivery = GetValueOrDefault<bool?>(messageOptions, "forceDelivery"),
ValidityPeriod = GetValueOrDefault<int?>(messageOptions, "validityPeriod"),
ProvideFeedback = GetValueOrDefault<bool?>(messageOptions, "provideFeedback"),
MaxPrice = GetValueOrDefault<decimal?>(messageOptions, "maxPrice"),
ApplicationSid = GetValueOrDefault<string>(messageOptions, "applicationSid"),
MessagingServiceSid = GetValueOrDefault<string>(messageOptions, "messagingServiceSid"),
PathAccountSid = GetValueOrDefault<string>(messageOptions, "pathAccountSid"),
Attempt = GetValueOrDefault<int?>(messageOptions, "attempt"),
SmartEncoded = GetValueOrDefault<bool?>(messageOptions, "smartEncoded"),
ShortenUrls = GetValueOrDefault<bool?>(messageOptions, "shortenUrls"),
SendAt = GetValueOrDefault<DateTime?>(messageOptions, "sendAt"),
SendAsMms = GetValueOrDefault<bool?>(messageOptions, "sendAsMms"),
ContentSid = GetValueOrDefault<string>(messageOptions, "contentSid"),
ContentVariables = GetValueOrDefault<string>(messageOptions, "contentVariables")
};
string value = GetValueOrDefault<string>(messageOptions, "from");
if (!string.IsNullOrEmpty(value))
{
options.From = new PhoneNumber(value);
}
value = GetValueOrDefault<string>(messageOptions, "statusCallback");
if (!string.IsNullOrEmpty(value))
{
options.StatusCallback = new Uri(value);
}
JArray mediaUrls = GetValueOrDefault<JArray>(messageOptions, "mediaUrl");
if (mediaUrls != null)
{
List<Uri> uris = new List<Uri>();
foreach (var url in mediaUrls)
{
uris.Add(new Uri((string)url));
}
options.MediaUrl = uris;
}
var contentRetention = GetValueOrDefault<string>(messageOptions, "ContentRetention");
if (!string.IsNullOrEmpty(contentRetention))
{
options.ContentRetention = contentRetention;
}
var addressRetention = GetValueOrDefault<string>(messageOptions, "addressRetention");
if (!string.IsNullOrEmpty(addressRetention))
{
options.AddressRetention = addressRetention;
}
var persistentActions = GetValueOrDefault<JArray>(messageOptions, "persistentAction");
if (persistentActions != null)
{
var actions = new List<string>();
foreach (var action in persistentActions)
{
actions.Add((string)action);
}
options.PersistentAction = actions;
}
var scheduleType = GetValueOrDefault<string>(messageOptions, "scheduleType");
if (!string.IsNullOrEmpty(scheduleType))
{
options.ScheduleType = scheduleType;
}
return options;
}
private static TValue GetValueOrDefault<TValue>(JObject messageObject, string propertyName)
{
if (messageObject.TryGetValue(propertyName, StringComparison.OrdinalIgnoreCase, out JToken result))
{
return result.Value<TValue>();
}
return default(TValue);
}
private static string ThrowMissingSettingException(string settingDisplayName, string settingName,
string configPropertyName)
{
string message = string.Format(
"The Twilio {0} must be set either via a '{1}' app setting, via a '{1}' environment variable, or directly in code via TwilioSmsConfiguration.{2}.",
settingDisplayName, settingName, configPropertyName);
throw new InvalidOperationException(message);
}
}
}