diff --git a/samples/ingestion/ingestion-client/FetchTranscription/Config/AppConfig.cs b/samples/ingestion/ingestion-client/FetchTranscription/Config/AppConfig.cs new file mode 100644 index 000000000..e7602d7d6 --- /dev/null +++ b/samples/ingestion/ingestion-client/FetchTranscription/Config/AppConfig.cs @@ -0,0 +1,109 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.md file in the project root for full license information. +// + +namespace FetchTranscription +{ + using System; + using Connector.Constants; + using Connector.Enums; + using Connector.Serializable.Language.Conversations; + + public class AppConfig + { + private ConversationSummarizationOptions conversationSummarizationOptions; + + private int initialPollingDelayInMinutes = Constants.DefaultInitialPollingDelayInMinutes; + + private int retryLimit = Constants.DefaultRetryLimit; + private int maxPollingDelayInMinutes = Constants.DefaultMaxPollingDelayInMinutes; + + public SentimentAnalysisSetting SentimentAnalysisSetting { get; set; } = SentimentAnalysisSetting.None; + + public PiiRedactionSetting PiiRedactionSetting { get; set; } = PiiRedactionSetting.None; + + public bool CreateHtmlResultFile { get; set; } + + public ConversationPiiSetting ConversationPiiSetting { get; set; } = ConversationPiiSetting.None; + + public string ConversationPiiCategories { get; set; } + + public string ConversationPiiInferenceSource { get; set; } + + public int ConversationPiiMaxChunkSize { get; set; } = Constants.DefaultConversationAnalysisMaxChunkSize; + + public ConversationSummarizationOptions ConversationSummarizationOptions + { + get + { + if (this.conversationSummarizationOptions == null) + { + var envVarValue = Environment.GetEnvironmentVariable(nameof(this.ConversationSummarizationOptions), EnvironmentVariableTarget.Process); + this.conversationSummarizationOptions = string.IsNullOrEmpty(envVarValue) + ? new ConversationSummarizationOptions() + : System.Text.Json.JsonSerializer.Deserialize(envVarValue); + } + + return this.conversationSummarizationOptions; + } + + set + { + this.conversationSummarizationOptions = value; + } + } + + public bool UseSqlDatabase { get; set; } + + public int InitialPollingDelayInMinutes + { + get => this.initialPollingDelayInMinutes; + set => this.initialPollingDelayInMinutes = Math.Clamp(value, 2, Constants.MaxInitialPollingDelayInMinutes); + } + + public int MaxPollingDelayInMinutes { get; set; } = Constants.DefaultMaxPollingDelayInMinutes; + + public int RetryLimit + { + get => this.retryLimit; + set => this.retryLimit = Math.Clamp(value, 1, Constants.MaxRetryLimit); + } + + public string AudioInputContainer { get; set; } + + public string AzureSpeechServicesKey { get; set; } + + public string AzureWebJobsStorage { get; set; } + + public string DatabaseConnectionString { get; set; } + + public string ErrorFilesOutputContainer { get; set; } + + public string ErrorReportOutputContainer { get; set; } + + public string FetchTranscriptionServiceBusConnectionString { get; set; } + + public string CompletedServiceBusConnectionString { get; set; } + + public string HtmlResultOutputContainer { get; set; } + + public string JsonResultOutputContainer { get; set; } + + public string StartTranscriptionServiceBusConnectionString { get; set; } + + public string TextAnalyticsKey { get; set; } + + public string TextAnalyticsEndpoint { get; set; } + + public string PiiCategories { get; set; } + + public bool CreateConsolidatedOutputFiles { get; set; } + + public string ConsolidatedFilesOutputContainer { get; set; } + + public bool CreateAudioProcessedContainer { get; set; } + + public string AudioProcessedContainer { get; set; } + } +} \ No newline at end of file diff --git a/samples/ingestion/ingestion-client/FetchTranscription/FetchTranscription.cs b/samples/ingestion/ingestion-client/FetchTranscription/FetchTranscription.cs index 5b0e49004..b48112db2 100644 --- a/samples/ingestion/ingestion-client/FetchTranscription/FetchTranscription.cs +++ b/samples/ingestion/ingestion-client/FetchTranscription/FetchTranscription.cs @@ -16,6 +16,7 @@ namespace FetchTranscription using Microsoft.Extensions.Azure; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; /// /// Fetch Transcription class. @@ -26,6 +27,7 @@ public class FetchTranscription private readonly IStorageConnector storageConnector; private readonly IAzureClientFactory serviceBusClientFactory; private readonly ILogger logger; + private readonly AppConfig appConfig; /// /// Initializes a new instance of the class. @@ -34,16 +36,19 @@ public class FetchTranscription /// The FetchTranscription logger. /// Storage Connector dependency /// Azure client factory for service bus clients + /// Environment configuration public FetchTranscription( IServiceProvider serviceProvider, ILogger logger, IStorageConnector storageConnector, - IAzureClientFactory serviceBusClientFactory) + IAzureClientFactory serviceBusClientFactory, + IOptions appConfig) { this.serviceProvider = serviceProvider; this.logger = logger; this.storageConnector = storageConnector; this.serviceBusClientFactory = serviceBusClientFactory; + this.appConfig = appConfig?.Value; } /// @@ -65,9 +70,9 @@ public async Task Run([ServiceBusTrigger("fetch_transcription_queue", Connection var serviceBusMessage = TranscriptionStartedMessage.DeserializeMessage(message); - var databaseContext = FetchTranscriptionEnvironmentVariables.UseSqlDatabase ? this.serviceProvider.GetRequiredService() : null; + var databaseContext = this.appConfig.UseSqlDatabase ? this.serviceProvider.GetRequiredService() : null; - var transcriptionProcessor = new TranscriptionProcessor(this.storageConnector, this.serviceBusClientFactory, databaseContext); + var transcriptionProcessor = new TranscriptionProcessor(this.storageConnector, this.serviceBusClientFactory, databaseContext, Options.Create(this.appConfig)); await transcriptionProcessor.ProcessTranscriptionJobAsync(serviceBusMessage, this.serviceProvider, this.logger).ConfigureAwait(false); } diff --git a/samples/ingestion/ingestion-client/FetchTranscription/FetchTranscriptionEnvironmentVariables.cs b/samples/ingestion/ingestion-client/FetchTranscription/FetchTranscriptionEnvironmentVariables.cs deleted file mode 100644 index 724982ae6..000000000 --- a/samples/ingestion/ingestion-client/FetchTranscription/FetchTranscriptionEnvironmentVariables.cs +++ /dev/null @@ -1,76 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE.md file in the project root for full license information. -// - -namespace FetchTranscription -{ - using System; - using Connector; - using Connector.Constants; - using Connector.Enums; - using Connector.Serializable.Language.Conversations; - - public static class FetchTranscriptionEnvironmentVariables - { - public static readonly SentimentAnalysisSetting SentimentAnalysisSetting = Enum.TryParse(Environment.GetEnvironmentVariable(nameof(SentimentAnalysisSetting), EnvironmentVariableTarget.Process), out SentimentAnalysisSetting) ? SentimentAnalysisSetting : SentimentAnalysisSetting.None; - - public static readonly PiiRedactionSetting PiiRedactionSetting = Enum.TryParse(Environment.GetEnvironmentVariable(nameof(PiiRedactionSetting), EnvironmentVariableTarget.Process), out PiiRedactionSetting) ? PiiRedactionSetting : PiiRedactionSetting.None; - - public static readonly bool CreateHtmlResultFile = bool.TryParse(Environment.GetEnvironmentVariable(nameof(CreateHtmlResultFile), EnvironmentVariableTarget.Process), out CreateHtmlResultFile) && CreateHtmlResultFile; - - public static readonly ConversationPiiSetting ConversationPiiSetting = Enum.TryParse(Environment.GetEnvironmentVariable(nameof(ConversationPiiSetting), EnvironmentVariableTarget.Process), out ConversationPiiSetting) ? ConversationPiiSetting : ConversationPiiSetting.None; - - public static readonly string ConversationPiiCategories = Environment.GetEnvironmentVariable(nameof(ConversationPiiCategories), EnvironmentVariableTarget.Process); - - public static readonly string ConversationPiiInferenceSource = Environment.GetEnvironmentVariable(nameof(ConversationPiiInferenceSource), EnvironmentVariableTarget.Process); - - public static readonly int ConversationPiiMaxChunkSize = int.TryParse(Environment.GetEnvironmentVariable(nameof(ConversationPiiMaxChunkSize), EnvironmentVariableTarget.Process), out ConversationPiiMaxChunkSize) ? ConversationPiiMaxChunkSize : Constants.DefaultConversationAnalysisMaxChunkSize; - - public static readonly ConversationSummarizationOptions ConversationSummarizationOptions = string.IsNullOrEmpty(Environment.GetEnvironmentVariable(nameof(ConversationSummarizationOptions), EnvironmentVariableTarget.Process)) ? new ConversationSummarizationOptions() : System.Text.Json.JsonSerializer.Deserialize(Environment.GetEnvironmentVariable(nameof(ConversationSummarizationOptions), EnvironmentVariableTarget.Process)); - - public static readonly bool UseSqlDatabase = bool.TryParse(Environment.GetEnvironmentVariable(nameof(UseSqlDatabase), EnvironmentVariableTarget.Process), out UseSqlDatabase) && UseSqlDatabase; - - public static readonly int InitialPollingDelayInMinutes = int.TryParse(Environment.GetEnvironmentVariable(nameof(InitialPollingDelayInMinutes), EnvironmentVariableTarget.Process), out InitialPollingDelayInMinutes) ? InitialPollingDelayInMinutes.ClampInt(2, Constants.MaxInitialPollingDelayInMinutes) : Constants.DefaultInitialPollingDelayInMinutes; - - public static readonly int MaxPollingDelayInMinutes = int.TryParse(Environment.GetEnvironmentVariable(nameof(MaxPollingDelayInMinutes), EnvironmentVariableTarget.Process), out MaxPollingDelayInMinutes) ? MaxPollingDelayInMinutes : Constants.DefaultMaxPollingDelayInMinutes; - - public static readonly int RetryLimit = int.TryParse(Environment.GetEnvironmentVariable(nameof(RetryLimit), EnvironmentVariableTarget.Process), out RetryLimit) ? RetryLimit.ClampInt(1, Constants.MaxRetryLimit) : Constants.DefaultRetryLimit; - - public static readonly string AudioInputContainer = Environment.GetEnvironmentVariable(nameof(AudioInputContainer), EnvironmentVariableTarget.Process); - - public static readonly string AzureSpeechServicesKey = Environment.GetEnvironmentVariable(nameof(AzureSpeechServicesKey), EnvironmentVariableTarget.Process); - - public static readonly string AzureWebJobsStorage = Environment.GetEnvironmentVariable(nameof(AzureWebJobsStorage), EnvironmentVariableTarget.Process); - - public static readonly string DatabaseConnectionString = Environment.GetEnvironmentVariable(nameof(DatabaseConnectionString), EnvironmentVariableTarget.Process); - - public static readonly string ErrorFilesOutputContainer = Environment.GetEnvironmentVariable(nameof(ErrorFilesOutputContainer), EnvironmentVariableTarget.Process); - - public static readonly string ErrorReportOutputContainer = Environment.GetEnvironmentVariable(nameof(ErrorReportOutputContainer), EnvironmentVariableTarget.Process); - - public static readonly string FetchTranscriptionServiceBusConnectionString = Environment.GetEnvironmentVariable(nameof(FetchTranscriptionServiceBusConnectionString), EnvironmentVariableTarget.Process); - - public static readonly string CompletedServiceBusConnectionString = Environment.GetEnvironmentVariable(nameof(CompletedServiceBusConnectionString), EnvironmentVariableTarget.Process); - - public static readonly string HtmlResultOutputContainer = Environment.GetEnvironmentVariable(nameof(HtmlResultOutputContainer), EnvironmentVariableTarget.Process); - - public static readonly string JsonResultOutputContainer = Environment.GetEnvironmentVariable(nameof(JsonResultOutputContainer), EnvironmentVariableTarget.Process); - - public static readonly string StartTranscriptionServiceBusConnectionString = Environment.GetEnvironmentVariable(nameof(StartTranscriptionServiceBusConnectionString), EnvironmentVariableTarget.Process); - - public static readonly string TextAnalyticsKey = Environment.GetEnvironmentVariable(nameof(TextAnalyticsKey), EnvironmentVariableTarget.Process); - - public static readonly string TextAnalyticsEndpoint = Environment.GetEnvironmentVariable(nameof(TextAnalyticsEndpoint), EnvironmentVariableTarget.Process); - - public static readonly string PiiCategories = Environment.GetEnvironmentVariable(nameof(PiiCategories), EnvironmentVariableTarget.Process); - - public static readonly bool CreateConsolidatedOutputFiles = bool.TryParse(Environment.GetEnvironmentVariable(nameof(CreateConsolidatedOutputFiles), EnvironmentVariableTarget.Process), out CreateConsolidatedOutputFiles) && CreateConsolidatedOutputFiles; - - public static readonly string ConsolidatedFilesOutputContainer = Environment.GetEnvironmentVariable(nameof(ConsolidatedFilesOutputContainer), EnvironmentVariableTarget.Process); - - public static readonly bool CreateAudioProcessedContainer = bool.TryParse(Environment.GetEnvironmentVariable(nameof(CreateAudioProcessedContainer), EnvironmentVariableTarget.Process), out CreateAudioProcessedContainer) && CreateAudioProcessedContainer; - - public static readonly string AudioProcessedContainer = Environment.GetEnvironmentVariable(nameof(AudioProcessedContainer), EnvironmentVariableTarget.Process); - } -} diff --git a/samples/ingestion/ingestion-client/FetchTranscription/Program.cs b/samples/ingestion/ingestion-client/FetchTranscription/Program.cs index 0ab3f5f4d..a07a6f477 100644 --- a/samples/ingestion/ingestion-client/FetchTranscription/Program.cs +++ b/samples/ingestion/ingestion-client/FetchTranscription/Program.cs @@ -5,7 +5,7 @@ namespace FetchTranscription { - using System; + using System.IO; using Azure.Storage; using Azure.Storage.Blobs; @@ -16,55 +16,68 @@ namespace FetchTranscription using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Azure; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; + using Microsoft.Extensions.Options; public static class Program { public static void Main(string[] args) { - var useSqlDatabase = FetchTranscriptionEnvironmentVariables.UseSqlDatabase; - var storageConnectionString = Environment.GetEnvironmentVariable("AzureWebJobsStorage"); - var blobServiceClient = new BlobServiceClient(storageConnectionString); - var storageCredential = new StorageSharedKeyCredential( - AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountName", storageConnectionString), - AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountKey", storageConnectionString)); - var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() - .ConfigureServices(s => + .ConfigureAppConfiguration((context, config) => + { + config.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables(); + }) + .ConfigureServices((context, services) => { + var configuration = context.Configuration; + var config = new AppConfig(); + configuration.GetSection("Values").Bind(config); + + var blobServiceClient = new BlobServiceClient(config.AzureWebJobsStorage); + var storageCredential = new StorageSharedKeyCredential( + AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountName", config.AzureWebJobsStorage), + AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountKey", config.AzureWebJobsStorage)); + // This is a unified way to configure logging filter for all functions. - s.ConfigureIngestionClientLogging(); + services.ConfigureIngestionClientLogging(); - if (useSqlDatabase) + if (config.UseSqlDatabase) { - s.AddDbContext( - options => SqlServerDbContextOptionsExtensions.UseSqlServer(options, FetchTranscriptionEnvironmentVariables.DatabaseConnectionString)); + services.AddDbContext( + options => SqlServerDbContextOptionsExtensions.UseSqlServer(options, config.DatabaseConnectionString)); } - s.AddSingleton(blobServiceClient); - s.AddSingleton(storageCredential); - s.AddTransient(); + services.AddSingleton(blobServiceClient); + services.AddSingleton(storageCredential); + services.AddTransient(); - s.AddAzureClients(clientBuilder => + services.AddAzureClients(clientBuilder => { - clientBuilder.AddServiceBusClient(FetchTranscriptionEnvironmentVariables.StartTranscriptionServiceBusConnectionString) + clientBuilder.AddServiceBusClient(config.StartTranscriptionServiceBusConnectionString) .WithName(ServiceBusClientName.StartTranscriptionServiceBusClient.ToString()); - clientBuilder.AddServiceBusClient(FetchTranscriptionEnvironmentVariables.FetchTranscriptionServiceBusConnectionString) + clientBuilder.AddServiceBusClient(config.FetchTranscriptionServiceBusConnectionString) .WithName(ServiceBusClientName.FetchTranscriptionServiceBusClient.ToString()); - if (!string.IsNullOrWhiteSpace(FetchTranscriptionEnvironmentVariables.CompletedServiceBusConnectionString)) + if (!string.IsNullOrWhiteSpace(config.CompletedServiceBusConnectionString)) { - clientBuilder.AddServiceBusClient(FetchTranscriptionEnvironmentVariables.CompletedServiceBusConnectionString) + clientBuilder.AddServiceBusClient(config.CompletedServiceBusConnectionString) .WithName(ServiceBusClientName.CompletedTranscriptionServiceBusClient.ToString()); } }); + services.Configure(configuration.GetSection("Values")); }) .Build(); + var config = host.Services.GetService>().Value; + // apply database migrations once during startup (not with every function execution): - if (useSqlDatabase) + if (config.UseSqlDatabase) { using var scope = host.Services.CreateScope(); var ingestionClientDbContext = scope.ServiceProvider.GetRequiredService(); diff --git a/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/Language/AnalyzeConversationsProvider.cs b/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/Language/AnalyzeConversationsProvider.cs index 8633a7632..bef69644a 100644 --- a/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/Language/AnalyzeConversationsProvider.cs +++ b/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/Language/AnalyzeConversationsProvider.cs @@ -23,6 +23,7 @@ namespace FetchTranscription using Connector.Serializable.TranscriptionStartedServiceBusMessage; using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; using Newtonsoft.Json; @@ -39,25 +40,28 @@ public class AnalyzeConversationsProvider : ITranscriptionAnalyticsProvider private readonly string locale; private readonly ILogger log; - public AnalyzeConversationsProvider(string locale, string subscriptionKey, string endpoint, ILogger log) + private readonly AppConfig appConfig; + + public AnalyzeConversationsProvider(string locale, string subscriptionKey, string endpoint, ILogger log, IOptions appConfig) { this.conversationAnalysisClient = new ConversationAnalysisClient(new Uri(endpoint), new AzureKeyCredential(subscriptionKey)); this.locale = locale; this.log = log; + this.appConfig = appConfig?.Value; } - public static bool IsConversationalPiiEnabled() + public bool IsConversationalPiiEnabled() { - return FetchTranscriptionEnvironmentVariables.ConversationPiiSetting != ConversationPiiSetting.None; + return this.appConfig.ConversationPiiSetting != ConversationPiiSetting.None; } - public static bool IsConversationalSummarizationEnabled() - => FetchTranscriptionEnvironmentVariables.ConversationSummarizationOptions.Enabled; + public bool IsConversationalSummarizationEnabled() + => this.appConfig.ConversationSummarizationOptions.Enabled; /// public async Task GetTranscriptionAnalyticsJobStatusAsync(IEnumerable audioFileInfos) { - if (!IsConversationalPiiEnabled() && !IsConversationalSummarizationEnabled()) + if (!this.IsConversationalPiiEnabled() && !this.IsConversationalSummarizationEnabled()) { return TranscriptionAnalyticsJobStatus.Completed; } @@ -299,7 +303,7 @@ private async Task> AddConversationalEntitiesAsync( speechTranscript = speechTranscript ?? throw new ArgumentNullException(nameof(speechTranscript)); var errors = new List(); - if (!(IsConversationalPiiEnabled() || IsConversationalSummarizationEnabled())) + if (!(this.IsConversationalPiiEnabled() || this.IsConversationalSummarizationEnabled())) { return new List(); } @@ -327,7 +331,7 @@ private async Task> AddConversationalEntitiesAsync( private void PrepareSummarizationRequest(SpeechTranscript speechTranscript, List data) { - if (!IsConversationalSummarizationEnabled()) + if (!this.IsConversationalSummarizationEnabled()) { this.log.LogInformation("Skip prepare summarization request because disabled"); return; @@ -360,7 +364,7 @@ private void PrepareSummarizationRequest(SpeechTranscript speechTranscript, List Tasks = new List(), }; - foreach (var aspect in FetchTranscriptionEnvironmentVariables.ConversationSummarizationOptions.Aspects) + foreach (var aspect in this.appConfig.ConversationSummarizationOptions.Aspects) { summarizationData.Tasks.Add(new AnalyzeConversationsTask { @@ -402,7 +406,7 @@ private void PrepareSummarizationRequest(SpeechTranscript speechTranscript, List }) }; - var stratergy = FetchTranscriptionEnvironmentVariables.ConversationSummarizationOptions.Stratergy; + var stratergy = this.appConfig.ConversationSummarizationOptions.Stratergy; var roleKey = stratergy.Key switch { RoleAssignmentMappingKey.Channel => recognizedPhrase.Channel, @@ -414,7 +418,7 @@ private void PrepareSummarizationRequest(SpeechTranscript speechTranscript, List role = stratergy.FallbackRole; } - if (role != Role.None && count + utterance.Text.Length < FetchTranscriptionEnvironmentVariables.ConversationSummarizationOptions.InputLengthLimit) + if (role != Role.None && count + utterance.Text.Length < this.appConfig.ConversationSummarizationOptions.InputLengthLimit) { utterance.Role = utterance.ParticipantId = role.ToString(); summarizationData.AnalysisInput.Conversations[0].ConversationItems.Add(utterance); @@ -430,7 +434,7 @@ private void PrepareSummarizationRequest(SpeechTranscript speechTranscript, List private void PreparePiiRequest(SpeechTranscript speechTranscript, List data) { - if (!IsConversationalPiiEnabled()) + if (!this.IsConversationalPiiEnabled()) { this.log.LogInformation("Skip prepare pii request"); return; @@ -447,7 +451,7 @@ private void PreparePiiRequest(SpeechTranscript speechTranscript, List FetchTranscriptionEnvironmentVariables.ConversationPiiMaxChunkSize) + if (count == -1 || (count + textCount) > this.appConfig.ConversationPiiMaxChunkSize) { count = 0; jobCount++; @@ -473,13 +477,13 @@ private void PreparePiiRequest(SpeechTranscript speechTranscript, List { { - "piiCategories", FetchTranscriptionEnvironmentVariables.ConversationPiiCategories.ToList() + "piiCategories", this.appConfig.ConversationPiiCategories.ToList() }, { - "redactionSource", FetchTranscriptionEnvironmentVariables.ConversationPiiInferenceSource ?? DefaultInferenceSource + "redactionSource", this.appConfig.ConversationPiiInferenceSource ?? DefaultInferenceSource }, { - "includeAudioRedaction", FetchTranscriptionEnvironmentVariables.ConversationPiiSetting == ConversationPiiSetting.IncludeAudioRedaction + "includeAudioRedaction", this.appConfig.ConversationPiiSetting == ConversationPiiSetting.IncludeAudioRedaction } } } diff --git a/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/TextAnalytics/TextAnalyticsProvider.cs b/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/TextAnalytics/TextAnalyticsProvider.cs index 68d66c909..0857fee03 100644 --- a/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/TextAnalytics/TextAnalyticsProvider.cs +++ b/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/TextAnalytics/TextAnalyticsProvider.cs @@ -19,6 +19,7 @@ namespace FetchTranscription using Connector.Serializable.TranscriptionStartedServiceBusMessage; using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; using static Connector.Serializable.TranscriptionStartedServiceBusMessage.TextAnalyticsRequest; @@ -34,23 +35,26 @@ public class TextAnalyticsProvider : ITranscriptionAnalyticsProvider private readonly ILogger log; - public TextAnalyticsProvider(string locale, string subscriptionKey, string endpoint, ILogger log) + private readonly AppConfig appConfig; + + public TextAnalyticsProvider(string locale, string subscriptionKey, string endpoint, ILogger log, IOptions appConfig) { this.textAnalyticsClient = new TextAnalyticsClient(new Uri(endpoint), new AzureKeyCredential(subscriptionKey)); this.locale = locale; this.log = log; + this.appConfig = appConfig?.Value; } - public static bool IsTextAnalyticsRequested() + public bool IsTextAnalyticsRequested() { - return FetchTranscriptionEnvironmentVariables.SentimentAnalysisSetting != SentimentAnalysisSetting.None || - FetchTranscriptionEnvironmentVariables.PiiRedactionSetting != PiiRedactionSetting.None; + return this.appConfig.SentimentAnalysisSetting != SentimentAnalysisSetting.None || + this.appConfig.PiiRedactionSetting != PiiRedactionSetting.None; } /// public async Task GetTranscriptionAnalyticsJobStatusAsync(IEnumerable audioFileInfos) { - if (!IsTextAnalyticsRequested()) + if (!this.IsTextAnalyticsRequested()) { return TranscriptionAnalyticsJobStatus.Completed; } @@ -116,15 +120,15 @@ public async Task> SubmitTranscriptionAnalyticsJobsAsync(Dic (var utteranceLevelJobIds, var utteranceLevelErrors) = await this.SubmitUtteranceLevelRequests( speechTranscript, - FetchTranscriptionEnvironmentVariables.SentimentAnalysisSetting).ConfigureAwait(false); + this.appConfig.SentimentAnalysisSetting).ConfigureAwait(false); var utteranceLevelRequests = utteranceLevelJobIds?.Select(jobId => new TextAnalyticsRequest(jobId, TextAnalyticsRequestStatus.Running)); textAnalyticsErrors.AddRange(utteranceLevelErrors); (var audioLevelJobIds, var audioLevelErrors) = await this.SubmitAudioLevelRequests( speechTranscript, - FetchTranscriptionEnvironmentVariables.SentimentAnalysisSetting, - FetchTranscriptionEnvironmentVariables.PiiRedactionSetting).ConfigureAwait(false); + this.appConfig.SentimentAnalysisSetting, + this.appConfig.PiiRedactionSetting).ConfigureAwait(false); var audioLevelRequests = audioLevelJobIds?.Select(jobId => new TextAnalyticsRequest(jobId, TextAnalyticsRequestStatus.Running)); textAnalyticsErrors.AddRange(audioLevelErrors); @@ -162,7 +166,7 @@ public async Task> AddTranscriptionAnalyticsResultsToTranscr var speechTranscript = speechTranscriptMapping.Value; var audioFileInfo = speechTranscriptMapping.Key; var fileName = audioFileInfo.FileName; - if (FetchTranscriptionEnvironmentVariables.PiiRedactionSetting != PiiRedactionSetting.None) + if (this.appConfig.PiiRedactionSetting != PiiRedactionSetting.None) { speechTranscript.RecognizedPhrases.ToList().ForEach(phrase => { @@ -264,9 +268,9 @@ public async Task> AddTranscriptionAnalyticsResultsToTranscr { var action = new RecognizePiiEntitiesAction(); - if (!string.IsNullOrEmpty(FetchTranscriptionEnvironmentVariables.PiiCategories)) + if (!string.IsNullOrEmpty(this.appConfig.PiiCategories)) { - var piiEntityCategories = FetchTranscriptionEnvironmentVariables.PiiCategories.Split(",").Select(c => new PiiEntityCategory(c)); + var piiEntityCategories = this.appConfig.PiiCategories.Split(",").Select(c => new PiiEntityCategory(c)); foreach (var category in piiEntityCategories) { @@ -362,7 +366,7 @@ private async Task> AddAudioLevelEntitiesAsync( }; } - if (!AnalyzeConversationsProvider.IsConversationalPiiEnabled()) + if (!(this.appConfig.ConversationPiiSetting != ConversationPiiSetting.None)) { var piiResult = piiResults.Where(document => document.Id.Equals($"{channel}", StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); if (piiResult != null) diff --git a/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/TranscriptionAnalyticsOrchestrator.cs b/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/TranscriptionAnalyticsOrchestrator.cs index 089dbae7c..78842df0d 100644 --- a/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/TranscriptionAnalyticsOrchestrator.cs +++ b/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionAnalytics/TranscriptionAnalyticsOrchestrator.cs @@ -14,25 +14,26 @@ namespace FetchTranscription using Connector.Serializable.TranscriptionStartedServiceBusMessage; using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; public sealed class TranscriptionAnalyticsOrchestrator { private readonly List providers; + private readonly AppConfig appConfig; + public TranscriptionAnalyticsOrchestrator( string locale, - ILogger logger) + ILogger logger, + IOptions appConfig) { - var textAnalyticsKey = FetchTranscriptionEnvironmentVariables.TextAnalyticsKey; - var textAnalyticsEndpoint = FetchTranscriptionEnvironmentVariables.TextAnalyticsEndpoint; - var textAnalyticsInfoProvided = !string.IsNullOrEmpty(textAnalyticsKey) && !string.IsNullOrEmpty(textAnalyticsEndpoint); - + this.appConfig = appConfig?.Value; this.providers = new List(); - if (textAnalyticsInfoProvided) + if (!string.IsNullOrEmpty(this.appConfig.TextAnalyticsKey) && !string.IsNullOrEmpty(this.appConfig.TextAnalyticsEndpoint)) { - this.providers.Add(new TextAnalyticsProvider(locale, textAnalyticsKey, textAnalyticsEndpoint, logger)); - this.providers.Add(new AnalyzeConversationsProvider(locale, textAnalyticsKey, textAnalyticsEndpoint, logger)); + this.providers.Add(new TextAnalyticsProvider(locale, this.appConfig.TextAnalyticsKey, this.appConfig.TextAnalyticsEndpoint, logger, Options.Create(this.appConfig))); + this.providers.Add(new AnalyzeConversationsProvider(locale, this.appConfig.TextAnalyticsKey, this.appConfig.TextAnalyticsEndpoint, logger, Options.Create(this.appConfig))); } } diff --git a/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionProcessor.cs b/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionProcessor.cs index f26f206da..faa4c7f10 100644 --- a/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionProcessor.cs +++ b/samples/ingestion/ingestion-client/FetchTranscription/TranscriptionProcessor.cs @@ -23,6 +23,8 @@ namespace FetchTranscription using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Azure; using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; + using Newtonsoft.Json; public class TranscriptionProcessor @@ -37,27 +39,31 @@ public class TranscriptionProcessor private readonly IStorageConnector storageConnector; + private readonly AppConfig appConfig; + public TranscriptionProcessor( IStorageConnector storageConnector, IAzureClientFactory serviceBusClientFactory, - IngestionClientDbContext databaseContext) + IngestionClientDbContext databaseContext, + IOptions appConfig) { this.storageConnector = storageConnector; this.databaseContext = databaseContext; + this.appConfig = appConfig?.Value; ArgumentNullException.ThrowIfNull(serviceBusClientFactory, nameof(serviceBusClientFactory)); var startTranscriptionServiceBusClient = serviceBusClientFactory.CreateClient(ServiceBusClientName.StartTranscriptionServiceBusClient.ToString()); - var startTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(FetchTranscriptionEnvironmentVariables.StartTranscriptionServiceBusConnectionString).EntityPath; + var startTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(this.appConfig.StartTranscriptionServiceBusConnectionString).EntityPath; this.startTranscriptionServiceBusSender = startTranscriptionServiceBusClient.CreateSender(startTranscriptionQueueName); var fetchTranscriptionServiceBusClient = serviceBusClientFactory.CreateClient(ServiceBusClientName.FetchTranscriptionServiceBusClient.ToString()); - var fetchTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(FetchTranscriptionEnvironmentVariables.FetchTranscriptionServiceBusConnectionString).EntityPath; + var fetchTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(this.appConfig.FetchTranscriptionServiceBusConnectionString).EntityPath; this.fetchTranscriptionServiceBusSender = fetchTranscriptionServiceBusClient.CreateSender(fetchTranscriptionQueueName); - if (!string.IsNullOrWhiteSpace(FetchTranscriptionEnvironmentVariables.CompletedServiceBusConnectionString)) + if (!string.IsNullOrWhiteSpace(this.appConfig.CompletedServiceBusConnectionString)) { var completedTranscriptionServiceBusClient = serviceBusClientFactory.CreateClient(ServiceBusClientName.CompletedTranscriptionServiceBusClient.ToString()); - var completedTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(FetchTranscriptionEnvironmentVariables.CompletedServiceBusConnectionString).EntityPath; + var completedTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(this.appConfig.CompletedServiceBusConnectionString).EntityPath; this.completedTranscriptionServiceBusSender = completedTranscriptionServiceBusClient.CreateSender(completedTranscriptionQueueName); } } @@ -70,12 +76,12 @@ public async Task ProcessTranscriptionJobAsync(TranscriptionStartedMessage servi throw new ArgumentNullException(nameof(serviceBusMessage)); } - var subscriptionKey = FetchTranscriptionEnvironmentVariables.AzureSpeechServicesKey; + var subscriptionKey = this.appConfig.AzureSpeechServicesKey; var jobName = serviceBusMessage.JobName; var transcriptionLocation = serviceBusMessage.TranscriptionLocation; log.LogInformation($"Received transcription at {transcriptionLocation} with name {jobName} from service bus message."); - var messageDelayTime = GetMessageDelayTime(serviceBusMessage.PollingCounter); + var messageDelayTime = GetMessageDelayTime(serviceBusMessage.PollingCounter, this.appConfig); serviceBusMessage.PollingCounter += 1; try @@ -149,18 +155,18 @@ await this.RetryOrFailJobAsync( } } - private static TimeSpan GetMessageDelayTime(int pollingCounter) + private static TimeSpan GetMessageDelayTime(int pollingCounter, AppConfig appConfig) { if (pollingCounter == 0) { - return TimeSpan.FromMinutes(FetchTranscriptionEnvironmentVariables.InitialPollingDelayInMinutes); + return TimeSpan.FromMinutes(appConfig.InitialPollingDelayInMinutes); } - var updatedDelay = Math.Pow(2, Math.Min(pollingCounter, 8)) * FetchTranscriptionEnvironmentVariables.InitialPollingDelayInMinutes; + var updatedDelay = Math.Pow(2, Math.Min(pollingCounter, 8)) * appConfig.InitialPollingDelayInMinutes; - if ((int)updatedDelay > FetchTranscriptionEnvironmentVariables.MaxPollingDelayInMinutes) + if ((int)updatedDelay > appConfig.MaxPollingDelayInMinutes) { - return TimeSpan.FromMinutes(FetchTranscriptionEnvironmentVariables.MaxPollingDelayInMinutes); + return TimeSpan.FromMinutes(appConfig.MaxPollingDelayInMinutes); } return TimeSpan.FromMinutes(updatedDelay); @@ -193,7 +199,7 @@ private async Task ProcessFailedTranscriptionAsync(string transcriptionLocation, errorReportOutput += $"\nReport file: \n {JsonConvert.SerializeObject(reportFileContent)}"; } - var errorOutputContainer = FetchTranscriptionEnvironmentVariables.ErrorReportOutputContainer; + var errorOutputContainer = this.appConfig.ErrorReportOutputContainer; await this.storageConnector.WriteTextFileToBlobAsync(errorReportOutput, errorOutputContainer, $"jobs/{jobName}.txt").ConfigureAwait(false); var retryAudioFile = IsRetryableError(safeErrorCode); @@ -202,7 +208,7 @@ private async Task ProcessFailedTranscriptionAsync(string transcriptionLocation, { var fileName = this.storageConnector.GetFileNameFromUri(new Uri(audio.FileUrl)); - if (retryAudioFile && audio.RetryCount < FetchTranscriptionEnvironmentVariables.RetryLimit) + if (retryAudioFile && audio.RetryCount < this.appConfig.RetryLimit) { log.LogInformation($"Retrying transcription with name {fileName} - retry count: {audio.RetryCount}"); var serviceBusMessage = new Connector.ServiceBusMessage @@ -223,9 +229,9 @@ private async Task ProcessFailedTranscriptionAsync(string transcriptionLocation, var message = $"Failed transcription with name {fileName} in job {jobName} after {audio.RetryCount} retries with error: {safeErrorMessage} (Error: {safeErrorCode})."; await this.storageConnector.WriteTextFileToBlobAsync(message, errorOutputContainer, $"{fileName}.txt").ConfigureAwait(false); await this.storageConnector.MoveFileAsync( - FetchTranscriptionEnvironmentVariables.AudioInputContainer, + this.appConfig.AudioInputContainer, fileName, - FetchTranscriptionEnvironmentVariables.ErrorFilesOutputContainer, + this.appConfig.ErrorFilesOutputContainer, fileName, false).ConfigureAwait(false); } @@ -259,12 +265,12 @@ private async Task ProcessReportFileAsync(TranscriptionReportFile transcriptionR var errorTxtname = fileName + ".txt"; await this.storageConnector.WriteTextFileToBlobAsync( message, - FetchTranscriptionEnvironmentVariables.ErrorReportOutputContainer, + this.appConfig.ErrorReportOutputContainer, errorTxtname).ConfigureAwait(false); await this.storageConnector.MoveFileAsync( - FetchTranscriptionEnvironmentVariables.AudioInputContainer, + this.appConfig.AudioInputContainer, fileName, - FetchTranscriptionEnvironmentVariables.ErrorFilesOutputContainer, + this.appConfig.ErrorFilesOutputContainer, fileName, false).ConfigureAwait(false); } @@ -274,9 +280,9 @@ private async Task RetryOrFailJobAsync(TranscriptionStartedMessage message, stri { log.LogError(errorMessage); message.FailedExecutionCounter += 1; - var messageDelayTime = GetMessageDelayTime(message.PollingCounter); + var messageDelayTime = GetMessageDelayTime(message.PollingCounter, this.appConfig); - if (message.FailedExecutionCounter <= FetchTranscriptionEnvironmentVariables.RetryLimit || isThrottled) + if (message.FailedExecutionCounter <= this.appConfig.RetryLimit || isThrottled) { log.LogInformation("Retrying.."); await ServiceBusUtilities.SendServiceBusMessageAsync(this.fetchTranscriptionServiceBusSender, message.CreateMessageString(), log, messageDelayTime).ConfigureAwait(false); @@ -291,7 +297,7 @@ private async Task RetryOrFailJobAsync(TranscriptionStartedMessage message, stri private async Task WriteFailedJobLogToStorageAsync(TranscriptionStartedMessage transcriptionStartedMessage, string errorMessage, string jobName, ILogger log) { log.LogError(errorMessage); - var errorOutputContainer = FetchTranscriptionEnvironmentVariables.ErrorReportOutputContainer; + var errorOutputContainer = this.appConfig.ErrorReportOutputContainer; var jobErrorFileName = $"jobs/{jobName}.txt"; await this.storageConnector.WriteTextFileToBlobAsync(errorMessage, errorOutputContainer, jobErrorFileName).ConfigureAwait(false); @@ -304,9 +310,9 @@ private async Task WriteFailedJobLogToStorageAsync(TranscriptionStartedMessage t { await this.storageConnector.WriteTextFileToBlobAsync(errorMessage, errorOutputContainer, errorFileName).ConfigureAwait(false); await this.storageConnector.MoveFileAsync( - FetchTranscriptionEnvironmentVariables.AudioInputContainer, + this.appConfig.AudioInputContainer, fileName, - FetchTranscriptionEnvironmentVariables.ErrorFilesOutputContainer, + this.appConfig.ErrorFilesOutputContainer, fileName, false).ConfigureAwait(false); } @@ -323,7 +329,7 @@ private async Task WriteErrorReportAsync(string errorString, string jobName) await this.storageConnector.WriteTextFileToBlobAsync( errorString, - FetchTranscriptionEnvironmentVariables.ErrorReportOutputContainer, + this.appConfig.ErrorReportOutputContainer, errorTxtname).ConfigureAwait(false); } @@ -331,13 +337,13 @@ private async Task ProcessSucceededTranscriptionAsync(string transcriptionLocati { log.LogInformation($"Got succeeded transcription for job {jobName}"); - var transcriptionAnalyticsOrchestrator = new TranscriptionAnalyticsOrchestrator(serviceBusMessage.Locale, log); + var transcriptionAnalyticsOrchestrator = new TranscriptionAnalyticsOrchestrator(serviceBusMessage.Locale, log, Options.Create(this.appConfig)); var transcriptionAnalyticsJobStatus = await transcriptionAnalyticsOrchestrator.GetTranscriptionAnalyticsJobsStatusAsync(serviceBusMessage).ConfigureAwait(false); if (transcriptionAnalyticsJobStatus == TranscriptionAnalyticsJobStatus.Running) { // If transcription analytics request is still running, re-queue message and get status again after X minutes: log.LogInformation($"Transcription analytics requests still running for job {jobName} - re-queueing message."); - await ServiceBusUtilities.SendServiceBusMessageAsync(this.fetchTranscriptionServiceBusSender, serviceBusMessage.CreateMessageString(), log, GetMessageDelayTime(serviceBusMessage.PollingCounter)).ConfigureAwait(false); + await ServiceBusUtilities.SendServiceBusMessageAsync(this.fetchTranscriptionServiceBusSender, serviceBusMessage.CreateMessageString(), log, GetMessageDelayTime(serviceBusMessage.PollingCounter, this.appConfig)).ConfigureAwait(false); return; } @@ -436,47 +442,47 @@ private async Task ProcessSucceededTranscriptionAsync(string transcriptionLocati var jsonFileName = $"{fileName}.json"; var archiveFileLocation = System.IO.Path.GetFileNameWithoutExtension(fileName); - var jsonFileUrl = await this.storageConnector.WriteTextFileToBlobAsync(editedTranscriptionResultJson, FetchTranscriptionEnvironmentVariables.JsonResultOutputContainer, jsonFileName).ConfigureAwait(false); + var jsonFileUrl = await this.storageConnector.WriteTextFileToBlobAsync(editedTranscriptionResultJson, this.appConfig.JsonResultOutputContainer, jsonFileName).ConfigureAwait(false); - if (!string.IsNullOrEmpty(FetchTranscriptionEnvironmentVariables.CompletedServiceBusConnectionString)) + if (!string.IsNullOrEmpty(this.appConfig.CompletedServiceBusConnectionString)) { var completedMessage = new CompletedMessage(audioFileInfo.FileUrl, jsonFileUrl); completedMessages.Add(completedMessage); } - var consolidatedContainer = FetchTranscriptionEnvironmentVariables.ConsolidatedFilesOutputContainer; - if (FetchTranscriptionEnvironmentVariables.CreateConsolidatedOutputFiles) + var consolidatedContainer = this.appConfig.ConsolidatedFilesOutputContainer; + if (this.appConfig.CreateConsolidatedOutputFiles) { var audioArchiveFileName = $"{archiveFileLocation}/{fileName}"; var jsonArchiveFileName = $"{archiveFileLocation}/{jsonFileName}"; - await this.storageConnector.MoveFileAsync(FetchTranscriptionEnvironmentVariables.AudioInputContainer, fileName, consolidatedContainer, audioArchiveFileName, true).ConfigureAwait(false); + await this.storageConnector.MoveFileAsync(this.appConfig.AudioInputContainer, fileName, consolidatedContainer, audioArchiveFileName, true).ConfigureAwait(false); await this.storageConnector.WriteTextFileToBlobAsync(editedTranscriptionResultJson, consolidatedContainer, jsonArchiveFileName).ConfigureAwait(false); } - if (FetchTranscriptionEnvironmentVariables.CreateHtmlResultFile) + if (this.appConfig.CreateHtmlResultFile) { - var htmlContainer = FetchTranscriptionEnvironmentVariables.HtmlResultOutputContainer; + var htmlContainer = this.appConfig.HtmlResultOutputContainer; var htmlFileName = $"{fileName}.html"; var displayResults = TranscriptionToHtml.ToHtml(speechTranscript, jobName); await this.storageConnector.WriteTextFileToBlobAsync(displayResults, htmlContainer, htmlFileName).ConfigureAwait(false); - if (FetchTranscriptionEnvironmentVariables.CreateConsolidatedOutputFiles) + if (this.appConfig.CreateConsolidatedOutputFiles) { var htmlArchiveFileName = $"{archiveFileLocation}/{htmlFileName}"; await this.storageConnector.WriteTextFileToBlobAsync(displayResults, consolidatedContainer, htmlArchiveFileName).ConfigureAwait(false); } } - if (FetchTranscriptionEnvironmentVariables.UseSqlDatabase) + if (this.appConfig.UseSqlDatabase) { var duration = string.IsNullOrEmpty(speechTranscript.Duration) ? TimeSpan.Zero : XmlConvert.ToTimeSpan(speechTranscript.Duration); var approximatedCost = CostEstimation.GetCostEstimation( duration, speechTranscript.CombinedRecognizedPhrases.Count(), serviceBusMessage.UsesCustomModel, - FetchTranscriptionEnvironmentVariables.SentimentAnalysisSetting, - FetchTranscriptionEnvironmentVariables.PiiRedactionSetting); + this.appConfig.SentimentAnalysisSetting, + this.appConfig.PiiRedactionSetting); var containsMultipleTranscriptions = resultFiles.Skip(1).Any(); var jobId = containsMultipleTranscriptions ? Guid.NewGuid() : new Guid(transcriptionLocation.Split('/').LastOrDefault()); @@ -498,15 +504,15 @@ await this.databaseContext.StoreTranscriptionAsync( } } - if (FetchTranscriptionEnvironmentVariables.CreateAudioProcessedContainer) + if (this.appConfig.CreateAudioProcessedContainer) { - await this.storageConnector.MoveFileAsync(FetchTranscriptionEnvironmentVariables.AudioInputContainer, fileName, FetchTranscriptionEnvironmentVariables.AudioProcessedContainer, fileName, false).ConfigureAwait(false); + await this.storageConnector.MoveFileAsync(this.appConfig.AudioInputContainer, fileName, this.appConfig.AudioProcessedContainer, fileName, false).ConfigureAwait(false); } } if (this.completedTranscriptionServiceBusSender != null) { - await ServiceBusUtilities.SendServiceBusMessageAsync(this.completedTranscriptionServiceBusSender, JsonConvert.SerializeObject(completedMessages), log, GetMessageDelayTime(serviceBusMessage.PollingCounter)).ConfigureAwait(false); + await ServiceBusUtilities.SendServiceBusMessageAsync(this.completedTranscriptionServiceBusSender, JsonConvert.SerializeObject(completedMessages), log, GetMessageDelayTime(serviceBusMessage.PollingCounter, this.appConfig)).ConfigureAwait(false); } var generalErrors = generalErrorsStringBuilder.ToString(); diff --git a/samples/ingestion/ingestion-client/StartTranscriptionByServiceBus/Program.cs b/samples/ingestion/ingestion-client/StartTranscriptionByServiceBus/Program.cs index ff471b03c..32c83453c 100644 --- a/samples/ingestion/ingestion-client/StartTranscriptionByServiceBus/Program.cs +++ b/samples/ingestion/ingestion-client/StartTranscriptionByServiceBus/Program.cs @@ -5,6 +5,8 @@ namespace StartTranscription { + using System.IO; + using Azure.Storage; using Azure.Storage.Blobs; @@ -12,6 +14,7 @@ namespace StartTranscription using Connector.Enums; using Microsoft.Extensions.Azure; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -28,30 +31,40 @@ public static class Program /// public static void Main(string[] args) { - var storageConnectionString = StartTranscriptionEnvironmentVariables.AzureWebJobsStorage; - var blobServiceClient = new BlobServiceClient(storageConnectionString); - - var storageCredential = new StorageSharedKeyCredential( - AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountName", storageConnectionString), - AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountKey", storageConnectionString)); - var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() - .ConfigureServices(s => + .ConfigureAppConfiguration((context, config) => { + config.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables(); + }) + .ConfigureServices((context, services) => + { + var configuration = context.Configuration; + var config = new AppConfig(); + configuration.GetSection("Values").Bind(config); + + var blobServiceClient = new BlobServiceClient(config.AzureWebJobsStorage); + var storageCredential = new StorageSharedKeyCredential( + AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountName", config.AzureWebJobsStorage), + AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountKey", config.AzureWebJobsStorage)); + // This is a unified way to configure logging filter for all functions. - s.ConfigureIngestionClientLogging(); - s.AddSingleton(blobServiceClient); - s.AddSingleton(storageCredential); - s.AddTransient(); + services.ConfigureIngestionClientLogging(); + services.AddSingleton(blobServiceClient); + services.AddSingleton(storageCredential); + services.AddTransient(); + services.AddTransient(); - s.AddAzureClients(clientBuilder => + services.AddAzureClients(clientBuilder => { - clientBuilder.AddServiceBusClient(StartTranscriptionEnvironmentVariables.StartTranscriptionServiceBusConnectionString) + clientBuilder.AddServiceBusClient(config.StartTranscriptionServiceBusConnectionString) .WithName(ServiceBusClientName.StartTranscriptionServiceBusClient.ToString()); - clientBuilder.AddServiceBusClient(StartTranscriptionEnvironmentVariables.FetchTranscriptionServiceBusConnectionString) + clientBuilder.AddServiceBusClient(config.FetchTranscriptionServiceBusConnectionString) .WithName(ServiceBusClientName.FetchTranscriptionServiceBusClient.ToString()); }); + services.Configure(configuration.GetSection("Values")); }) .Build(); diff --git a/samples/ingestion/ingestion-client/StartTranscriptionByServiceBus/StartTranscriptionByServiceBus.cs b/samples/ingestion/ingestion-client/StartTranscriptionByServiceBus/StartTranscriptionByServiceBus.cs index cd4487084..c89e34ccc 100644 --- a/samples/ingestion/ingestion-client/StartTranscriptionByServiceBus/StartTranscriptionByServiceBus.cs +++ b/samples/ingestion/ingestion-client/StartTranscriptionByServiceBus/StartTranscriptionByServiceBus.cs @@ -9,11 +9,7 @@ namespace StartTranscription using System.Threading.Tasks; using Azure.Messaging.ServiceBus; - using Connector; - using Connector.Enums; - using Microsoft.Azure.Functions.Worker; - using Microsoft.Extensions.Azure; using Microsoft.Extensions.Logging; using StartTranscriptionByTimer; @@ -23,39 +19,19 @@ namespace StartTranscription public class StartTranscriptionByServiceBus { private readonly ILogger logger; - - private readonly IStorageConnector storageConnector; - - private readonly ServiceBusReceiver startTranscriptionServiceBusReceiver; - - private readonly ServiceBusSender startTranscriptionServiceBusSender; - - private readonly ServiceBusSender fetchTranscriptionServiceBusSender; + private readonly IStartTranscriptionHelper transcriptionHelper; /// /// Initializes a new instance of the class. /// /// The StartTranscriptionByServiceBus Logger - /// Storage connector dependency - /// Azure client factory for service bus clients + /// public StartTranscriptionByServiceBus( ILogger logger, - IStorageConnector storageConnector, - IAzureClientFactory serviceBusClientFactory) + IStartTranscriptionHelper transcriptionHelper) { this.logger = logger; - this.storageConnector = storageConnector; - - serviceBusClientFactory = serviceBusClientFactory ?? throw new ArgumentNullException(nameof(serviceBusClientFactory)); - var startTranscriptionServiceBusClient = serviceBusClientFactory.CreateClient(ServiceBusClientName.StartTranscriptionServiceBusClient.ToString()); - - var startTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(StartTranscriptionEnvironmentVariables.StartTranscriptionServiceBusConnectionString).EntityPath; - this.startTranscriptionServiceBusReceiver = startTranscriptionServiceBusClient.CreateReceiver(startTranscriptionQueueName); - this.startTranscriptionServiceBusSender = startTranscriptionServiceBusClient.CreateSender(startTranscriptionQueueName); - - var fetchTranscriptionServiceBusClient = serviceBusClientFactory.CreateClient(ServiceBusClientName.FetchTranscriptionServiceBusClient.ToString()); - var fetchTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(StartTranscriptionEnvironmentVariables.FetchTranscriptionServiceBusConnectionString).EntityPath; - this.fetchTranscriptionServiceBusSender = fetchTranscriptionServiceBusClient.CreateSender(fetchTranscriptionQueueName); + this.transcriptionHelper = transcriptionHelper; } /// @@ -72,20 +48,13 @@ public async Task Run([ServiceBusTrigger("start_transcription_queue", Connection this.logger.LogInformation($"C# Isolated ServiceBus queue trigger function processed message: {message.Subject}"); this.logger.LogInformation($"Received message: SequenceNumber:{message.SequenceNumber} Body:{message.Body}"); - var transcriptionHelper = new StartTranscriptionHelper( - this.logger, - this.storageConnector, - this.startTranscriptionServiceBusSender, - this.startTranscriptionServiceBusReceiver, - this.fetchTranscriptionServiceBusSender); - - if (message == null || !transcriptionHelper.IsValidServiceBusMessage(message)) + if (message == null || !this.transcriptionHelper.IsValidServiceBusMessage(message)) { this.logger.LogInformation($"Service bus message is invalid."); return; } - await transcriptionHelper.StartTranscriptionAsync(message).ConfigureAwait(false); + await this.transcriptionHelper.StartTranscriptionAsync(message).ConfigureAwait(false); } } } diff --git a/samples/ingestion/ingestion-client/StartTranscriptionByTimer/Config/AppConfig.cs b/samples/ingestion/ingestion-client/StartTranscriptionByTimer/Config/AppConfig.cs new file mode 100644 index 000000000..672e0d552 --- /dev/null +++ b/samples/ingestion/ingestion-client/StartTranscriptionByTimer/Config/AppConfig.cs @@ -0,0 +1,83 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.md file in the project root for full license information. +// + +namespace StartTranscriptionByTimer +{ + using System; + + using Connector.Constants; + + public class AppConfig + { + private int messagesPerFunctionExecution = Constants.DefaultMessagesPerFunctionExecution; + + private int filesPerTranscriptionJob = Constants.DefaultFilesPerTranscriptionJob; + + private int initialPollingDelayInMinutes = Constants.DefaultInitialPollingDelayInMinutes; + private int retryLimit = Constants.DefaultRetryLimit; + + public bool AddDiarization { get; set; } + + public bool AddWordLevelTimestamps { get; set; } + + public bool IsAzureGovDeployment { get; set; } + + public bool IsByosEnabledSubscription { get; set; } + + public int MessagesPerFunctionExecution + { + get => this.messagesPerFunctionExecution; + set => this.messagesPerFunctionExecution = Math.Clamp(value, 1, Constants.MaxMessagesPerFunctionExecution); + } + + public int FilesPerTranscriptionJob + { + get => this.filesPerTranscriptionJob; + set => this.filesPerTranscriptionJob = Math.Clamp(value, 1, Constants.MaxFilesPerTranscriptionJob); + } + + public int RetryLimit + { + get => this.retryLimit; + set => this.retryLimit = Math.Clamp(value, 1, Constants.MaxRetryLimit); + } + + public int InitialPollingDelayInMinutes + { + get => this.initialPollingDelayInMinutes; + set => this.initialPollingDelayInMinutes = Math.Clamp(value, 2, Constants.MaxInitialPollingDelayInMinutes); + } + + public int MaxPollingDelayInMinutes { get; set; } = Constants.DefaultMaxPollingDelayInMinutes; + + public string AudioInputContainer { get; set; } + + public string AzureServiceBus { get; set; } + + public string AzureSpeechServicesKey { get; set; } + + public string AzureSpeechServicesEndpointUri { get; set; } + + public string AzureWebJobsStorage { get; set; } + + public string CustomModelId { get; set; } + + public string ErrorFilesOutputContainer { get; set; } + + public string ErrorReportOutputContainer { get; set; } + + public string FetchTranscriptionServiceBusConnectionString { get; set; } + + public string Locale { get; set; } + + public string ProfanityFilterMode { get; set; } + + public string PunctuationMode { get; set; } + + public string StartTranscriptionServiceBusConnectionString { get; set; } + + public string StartTranscriptionFunctionTimeInterval { get; set; } + } +} \ No newline at end of file diff --git a/samples/ingestion/ingestion-client/StartTranscriptionByTimer/Interfaces/IStartTranscriptionHelper.cs b/samples/ingestion/ingestion-client/StartTranscriptionByTimer/Interfaces/IStartTranscriptionHelper.cs new file mode 100644 index 000000000..13a80fc4d --- /dev/null +++ b/samples/ingestion/ingestion-client/StartTranscriptionByTimer/Interfaces/IStartTranscriptionHelper.cs @@ -0,0 +1,21 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.md file in the project root for full license information. +// + +namespace StartTranscriptionByTimer +{ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using Azure.Messaging.ServiceBus; + + public interface IStartTranscriptionHelper + { + Task StartTranscriptionsAsync(IEnumerable messages, DateTime startDateTime); + + Task StartTranscriptionAsync(ServiceBusReceivedMessage message); + + bool IsValidServiceBusMessage(ServiceBusReceivedMessage message); + } +} \ No newline at end of file diff --git a/samples/ingestion/ingestion-client/StartTranscriptionByTimer/Program.cs b/samples/ingestion/ingestion-client/StartTranscriptionByTimer/Program.cs index d8d59a99d..7c222c81d 100644 --- a/samples/ingestion/ingestion-client/StartTranscriptionByTimer/Program.cs +++ b/samples/ingestion/ingestion-client/StartTranscriptionByTimer/Program.cs @@ -5,6 +5,8 @@ namespace StartTranscriptionByTimer { + using System.IO; + using Azure.Storage; using Azure.Storage.Blobs; @@ -12,6 +14,7 @@ namespace StartTranscriptionByTimer using Connector.Enums; using Microsoft.Extensions.Azure; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -19,30 +22,41 @@ public static class Program { public static void Main(string[] args) { - var storageConnectionString = StartTranscriptionEnvironmentVariables.AzureWebJobsStorage; - var blobServiceClient = new BlobServiceClient(storageConnectionString); - - var storageCredential = new StorageSharedKeyCredential( - AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountName", storageConnectionString), - AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountKey", storageConnectionString)); - var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() - .ConfigureServices(s => + + .ConfigureAppConfiguration((context, config) => { + config.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables(); + }) + .ConfigureServices((context, services) => + { + var configuration = context.Configuration; + var config = new AppConfig(); + configuration.GetSection("Values").Bind(config); + + var blobServiceClient = new BlobServiceClient(config.AzureWebJobsStorage); + var storageCredential = new StorageSharedKeyCredential( + AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountName", config.AzureWebJobsStorage), + AzureStorageConnectionExtensions.GetValueFromConnectionString("AccountKey", config.AzureWebJobsStorage)); + // This is a unified way to configure logging filter for all functions. - s.ConfigureIngestionClientLogging(); - s.AddSingleton(blobServiceClient); - s.AddSingleton(storageCredential); - s.AddTransient(); + services.ConfigureIngestionClientLogging(); + services.AddSingleton(blobServiceClient); + services.AddSingleton(storageCredential); + services.AddTransient(); + services.AddTransient(); - s.AddAzureClients(clientBuilder => + services.AddAzureClients(clientBuilder => { - clientBuilder.AddServiceBusClient(StartTranscriptionEnvironmentVariables.StartTranscriptionServiceBusConnectionString) + clientBuilder.AddServiceBusClient(config.StartTranscriptionServiceBusConnectionString) .WithName(ServiceBusClientName.StartTranscriptionServiceBusClient.ToString()); - clientBuilder.AddServiceBusClient(StartTranscriptionEnvironmentVariables.FetchTranscriptionServiceBusConnectionString) + clientBuilder.AddServiceBusClient(config.FetchTranscriptionServiceBusConnectionString) .WithName(ServiceBusClientName.FetchTranscriptionServiceBusClient.ToString()); }); + services.Configure(configuration.GetSection("Values")); }) .Build(); diff --git a/samples/ingestion/ingestion-client/StartTranscriptionByTimer/StartTranscriptionByTimer.cs b/samples/ingestion/ingestion-client/StartTranscriptionByTimer/StartTranscriptionByTimer.cs index f1b0a1dd4..586014391 100644 --- a/samples/ingestion/ingestion-client/StartTranscriptionByTimer/StartTranscriptionByTimer.cs +++ b/samples/ingestion/ingestion-client/StartTranscriptionByTimer/StartTranscriptionByTimer.cs @@ -17,6 +17,7 @@ namespace StartTranscriptionByTimer using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Azure; using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; /// /// Start Transcription By Timer class. @@ -27,37 +28,34 @@ public class StartTranscriptionByTimer private readonly ILogger logger; - private readonly IStorageConnector storageConnector; - private readonly ServiceBusReceiver startTranscriptionServiceBusReceiver; - private readonly ServiceBusSender startTranscriptionServiceBusSender; + private readonly AppConfig appConfig; - private readonly ServiceBusSender fetchTranscriptionServiceBusSender; + private readonly IStartTranscriptionHelper transcriptionHelper; /// /// Initializes a new instance of the class. /// /// The StartTranscriptionByTimer logger - /// Storage connector dependency - /// Azure client factory for service bus clients + /// environment configuration + /// + /// public StartTranscriptionByTimer( ILogger logger, - IStorageConnector storageConnector, - IAzureClientFactory serviceBusClientFactory) + IOptions appConfig, + IAzureClientFactory serviceBusClientFactory, + IStartTranscriptionHelper transcriptionHelper) { this.logger = logger; - this.storageConnector = storageConnector; + this.appConfig = appConfig?.Value; + this.transcriptionHelper = transcriptionHelper; + serviceBusClientFactory = serviceBusClientFactory ?? throw new ArgumentNullException(nameof(serviceBusClientFactory)); var startTranscriptionServiceBusClient = serviceBusClientFactory.CreateClient(ServiceBusClientName.StartTranscriptionServiceBusClient.ToString()); - var startTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(StartTranscriptionEnvironmentVariables.StartTranscriptionServiceBusConnectionString).EntityPath; + var startTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(this.appConfig.StartTranscriptionServiceBusConnectionString).EntityPath; this.startTranscriptionServiceBusReceiver = startTranscriptionServiceBusClient.CreateReceiver(startTranscriptionQueueName); - this.startTranscriptionServiceBusSender = startTranscriptionServiceBusClient.CreateSender(startTranscriptionQueueName); - - var fetchTranscriptionServiceBusClient = serviceBusClientFactory.CreateClient(ServiceBusClientName.FetchTranscriptionServiceBusClient.ToString()); - var fetchTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(StartTranscriptionEnvironmentVariables.FetchTranscriptionServiceBusConnectionString).EntityPath; - this.fetchTranscriptionServiceBusSender = fetchTranscriptionServiceBusClient.CreateSender(fetchTranscriptionQueueName); } /// @@ -75,15 +73,9 @@ public async Task Run([TimerTrigger("%StartTranscriptionFunctionTimeInterval%")] this.logger.LogInformation($"C# Isolated Timer trigger function v4 executed at: {startDateTime}. Next occurrence on {timerInfo.ScheduleStatus.Next}."); var validServiceBusMessages = new List(); - var transcriptionHelper = new StartTranscriptionHelper( - this.logger, - this.storageConnector, - this.startTranscriptionServiceBusSender, - this.startTranscriptionServiceBusReceiver, - this.fetchTranscriptionServiceBusSender); this.logger.LogInformation("Pulling messages from queue..."); - var messages = await this.startTranscriptionServiceBusReceiver.ReceiveMessagesAsync(StartTranscriptionEnvironmentVariables.MessagesPerFunctionExecution, TimeSpan.FromSeconds(MessageReceiveTimeoutInSeconds)).ConfigureAwait(false); + var messages = await this.startTranscriptionServiceBusReceiver.ReceiveMessagesAsync(this.appConfig.MessagesPerFunctionExecution, TimeSpan.FromSeconds(MessageReceiveTimeoutInSeconds)).ConfigureAwait(false); if (messages == null || !messages.Any()) { @@ -98,7 +90,7 @@ public async Task Run([TimerTrigger("%StartTranscriptionFunctionTimeInterval%")] { try { - if (transcriptionHelper.IsValidServiceBusMessage(message)) + if (this.transcriptionHelper.IsValidServiceBusMessage(message)) { await this.startTranscriptionServiceBusReceiver.RenewMessageLockAsync(message).ConfigureAwait(false); validServiceBusMessages.Add(message); @@ -123,7 +115,7 @@ public async Task Run([TimerTrigger("%StartTranscriptionFunctionTimeInterval%")] this.logger.LogInformation($"Pulled {validServiceBusMessages.Count} valid messages from queue."); - await transcriptionHelper.StartTranscriptionsAsync(validServiceBusMessages, startDateTime).ConfigureAwait(false); + await this.transcriptionHelper.StartTranscriptionsAsync(validServiceBusMessages, startDateTime).ConfigureAwait(false); } } } diff --git a/samples/ingestion/ingestion-client/StartTranscriptionByTimer/StartTranscriptionEnvironmentVariables.cs b/samples/ingestion/ingestion-client/StartTranscriptionByTimer/StartTranscriptionEnvironmentVariables.cs deleted file mode 100644 index f3194d5c9..000000000 --- a/samples/ingestion/ingestion-client/StartTranscriptionByTimer/StartTranscriptionEnvironmentVariables.cs +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE.md file in the project root for full license information. -// - -namespace StartTranscriptionByTimer -{ - using System; - using Connector; - using Connector.Constants; - - public static class StartTranscriptionEnvironmentVariables - { - public static readonly bool AddDiarization = bool.TryParse(Environment.GetEnvironmentVariable(nameof(AddDiarization), EnvironmentVariableTarget.Process), out AddDiarization) && AddDiarization; - - public static readonly bool AddWordLevelTimestamps = bool.TryParse(Environment.GetEnvironmentVariable(nameof(AddWordLevelTimestamps), EnvironmentVariableTarget.Process), out AddWordLevelTimestamps) && AddWordLevelTimestamps; - - public static readonly bool IsAzureGovDeployment = bool.TryParse(Environment.GetEnvironmentVariable(nameof(IsAzureGovDeployment), EnvironmentVariableTarget.Process), out IsAzureGovDeployment) && IsAzureGovDeployment; - - // BYOS = Bring your own storage (https://docs.microsoft.com/azure/cognitive-services/speech-service/speech-encryption-of-data-at-rest#bring-your-own-storage-byos-for-customization-and-logging) - public static readonly bool IsByosEnabledSubscription = bool.TryParse(Environment.GetEnvironmentVariable(nameof(IsByosEnabledSubscription), EnvironmentVariableTarget.Process), out IsByosEnabledSubscription) && IsByosEnabledSubscription; - - public static readonly int MessagesPerFunctionExecution = int.TryParse(Environment.GetEnvironmentVariable(nameof(MessagesPerFunctionExecution), EnvironmentVariableTarget.Process), out MessagesPerFunctionExecution) ? MessagesPerFunctionExecution.ClampInt(1, Constants.MaxMessagesPerFunctionExecution) : Constants.DefaultMessagesPerFunctionExecution; - - public static readonly int FilesPerTranscriptionJob = int.TryParse(Environment.GetEnvironmentVariable(nameof(FilesPerTranscriptionJob), EnvironmentVariableTarget.Process), out FilesPerTranscriptionJob) ? FilesPerTranscriptionJob.ClampInt(1, Constants.MaxFilesPerTranscriptionJob) : Constants.DefaultFilesPerTranscriptionJob; - - public static readonly int RetryLimit = int.TryParse(Environment.GetEnvironmentVariable(nameof(RetryLimit), EnvironmentVariableTarget.Process), out RetryLimit) ? RetryLimit.ClampInt(1, Constants.MaxRetryLimit) : Constants.DefaultRetryLimit; - - public static readonly int InitialPollingDelayInMinutes = int.TryParse(Environment.GetEnvironmentVariable(nameof(InitialPollingDelayInMinutes), EnvironmentVariableTarget.Process), out InitialPollingDelayInMinutes) ? InitialPollingDelayInMinutes.ClampInt(2, Constants.MaxInitialPollingDelayInMinutes) : Constants.DefaultInitialPollingDelayInMinutes; - - public static readonly int MaxPollingDelayInMinutes = int.TryParse(Environment.GetEnvironmentVariable(nameof(MaxPollingDelayInMinutes), EnvironmentVariableTarget.Process), out MaxPollingDelayInMinutes) ? MaxPollingDelayInMinutes : Constants.DefaultMaxPollingDelayInMinutes; - - public static readonly string AudioInputContainer = Environment.GetEnvironmentVariable(nameof(AudioInputContainer), EnvironmentVariableTarget.Process); - - public static readonly string AzureServiceBus = Environment.GetEnvironmentVariable(nameof(AzureServiceBus), EnvironmentVariableTarget.Process); - - public static readonly string AzureSpeechServicesKey = Environment.GetEnvironmentVariable(nameof(AzureSpeechServicesKey), EnvironmentVariableTarget.Process); - - public static readonly string AzureSpeechServicesEndpointUri = Environment.GetEnvironmentVariable(nameof(AzureSpeechServicesEndpointUri), EnvironmentVariableTarget.Process).TrimEnd('/') + '/'; - - public static readonly string AzureWebJobsStorage = Environment.GetEnvironmentVariable(nameof(AzureWebJobsStorage), EnvironmentVariableTarget.Process); - - public static readonly string CustomModelId = Environment.GetEnvironmentVariable(nameof(CustomModelId), EnvironmentVariableTarget.Process); - - public static readonly string ErrorFilesOutputContainer = Environment.GetEnvironmentVariable(nameof(ErrorFilesOutputContainer), EnvironmentVariableTarget.Process); - - public static readonly string ErrorReportOutputContainer = Environment.GetEnvironmentVariable(nameof(ErrorReportOutputContainer), EnvironmentVariableTarget.Process); - - public static readonly string FetchTranscriptionServiceBusConnectionString = Environment.GetEnvironmentVariable(nameof(FetchTranscriptionServiceBusConnectionString), EnvironmentVariableTarget.Process); - - public static readonly string Locale = Environment.GetEnvironmentVariable(nameof(Locale), EnvironmentVariableTarget.Process); - - public static readonly string ProfanityFilterMode = Environment.GetEnvironmentVariable(nameof(ProfanityFilterMode), EnvironmentVariableTarget.Process); - - public static readonly string PunctuationMode = Environment.GetEnvironmentVariable(nameof(PunctuationMode), EnvironmentVariableTarget.Process); - - public static readonly string StartTranscriptionServiceBusConnectionString = Environment.GetEnvironmentVariable(nameof(StartTranscriptionServiceBusConnectionString), EnvironmentVariableTarget.Process); - - public static readonly string StartTranscriptionFunctionTimeInterval = Environment.GetEnvironmentVariable(nameof(StartTranscriptionFunctionTimeInterval), EnvironmentVariableTarget.Process); - } -} diff --git a/samples/ingestion/ingestion-client/StartTranscriptionByTimer/StartTranscriptionHelper.cs b/samples/ingestion/ingestion-client/StartTranscriptionByTimer/StartTranscriptionHelper.cs index 2a3871b53..b89da2d3b 100644 --- a/samples/ingestion/ingestion-client/StartTranscriptionByTimer/StartTranscriptionHelper.cs +++ b/samples/ingestion/ingestion-client/StartTranscriptionByTimer/StartTranscriptionHelper.cs @@ -16,11 +16,18 @@ namespace StartTranscriptionByTimer using Azure; using Azure.Messaging.ServiceBus; using Connector; + using Connector.Enums; + using Connector.Serializable.TranscriptionStartedServiceBusMessage; + + using Microsoft.Extensions.Azure; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; + using Newtonsoft.Json; - public class StartTranscriptionHelper + public class StartTranscriptionHelper : IStartTranscriptionHelper { private readonly ServiceBusSender startTranscriptionSender; @@ -28,34 +35,33 @@ public class StartTranscriptionHelper private readonly ServiceBusSender fetchTranscriptionSender; - private readonly string subscriptionKey = StartTranscriptionEnvironmentVariables.AzureSpeechServicesKey; - - private readonly string errorReportContaineName = StartTranscriptionEnvironmentVariables.ErrorReportOutputContainer; - - private readonly string audioInputContainerName = StartTranscriptionEnvironmentVariables.AudioInputContainer; - - private readonly int filesPerTranscriptionJob = StartTranscriptionEnvironmentVariables.FilesPerTranscriptionJob; - - private readonly ILogger logger; + private readonly ILogger logger; private readonly string locale; private readonly IStorageConnector storageConnector; + private readonly AppConfig appConfig; + public StartTranscriptionHelper( - ILogger logger, + ILogger logger, IStorageConnector storageConnector, - ServiceBusSender startTranscriptionSender, - ServiceBusReceiver startTranscriptionReceiver, - ServiceBusSender fetchTranscriptionSender) + IAzureClientFactory serviceBusClientFactory, + IOptions appConfig) { this.logger = logger; this.storageConnector = storageConnector; - this.locale = StartTranscriptionEnvironmentVariables.Locale.Split('|')[0].Trim(); - - this.startTranscriptionSender = startTranscriptionSender; - this.startTranscriptionReceiver = startTranscriptionReceiver; - this.fetchTranscriptionSender = fetchTranscriptionSender; + this.appConfig = appConfig?.Value; + this.locale = this.appConfig.Locale.Split('|')[0].Trim(); + + serviceBusClientFactory = serviceBusClientFactory ?? throw new ArgumentNullException(nameof(serviceBusClientFactory)); + var startTranscriptionServiceBusClient = serviceBusClientFactory.CreateClient(ServiceBusClientName.StartTranscriptionServiceBusClient.ToString()); + var startTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(this.appConfig.StartTranscriptionServiceBusConnectionString).EntityPath; + this.startTranscriptionReceiver = startTranscriptionServiceBusClient.CreateReceiver(startTranscriptionQueueName); + this.startTranscriptionSender = startTranscriptionServiceBusClient.CreateSender(startTranscriptionQueueName); + var fetchTranscriptionServiceBusClient = serviceBusClientFactory.CreateClient(ServiceBusClientName.FetchTranscriptionServiceBusClient.ToString()); + var fetchTranscriptionQueueName = ServiceBusConnectionStringProperties.Parse(this.appConfig.FetchTranscriptionServiceBusConnectionString).EntityPath; + this.fetchTranscriptionSender = fetchTranscriptionServiceBusClient.CreateSender(fetchTranscriptionQueueName); } public async Task StartTranscriptionsAsync(IEnumerable messages, DateTime startDateTime) @@ -63,9 +69,9 @@ public async Task StartTranscriptionsAsync(IEnumerable>(); var messageCount = messages.Count(); - for (var i = 0; i < messageCount; i += this.filesPerTranscriptionJob) + for (var i = 0; i < messageCount; i += this.appConfig.FilesPerTranscriptionJob) { - var chunk = messages.Skip(i).Take(Math.Min(this.filesPerTranscriptionJob, messageCount - i)).ToList(); + var chunk = messages.Skip(i).Take(Math.Min(this.appConfig.FilesPerTranscriptionJob, messageCount - i)).ToList(); chunkedMessages.Add(chunk); } @@ -135,7 +141,7 @@ public bool IsValidServiceBusMessage(ServiceBusReceivedMessage message) var serviceBusMessage = JsonConvert.DeserializeObject(messageBody); if (serviceBusMessage.EventType.Contains("BlobCreate", StringComparison.OrdinalIgnoreCase) && - this.storageConnector.GetContainerNameFromUri(serviceBusMessage.Data.Url).Equals(this.audioInputContainerName, StringComparison.Ordinal)) + this.storageConnector.GetContainerNameFromUri(serviceBusMessage.Data.Url).Equals(this.appConfig.AudioInputContainer, StringComparison.Ordinal)) { return true; } @@ -149,18 +155,18 @@ public bool IsValidServiceBusMessage(ServiceBusReceivedMessage message) return false; } - private static TimeSpan GetMessageDelayTime(int pollingCounter) + private static TimeSpan GetMessageDelayTime(int pollingCounter, AppConfig appConfig) { if (pollingCounter == 0) { - return TimeSpan.FromMinutes(StartTranscriptionEnvironmentVariables.InitialPollingDelayInMinutes); + return TimeSpan.FromMinutes(appConfig.InitialPollingDelayInMinutes); } - var updatedDelay = Math.Pow(2, Math.Min(pollingCounter, 8)) * StartTranscriptionEnvironmentVariables.InitialPollingDelayInMinutes; + var updatedDelay = Math.Pow(2, Math.Min(pollingCounter, 8)) * appConfig.InitialPollingDelayInMinutes; - if ((int)updatedDelay > StartTranscriptionEnvironmentVariables.MaxPollingDelayInMinutes) + if ((int)updatedDelay > appConfig.MaxPollingDelayInMinutes) { - return TimeSpan.FromMinutes(StartTranscriptionEnvironmentVariables.MaxPollingDelayInMinutes); + return TimeSpan.FromMinutes(appConfig.MaxPollingDelayInMinutes); } return TimeSpan.FromMinutes(updatedDelay); @@ -200,7 +206,7 @@ private async Task StartBatchTranscriptionJobAsync(IEnumerable(Encoding.UTF8.GetString(message.Body)); - if (serviceBusMessage.RetryCount <= StartTranscriptionEnvironmentVariables.RetryLimit || isThrottled) + if (serviceBusMessage.RetryCount <= this.appConfig.RetryLimit || isThrottled) { serviceBusMessage.RetryCount += 1; - var messageDelay = GetMessageDelayTime(serviceBusMessage.RetryCount); + var messageDelay = GetMessageDelayTime(serviceBusMessage.RetryCount, this.appConfig); var newMessage = new Azure.Messaging.ServiceBus.ServiceBusMessage(JsonConvert.SerializeObject(serviceBusMessage)); await ServiceBusUtilities.SendServiceBusMessageAsync(this.startTranscriptionSender, newMessage, this.logger, messageDelay).ConfigureAwait(false); } @@ -304,7 +310,7 @@ private async Task WriteFailedJobLogToStorageAsync(IEnumerable GetTranscriptionPropertyBag() { var properties = new Dictionary(); - var profanityFilterMode = StartTranscriptionEnvironmentVariables.ProfanityFilterMode; + var profanityFilterMode = this.appConfig.ProfanityFilterMode; properties.Add("ProfanityFilterMode", profanityFilterMode); this.logger.LogInformation($"Setting profanity filter mode to {profanityFilterMode}"); - var punctuationMode = StartTranscriptionEnvironmentVariables.PunctuationMode; + var punctuationMode = this.appConfig.PunctuationMode; punctuationMode = punctuationMode.Replace(" ", string.Empty, StringComparison.Ordinal); properties.Add("PunctuationMode", punctuationMode); this.logger.LogInformation($"Setting punctuation mode to {punctuationMode}"); - var addDiarization = StartTranscriptionEnvironmentVariables.AddDiarization; + var addDiarization = this.appConfig.AddDiarization; properties.Add("DiarizationEnabled", addDiarization.ToString(CultureInfo.InvariantCulture)); this.logger.LogInformation($"Setting diarization enabled to {addDiarization}"); - var addWordLevelTimestamps = StartTranscriptionEnvironmentVariables.AddWordLevelTimestamps; + var addWordLevelTimestamps = this.appConfig.AddWordLevelTimestamps; properties.Add("WordLevelTimestampsEnabled", addWordLevelTimestamps.ToString(CultureInfo.InvariantCulture)); this.logger.LogInformation($"Setting word level timestamps enabled to {addWordLevelTimestamps}"); @@ -342,11 +348,11 @@ private async Task ProcessFailedFileAsync(string fileName, string errorMessage, { try { - await this.storageConnector.WriteTextFileToBlobAsync(errorMessage, this.errorReportContaineName, logFileName).ConfigureAwait(false); + await this.storageConnector.WriteTextFileToBlobAsync(errorMessage, this.appConfig.ErrorReportOutputContainer, logFileName).ConfigureAwait(false); await this.storageConnector.MoveFileAsync( - this.audioInputContainerName, + this.appConfig.AudioInputContainer, fileName, - StartTranscriptionEnvironmentVariables.ErrorFilesOutputContainer, + this.appConfig.ErrorFilesOutputContainer, fileName, false).ConfigureAwait(false); } diff --git a/samples/ingestion/ingestion-client/Tests/EndToEndTests.cs b/samples/ingestion/ingestion-client/Tests/EndToEndTests.cs index c83635d90..e893bdf9a 100644 --- a/samples/ingestion/ingestion-client/Tests/EndToEndTests.cs +++ b/samples/ingestion/ingestion-client/Tests/EndToEndTests.cs @@ -10,7 +10,6 @@ namespace Tests using System.IO; using System.Linq; using System.Threading.Tasks; - using Connector; using Connector.Serializable.Language.Conversations; using Connector.Serializable.TranscriptionStartedServiceBusMessage; @@ -18,24 +17,31 @@ namespace Tests using FetchTranscription; using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; using Microsoft.VisualStudio.TestTools.UnitTesting; - using Moq; - using Newtonsoft.Json; + // using StartTranscriptionByTimer; [TestClass] public class EndToEndTests { - private static TestContext testContext; + private readonly Mock logger; - private static Mock Logger { get; set; } + private readonly IOptions appConfigOptions; - [ClassInitialize] - public static void ClassInitialize(TestContext context) + public EndToEndTests() { - testContext = context ?? throw new ArgumentNullException(nameof(context)); - Logger = new Mock(); + this.logger = new Mock(); + var appConfig = new AppConfig + { + AzureWebJobsStorage = "UseDevelopmentStorage=true", + StartTranscriptionServiceBusConnectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=testkey=", + FetchTranscriptionServiceBusConnectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=testkey=", + TextAnalyticsKey = "sometestkey", + TextAnalyticsEndpoint = "https://sometestendpoint", + }; + this.appConfigOptions = Options.Create(appConfig); } [TestMethod] @@ -52,9 +58,8 @@ public async Task AnalyzeConversationTestAsync() FallbackRole = Role.None, } })); - var region = testContext.Properties["LanguageServiceRegion"].ToString(); - var subscriptionKey = testContext.Properties["LanguageServiceSubscriptionKey"].ToString(); - var provider = new AnalyzeConversationsProvider("en-US", subscriptionKey, region, Logger.Object); + + var provider = new AnalyzeConversationsProvider("en-US", this.appConfigOptions.Value.TextAnalyticsKey, this.appConfigOptions.Value.TextAnalyticsEndpoint, this.logger.Object, this.appConfigOptions); var body = File.ReadAllText(@"TestFiles/summarizationInputSample.json"); var transcription = JsonConvert.DeserializeObject(body); diff --git a/samples/ingestion/ingestion-client/Tests/StartTranscriptionByServiceBus/StartTranscriptionByServiceBusTests.cs b/samples/ingestion/ingestion-client/Tests/StartTranscriptionByServiceBus/StartTranscriptionByServiceBusTests.cs new file mode 100644 index 000000000..f84feeadd --- /dev/null +++ b/samples/ingestion/ingestion-client/Tests/StartTranscriptionByServiceBus/StartTranscriptionByServiceBusTests.cs @@ -0,0 +1,142 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.md file in the project root for full license information. +// + +namespace Tests +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + using Azure.Messaging.ServiceBus; + + using Microsoft.Extensions.Logging; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using Moq; + using StartTranscription; + + using StartTranscriptionByTimer; + + [TestClass] + public class StartTranscriptionByServiceBusTests + { + private readonly Mock> mockLogger; + private readonly Mock mockTranscriptionHelper; + private readonly StartTranscriptionByServiceBus startTranscriptionByServiceBus; + + public StartTranscriptionByServiceBusTests() + { + this.mockLogger = new Mock>(); + this.mockTranscriptionHelper = new Mock(); + this.startTranscriptionByServiceBus = new StartTranscriptionByServiceBus( + this.mockLogger.Object, + this.mockTranscriptionHelper.Object); + } + + [TestMethod] + public async Task FunctionRunValidMessageCallsStartTranscriptionAsync() + { + // Arrange + var messages = new List + { + ServiceBusModelFactory.ServiceBusReceivedMessage( + messageId: "1", + subject: "TestSubject", + sequenceNumber: 123, + lockedUntil: DateTimeOffset.UtcNow.AddMinutes(1), + body: new BinaryData("EventType: TranscriptionStarted")), + }; + + this.mockTranscriptionHelper.Setup(h => h.IsValidServiceBusMessage(It.IsAny())) + .Returns(true); + + // Act + await this.startTranscriptionByServiceBus.Run(messages.First()); + + // Assert + this.mockLogger.Verify( + logger => logger.Log( + It.Is(logLevel => logLevel == LogLevel.Information), + It.IsAny(), + It.Is((v, t) => v.ToString().Contains("C# Isolated ServiceBus queue trigger function processed message: TestSubject")), + It.IsAny(), + It.Is>((v, t) => true)), + Times.Once); + + this.mockLogger.Verify( + logger => logger.Log( + It.Is(logLevel => logLevel == LogLevel.Information), + It.IsAny(), + It.Is((v, t) => v.ToString().Contains("Received message: SequenceNumber:123 Body:EventType: TranscriptionStarted")), + It.IsAny(), + It.Is>((v, t) => true)), + Times.Once); + + this.mockTranscriptionHelper.Verify(helper => helper.StartTranscriptionAsync(It.IsAny()), Times.Once); + } + + [TestMethod] + public async Task FunctionRunInvalidMessageLogsInvalidMessageAndDoesNotCallTranscription() + { + // Arrange + var messages = new List + { + ServiceBusModelFactory.ServiceBusReceivedMessage( + messageId: "1", + subject: "TestSubject", + sequenceNumber: 123, + lockedUntil: DateTimeOffset.UtcNow.AddMinutes(1), + body: new BinaryData("EventType: TranscriptionStarted")), + }; + + this.mockTranscriptionHelper.Setup(h => h.IsValidServiceBusMessage(It.IsAny())) + .Returns(false); + + // Act + await this.startTranscriptionByServiceBus.Run(messages.First()); + + // Assert + this.mockLogger.Verify( + logger => logger.Log( + It.Is(logLevel => logLevel == LogLevel.Information), + It.IsAny(), + It.Is((v, t) => v.ToString().Contains("C# Isolated ServiceBus queue trigger function processed message: TestSubject")), + It.IsAny(), + It.Is>((v, t) => true)), + Times.Once); + + this.mockLogger.Verify( + logger => logger.Log( + It.Is(logLevel => logLevel == LogLevel.Information), + It.IsAny(), + It.Is((v, t) => v.ToString().Contains("Received message: SequenceNumber:123 Body:EventType: TranscriptionStarted")), + It.IsAny(), + It.Is>((v, t) => true)), + Times.Once); + + this.mockLogger.Verify( + logger => logger.Log( + It.Is(logLevel => logLevel == LogLevel.Information), + It.IsAny(), + It.Is((v, t) => v.ToString().Contains("Service bus message is invalid.")), + It.IsAny(), + It.Is>((v, t) => true)), + Times.Once); + + this.mockTranscriptionHelper.Verify(helper => helper.StartTranscriptionAsync(It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task FunctionRunNullMessageThrowsArgumentNullException() + { + // Arrange + ServiceBusReceivedMessage message = null; + + // Act & Assert + await Assert.ThrowsExceptionAsync(() => this.startTranscriptionByServiceBus.Run(message)); + } + } +} \ No newline at end of file diff --git a/samples/ingestion/ingestion-client/Tests/StartTranscriptionByTimer/StartTranscriptionByTimerTests.cs b/samples/ingestion/ingestion-client/Tests/StartTranscriptionByTimer/StartTranscriptionByTimerTests.cs new file mode 100644 index 000000000..d36e977a4 --- /dev/null +++ b/samples/ingestion/ingestion-client/Tests/StartTranscriptionByTimer/StartTranscriptionByTimerTests.cs @@ -0,0 +1,215 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.md file in the project root for full license information. +// + +namespace Tests +{ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + using Azure.Messaging.ServiceBus; + using Connector.Enums; + + using Microsoft.Azure.Functions.Worker; + using Microsoft.Extensions.Azure; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using StartTranscriptionByTimer; + + [TestClass] + public class StartTranscriptionByTimerTests + { + private readonly Mock> mockLogger; + + // private readonly Mock mockStorageConnector; + private readonly Mock> mockServiceBusClientFactory; + private readonly Mock mockServiceBusClient; + private readonly Mock mockServiceBusReceiver; + private readonly Mock mockServiceBusSender; + private readonly IOptions appConfigOptions; + + private readonly Mock mockTranscriptionHelper; + + public StartTranscriptionByTimerTests() + { + this.mockLogger = new Mock>(); + + // this.mockStorageConnector = new Mock(); + this.mockServiceBusClientFactory = new Mock>(); + this.mockServiceBusClient = new Mock(); + this.mockServiceBusReceiver = new Mock(); + this.mockServiceBusSender = new Mock(); + this.mockTranscriptionHelper = new Mock(); + + var appConfig = new AppConfig + { + AzureWebJobsStorage = "UseDevelopmentStorage=true", + StartTranscriptionServiceBusConnectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=testkey=", + FetchTranscriptionServiceBusConnectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=testkey=", + MessagesPerFunctionExecution = 5, + Locale = "en-US | English (United States)", + }; + this.appConfigOptions = Options.Create(appConfig); + + this.mockServiceBusClientFactory + .Setup(f => f.CreateClient(ServiceBusClientName.StartTranscriptionServiceBusClient.ToString())) + .Returns(this.mockServiceBusClient.Object); + + this.mockServiceBusClientFactory + .Setup(f => f.CreateClient(ServiceBusClientName.FetchTranscriptionServiceBusClient.ToString())) + .Returns(this.mockServiceBusClient.Object); + + this.mockServiceBusClient + .Setup(c => c.CreateReceiver(It.IsAny())) + .Returns(this.mockServiceBusReceiver.Object); + + this.mockServiceBusClient + .Setup(c => c.CreateSender(It.IsAny())) + .Returns(this.mockServiceBusSender.Object); + } + + [TestMethod] + public async Task FunctionRunNoMessagesLogsNoMessages() + { + // Arrange + var function = new StartTranscriptionByTimer( + this.mockLogger.Object, + this.appConfigOptions, + this.mockServiceBusClientFactory.Object, + this.mockTranscriptionHelper.Object); + + var timerInfo = new TimerInfo + { + ScheduleStatus = new ScheduleStatus + { + Last = DateTime.UtcNow, + Next = DateTime.UtcNow.AddMinutes(5) + } + }; + + this.mockServiceBusReceiver + .Setup(r => r.ReceiveMessagesAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new List()); + + // Act + await function.Run(timerInfo); + + // Assert + this.mockLogger.Verify( + x => x.Log( + LogLevel.Information, + It.IsAny(), + It.Is((v, t) => v.ToString().Contains("Got no messages in this iteration.")), + null, + It.IsAny>()), + Times.Once); + } + + [TestMethod] + public async Task FunctionRunValidMessagesLogsPulledValidMessages() + { + // Arrange + var function = new StartTranscriptionByTimer( + this.mockLogger.Object, + this.appConfigOptions, + this.mockServiceBusClientFactory.Object, + this.mockTranscriptionHelper.Object); + + var timerInfo = new TimerInfo + { + ScheduleStatus = new ScheduleStatus + { + Last = DateTime.UtcNow, + Next = DateTime.UtcNow.AddMinutes(5) + } + }; + + var messages = new List + { + ServiceBusModelFactory.ServiceBusReceivedMessage( + messageId: "1", + lockedUntil: DateTimeOffset.UtcNow.AddMinutes(1), + body: new BinaryData("EventType: TranscriptionStarted")), + }; + this.mockServiceBusReceiver + .Setup(r => r.ReceiveMessagesAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(messages); + + this.mockTranscriptionHelper + .Setup(t => t.IsValidServiceBusMessage(It.IsAny())) + .Returns(true); + this.mockTranscriptionHelper + .Setup(t => t.StartTranscriptionsAsync(It.IsAny>(), It.IsAny())) + .Returns(Task.CompletedTask); + + // Act + await function.Run(timerInfo); + + // Assert + this.mockLogger.Verify( + x => x.Log( + LogLevel.Information, + It.IsAny(), + It.Is((v, t) => v.ToString().Contains("Pulled 1 valid messages from queue.")), + null, + It.IsAny>()), + Times.Once); + } + + [TestMethod] + public async Task FunctionRunInvalidMessagesLogsPulledNoValidMessages() + { + // Arrange + var function = new StartTranscriptionByTimer( + this.mockLogger.Object, + this.appConfigOptions, + this.mockServiceBusClientFactory.Object, + this.mockTranscriptionHelper.Object); + + var timerInfo = new TimerInfo + { + ScheduleStatus = new ScheduleStatus + { + Last = DateTime.UtcNow, + Next = DateTime.UtcNow.AddMinutes(5) + } + }; + + var messages = new List + { + ServiceBusModelFactory.ServiceBusReceivedMessage( + messageId: "1", + lockedUntil: DateTimeOffset.UtcNow.AddMinutes(1), + body: new BinaryData("EventType: TranscriptionStarted")), + }; + this.mockServiceBusReceiver + .Setup(r => r.ReceiveMessagesAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(messages); + + this.mockTranscriptionHelper + .Setup(t => t.IsValidServiceBusMessage(It.IsAny())) + .Returns(false); + this.mockTranscriptionHelper + .Setup(t => t.StartTranscriptionsAsync(It.IsAny>(), It.IsAny())) + .Returns(Task.CompletedTask); + + // Act + await function.Run(timerInfo); + + // Assert + this.mockLogger.Verify( + x => x.Log( + LogLevel.Information, + It.IsAny(), + It.Is((v, t) => v.ToString().Contains("No valid messages were found in this function execution.")), + null, + It.IsAny>()), + Times.Once); + } + } +} \ No newline at end of file diff --git a/samples/ingestion/ingestion-client/Tests/Tests.csproj b/samples/ingestion/ingestion-client/Tests/Tests.csproj index d0b584328..c0c436124 100644 --- a/samples/ingestion/ingestion-client/Tests/Tests.csproj +++ b/samples/ingestion/ingestion-client/Tests/Tests.csproj @@ -10,6 +10,8 @@ + +