diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Services/IAppPlansProvider.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Services/IAppPlansProvider.cs index c73aa5dc1..de04fdfe1 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Services/IAppPlansProvider.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Services/IAppPlansProvider.cs @@ -21,8 +21,8 @@ namespace Squidex.Domain.Apps.Entities.Apps.Services IAppLimitsPlan? GetPlan(string? planId); - IAppLimitsPlan GetPlanForApp(IAppEntity app); - IAppLimitsPlan GetFreePlan(); + + (IAppLimitsPlan Plan, string PlanId) GetPlanForApp(IAppEntity app); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/ConfigAppPlansProvider.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/ConfigAppPlansProvider.cs index 3be58073c..496f146a8 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/ConfigAppPlansProvider.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/ConfigAppPlansProvider.cs @@ -57,13 +57,6 @@ namespace Squidex.Domain.Apps.Entities.Apps.Services.Implementations return plansById.GetOrDefault(planId ?? string.Empty); } - public IAppLimitsPlan GetPlanForApp(IAppEntity app) - { - Guard.NotNull(app); - - return GetPlanCore(app.Plan?.PlanId); - } - public IAppLimitsPlan GetFreePlan() { return GetPlanCore(plansList.FirstOrDefault(x => string.IsNullOrWhiteSpace(x.Costs))?.Id); @@ -90,6 +83,23 @@ namespace Squidex.Domain.Apps.Entities.Apps.Services.Implementations return null; } + public (IAppLimitsPlan Plan, string PlanId) GetPlanForApp(IAppEntity app) + { + Guard.NotNull(app); + + var planId = app.Plan?.PlanId; + var plan = GetPlanCore(planId); + + if (plan.YearlyId != null && plan.YearlyId == planId) + { + return (plan, plan.YearlyId); + } + else + { + return (plan, plan.Id); + } + } + private ConfigAppLimitsPlan GetPlanCore(string? planId) { return plansById.GetOrDefault(planId ?? string.Empty) ?? plansById.Values.FirstOrDefault() ?? Infinite; diff --git a/backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs b/backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs index 611e78138..6c96aa68c 100644 --- a/backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs +++ b/backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs @@ -69,11 +69,11 @@ namespace Squidex.Web.Pipeline { using (Profiler.Trace("CheckUsage")) { - var plan = appPlansProvider.GetPlanForApp(app); + var (plan, _) = appPlansProvider.GetPlanForApp(app); var usage = await usageTracker.GetMonthlyCallsAsync(appId, DateTime.Today); - if (plan?.MaxApiCalls >= 0 && usage > plan.MaxApiCalls * 1.1) + if (plan.MaxApiCalls >= 0 && usage > plan.MaxApiCalls * 1.1) { context.Result = new StatusCodeResult(429); return; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs index 831a3b3a7..8ddd3f8f0 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs @@ -139,7 +139,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models PlanUpgrade = plans.GetPlanUpgradeForApp(app)?.Name; } - PlanName = plans.GetPlanForApp(app).Name; + PlanName = plans.GetPlanForApp(app).Plan.Name; } private void SetImage(IAppEntity app, ApiController controller) diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs index f18368ebd..c62652317 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorsDto.cs @@ -59,7 +59,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models private void WithPlan(IAppEntity app, IAppPlansProvider plans) { - MaxContributors = plans.GetPlanForApp(app).MaxContributors; + MaxContributors = plans.GetPlanForApp(app).Plan.MaxContributors; } private void WithInvited(bool isInvited) diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs index 8c8120e8d..0fb0e504b 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs @@ -296,7 +296,7 @@ namespace Squidex.Areas.Api.Controllers.Assets throw new ValidationException("Cannot create asset.", error); } - var plan = appPlansProvider.GetPlanForApp(App); + var (plan, _) = appPlansProvider.GetPlanForApp(App); var currentSize = await assetStatsRepository.GetTotalSizeAsync(AppId); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/AppPlansDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/AppPlansDto.cs index 5daae1ce8..500bd4f1a 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/AppPlansDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/AppPlansDto.cs @@ -37,7 +37,7 @@ namespace Squidex.Areas.Api.Controllers.Plans.Models public static AppPlansDto FromApp(IAppEntity app, IAppPlansProvider plans, bool hasPortal) { - var planId = plans.GetPlanForApp(app).Id; + var (_, planId) = plans.GetPlanForApp(app); var response = new AppPlansDto { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs b/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs index de21b538e..de06e0f96 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs @@ -97,7 +97,7 @@ namespace Squidex.Areas.Api.Controllers.Statistics { var count = await usageTracker.GetMonthlyCallsAsync(AppId.ToString(), DateTime.Today); - var plan = appPlansProvider.GetPlanForApp(App); + var (plan, _) = appPlansProvider.GetPlanForApp(App); var response = new CurrentCallsDto { Count = count, MaxAllowed = plan.MaxApiCalls }; @@ -151,7 +151,7 @@ namespace Squidex.Areas.Api.Controllers.Statistics { var size = await assetStatsRepository.GetTotalSizeAsync(AppId); - var plan = appPlansProvider.GetPlanForApp(App); + var (plan, _) = appPlansProvider.GetPlanForApp(App); var response = new CurrentStorageDto { Size = size, MaxAllowed = plan.MaxAssetSize }; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Billing/ConfigAppLimitsProviderTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Billing/ConfigAppLimitsProviderTests.cs index 429010e10..7ce24b899 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Billing/ConfigAppLimitsProviderTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Billing/ConfigAppLimitsProviderTests.cs @@ -63,9 +63,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Billing { var sut = new ConfigAppPlansProvider(Enumerable.Empty()); - var plan = sut.GetPlanForApp(CreateApp(planId)); + var result = sut.GetPlanForApp(CreateApp(planId)); - plan.Should().BeEquivalentTo(InfinitePlan); + result.Should().BeEquivalentTo((InfinitePlan, "infinite")); } [Fact] @@ -93,9 +93,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Billing { var sut = new ConfigAppPlansProvider(Plans); - var plan = sut.GetPlanForApp(CreateApp("basic")); + var result = sut.GetPlanForApp(CreateApp("basic")); - plan.Should().BeEquivalentTo(BasicPlan); + result.Should().BeEquivalentTo((BasicPlan, "basic")); } [Fact] @@ -103,9 +103,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Billing { var sut = new ConfigAppPlansProvider(Plans); - var plan = sut.GetPlanForApp(CreateApp("basic_yearly")); + var result = sut.GetPlanForApp(CreateApp("basic_yearly")); - plan.Should().BeEquivalentTo(BasicPlan); + result.Should().BeEquivalentTo((BasicPlan, "basic_yearly")); } [Fact] @@ -113,9 +113,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Billing { var sut = new ConfigAppPlansProvider(Plans); - var plan = sut.GetPlanForApp(CreateApp("enterprise")); + var result = sut.GetPlanForApp(CreateApp("enterprise")); - plan.Should().BeEquivalentTo(FreePlan); + result.Should().BeEquivalentTo((FreePlan, "free")); } [Fact] diff --git a/backend/tests/Squidex.Web.Tests/Pipeline/ApiCostsFilterTests.cs b/backend/tests/Squidex.Web.Tests/Pipeline/ApiCostsFilterTests.cs index ca25c58ef..f1134dd2d 100644 --- a/backend/tests/Squidex.Web.Tests/Pipeline/ApiCostsFilterTests.cs +++ b/backend/tests/Squidex.Web.Tests/Pipeline/ApiCostsFilterTests.cs @@ -55,7 +55,7 @@ namespace Squidex.Web.Pipeline .Returns(appPlan); A.CallTo(() => appPlansProvider.GetPlanForApp(appEntity)) - .Returns(appPlan); + .Returns((appPlan, "free")); A.CallTo(() => appPlan.MaxApiCalls) .ReturnsLazily(x => apiCallsMax); diff --git a/frontend/app/shared/state/plans.state.ts b/frontend/app/shared/state/plans.state.ts index 9e2358e8e..8352027f2 100644 --- a/frontend/app/shared/state/plans.state.ts +++ b/frontend/app/shared/state/plans.state.ts @@ -134,7 +134,11 @@ export class PlansState extends State { } private createPlan(plan: PlanDto, id: string) { - return { plan, isYearlySelected: plan.yearlyId === id, isSelected: plan.id === id }; + return { + plan, + isSelected: plan.id === id, + isYearlySelected: plan.yearlyId === id + }; } private get appName() {