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();
        }
Beispiel #3
0
        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"));
            }
        }
Beispiel #11
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
            }));
        }
Beispiel #12
0
 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}'"));
            }
        }