From 15bb84a5e20817febc48a66bc848891a2e217a08 Mon Sep 17 00:00:00 2001 From: Takashi Shinohara Date: Wed, 11 Dec 2024 12:02:49 +0900 Subject: [PATCH] 3.2.0 release --- .github/workflows/build-bot.yml | 24 +--- .github/workflows/build-func.yml | 22 ---- .github/workflows/build-web.yml | 26 +---- .github/workflows/develop.yml | 7 ++ .github/workflows/test.yml | 34 ++++++ .../Bots/ActivityBot.cs | 46 ++++++-- .../ConfigureServices.cs | 16 +-- .../Dialogs/EndMeetingDialog.cs | 28 ++--- .../Dialogs/InMeetingDialog.cs | 24 ++-- .../Dialogs/ResetDialog.cs | 16 +-- .../Dialogs/StartMeetingDialog.cs | 28 ++--- .../Karamem0.Commistant.Bot.csproj | 6 +- .../Mappings/AutoMapperProfile.cs | 32 +++--- .../Karamem0.Commistant.Common/Constants.cs | 35 ++++++ .../Karamem0.Commistant.Common.csproj | 6 +- .../Models/BlobContent.cs | 4 - .../Models/ConversationProperty.cs | 16 +-- ...ents.cs => ConversationPropertyOptions.cs} | 8 +- ...cs => ConversationPropertyOptionsValue.cs} | 6 +- .../Services/DateTimeService.cs | 4 - .../Services/OpenAIService.cs | 6 +- .../{QrCodeService.cs => QRCodeService.cs} | 4 +- .../Commands/EndMeetingCommand.cs | 6 +- .../Commands/InMeetingCommand.cs | 6 +- .../Commands/StartMeetingCommand.cs | 6 +- .../ConfigureServices.cs | 12 +- .../Karamem0.Commistant.Functions.csproj | 6 +- .../Commands/EndMeetingCommandTests.cs | 35 +++--- .../Commands/InMeetingCommandTests.cs | 23 ++-- .../Commands/StartMeetingCommandTests.cs | 35 +++--- .../Dialogs/EndMeetingDialogTests.cs | 108 ++++++++++++++++++ .../Dialogs/InMeetingDialogTests.cs | 108 ++++++++++++++++++ .../Dialogs/StartMeetingDialogTests.cs | 108 ++++++++++++++++++ .../Karamem0.Commistant.Tests.csproj | 10 +- .../ConfigureServices.cs | 7 +- .../Karamem0.Commistant.Web.csproj | 4 +- 36 files changed, 600 insertions(+), 272 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 source/server/Karamem0.Commistant.Common/Constants.cs rename source/server/Karamem0.Commistant.Common/Models/{ConversationPropertyArguments.cs => ConversationPropertyOptions.cs} (68%) rename source/server/Karamem0.Commistant.Common/Models/{ConversationPropertyArgumentsValue.cs => ConversationPropertyOptionsValue.cs} (81%) rename source/server/Karamem0.Commistant.Common/Services/{QrCodeService.cs => QRCodeService.cs} (88%) rename source/server/Karamem0.Commistant.Tests/{Functions => }/Commands/EndMeetingCommandTests.cs (93%) rename source/server/Karamem0.Commistant.Tests/{Functions => }/Commands/InMeetingCommandTests.cs (92%) rename source/server/Karamem0.Commistant.Tests/{Functions => }/Commands/StartMeetingCommandTests.cs (93%) create mode 100644 source/server/Karamem0.Commistant.Tests/Dialogs/EndMeetingDialogTests.cs create mode 100644 source/server/Karamem0.Commistant.Tests/Dialogs/InMeetingDialogTests.cs create mode 100644 source/server/Karamem0.Commistant.Tests/Dialogs/StartMeetingDialogTests.cs diff --git a/.github/workflows/build-bot.yml b/.github/workflows/build-bot.yml index 0c9b0d9..7ff3b4b 100644 --- a/.github/workflows/build-bot.yml +++ b/.github/workflows/build-bot.yml @@ -21,22 +21,13 @@ jobs: shell: pwsh run: | $content = Get-Content -Path ${{env.FILE_PATH}} - $content = $content -replace "{{APP_DOMAIN_NAME}}", "${{vars.APP_DOMAIN_NAME}}" + $content = $content -Replace "{{APP_DOMAIN_NAME}}", "${{vars.APP_DOMAIN_NAME}}" Out-File -FilePath ${{env.FILE_PATH}} -InputObject $content -Encoding UTF8 env: FILE_PATH: source/server/Karamem0.Commistant.Bot/wwwroot/index.html - name: Restore source run: dotnet restore working-directory: source/server - - name: Test source - shell: pwsh - run: | - dotnet test ` - Karamem0.Commistant.Tests/Karamem0.Commistant.Tests.csproj ` - --filter TestCategory=Karamem0.Commistant.Bot ` - -p:AltCover=true ` - -- NUnit.TestOutputXml=${{github.workspace}}/source/server/test - working-directory: source/server - name: Build source shell: pwsh run: | @@ -53,16 +44,3 @@ jobs: name: bot path: source/server/build include-hidden-files: true - - name: Upload test results - uses: enricomi/publish-unit-test-result-action/linux@v2 - if: always() - with: - files: source/server/test/*.xml - check_name: Bot test results - - name: Upload coverage reports - uses: codecov/codecov-action@v4 - if: always() - with: - fail_ci_if_error: true - token: ${{secrets.CODECOV_TOKEN}} - slug: karamem0/commistant diff --git a/.github/workflows/build-func.yml b/.github/workflows/build-func.yml index 6eb4f62..01edf70 100644 --- a/.github/workflows/build-func.yml +++ b/.github/workflows/build-func.yml @@ -20,15 +20,6 @@ jobs: - name: Restore source run: dotnet restore working-directory: source/server - - name: Test source - shell: pwsh - run: | - dotnet test ` - Karamem0.Commistant.Tests/Karamem0.Commistant.Tests.csproj ` - --filter TestCategory=Karamem0.Commistant.Functions ` - -p:AltCover=true ` - -- NUnit.TestOutputXml=${{github.workspace}}/source/server/test - working-directory: source/server - name: Build source shell: pwsh run: | @@ -45,16 +36,3 @@ jobs: name: func path: source/server/build include-hidden-files: true - - name: Upload test results - uses: enricomi/publish-unit-test-result-action/linux@v2 - if: always() - with: - files: source/server/test/*.xml - check_name: Func test results - - name: Upload coverage reports - uses: codecov/codecov-action@v4 - if: always() - with: - fail_ci_if_error: true - token: ${{secrets.CODECOV_TOKEN}} - slug: karamem0/commistant diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 1d2a4f8..1e7818f 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -25,8 +25,8 @@ jobs: shell: pwsh run: | $content = Get-Content -Path ${{env.FILE_PATH}} - $content = $content -replace "{{APP_DOMAIN_NAME}}", "${{vars.APP_DOMAIN_NAME}}" - $content = $content -replace "{{TELEMETRY_CONNECTION_STRING}}", "${{vars.TELEMETRY_CONNECTION_STRING}}" + $content = $content -Replace "{{APP_DOMAIN_NAME}}", "${{vars.APP_DOMAIN_NAME}}" + $content = $content -Replace "{{TELEMETRY_CONNECTION_STRING}}", "${{vars.TELEMETRY_CONNECTION_STRING}}" Out-File -FilePath ${{env.FILE_PATH}} -InputObject $content -Encoding UTF8 env: FILE_PATH: source/client/.env @@ -42,15 +42,6 @@ jobs: - name: Restore server run: dotnet restore working-directory: source/server - - name: Test source - shell: pwsh - run: | - dotnet test ` - Karamem0.Commistant.Tests/Karamem0.Commistant.Tests.csproj ` - --filter TestCategory=Karamem0.Commistant.Web ` - -p:AltCover=true ` - -- NUnit.TestOutputXml=${{github.workspace}}/source/server/test - working-directory: source/server - name: Build source shell: pwsh run: | @@ -67,16 +58,3 @@ jobs: name: web path: source/server/build include-hidden-files: true - - name: Upload test results - uses: enricomi/publish-unit-test-result-action/linux@v2 - if: always() - with: - files: source/server/test/*.xml - check_name: Web test results - - name: Upload coverage reports - uses: codecov/codecov-action@v4 - if: always() - with: - fail_ci_if_error: true - token: ${{secrets.CODECOV_TOKEN}} - slug: karamem0/commistant diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 3cc0367..fc9b0ef 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -4,8 +4,13 @@ on: - develop jobs: + test: + name: Test + uses: ./.github/workflows/test.yml + secrets: inherit build-bot: name: Build bot + needs: test uses: ./.github/workflows/build-bot.yml secrets: inherit with: @@ -19,6 +24,7 @@ jobs: environment: dev build-web: name: Build web + needs: test uses: ./.github/workflows/build-web.yml secrets: inherit with: @@ -32,6 +38,7 @@ jobs: environment: dev build-func: name: Build func + needs: test uses: ./.github/workflows/build-func.yml secrets: inherit with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..04c99af --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,34 @@ +on: workflow_call + +jobs: + run: + name: Run + runs-on: ubuntu-latest + steps: + - name: Checkout repos + uses: actions/checkout@v4 + - name: Setup .NET Core + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.x + - name: Test source + shell: pwsh + run: | + dotnet test ` + Karamem0.Commistant.Tests/Karamem0.Commistant.Tests.csproj ` + -p:AltCover=true ` + -- NUnit.TestOutputXml=${{github.workspace}}/source/server/test + working-directory: source/server + - name: Test results + uses: enricomi/publish-unit-test-result-action/linux@v2 + if: always() + with: + files: source/server/test/*.xml + check_name: Bot test results + - name: Upload coverage reports + uses: codecov/codecov-action@v4 + if: always() + with: + fail_ci_if_error: true + token: ${{secrets.CODECOV_TOKEN}} + slug: karamem0/commistant diff --git a/source/server/Karamem0.Commistant.Bot/Bots/ActivityBot.cs b/source/server/Karamem0.Commistant.Bot/Bots/ActivityBot.cs index fd27088..75e5eab 100644 --- a/source/server/Karamem0.Commistant.Bot/Bots/ActivityBot.cs +++ b/source/server/Karamem0.Commistant.Bot/Bots/ActivityBot.cs @@ -28,7 +28,7 @@ namespace Karamem0.Commistant.Bots; public class ActivityBot( ConversationState conversationState, DialogSet dialogSet, - OpenAIService openAIService, + IOpenAIService openAIService, ILogger logger ) : TeamsActivityHandler { @@ -37,7 +37,7 @@ ILogger logger private readonly DialogSet dialogSet = dialogSet; - private readonly OpenAIService openAIService = openAIService; + private readonly IOpenAIService openAIService = openAIService; private readonly ILogger logger = logger; @@ -46,7 +46,11 @@ public override async Task OnTurnAsync(ITurnContext turnContext, CancellationTok await base.OnTurnAsync(turnContext, cancellationToken); } - protected override async Task OnMembersAddedAsync(IList membersAdded, ITurnContext turnContext, CancellationToken cancellationToken) + protected override async Task OnMembersAddedAsync( + IList membersAdded, + ITurnContext turnContext, + CancellationToken cancellationToken = default + ) { foreach (var member in membersAdded) { @@ -68,7 +72,11 @@ protected override async Task OnMembersAddedAsync(IList membersA await base.OnMembersAddedAsync(membersAdded, turnContext, cancellationToken); } - protected override async Task OnMembersRemovedAsync(IList membersRemoved, ITurnContext turnContext, CancellationToken cancellationToken) + protected override async Task OnMembersRemovedAsync( + IList membersRemoved, + ITurnContext turnContext, + CancellationToken cancellationToken = default + ) { foreach (var member in membersRemoved) { @@ -80,7 +88,10 @@ protected override async Task OnMembersRemovedAsync(IList member await base.OnMembersRemovedAsync(membersRemoved, turnContext, cancellationToken); } - protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) + protected override async Task OnMessageActivityAsync( + ITurnContext turnContext, + CancellationToken cancellationToken = default + ) { var participant = await TeamsInfo.GetMeetingParticipantAsync( turnContext, @@ -102,13 +113,28 @@ protected override async Task OnMessageActivityAsync(ITurnContext await dc.BeginDialogAsync(nameof(StartMeetingDialog), arguments, cancellationToken: cancellationToken), - "会議終了前" => await dc.BeginDialogAsync(nameof(EndMeetingDialog), arguments, cancellationToken: cancellationToken), - "会議中" => await dc.BeginDialogAsync(nameof(InMeetingDialog), arguments, cancellationToken: cancellationToken), - "初期化" => await dc.BeginDialogAsync(nameof(ResetDialog), cancellationToken: cancellationToken), + Constants.StartMeetingCommand => await dc.BeginDialogAsync( + nameof(StartMeetingDialog), + arguments, + cancellationToken: cancellationToken + ), + Constants.EndMeetingCommand => await dc.BeginDialogAsync( + nameof(EndMeetingDialog), + arguments, + cancellationToken: cancellationToken + ), + Constants.InMeetingCommand => await dc.BeginDialogAsync( + nameof(InMeetingDialog), + arguments, + cancellationToken: cancellationToken + ), + Constants.ResetCommand => await dc.BeginDialogAsync( + nameof(ResetDialog), + cancellationToken: cancellationToken + ), _ => null, }; if (result is null) diff --git a/source/server/Karamem0.Commistant.Bot/ConfigureServices.cs b/source/server/Karamem0.Commistant.Bot/ConfigureServices.cs index d57c175..b3a2e59 100644 --- a/source/server/Karamem0.Commistant.Bot/ConfigureServices.cs +++ b/source/server/Karamem0.Commistant.Bot/ConfigureServices.cs @@ -24,6 +24,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Threading.Tasks; namespace Karamem0.Commistant; @@ -33,7 +34,7 @@ public static class ConfigureServices public static IServiceCollection AddBots(this IServiceCollection services, IConfiguration configuration) { - var blobContainerUrl = configuration.GetValue("AzureBotStatesStorageUrl") ?? throw new InvalidOperationException(); + var blobContainerUrl = configuration["AzureBotStatesStorageUrl"] ?? throw new InvalidOperationException(); _ = services.AddSingleton(); _ = services.AddSingleton(); _ = services.AddSingleton(new BlobsStorage( @@ -58,24 +59,25 @@ public static IServiceCollection AddDialogs(this IServiceCollection services) .Add(provider.GetService()) .Add(provider.GetService()) .Add(provider.GetService()) - .Add(provider.GetService())); + .Add(provider.GetService()) + ); return services; } public static IServiceCollection AddServices(this IServiceCollection services, IConfiguration configuration) { - var openAIEndpointUrl = configuration.GetValue("AzureOpenAIEndpointUrl") ?? throw new InvalidOperationException(); - var openAIModelName = configuration.GetValue("AzureOpenAIModelName") ?? throw new InvalidOperationException(); + var openAIEndpointUrl = configuration["AzureOpenAIEndpointUrl"] ?? throw new InvalidOperationException(); + var openAIModelName = configuration["AzureOpenAIModelName"] ?? throw new InvalidOperationException(); _ = services.AddScoped(provider => new AzureOpenAIClient( new Uri(openAIEndpointUrl), new DefaultAzureCredential() )); - _ = services.AddScoped(provider => new OpenAIService( - provider.GetRequiredService() ?? throw new InvalidOperationException(), + _ = services.AddScoped(provider => new OpenAIService( + provider.GetRequiredService(), openAIModelName )); _ = services.AddScoped(); - _ = services.AddScoped(); + _ = services.AddScoped(); return services; } diff --git a/source/server/Karamem0.Commistant.Bot/Dialogs/EndMeetingDialog.cs b/source/server/Karamem0.Commistant.Bot/Dialogs/EndMeetingDialog.cs index 7c1c592..e9cb583 100644 --- a/source/server/Karamem0.Commistant.Bot/Dialogs/EndMeetingDialog.cs +++ b/source/server/Karamem0.Commistant.Bot/Dialogs/EndMeetingDialog.cs @@ -30,7 +30,7 @@ namespace Karamem0.Commistant.Dialogs; public class EndMeetingDialog( ConversationState conversationState, - QrCodeService qrCodeService, + IQRCodeService qrCodeService, IMapper mapper, ILogger logger ) : ComponentDialog @@ -38,7 +38,7 @@ ILogger logger private readonly ConversationState conversationState = conversationState; - private readonly QrCodeService qrCodeService = qrCodeService; + private readonly IQRCodeService qrCodeService = qrCodeService; private readonly IMapper mapper = mapper; @@ -49,20 +49,20 @@ protected override async Task OnInitializeAsync(DialogContext dc) _ = this.AddDialog(new WaterfallDialog( nameof(WaterfallDialog), [ - this.BeforeConfirmAsync, - this.AfterConrifmAsync + this.OnBeforeAsync, + this.OnAfterAsync ] )); - _ = this.AddDialog(new TextPrompt(nameof(this.BeforeConfirmAsync), AdaptiveCardvalidator.Validate)); + _ = this.AddDialog(new TextPrompt(nameof(this.OnBeforeAsync), AdaptiveCardvalidator.Validate)); await base.OnInitializeAsync(dc); } - private async Task BeforeConfirmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) + private async Task OnBeforeAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default) { var accessor = this.conversationState.CreateProperty(nameof(ConversationProperty)); var property = await accessor.GetAsync(stepContext.Context, () => new(), cancellationToken); - var options = (ConversationPropertyArguments?)stepContext.Options; - var value = this.mapper.Map(options, property.Clone()); + var options = (ConversationPropertyOptions?)stepContext.Options; + var value = this.mapper.Map(options, property with {}); var card = new AdaptiveCard("1.3") { Body = @@ -107,7 +107,7 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste Id = "Message", IsMultiline = true, Label = "メッセージ", - Placeholder = "会議後に表示されるメッセージ", + Placeholder = "会議終了前に表示されるメッセージ", Style = AdaptiveTextInputStyle.Text, Value = value.EndMeetingMessage }, @@ -115,7 +115,7 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste { Id = "Url", Label = "URL", - Placeholder = "会議後に表示されるリンクの URL", + Placeholder = "会議終了前に表示されるリンクの URL", Style = AdaptiveTextInputStyle.Url, Value = value.EndMeetingUrl } @@ -145,11 +145,11 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste var activity = MessageFactory.Attachment(new Attachment() { ContentType = AdaptiveCard.ContentType, - Content = JsonConvert.DeserializeObject(card.ToJson()) + Content = card }); this.logger.SettingsUpdating(stepContext.Context.Activity); return await stepContext.PromptAsync( - nameof(this.BeforeConfirmAsync), + nameof(this.OnBeforeAsync), new PromptOptions() { Prompt = (Activity)activity @@ -158,7 +158,7 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste ); } - private async Task AfterConrifmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) + private async Task OnAfterAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default) { var value = (JObject)stepContext.Context.Activity.Value; if (value is null) @@ -300,7 +300,7 @@ private async Task AfterConrifmAsync(WaterfallStepContext step var activity = MessageFactory.Attachment(new Attachment() { ContentType = AdaptiveCard.ContentType, - Content = JsonConvert.DeserializeObject(card.ToJson()) + Content = card }); activity.Id = stepContext.Context.Activity.ReplyToId; _ = await stepContext.Context.UpdateActivityAsync(activity, cancellationToken); diff --git a/source/server/Karamem0.Commistant.Bot/Dialogs/InMeetingDialog.cs b/source/server/Karamem0.Commistant.Bot/Dialogs/InMeetingDialog.cs index eb167d2..48cffed 100644 --- a/source/server/Karamem0.Commistant.Bot/Dialogs/InMeetingDialog.cs +++ b/source/server/Karamem0.Commistant.Bot/Dialogs/InMeetingDialog.cs @@ -30,7 +30,7 @@ namespace Karamem0.Commistant.Dialogs; public class InMeetingDialog( ConversationState conversationState, - QrCodeService qrCodeService, + IQRCodeService qrCodeService, IMapper mapper, ILogger logger ) : ComponentDialog @@ -38,7 +38,7 @@ ILogger logger private readonly ConversationState conversationState = conversationState; - private readonly QrCodeService qrCodeService = qrCodeService; + private readonly IQRCodeService qrCodeService = qrCodeService; private readonly IMapper mapper = mapper; @@ -49,20 +49,20 @@ protected override async Task OnInitializeAsync(DialogContext dc) _ = this.AddDialog(new WaterfallDialog( nameof(WaterfallDialog), [ - this.BeforeConfirmAsync, - this.AfterConrifmAsync + this.OnBeforeAsync, + this.OnAfterAsync ] )); - _ = this.AddDialog(new TextPrompt(nameof(this.BeforeConfirmAsync), AdaptiveCardvalidator.Validate)); + _ = this.AddDialog(new TextPrompt(nameof(this.OnBeforeAsync), AdaptiveCardvalidator.Validate)); await base.OnInitializeAsync(dc); } - private async Task BeforeConfirmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) + private async Task OnBeforeAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default) { var accessor = this.conversationState.CreateProperty(nameof(ConversationProperty)); var property = await accessor.GetAsync(stepContext.Context, () => new(), cancellationToken); - var options = (ConversationPropertyArguments?)stepContext.Options; - var value = this.mapper.Map(options, property.Clone()); + var options = (ConversationPropertyOptions?)stepContext.Options; + var value = this.mapper.Map(options, property with {}); var card = new AdaptiveCard("1.3") { Body = @@ -150,11 +150,11 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste var activity = MessageFactory.Attachment(new Attachment() { ContentType = AdaptiveCard.ContentType, - Content = JsonConvert.DeserializeObject(card.ToJson()) + Content = card }); this.logger.SettingsUpdating(stepContext.Context.Activity); return await stepContext.PromptAsync( - nameof(this.BeforeConfirmAsync), + nameof(this.OnBeforeAsync), new PromptOptions() { Prompt = (Activity)activity @@ -163,7 +163,7 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste ); } - private async Task AfterConrifmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) + private async Task OnAfterAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default) { var value = (JObject)stepContext.Context.Activity.Value; if (value is null) @@ -305,7 +305,7 @@ private async Task AfterConrifmAsync(WaterfallStepContext step var activity = MessageFactory.Attachment(new Attachment() { ContentType = AdaptiveCard.ContentType, - Content = JsonConvert.DeserializeObject(card.ToJson()) + Content = card }); activity.Id = stepContext.Context.Activity.ReplyToId; _ = await stepContext.Context.UpdateActivityAsync(activity, cancellationToken); diff --git a/source/server/Karamem0.Commistant.Bot/Dialogs/ResetDialog.cs b/source/server/Karamem0.Commistant.Bot/Dialogs/ResetDialog.cs index 33a53b5..4c14a44 100644 --- a/source/server/Karamem0.Commistant.Bot/Dialogs/ResetDialog.cs +++ b/source/server/Karamem0.Commistant.Bot/Dialogs/ResetDialog.cs @@ -38,15 +38,15 @@ protected override async Task OnInitializeAsync(DialogContext dc) _ = this.AddDialog(new WaterfallDialog( nameof(WaterfallDialog), [ - this.BeforeConfirmAsync, - this.AfterConrifmAsync + this.OnBeforeAsync, + this.OnAfterAsync ] )); - _ = this.AddDialog(new TextPrompt(nameof(this.BeforeConfirmAsync), AdaptiveCardvalidator.Validate)); + _ = this.AddDialog(new TextPrompt(nameof(this.OnBeforeAsync), AdaptiveCardvalidator.Validate)); await base.OnInitializeAsync(dc); } - private async Task BeforeConfirmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) + private async Task OnBeforeAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default) { this.logger.SettingsResetting(stepContext.Context.Activity); var card = new AdaptiveCard("1.3") @@ -85,11 +85,11 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste var activity = MessageFactory.Attachment(new Attachment() { ContentType = AdaptiveCard.ContentType, - Content = JsonConvert.DeserializeObject(card.ToJson()) + Content = card }); this.logger.SettingsResetting(stepContext.Context.Activity); return await stepContext.PromptAsync( - nameof(this.BeforeConfirmAsync), + nameof(this.OnBeforeAsync), new PromptOptions() { Prompt = (Activity)activity @@ -98,7 +98,7 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste ); } - private async Task AfterConrifmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) + private async Task OnAfterAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default) { var value = (JObject)stepContext.Context.Activity.Value; if (value is null) @@ -144,7 +144,7 @@ private async Task AfterConrifmAsync(WaterfallStepContext step var activity = MessageFactory.Attachment(new Attachment() { ContentType = AdaptiveCard.ContentType, - Content = JsonConvert.DeserializeObject(card.ToJson()) + Content = card }); activity.Id = stepContext.Context.Activity.ReplyToId; _ = await stepContext.Context.UpdateActivityAsync(activity, cancellationToken); diff --git a/source/server/Karamem0.Commistant.Bot/Dialogs/StartMeetingDialog.cs b/source/server/Karamem0.Commistant.Bot/Dialogs/StartMeetingDialog.cs index dcd1f4c..d6fdf0d 100644 --- a/source/server/Karamem0.Commistant.Bot/Dialogs/StartMeetingDialog.cs +++ b/source/server/Karamem0.Commistant.Bot/Dialogs/StartMeetingDialog.cs @@ -30,7 +30,7 @@ namespace Karamem0.Commistant.Dialogs; public class StartMeetingDialog( ConversationState conversationState, - QrCodeService qrCodeService, + IQRCodeService qrCodeService, IMapper mapper, ILogger logger ) : ComponentDialog @@ -38,7 +38,7 @@ ILogger logger private readonly ConversationState conversationState = conversationState; - private readonly QrCodeService qrCodeService = qrCodeService; + private readonly IQRCodeService qrCodeService = qrCodeService; private readonly IMapper mapper = mapper; @@ -49,20 +49,20 @@ protected override async Task OnInitializeAsync(DialogContext dc) _ = this.AddDialog(new WaterfallDialog( nameof(WaterfallDialog), [ - this.BeforeConfirmAsync, - this.AfterConrifmAsync + this.OnBeforeAsync, + this.OnAfterAsync ] )); - _ = this.AddDialog(new TextPrompt(nameof(this.BeforeConfirmAsync), AdaptiveCardvalidator.Validate)); + _ = this.AddDialog(new TextPrompt(nameof(this.OnBeforeAsync), AdaptiveCardvalidator.Validate)); await base.OnInitializeAsync(dc); } - private async Task BeforeConfirmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) + private async Task OnBeforeAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default) { var accessor = this.conversationState.CreateProperty(nameof(ConversationProperty)); var property = await accessor.GetAsync(stepContext.Context, () => new(), cancellationToken); - var options = (ConversationPropertyArguments?)stepContext.Options; - var value = this.mapper.Map(options, property.Clone()); + var options = (ConversationPropertyOptions?)stepContext.Options; + var value = this.mapper.Map(options, property with {}); var card = new AdaptiveCard("1.3") { Body = @@ -107,7 +107,7 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste Id = "Message", IsMultiline = true, Label = "メッセージ", - Placeholder = "開始後に表示されるメッセージ", + Placeholder = "会議開始後に表示されるメッセージ", Style = AdaptiveTextInputStyle.Text, Value = value.StartMeetingMessage }, @@ -115,7 +115,7 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste { Id = "Url", Label = "URL", - Placeholder = "開始後に表示されるリンクの URL", + Placeholder = "会議開始後に表示されるリンクの URL", Style = AdaptiveTextInputStyle.Url, Value = value.StartMeetingUrl } @@ -145,11 +145,11 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste var activity = MessageFactory.Attachment(new Attachment() { ContentType = AdaptiveCard.ContentType, - Content = JsonConvert.DeserializeObject(card.ToJson()) + Content = card }); this.logger.SettingsUpdating(stepContext.Context.Activity); return await stepContext.PromptAsync( - nameof(this.BeforeConfirmAsync), + nameof(this.OnBeforeAsync), new PromptOptions() { Prompt = (Activity)activity @@ -158,7 +158,7 @@ private async Task BeforeConfirmAsync(WaterfallStepContext ste ); } - private async Task AfterConrifmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) + private async Task OnAfterAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default) { var value = (JObject)stepContext.Context.Activity.Value; if (value is null) @@ -300,7 +300,7 @@ private async Task AfterConrifmAsync(WaterfallStepContext step var activity = MessageFactory.Attachment(new Attachment() { ContentType = AdaptiveCard.ContentType, - Content = JsonConvert.DeserializeObject(card.ToJson()) + Content = card }); activity.Id = stepContext.Context.Activity.ReplyToId; _ = await stepContext.Context.UpdateActivityAsync(activity, cancellationToken); diff --git a/source/server/Karamem0.Commistant.Bot/Karamem0.Commistant.Bot.csproj b/source/server/Karamem0.Commistant.Bot/Karamem0.Commistant.Bot.csproj index 5f41506..096c538 100644 --- a/source/server/Karamem0.Commistant.Bot/Karamem0.Commistant.Bot.csproj +++ b/source/server/Karamem0.Commistant.Bot/Karamem0.Commistant.Bot.csproj @@ -22,14 +22,14 @@ - + - + - + diff --git a/source/server/Karamem0.Commistant.Bot/Mappings/AutoMapperProfile.cs b/source/server/Karamem0.Commistant.Bot/Mappings/AutoMapperProfile.cs index 80bb00a..27d1f8d 100644 --- a/source/server/Karamem0.Commistant.Bot/Mappings/AutoMapperProfile.cs +++ b/source/server/Karamem0.Commistant.Bot/Mappings/AutoMapperProfile.cs @@ -19,20 +19,14 @@ namespace Karamem0.Commistant.Mappings; public class AutoMapperProfile : Profile { - private static readonly int[] StartMeetingSchedules = [0, 5, 10, 15]; - - private static readonly int[] EndMeetingSchedules = [0, 5, 10, 15]; - - private static readonly int[] InMeetingSchedules = [5, 10, 15, 30, 60]; - public AutoMapperProfile() { - _ = this.CreateMap() + _ = this.CreateMap() .ForMember( d => d.StartMeetingSchedule, o => { - o.Condition(s => s.Type == "会議開始後"); + o.Condition(s => s.Type == Constants.StartMeetingCommand); o.MapFrom((s, d) => { if (s.Value is null) @@ -43,7 +37,7 @@ public AutoMapperProfile() { return d.StartMeetingSchedule; } - if (Array.TrueForAll(StartMeetingSchedules, _ => _ != s.Value.Schedule)) + if (Constants.StartMeetingSchedules.Any(_ => _ == s.Value.Schedule)) { return d.StartMeetingSchedule; } @@ -55,7 +49,7 @@ public AutoMapperProfile() d => d.StartMeetingMessage, o => { - o.Condition(s => s.Type == "会議開始後"); + o.Condition(s => s.Type == Constants.StartMeetingCommand); o.MapFrom((s, d) => { if (s.Value is null) @@ -78,7 +72,7 @@ public AutoMapperProfile() d => d.StartMeetingUrl, o => { - o.Condition(s => s.Type == "会議開始後"); + o.Condition(s => s.Type == Constants.StartMeetingCommand); o.MapFrom((s, d) => { if (s.Value is null) @@ -101,7 +95,7 @@ public AutoMapperProfile() d => d.EndMeetingSchedule, o => { - o.Condition(s => s.Type == "会議終了前"); + o.Condition(s => s.Type == Constants.EndMeetingCommand); o.MapFrom((s, d) => { if (s.Value is null) @@ -112,7 +106,7 @@ public AutoMapperProfile() { return d.EndMeetingSchedule; } - if (Array.TrueForAll(EndMeetingSchedules, _ => _ != s.Value.Schedule)) + if (Constants.EndMeetingSchedules.Any(_ => _ == s.Value.Schedule)) { return d.EndMeetingSchedule; } @@ -124,7 +118,7 @@ public AutoMapperProfile() d => d.EndMeetingMessage, o => { - o.Condition(s => s.Type == "会議終了前"); + o.Condition(s => s.Type == Constants.EndMeetingCommand); o.MapFrom((s, d) => { if (s.Value is null) @@ -147,7 +141,7 @@ public AutoMapperProfile() d => d.EndMeetingUrl, o => { - o.Condition(s => s.Type == "会議終了前"); + o.Condition(s => s.Type == Constants.EndMeetingCommand); o.MapFrom((s, d) => { if (s.Value is null) @@ -170,7 +164,7 @@ public AutoMapperProfile() d => d.InMeetingSchedule, o => { - o.Condition(s => s.Type == "会議中"); + o.Condition(s => s.Type == Constants.InMeetingCommand); o.MapFrom((s, d) => { if (s.Value is null) @@ -181,7 +175,7 @@ public AutoMapperProfile() { return d.InMeetingSchedule; } - if (Array.TrueForAll(InMeetingSchedules, _ => _ != s.Value.Schedule)) + if (Constants.InMeetingSchedules.Any(_ => _ == s.Value.Schedule)) { return d.InMeetingSchedule; } @@ -193,7 +187,7 @@ public AutoMapperProfile() d => d.InMeetingMessage, o => { - o.Condition(s => s.Type == "会議中"); + o.Condition(s => s.Type == Constants.InMeetingCommand); o.MapFrom((s, d) => { if (s.Value is null) @@ -216,7 +210,7 @@ public AutoMapperProfile() d => d.InMeetingUrl, o => { - o.Condition(s => s.Type == "会議中"); + o.Condition(s => s.Type == Constants.InMeetingCommand); o.MapFrom((s, d) => { if (s.Value is null) diff --git a/source/server/Karamem0.Commistant.Common/Constants.cs b/source/server/Karamem0.Commistant.Common/Constants.cs new file mode 100644 index 0000000..28308ff --- /dev/null +++ b/source/server/Karamem0.Commistant.Common/Constants.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) 2022-2024 karamem0 +// +// This software is released under the MIT License. +// +// https://github.com/karamem0/commistant/blob/main/LICENSE +// + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Karamem0.Commistant; + +public static class Constants +{ + + public const string StartMeetingCommand = "会議開始後"; + + public const string EndMeetingCommand = "会議終了前"; + + public const string InMeetingCommand = "会議中"; + + public const string ResetCommand = "初期化"; + + public static readonly ReadOnlyCollection StartMeetingSchedules = Array.AsReadOnly([0, 5, 10, 15]); + + public static readonly ReadOnlyCollection EndMeetingSchedules = Array.AsReadOnly([0, 5, 10, 15]); + + public static readonly ReadOnlyCollection InMeetingSchedules = Array.AsReadOnly([5, 10, 15, 30, 60]); + +} diff --git a/source/server/Karamem0.Commistant.Common/Karamem0.Commistant.Common.csproj b/source/server/Karamem0.Commistant.Common/Karamem0.Commistant.Common.csproj index 2fd8acd..22e32f4 100644 --- a/source/server/Karamem0.Commistant.Common/Karamem0.Commistant.Common.csproj +++ b/source/server/Karamem0.Commistant.Common/Karamem0.Commistant.Common.csproj @@ -20,11 +20,11 @@ - - + + - + diff --git a/source/server/Karamem0.Commistant.Common/Models/BlobContent.cs b/source/server/Karamem0.Commistant.Common/Models/BlobContent.cs index 0e50180..1b87ef5 100644 --- a/source/server/Karamem0.Commistant.Common/Models/BlobContent.cs +++ b/source/server/Karamem0.Commistant.Common/Models/BlobContent.cs @@ -18,10 +18,6 @@ namespace Karamem0.Commistant.Models; public class BlobContent { - public BlobContent() - { - } - public T? Data { get; set; } public ETag? ETag { get; set; } diff --git a/source/server/Karamem0.Commistant.Common/Models/ConversationProperty.cs b/source/server/Karamem0.Commistant.Common/Models/ConversationProperty.cs index fec8b93..2df4324 100644 --- a/source/server/Karamem0.Commistant.Common/Models/ConversationProperty.cs +++ b/source/server/Karamem0.Commistant.Common/Models/ConversationProperty.cs @@ -14,13 +14,9 @@ namespace Karamem0.Commistant.Models; -public class ConversationProperty : ICloneable +public record ConversationProperty { - public ConversationProperty() - { - } - public bool StartMeetingSended { get; set; } public int StartMeetingSchedule { get; set; } = -1; @@ -49,14 +45,4 @@ public ConversationProperty() public DateTime? ScheduledStartTime { get; set; } - object ICloneable.Clone() - { - return this.MemberwiseClone(); - } - - public ConversationProperty Clone() - { - return (ConversationProperty)this.MemberwiseClone(); - } - } diff --git a/source/server/Karamem0.Commistant.Common/Models/ConversationPropertyArguments.cs b/source/server/Karamem0.Commistant.Common/Models/ConversationPropertyOptions.cs similarity index 68% rename from source/server/Karamem0.Commistant.Common/Models/ConversationPropertyArguments.cs rename to source/server/Karamem0.Commistant.Common/Models/ConversationPropertyOptions.cs index 44697a5..4ce77f7 100644 --- a/source/server/Karamem0.Commistant.Common/Models/ConversationPropertyArguments.cs +++ b/source/server/Karamem0.Commistant.Common/Models/ConversationPropertyOptions.cs @@ -14,15 +14,11 @@ namespace Karamem0.Commistant.Models; -public class ConversationPropertyArguments +public record ConversationPropertyOptions { - public ConversationPropertyArguments() - { - } - public string? Type { get; set; } - public ConversationPropertyArgumentsValue? Value { get; set; } + public ConversationPropertyOptionsValue? Value { get; set; } } diff --git a/source/server/Karamem0.Commistant.Common/Models/ConversationPropertyArgumentsValue.cs b/source/server/Karamem0.Commistant.Common/Models/ConversationPropertyOptionsValue.cs similarity index 81% rename from source/server/Karamem0.Commistant.Common/Models/ConversationPropertyArgumentsValue.cs rename to source/server/Karamem0.Commistant.Common/Models/ConversationPropertyOptionsValue.cs index 3447a78..16b919c 100644 --- a/source/server/Karamem0.Commistant.Common/Models/ConversationPropertyArgumentsValue.cs +++ b/source/server/Karamem0.Commistant.Common/Models/ConversationPropertyOptionsValue.cs @@ -14,13 +14,9 @@ namespace Karamem0.Commistant.Models; -public class ConversationPropertyArgumentsValue +public record ConversationPropertyOptionsValue { - public ConversationPropertyArgumentsValue() - { - } - public bool Enabled { get; set; } public int Schedule { get; set; } = -1; diff --git a/source/server/Karamem0.Commistant.Common/Services/DateTimeService.cs b/source/server/Karamem0.Commistant.Common/Services/DateTimeService.cs index a436973..af06ab1 100644 --- a/source/server/Karamem0.Commistant.Common/Services/DateTimeService.cs +++ b/source/server/Karamem0.Commistant.Common/Services/DateTimeService.cs @@ -24,10 +24,6 @@ public interface IDateTimeService public class DateTimeService : IDateTimeService { - public DateTimeService() - { - } - public DateTime GetCurrentDateTime() { return DateTime.UtcNow; diff --git a/source/server/Karamem0.Commistant.Common/Services/OpenAIService.cs b/source/server/Karamem0.Commistant.Common/Services/OpenAIService.cs index 5b3ea68..40ed302 100644 --- a/source/server/Karamem0.Commistant.Common/Services/OpenAIService.cs +++ b/source/server/Karamem0.Commistant.Common/Services/OpenAIService.cs @@ -23,7 +23,7 @@ namespace Karamem0.Commistant.Services; public interface IOpenAIService { - Task GetArgumentsAsync(string text, CancellationToken cancellationToken = default); + Task GetConversationPropertyOptionsAsync(string text, CancellationToken cancellationToken = default); } @@ -34,7 +34,7 @@ public class OpenAIService(OpenAIClient openAIClient, string openAIModelName) : private readonly string openAIModelName = openAIModelName; - public async Task GetArgumentsAsync(string text, CancellationToken cancellationToken = default) + public async Task GetConversationPropertyOptionsAsync(string text, CancellationToken cancellationToken = default) { var chatCompletionsOptions = new ChatCompletionOptions() { @@ -72,7 +72,7 @@ public class OpenAIService(OpenAIClient openAIClient, string openAIModelName) : ); if (chatCompletion.Value.FinishReason == ChatFinishReason.ToolCalls) { - return JsonConvert.DeserializeObject( + return JsonConvert.DeserializeObject( chatCompletion.Value.ToolCalls .Select(item => item.FunctionArguments) .First() diff --git a/source/server/Karamem0.Commistant.Common/Services/QrCodeService.cs b/source/server/Karamem0.Commistant.Common/Services/QRCodeService.cs similarity index 88% rename from source/server/Karamem0.Commistant.Common/Services/QrCodeService.cs rename to source/server/Karamem0.Commistant.Common/Services/QRCodeService.cs index 1975b69..3470e5b 100644 --- a/source/server/Karamem0.Commistant.Common/Services/QrCodeService.cs +++ b/source/server/Karamem0.Commistant.Common/Services/QRCodeService.cs @@ -16,14 +16,14 @@ namespace Karamem0.Commistant.Services; -public interface IQrCodeService +public interface IQRCodeService { Task CreateAsync(string text, CancellationToken cancellationToken = default); } -public class QrCodeService(QRCodeGenerator qrCodeGenerator) : IQrCodeService +public class QRCodeService(QRCodeGenerator qrCodeGenerator) : IQRCodeService { private readonly QRCodeGenerator qrCodeGenerator = qrCodeGenerator; diff --git a/source/server/Karamem0.Commistant.Functions/Commands/EndMeetingCommand.cs b/source/server/Karamem0.Commistant.Functions/Commands/EndMeetingCommand.cs index 5d3f953..d52b115 100644 --- a/source/server/Karamem0.Commistant.Functions/Commands/EndMeetingCommand.cs +++ b/source/server/Karamem0.Commistant.Functions/Commands/EndMeetingCommand.cs @@ -27,7 +27,7 @@ namespace Karamem0.Commistant.Commands; public class EndMeetingCommand( IDateTimeService dateTimeService, IConnectorClientService connectorClientService, - IQrCodeService qrCodeService, + IQRCodeService qrCodeService, ILogger logger ) : Command() { @@ -36,7 +36,7 @@ ILogger logger private readonly IConnectorClientService connectorClientService = connectorClientService; - private readonly IQrCodeService qrCodeService = qrCodeService; + private readonly IQRCodeService qrCodeService = qrCodeService; private readonly ILogger logger = logger; @@ -104,7 +104,7 @@ public override async Task ExecuteAsync( var activity = MessageFactory.Attachment(new Attachment() { ContentType = AdaptiveCard.ContentType, - Content = JsonConvert.DeserializeObject(card.ToJson()) + Content = card }); activity.From = reference.Bot; activity.Recipient = reference.User; diff --git a/source/server/Karamem0.Commistant.Functions/Commands/InMeetingCommand.cs b/source/server/Karamem0.Commistant.Functions/Commands/InMeetingCommand.cs index 7fb1d6a..8dac341 100644 --- a/source/server/Karamem0.Commistant.Functions/Commands/InMeetingCommand.cs +++ b/source/server/Karamem0.Commistant.Functions/Commands/InMeetingCommand.cs @@ -27,7 +27,7 @@ namespace Karamem0.Commistant.Commands; public class InMeetingCommand( IDateTimeService dateTimeService, IConnectorClientService connectorClientService, - IQrCodeService qrCodeService, + IQRCodeService qrCodeService, ILogger logger ) : Command() { @@ -36,7 +36,7 @@ ILogger logger private readonly IConnectorClientService connectorClientService = connectorClientService; - private readonly IQrCodeService qrCodeService = qrCodeService; + private readonly IQRCodeService qrCodeService = qrCodeService; private readonly ILogger logger = logger; @@ -100,7 +100,7 @@ public override async Task ExecuteAsync( var activity = MessageFactory.Attachment(new Attachment() { ContentType = AdaptiveCard.ContentType, - Content = JsonConvert.DeserializeObject(card.ToJson()) + Content = card }); activity.From = reference.Bot; activity.Recipient = reference.User; diff --git a/source/server/Karamem0.Commistant.Functions/Commands/StartMeetingCommand.cs b/source/server/Karamem0.Commistant.Functions/Commands/StartMeetingCommand.cs index db2ddd7..e606a98 100644 --- a/source/server/Karamem0.Commistant.Functions/Commands/StartMeetingCommand.cs +++ b/source/server/Karamem0.Commistant.Functions/Commands/StartMeetingCommand.cs @@ -27,7 +27,7 @@ namespace Karamem0.Commistant.Commands; public class StartMeetingCommand( IDateTimeService dateTimeService, IConnectorClientService connectorClientService, - IQrCodeService qrCodeService, + IQRCodeService qrCodeService, ILogger logger ) : Command() { @@ -36,7 +36,7 @@ ILogger logger private readonly IConnectorClientService connectorClientService = connectorClientService; - private readonly IQrCodeService qrCodeService = qrCodeService; + private readonly IQRCodeService qrCodeService = qrCodeService; private readonly ILogger logger = logger; @@ -104,7 +104,7 @@ public override async Task ExecuteAsync( var activity = MessageFactory.Attachment(new Attachment() { ContentType = AdaptiveCard.ContentType, - Content = JsonConvert.DeserializeObject(card.ToJson()) + Content = card }); activity.From = reference.Bot; activity.Recipient = reference.User; diff --git a/source/server/Karamem0.Commistant.Functions/ConfigureServices.cs b/source/server/Karamem0.Commistant.Functions/ConfigureServices.cs index 4a73dbe..e3830db 100644 --- a/source/server/Karamem0.Commistant.Functions/ConfigureServices.cs +++ b/source/server/Karamem0.Commistant.Functions/ConfigureServices.cs @@ -29,7 +29,7 @@ public static class ConfigureServices public static IServiceCollection AddBlobContainerClient(this IServiceCollection services, IConfiguration configuration) { - var blobContainerUrl = configuration.GetValue("AzureBotStatesStorageUrl") ?? throw new InvalidOperationException(); + var blobContainerUrl = configuration["AzureBotStatesStorageUrl"] ?? throw new InvalidOperationException(); _ = services.AddSingleton(provider => new BlobContainerClient(new Uri(blobContainerUrl), new DefaultAzureCredential())); return services; } @@ -37,8 +37,9 @@ public static IServiceCollection AddBlobContainerClient(this IServiceCollection public static IServiceCollection AddServiceClientCredentials(this IServiceCollection services, IConfiguration configuration) { _ = services.AddSingleton(new MicrosoftAppCredentials( - configuration.GetValue("MicrosoftAppId"), - configuration.GetValue("MicrosoftAppPassword"))); + configuration["MicrosoftAppId"], + configuration["MicrosoftAppPassword"] + )); return services; } @@ -47,7 +48,7 @@ public static IServiceCollection AddServices(this IServiceCollection services) _ = services.AddScoped(); _ = services.AddScoped(); _ = services.AddScoped(); - _ = services.AddScoped(); + _ = services.AddScoped(); return services; } @@ -59,7 +60,8 @@ public static IServiceCollection AddCommands(this IServiceCollection services) _ = services.AddSingleton((provider) => new CommandSet() .Add(provider.GetService()) .Add(provider.GetService()) - .Add(provider.GetService())); + .Add(provider.GetService()) + ); return services; } diff --git a/source/server/Karamem0.Commistant.Functions/Karamem0.Commistant.Functions.csproj b/source/server/Karamem0.Commistant.Functions/Karamem0.Commistant.Functions.csproj index d720d0d..df63b8e 100644 --- a/source/server/Karamem0.Commistant.Functions/Karamem0.Commistant.Functions.csproj +++ b/source/server/Karamem0.Commistant.Functions/Karamem0.Commistant.Functions.csproj @@ -24,11 +24,11 @@ - + - + - + diff --git a/source/server/Karamem0.Commistant.Tests/Functions/Commands/EndMeetingCommandTests.cs b/source/server/Karamem0.Commistant.Tests/Commands/EndMeetingCommandTests.cs similarity index 93% rename from source/server/Karamem0.Commistant.Tests/Functions/Commands/EndMeetingCommandTests.cs rename to source/server/Karamem0.Commistant.Tests/Commands/EndMeetingCommandTests.cs index a1975e4..2869963 100644 --- a/source/server/Karamem0.Commistant.Tests/Functions/Commands/EndMeetingCommandTests.cs +++ b/source/server/Karamem0.Commistant.Tests/Commands/EndMeetingCommandTests.cs @@ -6,7 +6,6 @@ // https://github.com/karamem0/commistant/blob/main/LICENSE // -using Karamem0.Commistant.Commands; using Karamem0.Commistant.Logging; using Karamem0.Commistant.Models; using Karamem0.Commistant.Services; @@ -20,14 +19,14 @@ using System.Text; using System.Threading.Tasks; -namespace Karamem0.Commistant.Functions.Commands.Tests; +namespace Karamem0.Commistant.Commands.Tests; -[Category("Karamem0.Commistant.Functions")] +[Category("Karamem0.Commistant.Commands")] public class EndMeetingCommandTests { [Test()] - public async Task EndMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() + public async Task ExecuteAsync_OnSchedule_Succeeded() { // Setup var conversationReference = new ConversationReference() @@ -54,19 +53,19 @@ public async Task EndMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() var connectorClientService = Substitute.For(); _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.EndMeetingMessageNotifying(conversationReference, conversationProperty); logger.EndMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new EndMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference @@ -82,7 +81,7 @@ await command.ExecuteAsync( } [Test()] - public async Task EndMeetingCommand_ExecuteAsync_Succeeded_AfterSchedule() + public async Task ExecuteAsync_AfterSchedule_Succeeded() { // Setup var conversationReference = new ConversationReference() @@ -109,19 +108,19 @@ public async Task EndMeetingCommand_ExecuteAsync_Succeeded_AfterSchedule() var connectorClientService = Substitute.For(); _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.EndMeetingMessageNotifying(conversationReference, conversationProperty); logger.EndMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new EndMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference @@ -137,7 +136,7 @@ await command.ExecuteAsync( } [Test()] - public async Task EndMeetingCommand_ExecuteAsync_Skipped_BeforeSchedule() + public async Task ExecuteAsync_BeforeSchedule_Skipped() { // Setup var conversationReference = new ConversationReference() @@ -166,19 +165,19 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_BeforeSchedule() _ = connectorClientService .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.EndMeetingMessageNotifying(conversationReference, conversationProperty); logger.EndMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new EndMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference @@ -194,7 +193,7 @@ await command.ExecuteAsync( } [Test()] - public async Task EndMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() + public async Task ExecuteAsync_NotInMeeting_Skipped() { // Setup var conversationReference = new ConversationReference() @@ -223,20 +222,20 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() _ = connectorClientService .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService .CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.EndMeetingMessageNotifying(conversationReference, conversationProperty); logger.EndMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new EndMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference @@ -252,7 +251,7 @@ await command.ExecuteAsync( } [Test()] - public async Task EndMeetingCommand_ExecuteAsync_Skipped_AfterSended() + public async Task ExecuteAsync_AfterSended_Skipped() { // Setup var conversationReference = new ConversationReference() @@ -281,20 +280,20 @@ public async Task EndMeetingCommand_ExecuteAsync_Skipped_AfterSended() _ = connectorClientService .SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService .CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.EndMeetingMessageNotifying(conversationReference, conversationProperty); logger.EndMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new EndMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference diff --git a/source/server/Karamem0.Commistant.Tests/Functions/Commands/InMeetingCommandTests.cs b/source/server/Karamem0.Commistant.Tests/Commands/InMeetingCommandTests.cs similarity index 92% rename from source/server/Karamem0.Commistant.Tests/Functions/Commands/InMeetingCommandTests.cs rename to source/server/Karamem0.Commistant.Tests/Commands/InMeetingCommandTests.cs index 6e67a62..4cc59f3 100644 --- a/source/server/Karamem0.Commistant.Tests/Functions/Commands/InMeetingCommandTests.cs +++ b/source/server/Karamem0.Commistant.Tests/Commands/InMeetingCommandTests.cs @@ -6,7 +6,6 @@ // https://github.com/karamem0/commistant/blob/main/LICENSE // -using Karamem0.Commistant.Commands; using Karamem0.Commistant.Logging; using Karamem0.Commistant.Models; using Karamem0.Commistant.Services; @@ -20,14 +19,14 @@ using System.Text; using System.Threading.Tasks; -namespace Karamem0.Commistant.Functions.Commands.Tests; +namespace Karamem0.Commistant.Commands.Tests; -[Category("Karamem0.Commistant.Functions")] +[Category("Karamem0.Commistant.Commands")] public class InMeetingCommandTests { [Test()] - public async Task InMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() + public async Task ExecuteAsync_OnSchedule_Succeeded() { // Setup var conversationReference = new ConversationReference() @@ -53,19 +52,19 @@ public async Task InMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() var connectorClientService = Substitute.For(); _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.InMeetingMessageNotifying(conversationReference, conversationProperty); logger.InMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new InMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference @@ -80,7 +79,7 @@ await command.ExecuteAsync( } [Test()] - public async Task InMeetingCommand_ExecuteAsync_Skipped_OffSchedule() + public async Task ExecuteAsync_OffSchedule_Skipped() { // Setup var conversationReference = new ConversationReference() @@ -106,19 +105,19 @@ public async Task InMeetingCommand_ExecuteAsync_Skipped_OffSchedule() var connectorClientService = Substitute.For(); _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.InMeetingMessageNotifying(conversationReference, conversationProperty); logger.InMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new InMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference @@ -133,7 +132,7 @@ await command.ExecuteAsync( } [Test()] - public async Task InMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() + public async Task ExecuteAsync_NotInMeeting_Skipped() { // Setup var conversationReference = new ConversationReference() @@ -159,19 +158,19 @@ public async Task InMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() var connectorClientService = Substitute.For(); _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.InMeetingMessageNotifying(conversationReference, conversationProperty); logger.InMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new InMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference diff --git a/source/server/Karamem0.Commistant.Tests/Functions/Commands/StartMeetingCommandTests.cs b/source/server/Karamem0.Commistant.Tests/Commands/StartMeetingCommandTests.cs similarity index 93% rename from source/server/Karamem0.Commistant.Tests/Functions/Commands/StartMeetingCommandTests.cs rename to source/server/Karamem0.Commistant.Tests/Commands/StartMeetingCommandTests.cs index f74cbdd..beb7b06 100644 --- a/source/server/Karamem0.Commistant.Tests/Functions/Commands/StartMeetingCommandTests.cs +++ b/source/server/Karamem0.Commistant.Tests/Commands/StartMeetingCommandTests.cs @@ -6,7 +6,6 @@ // https://github.com/karamem0/commistant/blob/main/LICENSE // -using Karamem0.Commistant.Commands; using Karamem0.Commistant.Logging; using Karamem0.Commistant.Models; using Karamem0.Commistant.Services; @@ -20,14 +19,14 @@ using System.Text; using System.Threading.Tasks; -namespace Karamem0.Commistant.Functions.Commands.Tests; +namespace Karamem0.Commistant.Commands.Tests; -[Category("Karamem0.Commistant.Functions")] +[Category("Karamem0.Commistant.Commands")] public class StartMeetingCommandTests { [Test()] - public async Task StartMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() + public async Task ExecuteAsync_OnSchedule_Succeeded() { // Setup var conversationReference = new ConversationReference() @@ -54,19 +53,19 @@ public async Task StartMeetingCommand_ExecuteAsync_Succeeded_OnSchedule() var connectorClientService = Substitute.For(); _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.StartMeetingMessageNotifying(conversationReference, conversationProperty); logger.StartMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new StartMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference @@ -82,7 +81,7 @@ await command.ExecuteAsync( } [Test()] - public async Task StartMeetingCommand_ExecuteAsync_Succeeded_AfterSchedule() + public async Task ExecuteAsync_AfterSchedule_Succeeded() { // Setup var conversationReference = new ConversationReference() @@ -109,19 +108,19 @@ public async Task StartMeetingCommand_ExecuteAsync_Succeeded_AfterSchedule() var connectorClientService = Substitute.For(); _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.StartMeetingMessageNotifying(conversationReference, conversationProperty); logger.StartMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new StartMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference @@ -137,7 +136,7 @@ await command.ExecuteAsync( } [Test()] - public async Task StartMeetingCommand_ExecuteAsync_Skipped_BeforeSchedule() + public async Task ExecuteAsync_BeforeSchedule_Skipped() { // Setup var conversationReference = new ConversationReference() @@ -164,19 +163,19 @@ public async Task StartMeetingCommand_ExecuteAsync_Skipped_BeforeSchedule() var connectorClientService = Substitute.For(); _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.StartMeetingMessageNotifying(conversationReference, conversationProperty); logger.StartMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new StartMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference @@ -192,7 +191,7 @@ await command.ExecuteAsync( } [Test()] - public async Task StartMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() + public async Task ExecuteAsync_NotInMeeting_Skipped() { // Setup var conversationReference = new ConversationReference() @@ -219,19 +218,19 @@ public async Task StartMeetingCommand_ExecuteAsync_Skipped_NotInMeeting() var connectorClientService = Substitute.For(); _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.StartMeetingMessageNotifying(conversationReference, conversationProperty); logger.StartMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new StartMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference @@ -247,7 +246,7 @@ await command.ExecuteAsync( } [Test()] - public async Task StartMeetingCommand_ExecuteAsync_Skipped_AfterSended() + public async Task ExecuteAsync_AfterSended_Skipped() { // Setup var conversationReference = new ConversationReference() @@ -274,19 +273,19 @@ public async Task StartMeetingCommand_ExecuteAsync_Skipped_AfterSended() var connectorClientService = Substitute.For(); _ = connectorClientService.SendActivityAsync(new Uri("https://www.example.com/"), Arg.Any()) .Returns(new ResourceResponse()); - var qrCodeService = Substitute.For(); + var qrCodeService = Substitute.For(); _ = qrCodeService.CreateAsync("https://www.example.com/") .Returns([]); var logger = Substitute.For>(); logger.StartMeetingMessageNotifying(conversationReference, conversationProperty); logger.StartMeetingMessageNotified(conversationReference, conversationProperty); + // Execute var command = new StartMeetingCommand( dateTimeService, connectorClientService, qrCodeService, logger ); - // Execute await command.ExecuteAsync( conversationProperty, conversationReference diff --git a/source/server/Karamem0.Commistant.Tests/Dialogs/EndMeetingDialogTests.cs b/source/server/Karamem0.Commistant.Tests/Dialogs/EndMeetingDialogTests.cs new file mode 100644 index 0000000..85b2a9f --- /dev/null +++ b/source/server/Karamem0.Commistant.Tests/Dialogs/EndMeetingDialogTests.cs @@ -0,0 +1,108 @@ +// +// Copyright (c) 2022-2024 karamem0 +// +// This software is released under the MIT License. +// +// https://github.com/karamem0/commistant/blob/main/LICENSE +// + +using AutoMapper; +using Karamem0.Commistant.Dialogs; +using Karamem0.Commistant.Mappings; +using Karamem0.Commistant.Models; +using Karamem0.Commistant.Services; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Testing; +using Microsoft.Bot.Connector; +using Microsoft.Bot.Schema; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using NSubstitute; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Karamem0.Commistant.Commands.Tests; + +[Category("Karamem0.Commistant.Dialogs")] +public class EndMeetingDialogTests +{ + + [Test()] + public async Task EndMeetingDialog_Submit_Succeeded() + { + // Setup + var conversationState = new ConversationState(new MemoryStorage()); + var qrCodeService = Substitute.For(); + var mapperConfig = new MapperConfiguration(config => config.AddProfile()); + var mapper = mapperConfig.CreateMapper(); + var logger = Substitute.For>(); + var value = JObject.FromObject(new Dictionary() + { + ["Button"] = "Submit", + ["Schedule"] = "5", + ["Message"] = "Hello world!", + ["Url"] = "https://www.example.com" + }); + // Execute + var dialog = new EndMeetingDialog( + conversationState, + qrCodeService, + mapper, + logger + ); + var client = new DialogTestClient(Channels.Msteams, dialog); + var activity = await client.SendActivityAsync(new Activity(ActivityTypes.Message)); + var actual = await client.SendActivityAsync(new Activity(ActivityTypes.Message, value: value, replyToId: activity.Id)); + // Assert + var accessor = conversationState.CreateProperty(nameof(ConversationProperty)); + var property = accessor.GetAsync(client.DialogContext.Context, () => new()); + Assert.Multiple(() => + { + Assert.That(property.Result.EndMeetingSchedule, Is.EqualTo(5)); + Assert.That(property.Result.EndMeetingMessage, Is.EqualTo("Hello world!")); + Assert.That(property.Result.EndMeetingUrl, Is.EqualTo("https://www.example.com")); + }); + } + + [Test()] + public async Task EndMeetingDialog_Cancel_Succeeded() + { + // Setup + var conversationState = new ConversationState(new MemoryStorage()); + var qrCodeService = Substitute.For(); + var mapperConfig = new MapperConfiguration(config => config.AddProfile()); + var mapper = mapperConfig.CreateMapper(); + var logger = Substitute.For>(); + var value = JObject.FromObject(new Dictionary() + { + ["Button"] = "Cancel", + ["Schedule"] = "5", + ["Message"] = "Hello world!", + ["Url"] = "https://www.example.com" + }); + // Execute + var dialog = new EndMeetingDialog( + conversationState, + qrCodeService, + mapper, + logger + ); + var client = new DialogTestClient(Channels.Msteams, dialog); + var activity = await client.SendActivityAsync(new Activity(ActivityTypes.Message)); + var actual = await client.SendActivityAsync(new Activity(ActivityTypes.Message, value: value, replyToId: activity.Id)); + // Assert + var accessor = conversationState.CreateProperty(nameof(ConversationProperty)); + var property = accessor.GetAsync(client.DialogContext.Context, () => new()); + Assert.Multiple(() => + { + Assert.That(property.Result.EndMeetingSchedule, Is.EqualTo(-1)); + Assert.That(property.Result.EndMeetingMessage, Is.Null); + Assert.That(property.Result.EndMeetingUrl, Is.Null); + }); + } + +} diff --git a/source/server/Karamem0.Commistant.Tests/Dialogs/InMeetingDialogTests.cs b/source/server/Karamem0.Commistant.Tests/Dialogs/InMeetingDialogTests.cs new file mode 100644 index 0000000..4e1ed68 --- /dev/null +++ b/source/server/Karamem0.Commistant.Tests/Dialogs/InMeetingDialogTests.cs @@ -0,0 +1,108 @@ +// +// Copyright (c) 2022-2024 karamem0 +// +// This software is released under the MIT License. +// +// https://github.com/karamem0/commistant/blob/main/LICENSE +// + +using AutoMapper; +using Karamem0.Commistant.Dialogs; +using Karamem0.Commistant.Mappings; +using Karamem0.Commistant.Models; +using Karamem0.Commistant.Services; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Testing; +using Microsoft.Bot.Connector; +using Microsoft.Bot.Schema; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using NSubstitute; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Karamem0.Commistant.Commands.Tests; + +[Category("Karamem0.Commistant.Dialogs")] +public class InMeetingDialogTests +{ + + [Test()] + public async Task InMeetingDialog_Submit_Succeeded() + { + // Setup + var conversationState = new ConversationState(new MemoryStorage()); + var qrCodeService = Substitute.For(); + var mapperConfig = new MapperConfiguration(config => config.AddProfile()); + var mapper = mapperConfig.CreateMapper(); + var logger = Substitute.For>(); + var value = JObject.FromObject(new Dictionary() + { + ["Button"] = "Submit", + ["Schedule"] = "5", + ["Message"] = "Hello world!", + ["Url"] = "https://www.example.com" + }); + // Execute + var dialog = new InMeetingDialog( + conversationState, + qrCodeService, + mapper, + logger + ); + var client = new DialogTestClient(Channels.Msteams, dialog); + var activity = await client.SendActivityAsync(new Activity(ActivityTypes.Message)); + var actual = await client.SendActivityAsync(new Activity(ActivityTypes.Message, value: value, replyToId: activity.Id)); + // Assert + var accessor = conversationState.CreateProperty(nameof(ConversationProperty)); + var property = accessor.GetAsync(client.DialogContext.Context, () => new()); + Assert.Multiple(() => + { + Assert.That(property.Result.InMeetingSchedule, Is.EqualTo(5)); + Assert.That(property.Result.InMeetingMessage, Is.EqualTo("Hello world!")); + Assert.That(property.Result.InMeetingUrl, Is.EqualTo("https://www.example.com")); + }); + } + + [Test()] + public async Task InMeetingDialog_Cancel_Succeeded() + { + // Setup + var conversationState = new ConversationState(new MemoryStorage()); + var qrCodeService = Substitute.For(); + var mapperConfig = new MapperConfiguration(config => config.AddProfile()); + var mapper = mapperConfig.CreateMapper(); + var logger = Substitute.For>(); + var value = JObject.FromObject(new Dictionary() + { + ["Button"] = "Cancel", + ["Schedule"] = "5", + ["Message"] = "Hello world!", + ["Url"] = "https://www.example.com" + }); + // Execute + var dialog = new InMeetingDialog( + conversationState, + qrCodeService, + mapper, + logger + ); + var client = new DialogTestClient(Channels.Msteams, dialog); + var activity = await client.SendActivityAsync(new Activity(ActivityTypes.Message)); + var actual = await client.SendActivityAsync(new Activity(ActivityTypes.Message, value: value, replyToId: activity.Id)); + // Assert + var accessor = conversationState.CreateProperty(nameof(ConversationProperty)); + var property = accessor.GetAsync(client.DialogContext.Context, () => new()); + Assert.Multiple(() => + { + Assert.That(property.Result.InMeetingSchedule, Is.EqualTo(-1)); + Assert.That(property.Result.InMeetingMessage, Is.Null); + Assert.That(property.Result.InMeetingUrl, Is.Null); + }); + } + +} diff --git a/source/server/Karamem0.Commistant.Tests/Dialogs/StartMeetingDialogTests.cs b/source/server/Karamem0.Commistant.Tests/Dialogs/StartMeetingDialogTests.cs new file mode 100644 index 0000000..76aa15f --- /dev/null +++ b/source/server/Karamem0.Commistant.Tests/Dialogs/StartMeetingDialogTests.cs @@ -0,0 +1,108 @@ +// +// Copyright (c) 2022-2024 karamem0 +// +// This software is released under the MIT License. +// +// https://github.com/karamem0/commistant/blob/main/LICENSE +// + +using AutoMapper; +using Karamem0.Commistant.Dialogs; +using Karamem0.Commistant.Mappings; +using Karamem0.Commistant.Models; +using Karamem0.Commistant.Services; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Testing; +using Microsoft.Bot.Connector; +using Microsoft.Bot.Schema; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using NSubstitute; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Karamem0.Commistant.Commands.Tests; + +[Category("Karamem0.Commistant.Dialogs")] +public class StartMeetingDialogTests +{ + + [Test()] + public async Task StartMeetingDialog_Submit_Succeeded() + { + // Setup + var conversationState = new ConversationState(new MemoryStorage()); + var qrCodeService = Substitute.For(); + var mapperConfig = new MapperConfiguration(config => config.AddProfile()); + var mapper = mapperConfig.CreateMapper(); + var logger = Substitute.For>(); + var value = JObject.FromObject(new Dictionary() + { + ["Button"] = "Submit", + ["Schedule"] = "5", + ["Message"] = "Hello world!", + ["Url"] = "https://www.example.com" + }); + // Execute + var dialog = new StartMeetingDialog( + conversationState, + qrCodeService, + mapper, + logger + ); + var client = new DialogTestClient(Channels.Msteams, dialog); + var activity = await client.SendActivityAsync(new Activity(ActivityTypes.Message)); + var actual = await client.SendActivityAsync(new Activity(ActivityTypes.Message, value: value, replyToId: activity.Id)); + // Assert + var accessor = conversationState.CreateProperty(nameof(ConversationProperty)); + var property = accessor.GetAsync(client.DialogContext.Context, () => new()); + Assert.Multiple(() => + { + Assert.That(property.Result.StartMeetingSchedule, Is.EqualTo(5)); + Assert.That(property.Result.StartMeetingMessage, Is.EqualTo("Hello world!")); + Assert.That(property.Result.StartMeetingUrl, Is.EqualTo("https://www.example.com")); + }); + } + + [Test()] + public async Task StartMeetingDialog_Cancel_Succeeded() + { + // Setup + var conversationState = new ConversationState(new MemoryStorage()); + var qrCodeService = Substitute.For(); + var mapperConfig = new MapperConfiguration(config => config.AddProfile()); + var mapper = mapperConfig.CreateMapper(); + var logger = Substitute.For>(); + var value = JObject.FromObject(new Dictionary() + { + ["Button"] = "Cancel", + ["Schedule"] = "5", + ["Message"] = "Hello world!", + ["Url"] = "https://www.example.com" + }); + // Execute + var dialog = new StartMeetingDialog( + conversationState, + qrCodeService, + mapper, + logger + ); + var client = new DialogTestClient(Channels.Msteams, dialog); + var activity = await client.SendActivityAsync(new Activity(ActivityTypes.Message)); + var actual = await client.SendActivityAsync(new Activity(ActivityTypes.Message, value: value, replyToId: activity.Id)); + // Assert + var accessor = conversationState.CreateProperty(nameof(ConversationProperty)); + var property = accessor.GetAsync(client.DialogContext.Context, () => new()); + Assert.Multiple(() => + { + Assert.That(property.Result.StartMeetingSchedule, Is.EqualTo(-1)); + Assert.That(property.Result.StartMeetingMessage, Is.Null); + Assert.That(property.Result.StartMeetingUrl, Is.Null); + }); + } + +} diff --git a/source/server/Karamem0.Commistant.Tests/Karamem0.Commistant.Tests.csproj b/source/server/Karamem0.Commistant.Tests/Karamem0.Commistant.Tests.csproj index 305252e..9eddd31 100644 --- a/source/server/Karamem0.Commistant.Tests/Karamem0.Commistant.Tests.csproj +++ b/source/server/Karamem0.Commistant.Tests/Karamem0.Commistant.Tests.csproj @@ -20,13 +20,14 @@ - + + - - + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -34,6 +35,7 @@ + diff --git a/source/server/Karamem0.Commistant.Web/ConfigureServices.cs b/source/server/Karamem0.Commistant.Web/ConfigureServices.cs index f7b8f44..2630388 100644 --- a/source/server/Karamem0.Commistant.Web/ConfigureServices.cs +++ b/source/server/Karamem0.Commistant.Web/ConfigureServices.cs @@ -25,7 +25,7 @@ public static class ConfigureServices public static IServiceCollection AddBlobContainerClient(this IServiceCollection services, IConfiguration configuration) { - var blobContainerUrl = configuration.GetValue("AzureBotStatesStorageUrl") ?? throw new InvalidOperationException(); + var blobContainerUrl = configuration["AzureBotStatesStorageUrl"] ?? throw new InvalidOperationException(); _ = services.AddSingleton(provider => new BlobContainerClient(new Uri(blobContainerUrl), new DefaultAzureCredential())); return services; } @@ -33,8 +33,9 @@ public static IServiceCollection AddBlobContainerClient(this IServiceCollection public static IServiceCollection AddServiceClientCredentials(this IServiceCollection services, IConfiguration configuration) { _ = services.AddSingleton(new MicrosoftAppCredentials( - configuration.GetValue("MicrosoftAppId"), - configuration.GetValue("MicrosoftAppPassword"))); + configuration["MicrosoftAppId"], + configuration["MicrosoftAppPassword"] + )); return services; } diff --git a/source/server/Karamem0.Commistant.Web/Karamem0.Commistant.Web.csproj b/source/server/Karamem0.Commistant.Web/Karamem0.Commistant.Web.csproj index 5a386cb..cc69c07 100644 --- a/source/server/Karamem0.Commistant.Web/Karamem0.Commistant.Web.csproj +++ b/source/server/Karamem0.Commistant.Web/Karamem0.Commistant.Web.csproj @@ -23,10 +23,10 @@ - + - +