/// <inheritdoc />
        public async Task <IResult <bool> > IsStale(ConfigurationIdentifier identifier)
        {
            try
            {
                var list = await _domainObjectStore.ReplayObject <PreparedConfigurationList>();

                if (list.IsError)
                {
                    _logger.LogInformation($"could not retrieve ConfigurationList to determine staleness of '{identifier}', defaulting to true");
                    return(Result.Success(true));
                }

                // if it's on the list of known stale Configs return Stale / True
                if (list.Data
                    .GetStale()
                    .Any(id => id.Equals(identifier)))
                {
                    return(Result.Success(true));
                }

                // if it's not, but still known to us, it's not Stale / False
                if (list.Data.GetIdentifiers().Keys.Any(id => id.Equals(identifier)))
                {
                    return(Result.Success(false));
                }

                // otherwise it defaults to Stale / True
                return(Result.Success(true));
            }
            catch (Exception e)
            {
                _logger.LogError(e, "failed to retrieve projected configurations");
                return(Result.Error <bool>("failed to retrieve projected configurations", ErrorCode.DbQueryError));
            }
        }
        /// <inheritdoc />
        public PreparedConfiguration(ConfigurationIdentifier identifier)
        {
            if (identifier is null)
            {
                throw new ArgumentNullException(nameof(identifier));
            }

            if (identifier.Environment is null)
            {
                throw new ArgumentNullException(nameof(identifier), $"{nameof(identifier.Environment)} is null");
            }

            if (identifier.Structure is null)
            {
                throw new ArgumentNullException(nameof(identifier), $"{nameof(identifier.Structure)} is null");
            }

            Identifier           = identifier;
            Created              = false;
            Built                = false;
            ConfigurationVersion = -1;
            Json      = string.Empty;
            Keys      = new Dictionary <string, string>();
            UsedKeys  = new List <string>();
            ValidFrom = null;
            ValidTo   = null;
        }
        /// <inheritdoc />
        public async Task <IResult <string> > GetVersion(ConfigurationIdentifier identifier, DateTime when)
        {
            var formattedParams = "(" +
                                  $"{nameof(identifier.Environment)}{nameof(identifier.Environment.Category)}: {identifier.Environment.Category}; " +
                                  $"{nameof(identifier.Environment)}{nameof(identifier.Environment.Name)}: {identifier.Environment.Name}; " +
                                  $"{nameof(identifier.Structure)}{nameof(identifier.Structure.Name)}: {identifier.Structure.Name}; " +
                                  $"{nameof(identifier.Structure)}{nameof(identifier.Structure.Version)}: {identifier.Structure.Version}" +
                                  ")";

            try
            {
                _logger.LogDebug($"retrieving Config-Version of '{identifier}' at {when:O}");

                var configuration = await _domainObjectStore.ReplayObject(new PreparedConfiguration(identifier), identifier.ToString());

                if (configuration.IsError || !configuration.Data.Created)
                {
                    return(Result.Error <string>($"no configuration found with id: {formattedParams}", ErrorCode.NotFound));
                }

                var result = configuration.Data.ConfigurationVersion.ToString();

                _logger.LogDebug($"Config-Version of '{identifier}' = '{result}'");

                return(Result.Success(result));
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"failed to retrieve used environment keys for id: {formattedParams}");
                return(Result.Error <string>($"failed to retrieve used environment keys for id: {formattedParams}: {e}",
                                             ErrorCode.DbQueryError));
            }
        }
        /// <inheritdoc />
        public async Task <IResult <IDictionary <string, string> > > GetKeys(ConfigurationIdentifier identifier,
                                                                             DateTime when,
                                                                             QueryRange range)
        {
            var formattedParams = "(" +
                                  $"{nameof(identifier.Environment)}{nameof(identifier.Environment.Category)}: {identifier.Environment.Category}; " +
                                  $"{nameof(identifier.Environment)}{nameof(identifier.Environment.Name)}: {identifier.Environment.Name}; " +
                                  $"{nameof(identifier.Structure)}{nameof(identifier.Structure.Name)}: {identifier.Structure.Name}; " +
                                  $"{nameof(identifier.Structure)}{nameof(identifier.Structure.Version)}: {identifier.Structure.Version}" +
                                  ")";

            try
            {
                _logger.LogDebug($"retrieving keys of '{identifier}' at {when:O}, range={range}");

                var configuration = await _domainObjectStore.ReplayObject(new PreparedConfiguration(identifier), identifier.ToString());

                if (configuration.IsError || !configuration.Data.Created)
                {
                    return(Result.Error <IDictionary <string, string> >(
                               $"no configuration found with id: {formattedParams}",
                               ErrorCode.NotFound));
                }

                _logger.LogDebug($"compiling '{identifier}'");
                await configuration.Data.Compile(_domainObjectStore, _compiler, _parser, _translator, _logger);

                if (configuration.Data.Keys is null || !configuration.Data.Keys.Any())
                {
                    return(Result.Error <IDictionary <string, string> >(
                               $"no data found for configuration with id: {formattedParams}",
                               ErrorCode.NotFound));
                }

                var result = configuration.Data
                             .Keys
                             .OrderBy(k => k.Key)
                             .Skip(range.Offset)
                             .Take(range.Length)
                             .ToImmutableSortedDictionary(k => k.Key, k => k.Value, StringComparer.OrdinalIgnoreCase);

                _logger.LogDebug($"collected '{result.Count}' keys from '{identifier}'");

                return(Result.Success <IDictionary <string, string> >(result));
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"failed to retrieve projected configuration keys for id: {formattedParams}");
                return(Result.Error <IDictionary <string, string> >($"failed to retrieve projected configuration keys for id: {formattedParams}",
                                                                    ErrorCode.DbQueryError));
            }
        }
