Example #1
0
        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));
        }
Example #2
0
        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);
        }
Example #3
0
        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));
        }
Example #5
0
        /// <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);
        }
Example #6
0
        /// <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);
        }
Example #7
0
        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();
        }
Example #8
0
        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));
        }
Example #9
0
 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));
        }
Example #13
0
        /// <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();
        }