Quota: refactoring

This commit is contained in:
pavelbannov 2022-09-06 12:14:08 +03:00
parent fa1703be2d
commit 941689b6ca
12 changed files with 532 additions and 224 deletions

View File

@ -24,11 +24,6 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Net.Http.Headers;
using JsonConverter = System.Text.Json.Serialization.JsonConverter;
namespace ASC.Api.Core;
@ -122,6 +117,9 @@ public abstract class BaseStartup
services.AddEventBus(_configuration);
services.AddDistributedTaskQueue();
services.AddCacheNotify(_configuration);
services.AddScoped<ITenantQuotaFeatureChecker, ActiveUsersChecker>();
services.AddScoped<ActiveUsersChecker>();
DIHelper.TryAdd(typeof(IWebhookPublisher), typeof(WebhookPublisher));

View File

@ -26,6 +26,7 @@
global using System.ComponentModel;
global using System.Globalization;
global using System.IdentityModel.Tokens.Jwt;
global using System.Linq.Expressions;
global using System.Net;
global using System.Reflection;
@ -57,7 +58,6 @@ global using ASC.Common.Caching;
global using ASC.Common.DependencyInjection;
global using ASC.Common.Log;
global using ASC.Common.Logging;
global using ASC.Common.Notify.Engine;
global using ASC.Common.Threading;
global using ASC.Common.Utils;
global using ASC.Common.Web;
@ -66,6 +66,8 @@ global using ASC.Core.Common.EF;
global using ASC.Core.Common.EF.Context;
global using ASC.Core.Common.Hosting;
global using ASC.Core.Common.Hosting.Interfaces;
global using ASC.Core.Common.Quota;
global using ASC.Core.Common.Quota.Features;
global using ASC.Core.Common.Settings;
global using ASC.Core.Tenants;
global using ASC.Core.Users;
@ -81,7 +83,6 @@ global using ASC.MessagingSystem.EF.Model;
global using ASC.Security.Cryptography;
global using ASC.Web.Api.Routing;
global using ASC.Web.Core;
global using ASC.Web.Core.Helpers;
global using ASC.Web.Core.Users;
global using ASC.Web.Studio.Utility;
global using ASC.Webhooks.Core;
@ -97,6 +98,8 @@ global using Confluent.Kafka;
global using HealthChecks.UI.Client;
global using Microsoft.AspNetCore.Authentication;
global using Microsoft.AspNetCore.Authentication.Cookies;
global using Microsoft.AspNetCore.Authentication.JwtBearer;
global using Microsoft.AspNetCore.Authorization;
global using Microsoft.AspNetCore.Builder;
global using Microsoft.AspNetCore.Diagnostics.HealthChecks;
@ -124,8 +127,8 @@ global using Microsoft.Extensions.Hosting;
global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Options;
global using Microsoft.Extensions.Primitives;
global using Microsoft.AspNetCore.Authentication.JwtBearer;
global using Microsoft.IdentityModel.Tokens;
global using Microsoft.IdentityModel.Tokens;
global using Microsoft.Net.Http.Headers;
global using Newtonsoft.Json;
global using Newtonsoft.Json.Serialization;

View File

