/// <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)); } }
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)); } }
/// <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()); }
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)); } }