示例#5
0
        public void GetHashCodeStable(string envCategory, string envName, string structName, int structVersion, int version)
        {
            var identifier = new ConfigurationIdentifier(new EnvironmentIdentifier(envCategory, envName),
                                                         new StructureIdentifier(structName, structVersion),
                                                         version);

            var hashes = Enumerable.Range(0, 1000)
                         .Select(i => identifier.GetHashCode())
                         .ToList();

            var example = identifier.GetHashCode();

            Assert.True(hashes.All(h => h == example), "hashes.All(h=>h==example)");
        }
        /// <inheritdoc />
        public async Task <IResult> Build(ConfigurationIdentifier identifier, DateTime?validFrom, DateTime?validTo)
        {
            _logger.LogDebug($"building new '{nameof(PreparedConfiguration)}' using '{identifier}', from={validFrom}, to={validTo}");

            var configResult = await _domainObjectStore.ReplayObject(new PreparedConfiguration(identifier), identifier.ToString());

            if (configResult.IsError)
            {
                return(configResult);
            }

            var configuration = configResult.Data;

            var buildResult = configuration.Build(validFrom, validTo);

            if (buildResult.IsError)
            {
                return(buildResult);
            }

            // assumeLatestVersion=true, because otherwise it would use CurrentVersion of an non-replayed DomainObject
            // which defaults to -1 causing errors while getting target-env and target-struct
            //
            // because this configuration *will* be added to the stream, we can assume that we will get the *most up to date objects as of now*
            await configuration.Compile(_domainObjectStore, _compiler, _parser, _translator, _logger, true);

            _logger.LogDebug("validating resulting events");
            var errors = configuration.Validate(_validators);

            if (errors.Any())
            {
                return(Result.Error("failed to validate generated DomainEvents",
                                    ErrorCode.ValidationFailed,
                                    errors.Values
                                    .SelectMany(_ => _)
                                    .ToList()));
            }

            return(await configuration.WriteRecordedEvents(_eventStore));
        }
        /// <inheritdoc />
        public async Task <IResult <JsonElement> > GetJson(ConfigurationIdentifier identifier, DateTime when)
        {
            var formattedParams = "(" +
                                  $"{nameof(identifier.Environment)}{nameof(identifier.Environment.Category)}: {identifier.Environment.Category}; " +
                                  $"{nameof(identifier.Environment)}{nameof(identifier.Environment.Name)}: {identifier.Environment.Name}; " +
                                  $"{nameof(identifier.Structure)}{nameof(identifier.Structure.Name)}: {identifier.Structure.Name}; " +
                                  $"{nameof(identifier.Structure)}{nameof(identifier.Structure.Version)}: {identifier.Structure.Version}" +
                                  ")";

            try
            {
                _logger.LogDebug($"getting json of '{identifier}' at {when:O}");

                var configuration = await _domainObjectStore.ReplayObject(new PreparedConfiguration(identifier), identifier.ToString());

                if (configuration.IsError || !configuration.Data.Created)
                {
                    return(Result.Error <JsonElement>($"no configuration found with id: {formattedParams}", ErrorCode.NotFound));
                }

                _logger.LogDebug($"compiling '{identifier}'");
                await configuration.Data.Compile(_domainObjectStore, _compiler, _parser, _translator, _logger);

                if (configuration.Data.Json is null)
                {
                    return(Result.Error <JsonElement>($"no json-data found for configuration with id: {formattedParams}", ErrorCode.NotFound));
                }

                _logger.LogDebug($"parsing Config-Keys to json");
                var jsonElement = JsonDocument.Parse(configuration.Data.Json);

                return(Result.Success(jsonElement.RootElement));
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"failed to retrieve projected configuration keys for id: {formattedParams}");
                return(Result.Error <JsonElement>($"failed to retrieve projected configuration keys for id: {formattedParams}",
                                                  ErrorCode.DbQueryError));
            }
        }
示例#8
0
        /// <summary>
        ///     checks the given <see cref="ConfigurationIdentifier" /> for validity
        /// </summary>
        /// <param name="identifier"></param>
        /// <returns></returns>
        private IResult ValidateIdentifier(ConfigurationIdentifier identifier)
        {
            if (identifier is null)
            {
                return(Result.Error("invalid identifier (null", ErrorCode.ValidationFailed));
            }

            var environmentResult = ValidateIdentifier(identifier.Environment);

            if (environmentResult.IsError)
            {
                return(environmentResult);
            }

            var structureResult = ValidateIdentifier(identifier.Structure);

            if (structureResult.IsError)
            {
                return(structureResult);
            }

            return(Result.Success());
        }