@ -79,6 +79,8 @@ public class TariffService : ITariffService
private readonly IDbContextFactory<CoreDbContext> _dbContextFactory;
private readonly TariffServiceStorage _tariffServiceStorage;
private readonly BillingClient _billingClient;
private readonly IServiceProvider _serviceProvider;
//private readonly int _activeUsersMin;
//private readonly int _activeUsersMax;
@ -97,7 +99,8 @@ public class TariffService : ITariffService
IDbContextFactory<CoreDbContext> coreDbContextManager,
TariffServiceStorage tariffServiceStorage,
ILogger<TariffService> logger,
BillingClient billingClient)
BillingClient billingClient,
IServiceProvider serviceProvider)
: this()
{
@ -108,6 +111,7 @@ public class TariffService : ITariffService
_coreSettings = coreSettings;
_tariffServiceStorage = tariffServiceStorage;
_billingClient = billingClient;
_serviceProvider = serviceProvider;
_coreBaseSettings = coreBaseSettings;
_paymentDelay = configuration.GetSection("core:payment").Get<PaymentConfiguration>().Delay;
@ -261,7 +265,7 @@ public class TariffService : ITariffService
updatedQuota += quota;
}
CheckQuota(updatedQuota);
updatedQuota.Check(_serviceProvider);
var productIds = newQuotas.Select(q => q.ProductId);
@ -281,20 +285,6 @@ public class TariffService : ITariffService
return true;
}
// TODO: compare fields with current parameters
public bool CheckQuota(TenantQuota quota)
{
if (quota.MaxTotalSize != long.MaxValue
&& false) throw new Exception("The used storage size should not exceed " + quota.MaxTotalSize);
if (quota.ActiveUsers != int.MaxValue
&& false) throw new Exception("The number of active users should not exceed " + quota.ActiveUsers);
if (quota.CountAdmin != int.MaxValue
&& false) throw new Exception("The number of managers should not exceed " + quota.CountAdmin);
if (quota.CountRoom != int.MaxValue
&& false) throw new Exception("The number of rooms should not exceed " + quota.CountRoom);
return true;
}
public void SetTariff(int tenantId, Tariff tariff)
{
@ -401,7 +391,7 @@ public class TariffService : ITariffService
updatedQuota += quota;
}
CheckQuota(updatedQuota);
updatedQuota.Check(_serviceProvider);
var productIds = newQuotas.Select(q => q.ProductId);

View File

@ -90,6 +90,8 @@ global using ASC.Core.Common.Notify.IntegrationEvents.Events;
global using ASC.Core.Common.Notify.Jabber;
global using ASC.Core.Common.Notify.Push;
global using ASC.Core.Common.Notify.Telegram;
global using ASC.Core.Common.Quota;
global using ASC.Core.Common.Quota.Features;
global using ASC.Core.Common.Security;
global using ASC.Core.Common.Settings;
global using ASC.Core.Configuration;
@ -106,9 +108,9 @@ global using ASC.EventBus.Abstractions;
global using ASC.EventBus.Events;
global using ASC.Geolocation;
global using ASC.MessagingSystem.Core;
global using ASC.MessagingSystem.EF.Context;
global using ASC.MessagingSystem.EF.Model;
global using ASC.MessagingSystem.Mapping;
global using ASC.MessagingSystem.EF.Context;
global using ASC.Notify;
global using ASC.Notify.Channels;
global using ASC.Notify.Cron;
@ -163,7 +165,7 @@ global using Telegram.Bot;
global using static ASC.Security.Cryptography.EmailValidationKeyProvider;
global using JsonIgnoreAttribute = System.Text.Json.Serialization.JsonIgnoreAttribute;
global using FirebaseAdminMessaging = FirebaseAdmin.Messaging;
global using FirebaseApp = FirebaseAdmin.FirebaseApp;
global using AppOptions = FirebaseAdmin.AppOptions;
global using FirebaseApp = FirebaseAdmin.FirebaseApp;
global using FirebaseAdminMessaging = FirebaseAdmin.Messaging;
global using JsonIgnoreAttribute = System.Text.Json.Serialization.JsonIgnoreAttribute;

View File

@ -0,0 +1,55 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Common.Quota.Features;
public class ActiveUsersFeature : TenantQuotaFeatureCount
{
public override string Name { get => "users"; }
public override bool Paid { get => true; }
public ActiveUsersFeature(TenantQuota tenantQuota) : base(tenantQuota)
{
}
}
public class ActiveUsersChecker : ITenantQuotaFeatureChecker
{
private readonly UserManager _userManager;
public ActiveUsersChecker(UserManager userManager)
{
_userManager = userManager;
}
public bool Check(TenantQuota quota)
{
return _userManager.GetUsersByGroup(Users.Constants.GroupUser.ID).Length >= quota.ActiveUsers;
}
public string Exception(TenantQuota quota)
{
return "The number of active users should not exceed " + quota.ActiveUsers;
}
}

View File

