public async Task <TSettings> GetSettings(SelectExpandArguments args, CancellationToken cancellation) { var permissions = await UserPermissions(Constants.Read, cancellation); if (!permissions.Any()) { throw new ForbiddenException(); } return(await GetExecute(args, cancellation)); }
protected override async Task <GeneralSettings> GetExecute(SelectExpandArguments args, CancellationToken cancellation) { var settings = await _repo.GeneralSettings .Select(args.Select) .Expand(args.Expand) .OrderBy("PrimaryLanguageId") .FirstOrDefaultAsync(cancellation); if (settings == null) { // Programmer mistake throw new InvalidOperationException("Bug: Settings have not been initialized"); } return(settings); }
protected override async Task <FinancialSettings> GetExecute(SelectExpandArguments args, CancellationToken cancellation) { var ctx = new QueryContext(UserId, Today); var settings = await Repository.FinancialSettings .Select(args.Select) .Expand(args.Expand) .OrderBy("FinancialModifiedById") .FirstOrDefaultAsync(ctx, cancellation); if (settings == null) { // Programmer mistake throw new InvalidOperationException("Bug: Settings have not been initialized."); } return(settings); }
/// <summary> /// Returns a <see cref="List{TEntity}"/> as per the Ids and the specifications /// in <paramref name="args"/>, after verifying the user's permissions. /// </summary> /// <remarks> /// This function does not call <see cref="ServiceBase.Initialize"/>. That is the responsibility of the caller. /// </remarks> protected virtual async Task <TEntitiesResult> GetByIds(IList <TKey> ids, SelectExpandArguments args, string action, CancellationToken cancellation) { await Initialize(cancellation); // Parse the parameters var expand = ExpressionExpand.Parse(args?.Expand); var select = ParseSelect(args?.Select); // Prepare the permissions filter var permissionsFilter = await UserPermissionsFilter(action, cancellation); // Load the data var data = await GetEntitiesByIds(ids, expand, select, permissionsFilter, cancellation); // Return result return(await ToEntitiesResult(data, data.Count, cancellation)); }
/// <summary> /// Returns a <see cref="List{TEntity}"/> as per the Ids and the specifications in the <see cref="SelectExpandArguments"/>, after verifying the user's permissions /// </summary> public virtual async Task <(List <TEntity>, Extras)> GetByIds(List <TKey> ids, SelectExpandArguments args, string action, CancellationToken cancellation) { // Parse the parameters var expand = ExpressionExpand.Parse(args?.Expand); var select = ParseSelect(args?.Select); // Prepare the permissions filter var permissionsFilter = await UserPermissionsFilter(action, cancellation); // Load the data var data = await GetEntitiesByIds(ids, expand, select, permissionsFilter, cancellation); var extras = await GetExtras(data, cancellation); // Return result return(data, extras); }
/// <summary> /// Returns a <see cref="List{TEntity}"/> as per the propName, values and the specifications in the <see cref="ExpressionExpand"/> and <see cref="ExpressionSelect"/>, /// after verifying the user's READ permissions, returns the entities in the same order as the supplied Ids /// </summary> public virtual async Task <(List <TEntity>, Extras)> GetByPropertyValues(string propName, IEnumerable <object> values, SelectExpandArguments args, CancellationToken cancellation) { if (propName is null) { throw new ArgumentNullException(nameof(propName)); } // Load the data List <TEntity> data; if (values == null || !values.Any()) { data = new List <TEntity>(); } else { // Parse the parameters var expand = ExpressionExpand.Parse(args?.Expand); var select = ParseSelect(args?.Select); data = await GetEntitiesByCustomQuery(q => q.FilterByPropertyValues(propName, values), expand, select, null, null, cancellation); } // Load the extras var extras = await GetExtras(data, cancellation); // Return return(data, extras); }
protected override async Task SaveExecute(FinancialSettingsForSave settingsForSave, SelectExpandArguments args) { // Make sure the archive date is not in the future if (settingsForSave.ArchiveDate != null && settingsForSave.ArchiveDate.Value > DateTime.Today.AddDays(1)) { ModelState.AddError(nameof(settingsForSave.ArchiveDate), _localizer["Error_DateCannotBeInTheFuture"]); } // Persist var result = await Repository.FinancialSettings__Save( settingsForSave : settingsForSave, validateOnly : ModelState.IsError, top : ModelState.RemainingErrors, userId : UserId); AddLocalizedErrors(result.Errors, _localizer); ModelState.ThrowIfInvalid(); }
public virtual async Task <ActionResult <GetEntityResponse <TSettings> > > GetSettings([FromQuery] SelectExpandArguments args, CancellationToken cancellation) { return(await ControllerUtilities.InvokeActionImpl(async() => { var _service = GetSettingsService(); var settings = await _service.GetSettings(args, cancellation); var singleton = new TSettings[] { settings }; var relatedEntities = ControllerUtilities.FlattenAndTrim(singleton, cancellation: default); var result = new GetEntityResponse <TSettings> { Result = settings, RelatedEntities = relatedEntities }; return Ok(result); }, _logger)); }
protected override async Task SaveExecute(GeneralSettingsForSave settingsForSave, SelectExpandArguments args) { // Persist await _repo.GeneralSettings__Save(settingsForSave); }
/// <summary> /// Returns a <see cref="List{TEntity}"/> as per the Ids and the specifications /// in <paramref name="args"/>, after verifying the user's READ permissions. /// </summary> public virtual async Task <TEntitiesResult> GetByIds(IList <TKey> ids, SelectExpandArguments args, CancellationToken cancellation) { await Initialize(cancellation); return(await GetByIds(ids, args, PermissionActions.Read, cancellation)); }
async Task <EntitiesResult <EntityWithKey> > IFactWithIdService.GetByPropertyValues(string propName, IEnumerable <object> values, SelectExpandArguments args, CancellationToken cancellation) { var result = await GetByPropertyValues(propName, values, args, cancellation); var genericData = result.Data.Cast <EntityWithKey>().ToList(); var count = result.Count; return(new EntitiesResult <EntityWithKey>(genericData, count)); }
async Task <EntitiesResult <EntityWithKey> > IFactWithIdService.GetByIds(IList ids, SelectExpandArguments args, CancellationToken cancellation) { var result = await GetByIds(ids.Cast <TKey>().ToList(), args, cancellation); var genericData = result.Data.Cast <EntityWithKey>().ToList(); var count = result.Count; return(new EntitiesResult <EntityWithKey>(genericData, count)); }
/// <summary> /// Inspects the data and mapping and loads all related entities from the API, that are referenced by custom use keys like Code and Name. /// The purpose is to use the Ids of these entities in the constructed Entity objects that are being imported /// </summary> private async Task <RelatedEntities> LoadRelatedEntities(IEnumerable <string[]> dataWithoutHeader, MappingInfo mapping, ImportErrors errors) { if (errors is null) { throw new ArgumentNullException(nameof(errors)); } // Each set of foreign keys will result in an API query to retrieve the corresponding Ids for FK hydration // So we group foreign keys by the target type, the target definition Id and the key property (e.g. Code or Name) var queryInfos = new List <(Type navType, int?navDefId, PropertyMetadata keyPropMeta, HashSet <object> keysSet)>(); foreach (var g in mapping.GetForeignKeys().Where(p => p.NotUsingIdAsKey) .GroupBy(fk => (fk.TargetType, fk.TargetDefId, fk.KeyPropertyMetadata))) { var(navType, navDefId, keyPropMetadata) = g.Key; HashSet <object> keysSet = new HashSet <object>(); foreach (var fkMapping in g) { int rowNumber = 2; foreach (var row in dataWithoutHeader) { string stringKey = row[fkMapping.Index]; if (string.IsNullOrEmpty(stringKey)) { continue; } switch (fkMapping.KeyType) { case KeyType.String: keysSet.Add(stringKey); break; case KeyType.Int: if (int.TryParse(stringKey, out int intKey)) { keysSet.Add(intKey); } else if (!errors.AddImportError(rowNumber, fkMapping.ColumnNumber, _localizer[$"Error_TheValue0IsNotAValidInteger", stringKey])) { // This means the validation errors are at maximum capacity, pointless to keep going. return(null); } break; default: throw new InvalidOperationException("Bug: Only int and string IDs are supported"); } rowNumber++; } } if (keysSet.Any()) { // Actual API calls are delayed till the end in case there are any errors queryInfos.Add((navType, navDefId, keyPropMetadata, keysSet)); } } if (!errors.IsValid) { return(null); } using var _1 = _instrumentation.Block("Loading Related Entities"); using var _2 = _instrumentation.Disable(); var result = new RelatedEntities(); if (queryInfos.Any()) { // Load all related entities in parallel // If they're the same type we load them in sequence because it will be the same controller instance and it might cause issues await Task.WhenAll(queryInfos.GroupBy(e => e.navType).Select(async g => { var navType = g.Key; foreach (var queryInfo in g) { var(_, navDefId, keyPropMeta, keysSet) = queryInfo; // Deconstruct the queryInfo var service = _sp.FactWithIdServiceByEntityType(navType.Name, navDefId); var keyPropDesc = keyPropMeta.Descriptor; var keyPropName = keyPropDesc.Name; var args = new SelectExpandArguments { Select = keyPropName }; var(data, _) = await service.GetByPropertyValues(keyPropName, keysSet, args, cancellation: default); var grouped = data.GroupBy(e => keyPropDesc.GetValue(e)).ToDictionary(g => g.Key, g => (IEnumerable <EntityWithKey>)g); result.TryAdd((navType, navDefId, keyPropName), grouped); } // The code above might override the definition Id of the service calling it, here we fix that _sp.FactWithIdServiceByEntityType(navType.Name, mapping.MetadataForSave.DefinitionId); })); } return(result); }
protected override async Task SaveExecute(GeneralSettingsForSave settings, SelectExpandArguments args) { // C# validation if (!string.IsNullOrWhiteSpace(settings.SecondaryLanguageId) || !string.IsNullOrWhiteSpace(settings.TernaryLanguageId)) { if (string.IsNullOrWhiteSpace(settings.PrimaryLanguageSymbol)) { ModelState.AddError(nameof(settings.PrimaryLanguageSymbol), _localizer[ErrorMessages.Error_Field0IsRequired, _localizer["Settings_PrimaryLanguageSymbol"]]); } } if (string.IsNullOrWhiteSpace(settings.SecondaryLanguageId)) { settings.SecondaryLanguageSymbol = null; } else { if (string.IsNullOrWhiteSpace(settings.SecondaryLanguageSymbol)) { ModelState.AddError(nameof(settings.SecondaryLanguageSymbol), _localizer[ErrorMessages.Error_Field0IsRequired, _localizer["Settings_SecondaryLanguageSymbol"]]); } if (settings.SecondaryLanguageId == settings.PrimaryLanguageId) { ModelState.AddError(nameof(settings.SecondaryLanguageId), _localizer["Error_SecondaryLanguageCannotBeTheSameAsPrimaryLanguage"]); } } if (string.IsNullOrWhiteSpace(settings.TernaryLanguageId)) { settings.TernaryLanguageSymbol = null; } else { if (string.IsNullOrWhiteSpace(settings.TernaryLanguageSymbol)) { ModelState.AddError(nameof(settings.TernaryLanguageSymbol), _localizer[ErrorMessages.Error_Field0IsRequired, _localizer["Settings_TernaryLanguageSymbol"]]); } if (settings.TernaryLanguageId == settings.PrimaryLanguageId) { ModelState.AddError(nameof(settings.TernaryLanguageId), _localizer["Error_TernaryLanguageCannotBeTheSameAsPrimaryLanguage"]); } if (settings.TernaryLanguageId == settings.SecondaryLanguageId) { ModelState.AddError(nameof(settings.TernaryLanguageId), _localizer["Error_TernaryLanguageCannotBeTheSameAsSecondaryLanguage"]); } } if (!string.IsNullOrWhiteSpace(settings.SecondaryCalendar)) { if (settings.PrimaryCalendar == settings.SecondaryCalendar) { ModelState.AddError(nameof(settings.SecondaryCalendar), _localizer["Error_SecondaryCalendarCannotBeTheSameAsPrimaryCalendar"]); } } // Make sure the color is a valid HTML color // Credit: https://bit.ly/2ToV6x4 if (!string.IsNullOrWhiteSpace(settings.BrandColor) && !Regex.IsMatch(settings.BrandColor, "^#(?:[0-9a-fA-F]{3}){1,2}$")) { ModelState.AddError(nameof(settings.BrandColor), _localizer["Error_TheField0MustBeAValidColorFormat", _localizer["Settings_BrandColor"]]); } // Persist var result = await Repository.GeneralSettings__Save( settingsForSave : settings, validateOnly : ModelState.IsError, top : ModelState.RemainingErrors, userId : UserId); AddLocalizedErrors(result.Errors, _localizer); ModelState.ThrowIfInvalid(); }