示例#9
0
        public async Task <IActionResult> BuildConfiguration([FromRoute] string environmentCategory,
                                                             [FromRoute] string environmentName,
                                                             [FromRoute] string structureName,
                                                             [FromRoute] int structureVersion,
                                                             [FromBody] ConfigurationBuildOptions buildOptions)
        {
            var buildError = ValidateBuildOptions(buildOptions);

            if (!(buildError is null))
            {
                return(buildError);
            }

            var availableStructures = await _store.Structures.GetAvailable(QueryRange.All);

            if (availableStructures.IsError)
            {
                return(ProviderError(availableStructures));
            }

            var availableEnvironments = await _store.Environments.GetAvailable(QueryRange.All);

            if (availableEnvironments.IsError)
            {
                return(ProviderError(availableEnvironments));
            }

            var environment = availableEnvironments.Data
                              .FirstOrDefault(e => e.Category.Equals(environmentCategory, StringComparison.InvariantCultureIgnoreCase) &&
                                              e.Name.Equals(environmentName, StringComparison.InvariantCultureIgnoreCase));

            if (environment is null)
            {
                return(NotFound($"no environment '{environmentCategory}/{environmentName}' found"));
            }

            var structure = availableStructures.Data
                            .FirstOrDefault(s => s.Name.Equals(structureName) &&
                                            s.Version == structureVersion);

            if (structure is null)
            {
                return(NotFound($"no versions of structure '{structureName}' found"));
            }

            var configId = new ConfigurationIdentifier(environment, structure, 0);

            var stalenessResult = await _store.Configurations.IsStale(configId);

            if (stalenessResult.IsError)
            {
                return(ProviderError(stalenessResult));
            }

            bool requestApproved;

            if (stalenessResult.Data)
            {
                var result = await _store.Configurations.Build(configId,
                                                               buildOptions?.ValidFrom,
                                                               buildOptions?.ValidTo);

                if (result.IsError)
                {
                    return(ProviderError(result));
                }

                requestApproved = true;
            }
            else
            {
                Logger.LogInformation($"request for new Configuration ({configId}) denied due to it not being stale");
                requestApproved = false;
            }

            // add a header indicating if a new Configuration was actually built or not
            HttpContext.Response.OnStarting(state => Task.FromResult(HttpContext.Response.Headers.TryAdd("x-built", ((bool)state).ToString())),
                                            requestApproved);

            return(AcceptedAtAction(
                       nameof(GetConfiguration),
                       RouteUtilities.ControllerName <ConfigurationController>(),
                       new
            {
                version = ApiVersions.V1,
                environmentCategory = environment.Category,
                environmentName = environment.Name,
                structureName = structure.Name,
                structureVersion = structure.Version,
                when = DateTime.MinValue,
                offset = -1,
                length = -1
            }));
        }
        /// <inheritdoc />
        public async Task <IResult <IEnumerable <string> > > GetUsedConfigurationKeys(ConfigurationIdentifier identifier,
                                                                                      DateTime when,
                                                                                      QueryRange range)
        {
            var formattedParams = "(" +
                                  $"{nameof(identifier.Environment)}{nameof(identifier.Environment.Category)}: {identifier.Environment.Category}; " +
                                  $"{nameof(identifier.Environment)}{nameof(identifier.Environment.Name)}: {identifier.Environment.Name}; " +
                                  $"{nameof(identifier.Structure)}{nameof(identifier.Structure.Name)}: {identifier.Structure.Name}; " +
                                  $"{nameof(identifier.Structure)}{nameof(identifier.Structure.Version)}: {identifier.Structure.Version}" +
                                  ")";

            try
            {
                _logger.LogDebug($"retrieving used Env-Keys for configuration '{identifier}'");

                var configuration = await _domainObjectStore.ReplayObject(new PreparedConfiguration(identifier), identifier.ToString());

                if (configuration.IsError || !configuration.Data.Created)
                {
                    return(Result.Error <IEnumerable <string> >($"no configuration found with id: {formattedParams}", ErrorCode.NotFound));
                }

                _logger.LogDebug($"compiling '{identifier}'");
                await configuration.Data.Compile(_domainObjectStore, _compiler, _parser, _translator, _logger);

                if (configuration.Data.UsedKeys is null || !configuration.Data.UsedKeys.Any())
                {
                    return(Result.Error <IEnumerable <string> >($"no used-keys for configuration with id: {formattedParams}", ErrorCode.NotFound));
                }

                var result = configuration.Data
                             .UsedKeys
                             .OrderBy(k => k, StringComparer.OrdinalIgnoreCase)
                             .Skip(range.Offset)
                             .Take(range.Length)
                             .ToArray();

                _logger.LogDebug($"collected '{result.Length}' keys");

                return(Result.Success <IEnumerable <string> >(result));
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"failed to retrieve used environment keys for id: {formattedParams}");
                return(Result.Error <IEnumerable <string> >($"failed to retrieve used environment keys for id: {formattedParams}: {e}",
                                                            ErrorCode.DbQueryError));
            }
        }