@ -0,0 +1,34 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Common.Quota.Features;
public class CountAdminFeature : TenantQuotaFeatureCount
{
public override string Name { get => "admin"; }
public CountAdminFeature(TenantQuota tenantQuota) : base(tenantQuota)
{
}
}

View File

@ -0,0 +1,34 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Common.Quota.Features;
public class CountRoomFeature : TenantQuotaFeatureCount
{
public override string Name { get => "room"; }
public CountRoomFeature(TenantQuota tenantQuota) : base(tenantQuota)
{
}
}

View File

@ -0,0 +1,35 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Common.Quota.Features;
public class MaxTotalSizeFeature : TenantQuotaFeatureLength
{
public override string Name { get => "total_size"; }
public MaxTotalSizeFeature(TenantQuota tenantQuota) : base(tenantQuota)
{
}
}

View File

@ -0,0 +1,32 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Common.Quota;
public interface ITenantQuotaFeatureChecker
{
public bool Check(TenantQuota value);
public string Exception(TenantQuota value);
}

View File

@ -0,0 +1,138 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Common.Quota;
public class TenantQuotaFeature
{
public int Order { get; init; }
public virtual bool Visible { get; init; } = true;
public virtual string Name { get; init; }
public virtual bool Paid { get; init; }
protected internal virtual void Multiply(int quantity)
{
}
}
public class TenantQuotaFeature<T> : TenantQuotaFeature
{
private readonly TenantQuota _tenantQuota;
public T Value
{
get
{
var parsed = _tenantQuota.GetFeature(Name);
if (parsed == null)
{
return Default;
}
if (!TryParse(parsed, out var result))
{
return Default;
}
return result;
}
set
{
_tenantQuota.ReplaceFeature(Name, value);
}
}
protected virtual T Default { get; }
public TenantQuotaFeature(TenantQuota tenantQuota)
{
_tenantQuota = tenantQuota;
}
protected virtual bool TryParse(string s, out T result)
{
result = default;
return false;
}
}
public class TenantQuotaFeatureCount : TenantQuotaFeature<int>
{
protected override int Default => int.MaxValue;
public TenantQuotaFeatureCount(TenantQuota tenantQuota) : base(tenantQuota)
{
}
protected override bool TryParse(string s, out int result)
{
return int.TryParse(s.Substring(s.IndexOf(':') + 1), out result);
}
protected internal override void Multiply(int quantity)
{
if (Value != int.MaxValue)
{
Value *= quantity;
}
}
}
public class TenantQuotaFeatureLength : TenantQuotaFeature<long>
{
protected override long Default => long.MaxValue;
public TenantQuotaFeatureLength(TenantQuota tenantQuota) : base(tenantQuota)
{
}
protected override bool TryParse(string s, out long result)
{
return long.TryParse(s.Substring(s.IndexOf(':') + 1), out result);
}
protected internal override void Multiply(int quantity)
{
if (Value != long.MaxValue)
{
Value *= quantity;
}
}
}
public class TenantQuotaFeatureFlag : TenantQuotaFeature<bool>
{
public TenantQuotaFeatureFlag(TenantQuota tenantQuota) : base(tenantQuota)
{
}
protected override bool TryParse(string s, out bool result)
{
result = true;
return true;
}
}

View File

