public async Task <IActionResult> AddEnvironment(string category, string name) { if (string.IsNullOrWhiteSpace(category)) { return(BadRequest($"{nameof(category)} is empty")); } if (string.IsNullOrWhiteSpace(name)) { return(BadRequest($"{nameof(name)} is empty")); } try { var result = await _store.Environments.Create(new EnvironmentIdentifier(category, name), false); if (result.IsError) { return(ProviderError(result)); } return(AcceptedAtAction(nameof(GetKeys), RouteUtilities.ControllerName <EnvironmentController>(), new { version = ApiVersions.V1, category, name })); } catch (Exception e) { KnownMetrics.Exception.WithLabels(e.GetType().Name).Inc(); Logger.LogError(e, $"failed to add new environment at ({nameof(category)}: {category}; {nameof(name)}: {name})"); return(StatusCode(HttpStatusCode.InternalServerError, "failed to add new environment")); } }
public async Task AddStructureAcceptsArray() { _projectionStore.Setup(s => s.Structures.Create(It.IsAny <StructureIdentifier>(), It.IsAny <IDictionary <string, string> >(), It.IsAny <IDictionary <string, string> >())) .ReturnsAsync(Result.Success) .Verifiable("structure not created"); _translator.Setup(t => t.ToDictionary(It.IsAny <JsonElement>())) .Returns(() => new Dictionary <string, string> { { "0000", "Foo" }, { "0001", "Bar" } }) .Verifiable("keys / variables not translated to dict"); var result = await TestAction <AcceptedAtActionResult>(c => c.AddStructure(new DtoStructure { Name = "Foo", Version = 42, Structure = JsonDocument.Parse("[\"Foo\",\"Bar\"]").RootElement, Variables = new Dictionary <string, object>() })); Assert.Equal(RouteUtilities.ControllerName <StructureController>(), result.ControllerName); Assert.Equal(nameof(StructureController.GetStructureKeys), result.ActionName); _projectionStore.Verify(); _translator.Verify(); }
public async Task BuildConfigurationNotStale() { _projectionStore.Setup(s => s.Structures.GetAvailable(QueryRange.All)) .ReturnsAsync(() => Result.Success <IList <StructureIdentifier> >(new List <StructureIdentifier> { new StructureIdentifier("Foo", 42) })) .Verifiable("available structures not retrieved"); _projectionStore.Setup(s => s.Environments.GetAvailable(QueryRange.All)) .ReturnsAsync(() => Result.Success <IList <EnvironmentIdentifier> >(new List <EnvironmentIdentifier> { new EnvironmentIdentifier("Foo", "Bar") })) .Verifiable("available environments not retrieved"); _projectionStore.Setup(s => s.Configurations.IsStale(new ConfigurationIdentifier(new EnvironmentIdentifier("Foo", "Bar"), new StructureIdentifier("Foo", 42), 0))) .ReturnsAsync(() => Result.Success(false)) .Verifiable("staleness of configuration not checked"); var result = await TestAction <AcceptedAtActionResult>(c => c.BuildConfiguration("Foo", "Bar", "Foo", 42, new ConfigurationBuildOptions { ValidFrom = DateTime.MinValue, ValidTo = DateTime.MaxValue }), c => c.ControllerContext.HttpContext = new DefaultHttpContext()); Assert.Equal(RouteUtilities.ControllerName <ConfigurationController>(), result.ControllerName); Assert.Equal(nameof(ConfigurationController.GetConfiguration), result.ActionName); _projectionStore.Verify(); }
public async Task RemoveVariables() { _projectionStore.Setup(s => s.Structures.DeleteVariables(It.IsAny <StructureIdentifier>(), It.IsAny <ICollection <string> >())) .ReturnsAsync(Result.Success) .Verifiable("variables not removed"); var result = await TestAction <AcceptedAtActionResult>(c => c.RemoveVariables("Foo", 42, new[] { "Foo" })); Assert.Equal(RouteUtilities.ControllerName <StructureController>(), result.ControllerName); Assert.Equal(nameof(StructureController.GetVariables), result.ActionName); _projectionStore.Verify(); }
public async Task DeleteKeys() { _projectionStoreMock.Setup(s => s.Environments.DeleteKeys(It.IsAny <EnvironmentIdentifier>(), It.IsAny <ICollection <string> >())) .ReturnsAsync(Result.Success) .Verifiable("deletion of keys not triggered"); var result = await TestAction <AcceptedAtActionResult>(c => c.DeleteKeys("Foo", "Bar", new[] { "Foo", "Bar" })); Assert.Equal(RouteUtilities.ControllerName <EnvironmentController>(), result.ControllerName); Assert.Equal(nameof(EnvironmentController.GetKeys), result.ActionName); Assert.Contains("version", result.RouteValues.Keys); _projectionStoreMock.Verify(); }
public async Task AddEnvironment() { _projectionStoreMock.Setup(s => s.Environments.Create(It.IsAny <EnvironmentIdentifier>(), false)) .ReturnsAsync(Result.Success) .Verifiable("environment-creation has not been triggered"); var result = await TestAction <AcceptedAtActionResult>(c => c.AddEnvironment("Foo", "Bar")); Assert.Equal(nameof(EnvironmentController.GetKeys), result.ActionName); Assert.Equal(RouteUtilities.ControllerName <EnvironmentController>(), result.ControllerName); Assert.Contains("version", result.RouteValues.Keys); _projectionStoreMock.Verify(); }
public async Task <IActionResult> UpdateKeys([FromRoute] string category, [FromRoute] string name, [FromBody] DtoConfigKey[] keys) { if (string.IsNullOrWhiteSpace(category)) { return(BadRequest($"{nameof(category)} is empty")); } if (string.IsNullOrWhiteSpace(name)) { return(BadRequest($"{nameof(name)} is empty")); } if (keys == null || !keys.Any()) { return(BadRequest("no keys received")); } var groups = keys.GroupBy(k => k.Key) .ToArray(); if (groups.Any(g => g.Count() > 1)) { return(BadRequest("duplicate keys received: " + string.Join(';', groups.Where(g => g.Count() > 1) .Select(g => $"'{g.Key}'")))); } try { var result = await _store.Environments.UpdateKeys(new EnvironmentIdentifier(category, name), keys); if (result.IsError) { return(ProviderError(result)); } return(AcceptedAtAction(nameof(GetKeys), RouteUtilities.ControllerName <EnvironmentController>(), new { version = ApiVersions.V1, category, name })); } catch (Exception e) { Logger.LogError(e, $"failed to update keys of Environment ({nameof(category)}: {category}; {nameof(name)}: {name})"); return(StatusCode(HttpStatusCode.InternalServerError, "failed to update keys")); } }
public async Task UpdateKeys() { _projectionStoreMock.Setup(s => s.Environments.UpdateKeys(It.IsAny <EnvironmentIdentifier>(), It.IsAny <ICollection <DtoConfigKey> >())) .ReturnsAsync(Result.Success) .Verifiable("keys not updated"); var result = await TestAction <AcceptedAtActionResult>(c => c.UpdateKeys("Foo", "Bar", new[] { new DtoConfigKey { Key = "Foo", Value = "Bar" } })); Assert.Equal(nameof(EnvironmentController.GetKeys), result.ActionName); Assert.Equal(RouteUtilities.ControllerName <EnvironmentController>(), result.ControllerName); Assert.Contains("version", result.RouteValues.Keys); _projectionStoreMock.Verify(); }
public async Task <IActionResult> UpdateVariables([FromRoute] string name, [FromRoute] int structureVersion, [FromBody] Dictionary <string, string> changes) { if (string.IsNullOrWhiteSpace(name)) { return(BadRequest("no name received")); } if (structureVersion <= 0) { return(BadRequest("invalid version version received")); } if (changes is null || !changes.Any()) { return(BadRequest("no changes received")); } try { var identifier = new StructureIdentifier(name, structureVersion); var result = await _store.Structures.UpdateVariables(identifier, changes); if (result.IsError) { return(ProviderError(result)); } return(AcceptedAtAction(nameof(GetVariables), RouteUtilities.ControllerName <StructureController>(), new { version = ApiVersions.V1, name, structureVersion })); } catch (Exception e) { Logger.LogError(e, $"failed to update structure-variables for ({nameof(name)}: {name}, {nameof(structureVersion)}: {structureVersion})"); return(StatusCode(HttpStatusCode.InternalServerError, $"failed to update structure-variables for ({nameof(name)}: {name}, {nameof(structureVersion)}: {structureVersion})")); } }
public async Task <IActionResult> DeleteKeys([FromRoute] string category, [FromRoute] string name, [FromBody] string[] keys) { if (string.IsNullOrWhiteSpace(category)) { return(BadRequest($"{nameof(category)} is empty")); } if (string.IsNullOrWhiteSpace(name)) { return(BadRequest($"{nameof(name)} is empty")); } if (keys == null || !keys.Any()) { return(BadRequest("no keys received")); } try { var result = await _store.Environments.DeleteKeys(new EnvironmentIdentifier(category, name), keys); if (result.IsError) { return(ProviderError(result)); } return(AcceptedAtAction(nameof(GetKeys), RouteUtilities.ControllerName <EnvironmentController>(), new { version = ApiVersions.V1, category, name })); } catch (Exception e) { KnownMetrics.Exception.WithLabels(e.GetType().Name).Inc(); Logger.LogError(e, $"failed to delete keys from Environment ({nameof(category)}: {category}; {nameof(name)}: {name})"); return(StatusCode(HttpStatusCode.InternalServerError, "failed delete keys")); } }
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 })); }
public IActionResult GetAvailableConfigurations([FromQuery] DateTime when, [FromQuery] int offset = -1, [FromQuery] int length = -1) => RedirectToActionPermanent(nameof(GetConfigurations), RouteUtilities.ControllerName <ConfigurationController>(), new { when, offset, length, version = ApiVersions.V1 });
public void StripControllerFromName() { Assert.Equal("Test", RouteUtilities.ControllerName <TestController>()); }
public void NoNullReturned() { Assert.NotNull(RouteUtilities.ControllerName <TestController>()); }
public void SucceedWithWrongName() { Assert.Equal("", RouteUtilities.ControllerName <Controller>()); }
public async Task <IActionResult> AddStructure([FromBody] DtoStructure structure) { if (structure is null) { return(BadRequest("no Structure received")); } if (string.IsNullOrWhiteSpace(structure.Name)) { return(BadRequest($"Structure.{nameof(DtoStructure.Name)} is empty")); } if (structure.Version <= 0) { return(BadRequest($"invalid version provided '{structure.Version}'")); } switch (structure.Structure.ValueKind) { case JsonValueKind.Object: if (!structure.Structure.EnumerateObject().Any()) { return(BadRequest("empty structure-body given")); } break; case JsonValueKind.Array: if (!structure.Structure.EnumerateArray().Any()) { return(BadRequest("empty structure-body given")); } break; default: return(BadRequest("invalid structure-body given (invalid type or null)")); } if (structure.Variables is null || !structure.Variables.Any()) { Logger.LogDebug($"Structure.{nameof(DtoStructure.Variables)} is null or empty, seems fishy but may be correct"); } try { var keys = _translator.ToDictionary(structure.Structure); var variables = (structure.Variables ?? new Dictionary <string, object>()).ToDictionary(kvp => kvp.Key, kvp => kvp.Value?.ToString()); var result = await _store.Structures.Create(new StructureIdentifier(structure.Name, structure.Version), keys, variables); if (result.IsError) { return(ProviderError(result)); } return(AcceptedAtAction(nameof(GetStructureKeys), RouteUtilities.ControllerName <StructureController>(), new { version = ApiVersions.V1, name = structure.Name, structureVersion = structure.Version, offset = -1, length = -1 }, keys)); } catch (Exception e) { Logger.LogError(e, $"failed to process given Structure.{nameof(DtoStructure.Structure)}"); return(StatusCode((int)HttpStatusCode.InternalServerError, $"failed to process given Structure.{nameof(DtoStructure.Structure)}")); } }
public async Task <IActionResult> Import(IFormFile file) { if (file is null || file.Length == 0) { return(BadRequest("no or empty file uploaded")); } byte[] buffer; using (var memStream = new MemoryStream()) { await file.CopyToAsync(memStream); memStream.Seek(0, SeekOrigin.Begin); buffer = new byte[memStream.Length]; memStream.Read(buffer, 0, buffer.Length); } if (!buffer.Any()) { return(BadRequest("no or empty file uploaded")); } var json = Encoding.UTF8.GetString(buffer); ConfigExport export; // try to strip utf8-byte-order-mark from the incoming text var bom = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()); if (json.StartsWith(bom)) { json = json.TrimStart(bom.ToCharArray()); } try { export = JsonSerializer.Deserialize <ConfigExport>(json); if (export is null) { return(BadRequest("uploaded file can't be mapped to object")); } } catch (JsonException e) { Logger.LogWarning($"uploaded file can't be deserialized to '{nameof(ConfigExport)}': {e}"); return(BadRequest("uploaded file can't be mapped to object")); } try { var result = await _importer.Import(export); if (result.IsError) { return(ProviderError(result)); } return(AcceptedAtAction(nameof(EnvironmentController.GetAvailableEnvironments), RouteUtilities.ControllerName <EnvironmentController>(), new { version = ApiVersions.V1 })); } catch (Exception e) { var targetEnvironments = string.Join(", ", export.Environments.Select(eid => $"{eid.Category}/{eid.Name}")); KnownMetrics.Exception.WithLabels(e.GetType().Name).Inc(); Logger.LogError(e, $"failed to export '{export.Environments.Length}' environments ({targetEnvironments})"); return(StatusCode(HttpStatusCode.InternalServerError, $"failed to export environments '{targetEnvironments}'")); } }