Features Provider¶
Feature system is used to enable, disable or change the behavior of the application features on runtime.
- Do define
<ModuleName>Consts
at domain shared package.
public static class OdmsConsts
{
public const string GroupName = "Odms";
}
- Do define feature provider in the domain shared package.
- Do define feature for each future requirement.
- Do add feature group for application or module.
- Do add localization for each future.
public class OdmsFeatureDefinitionProvider : FeatureDefinitionProvider
{
public override void Define(IFeatureDefinitionContext context)
{
var odmsFeatureGroup = context.AddGroup(
name: OdmsFeatures.GroupName,
displayName: L("Feature:Odms")
);
var modelManagement = odmsFeatureGroup.AddFeature(
name: OdmsFeatures.ModelManagement,
defaultValue: "true",
displayName: L("Feature:ModelManagement"),
description: L("Feature:ModelManagement:Description"),
valueType: new ToggleStringValueType(),
isVisibleToClients: true,
isAvailableToHost: false
);
modelManagement.CreateChildFeature(
name: OdmsFeatures.Version130,
defaultValue: "true",
displayName: L("Feature:ModelManagement:Version:13.0"),
description: L("Feature:ModelManagement:Version:13.0:Description"),
valueType: new ToggleStringValueType(),
isVisibleToClients: true,
isAvailableToHost: false
);
modelManagement.CreateChildFeature(
name: OdmsFeatures.Version131,
defaultValue: "true",
displayName: L("Feature:ModelManagement:Version:13.1"),
description: L("Feature:ModelManagement:Version:13.1:Description"),
valueType: new ToggleStringValueType(),
isVisibleToClients: true,
isAvailableToHost: false
);
odmsFeatureGroup.AddFeature(
name: OdmsFeatures.ElementQuery,
defaultValue: "true",
displayName: L("Feature:ElementQuery"),
description: L("Feature:ElementQuery:Description"),
valueType: new ToggleStringValueType()
);
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<OdmsResource>(name);
}
}
ValueType: Type of the feature value. Can be a class implementing the IStringValueType.
Predefined built-in types:
- ToggleStringValueType: Used to define true/false, on/off, enabled/disabled style features. A checkbox is shown on the UI.
- FreeTextStringValueType: Used to define free text values. A textbox is shown on the UI.
- SelectionStringValueType: Used to force the value to be selected from a list. A dropdown list is shown on the UI.
- Do set true IsVisibleToClients Set false to hide the value of this feature from ui clients (browsers).
- Do add child feature if feature requires enablement of another feature.
- Do create reflection helpers to read future details
public static class OdmsFeatures
{
public const string GroupName = OdmsConsts.GroupName;
public const string ModelManagement = GroupName + ".ModelManagement";
public const string Version130 = ModelManagement + ".Version.13.0";
public const string Version131 = ModelManagement + ".Version.13.1";
public const string ElementQuery = GroupName + ".ElementQuery";
public static string[] GetAll()
{
return ReflectionHelper.GetPublicConstantsRecursively(typeof(OdmsFeatures));
}
}
- Do protect each application service according to feature setup.
[Authorize(OdmsPermissions.ModelManagement.Permission)]
[RequiresFeature(OdmsFeatures.ModelManagement, OdmsFeatures.Version130, RequiresAll = true)]
public class ModelAppService : OdmsAppService, IModelAppService
{
protected IModelManager ModelManager { get; }
protected IModelRepository ModelRepository { get; }
/* Can use application services of others only if;
* They are parts of another module / microservice.
* The current module has only reference to the application contracts of the used module.
*/
protected IFileDescriptorAppService FileDescriptorAppService { get; } //
protected IOdmsUserLookupService UserLookupService { get; }
protected IOdmsUserRepository UserRepository { get; }
protected IOdmsAdapterFactory OdmsAdapterFactory { get; }
protected IOdmsFileHandlerServiceFactory OdmsFileHandlerServiceFactory { get; }
protected IFileZipperFactory FileZipperFactory { get; }
public ModelAppService(
IModelManager modelManager,
IModelRepository modelRepository,
IFileDescriptorAppService fileDescriptorAppService,
IOdmsUserLookupService userLookupService,
IOdmsUserRepository userRepository,
IOdmsAdapterFactory odmsAdapterFactory,
IOdmsFileHandlerServiceFactory odmsFileHandlerServiceFactory,
IFileZipperFactory fileZipperFactory)
{
ModelManager = modelManager;
ModelRepository = modelRepository;
UserLookupService = userLookupService;
UserRepository = userRepository;
OdmsAdapterFactory = odmsAdapterFactory;
OdmsFileHandlerServiceFactory = odmsFileHandlerServiceFactory;
FileDescriptorAppService = fileDescriptorAppService;
FileZipperFactory = fileZipperFactory;
}
#region Model
[Authorize(OdmsPermissions.ModelManagement.Create)]
public virtual async Task<ModelWithDetailsDto> CreateAsync(CreateModelInput input)
{
IModelMethods modelAdapter = OdmsAdapterFactory.CreateModelAdapter();
if (await modelAdapter.CheckExistsAsync(name: input.Name, serverName: input.ServerName))
{
if (input.Overwrite)
{
await modelAdapter.DeleteAsync(
name: input.Name,
serverName: input.ServerName
);
}
else
{
throw new ModelExistException(
name: input.Name,
serverName: input.ServerName
);
}
};
await modelAdapter.CreateAsync(
name: input.Name,
serverName: input.ServerName,
cimVersion: (CIMVersion)input.CIMVersionType
);
IDatabaseProperties databaseAdapter = OdmsAdapterFactory.CreateDatabaseAdapter(
name: input.Name,
serverName: input.ServerName
);
using (databaseAdapter)
{
if (databaseAdapter.Name == input.Name )
{
var model = await ModelManager.CreateAsync(
name: input.Name,
serverName: input.ServerName,
description: input.Description,
cimVersionType: input.CIMVersionType,
odmsVersion: CIMdbNETConsts.Version,
overWrite: true
);
input.MapExtraPropertiesTo(model);
await ModelRepository.InsertAsync(
entity: model,
autoSave: true
);
return await MapUsersAsync(model);
}
else
{
await modelAdapter.DeleteAsync(
name: input.Name,
serverName: input.ServerName
);
throw new ModelCreationException(
name: input.Name,
serverName: input.ServerName
);
}
}
}
}
There are three pre-defined value providers, executed by the given order:
- TenantFeatureValueProvider tries to get if the feature value is explicitly set for the current tenant.
- EditionFeatureValueProvider tries to get the feature value for the current edition. Edition Id is obtained from the current principal identity (ICurrentPrincipalAccessor) with the claim name editionid (a constant defined as AbpClaimTypes.EditionId).
- DefaultValueFeatureValueProvider gets the default value of the feature.
You can write your own provider by inheriting the FeatureValueProvider.
public class SystemAdminFeatureValueProvider : FeatureValueProvider
{
public override string Name => "SA";
private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
public SystemAdminFeatureValueProvider(
IFeatureStore featureStore,
ICurrentPrincipalAccessor currentPrincipalAccessor)
: base(featureStore)
{
_currentPrincipalAccessor = currentPrincipalAccessor;
}
public override Task<string> GetOrNullAsync(FeatureDefinition feature)
{
if (feature.ValueType is ToggleStringValueType &&
_currentPrincipalAccessor.Principal?.FindFirst("User_Type")?.Value == "SystemAdmin")
{
return Task.FromResult("true");
}
return null;
}
}
If a provider returns null, then the next provider is executed.
Once a provider is defined, it should be added to the AbpFeatureOptions as shown below:
Configure<AbpFeatureOptions>(options =>
{
options.ValueProviders.Add<SystemAdminFeatureValueProvider>();
});