@ -27,7 +27,7 @@
namespace ASC.Core.Tenants;
[DebuggerDisplay("{Tenant} {Name}")]
public class TenantQuota : ICloneable, IMapFrom<DbQuota>
public class TenantQuota : IMapFrom<DbQuota>
{
public static readonly TenantQuota Default = new TenantQuota(Tenants.Tenant.DefaultTenant)
{
@ -46,6 +46,10 @@ public class TenantQuota : ICloneable, IMapFrom<DbQuota>
public bool Visible { get; set; }
public long MaxFileSize { get; set; }
public IReadOnlyList<TenantQuotaFeature> TenantQuotaFeatures { get; private set; }
private List<string> _featuresList;
public string Features
{
get
@ -64,142 +68,213 @@ public class TenantQuota : ICloneable, IMapFrom<DbQuota>
}
}
}
private readonly MaxTotalSizeFeature _maxTotalSizeFeature;
public long MaxTotalSize
{
get => ByteConverter.GetInBytes(GetFeature("total_size", () => Default.MaxTotalSize));
set => SetFeature("total_size", ByteConverter.GetInMBytes(value));
get => ByteConverter.GetInBytes(_maxTotalSizeFeature.Value);
set => _maxTotalSizeFeature.Value = ByteConverter.GetInMBytes(value);
}
private readonly ActiveUsersFeature _activeUsersFeature;
public int ActiveUsers
{
get => GetFeature("users", () => Default.ActiveUsers);
set => SetFeature("users", value);
get => _activeUsersFeature.Value;
set => _activeUsersFeature.Value = value;
}
private readonly CountAdminFeature _countAdminFeature;
public int CountAdmin
{
get => GetFeature("admin", () => Default.CountAdmin);
set => SetFeature("admin", value);
get => _countAdminFeature.Value;
set => _countAdminFeature.Value = value;
}
private readonly CountRoomFeature _countRoomFeature;
public int CountRoom
{
get => GetFeature("room", () => Default.CountRoom);
set => SetFeature("room", value);
get => _countRoomFeature.Value;
set => _countRoomFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _nonProfitFeature;
public bool NonProfit
{
get => GetFeature("non-profit");
set => SetFeature("non-profit", value);
get => _nonProfitFeature.Value;
set => _nonProfitFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _trialFeature;
public bool Trial
{
get => GetFeature("trial");
set => SetFeature("trial", value);
get => _trialFeature.Value;
set => _trialFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _freeFeature;
public bool Free
{
get => GetFeature("free");
set => SetFeature("free", value);
get => _freeFeature.Value;
set => _freeFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _openFeature;
public bool Open
{
get => GetFeature("open");
set => SetFeature("open", value);
get => _openFeature.Value;
set => _openFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _updateFeature;
public bool Update
{
get => GetFeature("update");
set => SetFeature("update", value);
get => _updateFeature.Value;
set => _updateFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _auditFeature;
public bool Audit
{
get => GetFeature("audit");
set => SetFeature("audit", value);
get => _auditFeature.Value;
set => _auditFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _docsEditionFeature;
public bool DocsEdition
{
get => GetFeature("docs");
set => SetFeature("docs", value);
get => _docsEditionFeature.Value;
set => _docsEditionFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _hasMigrationFeature;
public bool HasMigration
{
get => GetFeature("migration");
set => SetFeature("migration", value);
get => _hasMigrationFeature.Value;
set => _hasMigrationFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _ldapFeature;
public bool Ldap
{
get => GetFeature("ldap");
set => SetFeature("ldap", value);
get => _ldapFeature.Value;
set => _ldapFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _ssoFeature;
public bool Sso
{
get => GetFeature("sso");
set => SetFeature("sso", value);
get => _ssoFeature.Value;
set => _ssoFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _whiteLabelFeature;
public bool WhiteLabel
{
get => GetFeature("whitelabel");
set => SetFeature("whitelabel", value);
get => _whiteLabelFeature.Value;
set => _whiteLabelFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _customizationFeature;
public bool Customization
{
get => GetFeature("customization");
set => SetFeature("customization", value);
get => _customizationFeature.Value;
set => _customizationFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _customFeature;
public bool Custom
{
get => GetFeature("custom");
set => SetFeature("custom", value);
get => _customFeature.Value;
set => _customFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _restoreFeature;
public bool Restore
{
get => GetFeature("restore");
set => SetFeature("restore", value);
get => _restoreFeature.Value;
set => _restoreFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _autoBackupFeature;
public bool AutoBackup
{
get => GetFeature("autobackup");
set => SetFeature("autobackup", value);
get => _autoBackupFeature.Value;
set => _autoBackupFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _oauthFeature;
public bool Oauth
{
get => GetFeature("oauth");
set => SetFeature("oauth", value);
get => _oauthFeature.Value;
set => _oauthFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _contentSearchFeature;
public bool ContentSearch
{
get => GetFeature("contentsearch");
set => SetFeature("contentsearch", value);
get => _contentSearchFeature.Value;
set => _contentSearchFeature.Value = value;
}
private readonly TenantQuotaFeatureFlag _thirdPartyFeature;
public bool ThirdParty
{
get => GetFeature("thirdparty");
set => SetFeature("thirdparty", value);
get => _thirdPartyFeature.Value;
set => _thirdPartyFeature.Value = value;
}
private List<string> _featuresList;
public TenantQuota()
{
_featuresList = new List<string>();
_activeUsersFeature = new ActiveUsersFeature(this) { Order = 1 };
_countAdminFeature = new CountAdminFeature(this);
_countRoomFeature = new CountRoomFeature(this) { Order = 2 };
_maxTotalSizeFeature = new MaxTotalSizeFeature(this) { Order = 3 };
_nonProfitFeature = new TenantQuotaFeatureFlag(this) { Name = "non-profit", Visible = false };
_trialFeature = new TenantQuotaFeatureFlag(this) { Name = "trial", Visible = false };
_freeFeature = new TenantQuotaFeatureFlag(this) { Name = "free", Visible = false };
_openFeature = new TenantQuotaFeatureFlag(this) { Name = "open", Visible = false };
_updateFeature = new TenantQuotaFeatureFlag(this) { Name = "update", Visible = false };
_auditFeature = new TenantQuotaFeatureFlag(this) { Name = "audit", Order = 7 };
_docsEditionFeature = new TenantQuotaFeatureFlag(this) { Name = "docs", Visible = false };
_hasMigrationFeature = new TenantQuotaFeatureFlag(this) { Name = "migration", Visible = false };
_ldapFeature = new TenantQuotaFeatureFlag(this) { Name = "ldap", Visible = false };
_ssoFeature = new TenantQuotaFeatureFlag(this) { Name = "sso", Order = 5 };
_whiteLabelFeature = new TenantQuotaFeatureFlag(this) { Name = "whitelabel", Order = 4 };
_customizationFeature = new TenantQuotaFeatureFlag(this) { Name = "customization", Visible = false };
_customFeature = new TenantQuotaFeatureFlag(this) { Name = "custom", Visible = false };
_restoreFeature = new TenantQuotaFeatureFlag(this) { Name = "restore", Order = 6 };
_autoBackupFeature = new TenantQuotaFeatureFlag(this) { Name = "autobackup", Visible = false };
_oauthFeature = new TenantQuotaFeatureFlag(this) { Name = "oauth", Visible = false };
_contentSearchFeature = new TenantQuotaFeatureFlag(this) { Name = "contentsearch", Visible = false };
_thirdPartyFeature = new TenantQuotaFeatureFlag(this) { Name = "thirdparty", Visible = false };
TenantQuotaFeatures = new List<TenantQuotaFeature>
{
_activeUsersFeature,
_countAdminFeature,
_countRoomFeature,
_maxTotalSizeFeature,
_nonProfitFeature,
_trialFeature,
_freeFeature,
_openFeature,
_updateFeature,
_auditFeature,
_docsEditionFeature,
_hasMigrationFeature,
_ldapFeature,
_ssoFeature,
_whiteLabelFeature,
_customizationFeature,
_customFeature,
_restoreFeature,
_autoBackupFeature,
_oauthFeature,
_contentSearchFeature,
_thirdPartyFeature
};
}
public TenantQuota(int tenant) : this()
@ -207,7 +282,7 @@ public class TenantQuota : ICloneable, IMapFrom<DbQuota>
Tenant = tenant;
}
public TenantQuota(TenantQuota quota)
public TenantQuota(TenantQuota quota) : this()
{
Tenant = quota.Tenant;
Name = quota.Name;
@ -228,78 +303,24 @@ public class TenantQuota : ICloneable, IMapFrom<DbQuota>
return obj is TenantQuota q && q.Tenant == Tenant;
}
private bool GetFeature(string feature)
public void Check(IServiceProvider serviceProvider)
{
return _featuresList.Contains(feature);
}
private int GetFeature(string feature, Func<int> @default)
{
var featureValue = GetFeatureValue(feature);
if (featureValue == null || !int.TryParse(featureValue, out var result))
foreach (var checker in serviceProvider.GetServices<ITenantQuotaFeatureChecker>())
{
return @default();
if (!checker.Check(this))
{
throw new Exception(checker.Exception(this));
}
}
return result;
}
private long GetFeature(string feature, Func<long> @default)
{
var featureValue = GetFeatureValue(feature);
if (featureValue == null || !long.TryParse(featureValue, out var result))
{
return @default();
}
return result;
}
private string GetFeatureValue(string feature)
{
var parsed = _featuresList.FirstOrDefault(f => f.StartsWith($"{feature}:"));
if (parsed == null)
{
return null;
}
return parsed.Replace($"{feature}:", "");
}
private void SetFeature(string feature, bool set)
{
if (set && !_featuresList.Contains(feature))
{
_featuresList.Add(feature);
}
else if (!set && _featuresList.Contains(feature))
{
_featuresList.Remove(feature);
}
}
private void SetFeature(string feature, int @value)
{
SetFeature(feature, @value > 0 ? value.ToString() : null);
}
private void SetFeature(string feature, long @value)
{
SetFeature(feature, @value > 0 ? value.ToString() : null);
}
internal void SetFeature(string feature, string @value)
{
var featureValue = _featuresList.FirstOrDefault(f => f.StartsWith($"{feature}:"));
_featuresList.Remove(featureValue);
if (!string.IsNullOrEmpty(@value))
{
_featuresList.Add($"{feature}:{@value}");
}
//if (MaxTotalSize != long.MaxValue
// && false) throw new Exception("The used storage size should not exceed " + MaxTotalSize);
//if (ActiveUsers != int.MaxValue
// && false) throw new Exception("The number of active users should not exceed " + ActiveUsers);
//if (CountAdmin != int.MaxValue
// && false) throw new Exception("The number of managers should not exceed " + CountAdmin);
//if (CountRoom != int.MaxValue
// && false) throw new Exception("The number of rooms should not exceed " + CountRoom);
}
public static TenantQuota operator *(TenantQuota quota, int quantity)
@ -308,24 +329,9 @@ public class TenantQuota : ICloneable, IMapFrom<DbQuota>
newQuota.Price *= quantity;
if (newQuota.MaxTotalSize != long.MaxValue)
for (var i = 0; i < newQuota.TenantQuotaFeatures.Count; i++)
{
newQuota.MaxTotalSize *= quantity;
}
if (newQuota.ActiveUsers != int.MaxValue)
{
newQuota.ActiveUsers *= quantity;
}
if (newQuota.CountAdmin != int.MaxValue)
{
newQuota.CountAdmin *= quantity;
}
if (newQuota.CountRoom != int.MaxValue)
{
newQuota.CountRoom *= quantity;
newQuota.TenantQuotaFeatures[i].Multiply(quantity);
}
return newQuota;
@ -345,58 +351,49 @@ public class TenantQuota : ICloneable, IMapFrom<DbQuota>
newQuota.Visible &= quota.Visible;
newQuota.ProductId = "";
var features = newQuota._featuresList.Concat(quota._featuresList).ToList();
for (var i = 0; i < features.Count - 1; i++)
foreach (var f in newQuota.TenantQuotaFeatures)
{
for (var j = i + 1; j < features.Count; j++)
if (f is TenantQuotaFeature<int> count)
{
if (features[i].Contains(':'))
{
if (features[j].Contains(':'))
{
var pref1 = features[i].Split(':')[0];
var pref2 = features[j].Split(':')[0];
if (pref1 == pref2)
{
if (int.TryParse(features[i].Replace(pref1 + ":", ""), out var val1) &&
int.TryParse(features[j].Replace(pref1 + ":", ""), out var val2))
{
features[i] = $"{pref1}:{val1 + val2}";
features.RemoveAt(j);
j--;
}
else if (long.TryParse(features[i].Replace(pref1 + ":", ""), out var val3) &&
long.TryParse(features[j].Replace(pref1 + ":", ""), out var val4))
{
features[i] = $"{pref1}:{val3 + val4}";
features.RemoveAt(j);
j--;
}
}
}
}
else if (features[i] == features[j])
{
features.RemoveAt(j);
j--;
}
count.Value += quota.GetFeature<int>(f.Name).Value;
}
else if (f is TenantQuotaFeatureLength length)
{
length.Value += quota.GetFeature<long>(f.Name).Value;
}
else if (f is TenantQuotaFeatureFlag flag)
{
flag.Value |= quota.GetFeature<bool>(f.Name).Value;
}
}
newQuota._featuresList = features;
return newQuota;
}
public object Clone()
{
return MemberwiseClone();
}
public void Mapping(Profile profile)
{
profile.CreateMap<DbQuota, TenantQuota>()
.ForMember(dest => dest.MaxFileSize, opt => opt.MapFrom(src => ByteConverter.GetInBytes(src.MaxFileSize)));
}
}
internal TenantQuotaFeature<T> GetFeature<T>(string name)
{
return TenantQuotaFeatures.OfType<TenantQuotaFeature<T>>().FirstOrDefault(r => r.Name == name);
}
internal string GetFeature(string name)
{
return _featuresList.FirstOrDefault(f => f.StartsWith($"{name}"));
}
internal void ReplaceFeature<T>(string name, T value)
{
var featureValue = GetFeature(name);
_featuresList.Remove(featureValue);
if (!EqualityComparer<T>.Default.Equals(value, default))
{
_featuresList.Add($"{name}:{value}");
}
}
}

View File

@ -49,12 +49,9 @@ public class QuotaHelper
private QuotaDto ToQuotaDto(TenantQuota tenantQuota, IDictionary<string, Dictionary<string, decimal>> priceInfo, RegionInfo currentRegion)
{
var defaultFeatures = GetFeatures(TenantQuota.Default, priceInfo, currentRegion);
var quotaFeatures = GetFeatures(tenantQuota, priceInfo, currentRegion);
var features = GetFeatures(tenantQuota, priceInfo, currentRegion);
var features = quotaFeatures.Union(defaultFeatures);
return new QuotaDto()
return new QuotaDto
{
Id = tenantQuota.Tenant,
Title = Resource.ResourceManager.GetString($"Tariffs_{tenantQuota.Name}"),
@ -96,26 +93,19 @@ public class QuotaHelper
var features = tenantQuota.Features.Split(' ', ',', ';');
foreach (var feature in features)
foreach (var feature in tenantQuota.TenantQuotaFeatures.Where(r => r.Visible).OrderBy(r => r.Order))
{
var result = new QuotaFeatureDto();
var id = feature;
if (id.Contains(':'))
{
id = id.Split(':')[0];
}
if (id == "admin" || features.Length == 1) //TODO
if (feature.Name == "admin" || features.Length == 1) //TODO
{
var val = GetPrice(tenantQuota, priceInfo, currentRegion);
result.Price = new FeaturePriceDto
{
Value = val,
CurrencySymbol = currentRegion.CurrencySymbol,
Per = string.Format(Resource.ResourceManager.GetString($"TariffsFeature_{id}_price_per"), GetPriceString(val, currentRegion)),
Count = Resource.ResourceManager.GetString($"TariffsFeature_{id}_price_count"),
Per = string.Format(Resource.ResourceManager.GetString($"TariffsFeature_{feature.Name}_price_per"), GetPriceString(val, currentRegion)),
Count = Resource.ResourceManager.GetString($"TariffsFeature_{feature.Name}_price_count"),
Range = new FeaturePriceRangeDto
{
Value = 1,
@ -125,15 +115,15 @@ public class QuotaHelper
};
}
result.Id = id;
result.Title = Resource.ResourceManager.GetString($"TariffsFeature_{id}");
result.Id = feature.Name;
result.Title = Resource.ResourceManager.GetString($"TariffsFeature_{feature.Name}");
if (id == "total_size") //TODO
if (feature.Name == "total_size") //TODO
{
result.Title = string.Format(result.Title, FileSizeComment.FilesSizeToString(tenantQuota.MaxTotalSize));
}
var img = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.img.{id}.svg");
var img = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.img.{feature.Name}.svg");
if (img != null)
{