diff --git a/.github/workflows/dotnet-deploy.yaml b/.github/workflows/dotnet-deploy.yaml index ccba5e4..d6f5a30 100644 --- a/.github/workflows/dotnet-deploy.yaml +++ b/.github/workflows/dotnet-deploy.yaml @@ -1,11 +1,14 @@ name: "Deploy to Nuget" +# Step One: Perhaps most importantly, the trigger has changed, this workflow will run whenever a tag is pushed +# to the repository that matches the v#.#.# pattern (where # is one or more digits). on: push: tags: + # note: this could be more constrained—"v[0-9]+.[0-9]+.[0-9]+" - "v*.*.*" env: - PROJECT_PATH: "./src/Novu/Novu.csproj" + WORKING_DIRECTORY: "./src/" PACKAGE_OUTPUT_DIRECTORY: ${{ github.workspace }}/output NUGET_SOURCE_URL: "https://api.nuget.org/v3/index.json" @@ -18,17 +21,35 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: 7.0.x - - - name: Build Novu - working-directory: ./src/Novu + + # Step Two: build + - name: Build Novu Solution (all projects) + working-directory: ${{ WORKING_DIRECTORY }} run: dotnet build --configuration Release + # Step Three: package each of the libraries with the version updated (note: this allow source to hold version 0.0.0) - name: Get version id: version uses: battila7/get-version-action@v2 - - name: Create Nuget package - run: dotnet pack ${{ env.PROJECT_PATH }} --no-restore --no-build --configuration Release -p:PackageVersion=${{ steps.version.outputs.version-without-v }} --output ${{ env.PACKAGE_OUTPUT_DIRECTORY }} + - name: Novu — create Nuget package + ## dotnet pack Novu/Novu.csproj --no-restore --no-build --configuration Release -p:PackageVersion=0.0.0.0 -p:Version=0.0.0.0 --output . + run: dotnet pack ${{ env.WORKING_DIRECTORY }}/Novu/Novu.csproj --no-restore --no-build --configuration Release -p:PackageVersion=${{ steps.version.outputs.version-without-v }} -p:Version=${{ steps.version.outputs.version-without-v }} --output ${{ env.PACKAGE_OUTPUT_DIRECTORY }} + + - name: Novu.Extensions — create Nuget package + ## dotnet pack Novu.Extensions/Novu.Extensions.csproj --no-restore --no-build --configuration Release -p:PackageVersion=0.0.0.0 -p:Version=0.0.0.0 --output . + run: dotnet pack ${{ env.WORKING_DIRECTORY }}/Novu.Extensions/Novu.Extensions.csproj --no-restore --no-build --configuration Release -p:PackageVersion=${{ steps.version.outputs.version-without-v }} -p:Version=${{ steps.version.outputs.version-without-v }} --output ${{ env.PACKAGE_OUTPUT_DIRECTORY }} + + - name: Novu.Sync — create Nuget package + ## dotnet pack Novu.Sync/Novu.Sync.csproj --no-restore --no-build --configuration Release -p:PackageVersion=0.0.0.0 -p:Version=0.0.0.0 --output . + run: dotnet pack ${{ env.WORKING_DIRECTORY }}/Novu.Sync/Novu.Sync.csproj --no-restore --no-build --configuration Release -p:PackageVersion=${{ steps.version.outputs.version-without-v }} -p:Version=${{ steps.version.outputs.version-without-v }} --output ${{ env.PACKAGE_OUTPUT_DIRECTORY }} + + # Step Four: publish all + - name: Novu — push Nuget package + run: dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY}}/Novu.${{ steps.version.outputs.version-without-v }}.nupkg -k ${{ secrets.NUGET_AUTH_TOKEN }} -s ${{ env.NUGET_SOURCE_URL}} + + - name: Novu.Extensions — push Nuget package + run: dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY}}/Novu.Extensions.${{ steps.version.outputs.version-without-v }}.nupkg -k ${{ secrets.NUGET_AUTH_TOKEN }} -s ${{ env.NUGET_SOURCE_URL}} - - name: Push Nuget package - run: dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY}}/*.nupkg -k ${{ secrets.NUGET_AUTH_TOKEN }} -s ${{ env.NUGET_SOURCE_URL}} + - name: Novu.Sync — push Nuget package + run: dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY}}/Novu.Sync.${{ steps.version.outputs.version-without-v }}.nupkg -k ${{ secrets.NUGET_AUTH_TOKEN }} -s ${{ env.NUGET_SOURCE_URL}} diff --git a/.github/workflows/dotnet-test.yaml b/.github/workflows/dotnet-test.yaml index f456165..98511ae 100644 --- a/.github/workflows/dotnet-test.yaml +++ b/.github/workflows/dotnet-test.yaml @@ -2,15 +2,9 @@ name: "Test Pull Request" on: pull_request: - branches: [main] + branches: [ main ] - workflow_dispatch: - - -env: - PROJECT_PATH: "./src/Novu/Novu.csproj" - PACKAGE_OUTPUT_DIRECTORY: ${{ github.workspace }}\output - NUGET_SOURCE_URL: "https://api/nuget/org/v3/index.json" + workflow_dispatch: jobs: build_and_test: @@ -22,36 +16,56 @@ jobs: with: dotnet-version: 7.0.x - ## Restore Novu + ## Restore Novu as a solution - name: Restore dependencies - working-directory: ./src/Novu + working-directory: ./src/ run: dotnet restore - ## Build Novu - - name: Build Novu - working-directory: ./src/Novu - run: dotnet build --no-restore - - ## Restore Novu.Tests - - name: Restore test dependencies - working-directory: ./src/Novu.Tests - run: dotnet restore + ## each test type is targeted specifically via a filter + ## see https://learn.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests?pivots=mstest + + ## Test Novu + - name: Micro Tests + working-directory: ./src/ + run: dotnet test --filter FullyQualifiedName~MicroTests + env: + NOVU_API_URL: https://api.novu.co/v1 + NOVU_API_KEY: ${{ secrets.NOVU_API_KEY }} - - name: Delete current appsettings.Integration.json file - run: | - rm ./src/Novu.Tests/appsettings.Integration.json + ## + ## FOR TESTS WITH REAL CONNECTIONS + ## + ## this run should work but something is not picking up values and mapping in the configuration + ## run: dotnet test -p:Novu__Url=https://api.novu.co/v1 -p:Novu__ApiKey=${{ secrets.NOVU_API_KEY }} + ## instead inject using the ad hoc environment variable override + ## see https://github.com/novuhq/novu-dotnet/blob/main/src/Novu.Extensions/ConfigurationExtensions.cs + ## + ## also up the logging because when something is wrong it is hard to diagnose + ## + + + ## Test that is small and short to indicate system is up and going (enough) + - name: Integration Tests (ping) + working-directory: ./src/ + run: dotnet test --filter FullyQualifiedName~IntegrationTests.IntegrationTests + env: + NOVU_API_URL: https://api.novu.co/v1 + NOVU_API_KEY: ${{ secrets.NOVU_API_KEY }} + +# TODO: understand why these hang - - name: Update appsettings.json with test values - uses: jsdaniell/create-json@v1.2.2 - with: - name: "appsettings.json" - json: '{ "Novu": {"Url": "https://api.novu.co/v1", "ApiKey": ${{ secrets.NOVU_API_KEY }}}}' - dir: 'src/Novu.Tests' +# - name: Integration Tests +# working-directory: ./src/ +# run: dotnet test --verbosity normal --filter FullyQualifiedName~IntegrationTests +# env: +# NOVU_API_URL: https://api.novu.co/v1 +# NOVU_API_KEY: ${{ secrets.NOVU_API_KEY }} - ## Test Novu - - name: Test - working-directory: ./src - run: dotnet test + - name: Acceptance Tests + working-directory: ./src/ + + run: dotnet test --verbosity normal --filter FullyQualifiedName~AcceptanceTests env: - NOVU_API: https://api.novu.co/v1 - NOVU_API_KEY: ${{ secrets.NOVU_API_KEY }} \ No newline at end of file + NOVU_API_URL: https://api.novu.co/v1 + NOVU_API_KEY: ${{ secrets.NOVU_API_KEY }} + \ No newline at end of file diff --git a/README.md b/README.md index 215ac9a..03e6933 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ novu-dotnet targets .NET Standard 2.0 and is compatible with .NET Core 2.0+ and |-------------|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 0.2.2 | <= 0.17 | Singleton client with Refit's use of RestService | | 0.3.0 | >= 0.18 | 0.3.0 is not compatible with 0.2.2 and requires upgrade to code. Also 0.18 introduced a breaking change only found in 0.3.0. All 0.2.2 must be upgraded if used against the production system. HttpClient can now be used and injected. | +| 0.3.1 | >= 0.18 | [BREAKING} Obsolete Notification Templates has been removed. Service registration separation of single client and each client. Novu.Extension and Novu.Sync released as packages. | ## Installation @@ -135,3 +136,23 @@ The key folders to look into: - [Interfaces](https://github.com/novuhq/novu-dotnet/tree/main/src/Novu/Interfaces) directory holds all interfaces that are intended to outline how a class should be structured - [Models](https://github.com/novuhq/novu-dotnet/tree/main/src/Novu/Models) directory holds various models that are sub-resources inside the DTOs +### Major changes + +Github issues closed + +#### 0.3.1 +- #57 +- #58 +- #59 +- #60 +- #55 +#### 0.3.0 +- #19 +- #20 +- #21 +- #34 +- #48 +- #49 +- #50 +- #47 +- #45 diff --git a/src/Novu.Extensions/ConfigurationExtensions.cs b/src/Novu.Extensions/ConfigurationExtensions.cs index 10dedf6..3f601dc 100644 --- a/src/Novu.Extensions/ConfigurationExtensions.cs +++ b/src/Novu.Extensions/ConfigurationExtensions.cs @@ -50,6 +50,12 @@ public static NovuClientConfiguration GetNovuClientConfiguration(this IConfigura novuConfiguration.Url = novuConfigurationUrl; } + var novuConfigurationApiKey = Environment.GetEnvironmentVariable("NOVU_API_KEY"); + if (novuConfigurationApiKey is not null) + { + novuConfiguration.ApiKey = novuConfigurationApiKey; + } + return novuConfiguration; } } \ No newline at end of file diff --git a/src/Novu.Extensions/IocNovuRegistrationExtensions.cs b/src/Novu.Extensions/IocNovuRegistrationExtensions.cs index 47b8fc5..3931dd9 100644 --- a/src/Novu.Extensions/IocNovuRegistrationExtensions.cs +++ b/src/Novu.Extensions/IocNovuRegistrationExtensions.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Novu.Interfaces; -using Novu.NotificationTemplates; using Refit; namespace Novu.Extensions; @@ -20,28 +19,24 @@ public static IServiceCollection RegisterNovuClients( { var novuConfiguration = configuration.GetNovuClientConfiguration(); - Action configureClient = c => + Action configureClient = client => { - c.BaseAddress = new Uri(novuConfiguration.Url); + client.BaseAddress = new Uri(novuConfiguration.Url); // allow for multiple registrations—authorization cannot have multiple entries - if (c.DefaultRequestHeaders.Contains("Authorization")) + if (client.DefaultRequestHeaders.Contains("Authorization")) { - c.DefaultRequestHeaders.Remove("Authorization"); + client.DefaultRequestHeaders.Remove("Authorization"); } - c.DefaultRequestHeaders.Add("Authorization", $"ApiKey {novuConfiguration.ApiKey}"); - }; - var settings = refitSettings ?? new RefitSettings - { - ContentSerializer = new NewtonsoftJsonContentSerializer(NovuClient.DefaultSerializerSettings), + client.DefaultRequestHeaders.Add("Authorization", $"ApiKey {novuConfiguration.ApiKey}"); }; + var settings = RefitSettings(refitSettings); services.AddRefitClient(settings).ConfigureHttpClient(configureClient); services.AddRefitClient(settings).ConfigureHttpClient(configureClient); services.AddRefitClient(settings).ConfigureHttpClient(configureClient); services.AddRefitClient(settings).ConfigureHttpClient(configureClient); - services.AddRefitClient(settings).ConfigureHttpClient(configureClient); services.AddRefitClient(settings).ConfigureHttpClient(configureClient); services.AddRefitClient(settings).ConfigureHttpClient(configureClient); services.AddRefitClient(settings).ConfigureHttpClient(configureClient); @@ -50,6 +45,26 @@ public static IServiceCollection RegisterNovuClients( services.AddRefitClient(settings).ConfigureHttpClient(configureClient); return services - .AddTransient(); + .AddTransient(_ => novuConfiguration); + } + + + public static IServiceCollection RegisterNovuClient( + this IServiceCollection services, + IConfiguration configuration, + RefitSettings refitSettings = null) + { + return services + .AddTransient(_ => new NovuClient( + configuration.GetNovuClientConfiguration(), + refitSettings: RefitSettings(refitSettings))); + } + + private static RefitSettings RefitSettings(RefitSettings refitSettings) + { + return refitSettings ?? new RefitSettings + { + ContentSerializer = new NewtonsoftJsonContentSerializer(NovuClient.DefaultSerializerSettings), + }; } } \ No newline at end of file diff --git a/src/Novu.Extensions/LICENSE.txt b/src/Novu.Extensions/LICENSE.txt new file mode 100644 index 0000000..591d715 --- /dev/null +++ b/src/Novu.Extensions/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 picmi/toddb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/Novu.Extensions/Novu.Extensions.csproj b/src/Novu.Extensions/Novu.Extensions.csproj index 53a29ae..080d024 100644 --- a/src/Novu.Extensions/Novu.Extensions.csproj +++ b/src/Novu.Extensions/Novu.Extensions.csproj @@ -9,8 +9,8 @@ Novu.Extensions - 0.3.0 - Novu + 0.0.0 + Novu.Extensions Novu .NET SDK git https://github.com/novuhq/novu-dotnet @@ -35,4 +35,10 @@ + + + + + + diff --git a/src/Novu.Sync/LICENSE.txt b/src/Novu.Sync/LICENSE.txt new file mode 100644 index 0000000..591d715 --- /dev/null +++ b/src/Novu.Sync/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 picmi/toddb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/Novu.Sync/Novu.Sync.csproj b/src/Novu.Sync/Novu.Sync.csproj index 10e7bf5..3ce48a1 100644 --- a/src/Novu.Sync/Novu.Sync.csproj +++ b/src/Novu.Sync/Novu.Sync.csproj @@ -8,9 +8,9 @@ - Novu.Extensions - 0.3.0 - Novu + Novu.Sync + 0.0.0 + Novu.Sync Novu .NET SDK git https://github.com/novuhq/novu-dotnet @@ -23,8 +23,14 @@ - - + + + + + + + + diff --git a/src/Novu.Sync/Services/IntegrationSync.cs b/src/Novu.Sync/Services/IntegrationSync.cs index dc16bb3..5a20290 100644 --- a/src/Novu.Sync/Services/IntegrationSync.cs +++ b/src/Novu.Sync/Services/IntegrationSync.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Novu.DTO.Integrations; using Novu.Interfaces; +using Novu.Models; using Novu.Sync.Comparers; using Novu.Sync.Models; using Novu.Sync.Utils; @@ -78,16 +79,7 @@ await Task.WhenAll(changeSet.Delete.Select(x => .Single(c => c.ProviderId == x.ProviderId && c.Channel == x.Channel); - return Task.Run(() => _integrationClient.Update( - x.Id, - new IntegrationEditData - { - Identifier = updateTo.Identifier, - Channel = updateTo.Channel, - Active = updateTo.Active, - Credentials = updateTo.Credentials, - Name = updateTo.Name, - })); + return Task.Run(() => _integrationClient.Update(x.Id, updateTo.ToEditData())); })); } diff --git a/src/Novu.Tests/AcceptanceTests/IntegrationTests.cs b/src/Novu.Tests/AcceptanceTests/IntegrationTests.cs index 008fc37..ee4243b 100644 --- a/src/Novu.Tests/AcceptanceTests/IntegrationTests.cs +++ b/src/Novu.Tests/AcceptanceTests/IntegrationTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using FluentAssertions; using Novu.DTO.Integrations; +using Novu.Models; using Novu.Tests.IntegrationTests; using Xunit; using Xunit.Abstractions; @@ -17,6 +18,7 @@ public IntegrationTests(ITestOutputHelper output) : base(output) /// /// TODO: add more integrations to this list + /// TODO: have integrations as an enumeration /// public static IEnumerable Data => new List @@ -36,16 +38,32 @@ public async Task AcceptanceTest( string provider) { var integrations = await Integration.Get(); - var existingIntegration = integrations.Data.FirstOrDefault(x => x.ProviderId == provider); + + // the understanding is that providers are always unique by provider id + var existingIntegration = integrations.Data.SingleOrDefault(x => x.ProviderId == provider); if (existingIntegration is not null) { // delete and not find await Integration.Delete(existingIntegration.Id); + var deletedIntegration = await Integration.Get(existingIntegration.Id); + deletedIntegration.Data.Should().BeNull(); + + // now remake + var newIntegration = await Make(providerId: provider); + newIntegration.Active.Should().BeTrue(); + + // reinstate the integration given this might be a working test system + await Integration.Update(newIntegration.Id, existingIntegration.ToEditData()); } + else + { + // create + var integration = await Make(providerId: provider); + integration.Active.Should().BeTrue(); - // create (and delete in is teardown) - var integration = await Make(providerId: provider); - integration.Active.Should().BeTrue(); + // teardown here rather than in base + await Integration.Delete(integration.Id); + } } } \ No newline at end of file diff --git a/src/Novu.Tests/AcceptanceTests/NotificationTests.cs b/src/Novu.Tests/AcceptanceTests/NotificationTests.cs index 38b6b9c..3dd208d 100644 --- a/src/Novu.Tests/AcceptanceTests/NotificationTests.cs +++ b/src/Novu.Tests/AcceptanceTests/NotificationTests.cs @@ -14,6 +14,7 @@ using Novu.DTO.WorkflowGroups; using Novu.DTO.Workflows; using Novu.Interfaces; +using Novu.Models; using Novu.Models.Subscribers.Preferences; using Novu.Models.Triggers; using Novu.Models.Workflows; @@ -53,7 +54,7 @@ public async Task E2E_InApp_Event_Test() await VerifyNotifications(subscriber); } - + [RunnableInDebugOnly] public async Task E2E_InApp_Topic_Test() { @@ -189,15 +190,7 @@ await retryPolicy.ExecuteAsync(async () => { await Integration.Update( existingIntegration.Id, - new IntegrationEditData - { - Active = true, - Identifier = existingIntegration.Identifier, - Name = existingIntegration.Name, - Credentials = existingIntegration.Credentials, - Channel = existingIntegration.Channel, - EnvironmentId = existingIntegration.EnvironmentId, - }); + existingIntegration.ToEditData(x => x.Active = true)); } else if (existingIntegration is null) { diff --git a/src/Novu.Tests/IntegrationTests/BaseIntegrationTest.cs b/src/Novu.Tests/IntegrationTests/BaseIntegrationTest.cs index d169e70..d7dbd4a 100644 --- a/src/Novu.Tests/IntegrationTests/BaseIntegrationTest.cs +++ b/src/Novu.Tests/IntegrationTests/BaseIntegrationTest.cs @@ -20,7 +20,6 @@ using Novu.Models.Subscribers; using Novu.Models.Workflows; using Novu.Models.Workflows.Step.Message; -using Novu.NotificationTemplates; using Novu.Sync; using ParkSquare.Testing.Generators; using Refit; @@ -62,7 +61,6 @@ protected BaseIntegrationTest(ITestOutputHelper output) protected IEventClient Event => Get(); protected ITopicClient Topic => Get(); protected IWorkflowGroupClient WorkflowGroup => Get(); - protected INotificationTemplatesClient NotificationTemplates => Get(); protected IWorkflowClient Workflow => Get(); protected ILayoutClient Layout => Get(); protected IIntegrationClient Integration => Get(); diff --git a/src/Novu.Tests/IntegrationTests/NotificationTemplatesTests.cs b/src/Novu.Tests/IntegrationTests/NotificationTemplatesTests.cs deleted file mode 100644 index b54a741..0000000 --- a/src/Novu.Tests/IntegrationTests/NotificationTemplatesTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using FluentAssertions; -using Novu.NotificationTemplates; -using Xunit; -using Xunit.Abstractions; - -namespace Novu.Tests.IntegrationTests; - -public class NotificationTemplatesTests : BaseIntegrationTest -{ - public NotificationTemplatesTests(ITestOutputHelper output) : base(output) - { - } - - [Fact(Skip = "Interface is deprecated")] - public async Task Should_Get_Notification_Templates() - { - var templates = await NotificationTemplates.GetTemplates(); - templates.Should().NotBeNull(); - templates.Page.Should().Be(0); - templates.Data.Should().NotBeEmpty(); - } - - [Fact(Skip = "Interface is deprecated")] - public async Task Should_SetStatus() - { - var templates = await NotificationTemplates.GetTemplates(); - var template = templates.Data.First(); - var result = await NotificationTemplates.UpdateStatus(template.Id, - new UpdateTemplateStatusRequest - { - Active = !template.Active, - }); - result.Data.Should().NotBeNull(); - result.Data.Active.Should().Be(!template.Active); - } -} \ No newline at end of file diff --git a/src/Novu.Tests/IntegrationTests/WorkflowTests.cs b/src/Novu.Tests/IntegrationTests/WorkflowTests.cs index c534f46..2b67f8e 100644 --- a/src/Novu.Tests/IntegrationTests/WorkflowTests.cs +++ b/src/Novu.Tests/IntegrationTests/WorkflowTests.cs @@ -8,8 +8,10 @@ using Novu.Models.Workflows; using Novu.Models.Workflows.Step; using ParkSquare.Testing.Generators; +using Polly; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; namespace Novu.Tests.IntegrationTests; @@ -108,18 +110,31 @@ public async Task Should_Delete_Workflow() result.Data.Should().BeNull(); } + /// + /// This is a flaky test so add handles around it + /// [Fact] public async Task Should_SetStatus() { var workflows = await Workflow.Get(); var workflow = workflows.Data.First(); - var result = await Workflow.UpdateStatus( - workflow.Id, - new WorkflowStatusEditData - { - Active = !workflow.Active, - }); - result.Data.Should().NotBeNull(); - result.Data.Active.Should().NotBe(workflow.Active); + + // WAIT for system to catch up given it is async + const int maxRetryAttempts = 3; + var retryPolicy = Policy + .Handle() + .WaitAndRetryAsync(maxRetryAttempts, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); + + await retryPolicy.ExecuteAsync(async () => + { + var result = await Workflow.UpdateStatus( + workflow.Id, + new WorkflowStatusEditData + { + Active = !workflow.Active, + }); + result.Data.Should().NotBeNull(); + result.Data.Active.Should().NotBe(workflow.Active); + }); } } \ No newline at end of file diff --git a/src/Novu.Tests/JsonConverters/MetaDataConverterTests.cs b/src/Novu.Tests/MicroTests/JsonConverters/MetaDataConverterTests.cs similarity index 98% rename from src/Novu.Tests/JsonConverters/MetaDataConverterTests.cs rename to src/Novu.Tests/MicroTests/JsonConverters/MetaDataConverterTests.cs index c0d9267..5c34aaa 100644 --- a/src/Novu.Tests/JsonConverters/MetaDataConverterTests.cs +++ b/src/Novu.Tests/MicroTests/JsonConverters/MetaDataConverterTests.cs @@ -7,7 +7,7 @@ using Novu.Models.Workflows.Step; using Xunit; -namespace Novu.Tests.JsonConverters; +namespace Novu.Tests.MicroTests.JsonConverters; public class MetaDataConverterTests { diff --git a/src/Novu.Tests/JsonConverters/TypeorStringConverterTests.cs b/src/Novu.Tests/MicroTests/JsonConverters/TypeorStringConverterTests.cs similarity index 97% rename from src/Novu.Tests/JsonConverters/TypeorStringConverterTests.cs rename to src/Novu.Tests/MicroTests/JsonConverters/TypeorStringConverterTests.cs index d4d3ac4..d7bd6a9 100644 --- a/src/Novu.Tests/JsonConverters/TypeorStringConverterTests.cs +++ b/src/Novu.Tests/MicroTests/JsonConverters/TypeorStringConverterTests.cs @@ -7,7 +7,7 @@ using Novu.Models.Workflows.Step.Message; using Xunit; -namespace Novu.Tests.JsonConverters; +namespace Novu.Tests.MicroTests.JsonConverters; public class TypeOrStringConverterTests { diff --git a/src/Novu.Tests/Sync/SyncIntegrationTests.cs b/src/Novu.Tests/MicroTests/Sync/SyncIntegrationTests.cs similarity index 99% rename from src/Novu.Tests/Sync/SyncIntegrationTests.cs rename to src/Novu.Tests/MicroTests/Sync/SyncIntegrationTests.cs index 3acf795..8d406f4 100644 --- a/src/Novu.Tests/Sync/SyncIntegrationTests.cs +++ b/src/Novu.Tests/MicroTests/Sync/SyncIntegrationTests.cs @@ -7,7 +7,6 @@ using Novu.Interfaces; using Novu.Models.Integrations; using Novu.Models.Subscribers.Preferences; -using Novu.Sync; using Novu.Sync.Models; using Novu.Sync.Services; using Novu.Tests.IntegrationTests; @@ -15,7 +14,7 @@ using Xunit; using Xunit.Abstractions; -namespace Novu.Tests.Sync; +namespace Novu.Tests.MicroTests.Sync; public class SyncIntegrationTests : BaseIntegrationTest { diff --git a/src/Novu.Tests/Sync/SyncLayoutTests.cs b/src/Novu.Tests/MicroTests/Sync/SyncLayoutTests.cs similarity index 99% rename from src/Novu.Tests/Sync/SyncLayoutTests.cs rename to src/Novu.Tests/MicroTests/Sync/SyncLayoutTests.cs index f355df2..3531f6c 100644 --- a/src/Novu.Tests/Sync/SyncLayoutTests.cs +++ b/src/Novu.Tests/MicroTests/Sync/SyncLayoutTests.cs @@ -6,7 +6,6 @@ using Novu.DTO; using Novu.DTO.Layouts; using Novu.Interfaces; -using Novu.Sync; using Novu.Sync.Models; using Novu.Sync.Services; using Novu.Tests.IntegrationTests; @@ -14,7 +13,7 @@ using Xunit; using Xunit.Abstractions; -namespace Novu.Tests.Sync; +namespace Novu.Tests.MicroTests.Sync; public class SyncLayoutTests : BaseIntegrationTest { diff --git a/src/Novu.Tests/Sync/SyncWorkflowGroupTests.cs b/src/Novu.Tests/MicroTests/Sync/SyncWorkflowGroupTests.cs similarity index 99% rename from src/Novu.Tests/Sync/SyncWorkflowGroupTests.cs rename to src/Novu.Tests/MicroTests/Sync/SyncWorkflowGroupTests.cs index 8b11ac2..a45387a 100644 --- a/src/Novu.Tests/Sync/SyncWorkflowGroupTests.cs +++ b/src/Novu.Tests/MicroTests/Sync/SyncWorkflowGroupTests.cs @@ -13,7 +13,7 @@ using Xunit; using Xunit.Abstractions; -namespace Novu.Tests.Sync; +namespace Novu.Tests.MicroTests.Sync; public class SyncWorkflowGroupTests : BaseIntegrationTest { diff --git a/src/Novu.Tests/Sync/SyncWorkflowTests.cs b/src/Novu.Tests/MicroTests/Sync/SyncWorkflowTests.cs similarity index 99% rename from src/Novu.Tests/Sync/SyncWorkflowTests.cs rename to src/Novu.Tests/MicroTests/Sync/SyncWorkflowTests.cs index 135d5b5..1ed4e26 100644 --- a/src/Novu.Tests/Sync/SyncWorkflowTests.cs +++ b/src/Novu.Tests/MicroTests/Sync/SyncWorkflowTests.cs @@ -13,7 +13,7 @@ using Xunit; using Xunit.Abstractions; -namespace Novu.Tests.Sync; +namespace Novu.Tests.MicroTests.Sync; public class SyncWorkflowTests : BaseIntegrationTest { diff --git a/src/Novu.Tests/Novu.Tests.csproj b/src/Novu.Tests/Novu.Tests.csproj index aeebfa8..c8e20b4 100644 --- a/src/Novu.Tests/Novu.Tests.csproj +++ b/src/Novu.Tests/Novu.Tests.csproj @@ -4,22 +4,23 @@ net7.0 false Novu.Tests + 0.0.0 - - - - - - + + + + + + - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -45,14 +46,8 @@ Always - PreserveNewest + PreserveNewest - - - - - - - + diff --git a/src/Novu/DTO/Integrations/Integration.cs b/src/Novu/DTO/Integrations/Integration.cs index 2b2b339..bc743e1 100644 --- a/src/Novu/DTO/Integrations/Integration.cs +++ b/src/Novu/DTO/Integrations/Integration.cs @@ -11,11 +11,26 @@ public class Integration [JsonProperty("_organizationId")] public string OrganizationId { get; set; } - [JsonProperty("name")] public string Name { get; set; } - - [JsonProperty("identifier")] public string Identifier { get; set; } - - [JsonProperty("providerId")] public string ProviderId { get; set; } + /// + /// Display name of the integration + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Unique name of the provider, and by conventions usually prefixed by the provider + /// + [JsonProperty("identifier")] + public string Identifier { get; set; } + + /// + /// Always unique by provider id + /// + /// + /// Until an enum strategy is implemented see https://github.com/novuhq/novu/blob/next/libs/shared/src/consts/providers/provider.enum.ts + /// + [JsonProperty("providerId")] + public string ProviderId { get; set; } [JsonProperty("channel")] public string Channel { get; set; } @@ -23,7 +38,11 @@ public class Integration [JsonProperty("active")] public bool Active { get; set; } - [JsonProperty("deleted")] public bool Deleted { get; set; } + /// + /// A deleted integration can only be PUT and not POSTed on + /// + [JsonProperty("deleted")] + public bool Deleted { get; set; } [JsonProperty("deletedAt")] public string DeletedAt { get; set; } diff --git a/src/Novu/DTO/Integrations/IntegrationEditData.cs b/src/Novu/DTO/Integrations/IntegrationEditData.cs index 2f55d42..c8ec1d4 100644 --- a/src/Novu/DTO/Integrations/IntegrationEditData.cs +++ b/src/Novu/DTO/Integrations/IntegrationEditData.cs @@ -15,6 +15,12 @@ public class IntegrationEditData [JsonProperty("active")] public bool Active { get; set; } - [JsonProperty("check")] public bool Check { get; set; } [JsonProperty("channel")] public string Channel { get; set; } + + /// + /// Flag that is not stored on the resource but rather has the server perform a check that the integration + /// can make a connection + /// + [JsonProperty("check")] + public bool Check { get; set; } } \ No newline at end of file diff --git a/src/Novu/Interfaces/INovuClient.cs b/src/Novu/Interfaces/INovuClient.cs index 4936e02..68d61ba 100644 --- a/src/Novu/Interfaces/INovuClient.cs +++ b/src/Novu/Interfaces/INovuClient.cs @@ -1,16 +1,11 @@ -using Novu.NotificationTemplates; - -namespace Novu.Interfaces; +namespace Novu.Interfaces; public interface INovuClient { - public ISubscriberClient Subscriber { get; } - - public IEventClient Event { get; } - - public ITopicClient Topic { get; } - public INotificationTemplatesClient NotificationTemplates { get; } - public IWorkflowGroupClient WorkflowGroup { get; } + ISubscriberClient Subscriber { get; } + IEventClient Event { get; } + ITopicClient Topic { get; } + IWorkflowGroupClient WorkflowGroup { get; } IWorkflowClient Workflow { get; } ILayoutClient Layout { get; } IIntegrationClient Integration { get; } diff --git a/src/Novu/Models/IntegrationExtensions.cs b/src/Novu/Models/IntegrationExtensions.cs new file mode 100644 index 0000000..8a1076f --- /dev/null +++ b/src/Novu/Models/IntegrationExtensions.cs @@ -0,0 +1,25 @@ +using Novu.DTO.Integrations; + +namespace Novu.Models; + +public static class IntegrationExtensions +{ + public static IntegrationEditData ToEditData( + this Integration integration, + Action? overrides = null) + { + var editData = new IntegrationEditData + { + Name = integration.Name, + Credentials = integration.Credentials, + Channel = integration.Channel, + EnvironmentId = integration.EnvironmentId, + Identifier = integration.Identifier, + Active = integration.Active, + }; + + overrides?.Invoke(editData); + + return editData; + } +} \ No newline at end of file diff --git a/src/Novu/NotificationTemplates/Child.cs b/src/Novu/NotificationTemplates/Child.cs deleted file mode 100644 index e1ae5cc..0000000 --- a/src/Novu/NotificationTemplates/Child.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Newtonsoft.Json; - -namespace Novu.NotificationTemplates; - -public class Child -{ - [JsonProperty("field")] public string Field { get; set; } - - [JsonProperty("value")] public string Value { get; set; } - - [JsonProperty("operator")] public string Operator { get; set; } - - [JsonProperty("on")] public string On { get; set; } -} \ No newline at end of file diff --git a/src/Novu/NotificationTemplates/Filter.cs b/src/Novu/NotificationTemplates/Filter.cs deleted file mode 100644 index 8d62c8d..0000000 --- a/src/Novu/NotificationTemplates/Filter.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Newtonsoft.Json; - -namespace Novu.NotificationTemplates; - -public class Filter -{ - [JsonProperty("isNegated")] public bool IsNegated { get; set; } - - [JsonProperty("type")] public string Type { get; set; } - - [JsonProperty("value")] public string Value { get; set; } - - [JsonProperty("children")] public Child[] Children { get; set; } -} \ No newline at end of file diff --git a/src/Novu/NotificationTemplates/INotificationTemplatesClient.cs b/src/Novu/NotificationTemplates/INotificationTemplatesClient.cs deleted file mode 100644 index 7f37e9a..0000000 --- a/src/Novu/NotificationTemplates/INotificationTemplatesClient.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Novu.DTO; -using Refit; - -namespace Novu.NotificationTemplates; - -/// -/// see https://docs.novu.co/api/get-notification-templates/ -/// -[Obsolete("Notification templates have been renamed to Workflows, Please use the new workflows controller")] -public interface INotificationTemplatesClient -{ - [Get("/notification-templates")] - public Task> GetTemplates([Query] int page = 0, [Query] int limit = 10); - - [Delete("/notification-templates/{templateId}")] - public Task DeleteTemplate(string templateId); - - [Get("/notification-templates/{templateId}")] - public Task> GetTemplate(string templateId); - - [Put("/notification-templates/{templateId}/status")] - public Task> UpdateStatus( - string templateId, - [Body] UpdateTemplateStatusRequest request); -} \ No newline at end of file diff --git a/src/Novu/NotificationTemplates/NotificationGroup.cs b/src/Novu/NotificationTemplates/NotificationGroup.cs deleted file mode 100644 index 13faf1c..0000000 --- a/src/Novu/NotificationTemplates/NotificationGroup.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Newtonsoft.Json; - -namespace Novu.NotificationTemplates; - -public class NotificationGroup -{ - [JsonProperty("_id")] public string Id { get; set; } - - [JsonProperty("_organizationId")] public string OrganizationId { get; set; } - - [JsonProperty("_environmentId")] public string EnvironmentId { get; set; } - - [JsonProperty("_parentId")] public string ParentId { get; set; } - - [JsonProperty("name")] public string Name { get; set; } -} \ No newline at end of file diff --git a/src/Novu/NotificationTemplates/NotificationTemplate.cs b/src/Novu/NotificationTemplates/NotificationTemplate.cs deleted file mode 100644 index 2855e6a..0000000 --- a/src/Novu/NotificationTemplates/NotificationTemplate.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Newtonsoft.Json; - -namespace Novu.NotificationTemplates; - -public class NotificationTemplate -{ - [JsonProperty("_id")] public string Id { get; set; } - - [JsonProperty("_organizationId")] public string OrganizationId { get; set; } - - [JsonProperty("_creatorId")] public string CreatorId { get; set; } - - [JsonProperty("_environmentId")] public string EnvironmentId { get; set; } - - [JsonProperty("_notificationGroupId")] public string NotificationGroupId { get; set; } - - [JsonProperty("_parentId")] public string ParentId { get; set; } - - [JsonProperty("name")] public string Name { get; set; } - - [JsonProperty("description")] public string Description { get; set; } - - [JsonProperty("active")] public bool Active { get; set; } - - [JsonProperty("draft")] public bool Draft { get; set; } - - [JsonProperty("critical")] public bool Critical { get; set; } - - [JsonProperty("tags")] public string[] Tags { get; set; } - - [JsonProperty("preferenceSettings")] public PreferenceSettings PreferenceSettings { get; set; } - - [JsonProperty("steps")] public Step[] Steps { get; set; } - - [JsonProperty("triggers")] public Trigger[] Triggers { get; set; } - - [JsonProperty("deleted")] public bool Deleted { get; set; } - - [JsonProperty("deletedAt")] public DateTime DeletedAt { get; set; } - - [JsonProperty("deletedBy")] public string DeletedBy { get; set; } - - [JsonProperty("notificationGroup")] public NotificationGroup NotificationGroup { get; set; } -} \ No newline at end of file diff --git a/src/Novu/NotificationTemplates/PreferenceSettings.cs b/src/Novu/NotificationTemplates/PreferenceSettings.cs deleted file mode 100644 index 5557410..0000000 --- a/src/Novu/NotificationTemplates/PreferenceSettings.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Newtonsoft.Json; - -namespace Novu.NotificationTemplates; - -public class PreferenceSettings -{ - [JsonProperty("email")] public bool Email { get; set; } - - [JsonProperty("sms")] public bool Sms { get; set; } - - [JsonProperty("chat")] public bool Chat { get; set; } - - [JsonProperty("push")] public bool Push { get; set; } - - [JsonProperty("in_app")] public bool InApp { get; set; } -} \ No newline at end of file diff --git a/src/Novu/NotificationTemplates/Step.cs b/src/Novu/NotificationTemplates/Step.cs deleted file mode 100644 index 1eb0614..0000000 --- a/src/Novu/NotificationTemplates/Step.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Newtonsoft.Json; - -namespace Novu.NotificationTemplates; - -public class Step -{ - [JsonProperty("_id")] public string Id { get; set; } - - [JsonProperty("name")] public string Name { get; set; } - - [JsonProperty("_templateId")] public string TemplateId { get; set; } - - [JsonProperty("active")] public bool Active { get; set; } - - [JsonProperty("shouldStopOnFail")] public bool ShouldStopOnFail { get; set; } - - [JsonProperty("template")] public object Template { get; set; } - - [JsonProperty("filters")] public Filter[] Filters { get; set; } - - [JsonProperty("_parentId")] public string ParentId { get; set; } - - [JsonProperty("metadata")] public object Metadata { get; set; } - - [JsonProperty("replyCallback")] public object ReplyCallback { get; set; } -} \ No newline at end of file diff --git a/src/Novu/NotificationTemplates/Trigger.cs b/src/Novu/NotificationTemplates/Trigger.cs deleted file mode 100644 index 7b2b4aa..0000000 --- a/src/Novu/NotificationTemplates/Trigger.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Newtonsoft.Json; - -namespace Novu.NotificationTemplates; - -public class Trigger -{ - [JsonProperty("type")] public string Type { get; set; } - - [JsonProperty("identifier")] public string Identifier { get; set; } - - [JsonProperty("variables")] public Variable[] Variables { get; set; } - - [JsonProperty("subscriberVariables")] public Variable[] SubscriberVariables { get; set; } -} \ No newline at end of file diff --git a/src/Novu/NotificationTemplates/UpdateTemplateStatusRequest.cs b/src/Novu/NotificationTemplates/UpdateTemplateStatusRequest.cs deleted file mode 100644 index 7cee2d3..0000000 --- a/src/Novu/NotificationTemplates/UpdateTemplateStatusRequest.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Newtonsoft.Json; - -namespace Novu.NotificationTemplates; - -public class UpdateTemplateStatusRequest -{ - [JsonProperty("active")] public bool Active { get; set; } -} \ No newline at end of file diff --git a/src/Novu/NotificationTemplates/Variable.cs b/src/Novu/NotificationTemplates/Variable.cs deleted file mode 100644 index a9dcca5..0000000 --- a/src/Novu/NotificationTemplates/Variable.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Newtonsoft.Json; - -namespace Novu.NotificationTemplates; - -public class Variable -{ - [JsonProperty("name")] public string Name { get; set; } -} \ No newline at end of file diff --git a/src/Novu/Novu.csproj b/src/Novu/Novu.csproj index 30453a8..712e645 100644 --- a/src/Novu/Novu.csproj +++ b/src/Novu/Novu.csproj @@ -13,7 +13,7 @@ Novu - 0.3.0 + 0.0.0 Novu Novu .NET SDK git @@ -38,6 +38,10 @@ + + + + diff --git a/src/Novu/NovuClient.cs b/src/Novu/NovuClient.cs index a20cda3..9ecd412 100644 --- a/src/Novu/NovuClient.cs +++ b/src/Novu/NovuClient.cs @@ -2,7 +2,6 @@ using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using Novu.Interfaces; -using Novu.NotificationTemplates; using Refit; namespace Novu; @@ -24,34 +23,6 @@ public class NovuClient : INovuClient }, }; - public NovuClient(INovuClientConfiguration configuration) : this(configuration, default) - { - } - - public NovuClient( - ISubscriberClient subscriber, - IEventClient @event, - ITopicClient topic, - INotificationTemplatesClient notificationTemplates, - IWorkflowClient workflow, - IWorkflowGroupClient workflowGroup, - IIntegrationClient integration, - INotificationsClient notifications, - IMessageClient message, - ILayoutClient layout) - { - Subscriber = subscriber; - Event = @event; - Topic = topic; - NotificationTemplates = notificationTemplates; - Workflow = workflow; - WorkflowGroup = workflowGroup; - Integration = integration; - Notifications = notifications; - Message = message; - Layout = layout; - } - public NovuClient( INovuClientConfiguration configuration, HttpClient? client = default, @@ -69,7 +40,6 @@ public NovuClient( Subscriber = RestService.For(httpClient, refitSettings); Event = RestService.For(httpClient, refitSettings); Topic = RestService.For(httpClient, refitSettings); - NotificationTemplates = RestService.For(httpClient, refitSettings); WorkflowGroup = RestService.For(httpClient, refitSettings); Workflow = RestService.For(httpClient, refitSettings); Layout = RestService.For(httpClient, refitSettings); @@ -89,6 +59,5 @@ public NovuClient( public ISubscriberClient Subscriber { get; } public IEventClient Event { get; } public ITopicClient Topic { get; } - public INotificationTemplatesClient NotificationTemplates { get; } public IWorkflowGroupClient WorkflowGroup { get; } } \ No newline at end of file