diff --git a/GpioController/Controllers/GpioController.cs b/GpioController/Controllers/GpioController.cs index d16d902..890b778 100644 --- a/GpioController/Controllers/GpioController.cs +++ b/GpioController/Controllers/GpioController.cs @@ -19,7 +19,8 @@ public IActionResult Get() return Unauthorized(); var gpios = gpioService.GetGpios(); - var filteredResults = gpioService.OrderResultsByFilter(gpios); + var mappedResults = gpioService.MapGpioNames(gpios); + var filteredResults = gpioService.OrderResultsByFilter(mappedResults); return Ok(filteredResults); } diff --git a/GpioController/Models/AuthorizationSettings.cs b/GpioController/Models/AuthorizationSettings.cs index 9119519..4672c40 100644 --- a/GpioController/Models/AuthorizationSettings.cs +++ b/GpioController/Models/AuthorizationSettings.cs @@ -3,5 +3,5 @@ namespace GpioController.Models; public class AuthorizationSettings { public required bool Enabled { get; set; } - public required List AuthorizedEmails { get; set; } + public required List AuthorizedEmails { get; set; } = new(); } \ No newline at end of file diff --git a/GpioController/Models/FilterSettings.cs b/GpioController/Models/FilterSettings.cs index ab12427..bfb5b44 100644 --- a/GpioController/Models/FilterSettings.cs +++ b/GpioController/Models/FilterSettings.cs @@ -2,12 +2,6 @@ namespace GpioController.Models; public class FilterSettings { - public FilterSettings() - { - AllowOnlyTheseChipsets = new(); - AllowOnlyTheseGpios = new(); - } - - public List AllowOnlyTheseChipsets { get; set; } - public List AllowOnlyTheseGpios { get; set; } + public List AllowOnlyTheseChipsets { get; set; } = new(); + public List AllowOnlyTheseGpios { get; set; } = new(); } \ No newline at end of file diff --git a/GpioController/Models/MappingSettings.cs b/GpioController/Models/MappingSettings.cs new file mode 100644 index 0000000..8b41892 --- /dev/null +++ b/GpioController/Models/MappingSettings.cs @@ -0,0 +1,12 @@ +namespace GpioController.Models; + +public class MappingSettings +{ + public List GpioNames { get; set; } = new(); +} + +public class Map +{ + public int Id { get; set; } + public required string Name { get; set; } +} \ No newline at end of file diff --git a/GpioController/Program.cs b/GpioController/Program.cs index 383701b..3d2a2a1 100644 --- a/GpioController/Program.cs +++ b/GpioController/Program.cs @@ -26,6 +26,10 @@ public static void Main(string[] args) builder.Configuration.GetSection("Filters") ); + builder.Services.Configure( + builder.Configuration.GetSection("Mappings") + ); + var requireAuth = builder.Configuration.GetValue("Authorization:Enabled"); if (requireAuth) diff --git a/GpioController/Services/GpioService.cs b/GpioController/Services/GpioService.cs index 9aa2f4f..fc4ac75 100644 --- a/GpioController/Services/GpioService.cs +++ b/GpioController/Services/GpioService.cs @@ -7,7 +7,7 @@ namespace GpioController.Services; -public class GpioService(ICommandFactory commandFactory, IOptions filterSettings) : IGpioService +public class GpioService(ICommandFactory commandFactory, IOptions filterSettings, IOptions mappingSettings) : IGpioService { public IEnumerable GetGpios() { @@ -28,6 +28,23 @@ public IEnumerable GetGpios() return results; } + public IEnumerable MapGpioNames(IEnumerable results) + { + var nameMap = mappingSettings?.Value?.GpioNames ?? []; + + if (!results.Any() || !nameMap.Any()) return results; + + var mapById = nameMap.ToDictionary(m => m.Id, m => m.Name); + + foreach (var gpio in results) + { + if (mapById.TryGetValue(gpio.Id, out var customMappedName)) + gpio.Name = customMappedName; + } + + return results; + } + public IEnumerable OrderResultsByFilter(IEnumerable results) { var filter = filterSettings?.Value?.AllowOnlyTheseGpios ?? []; diff --git a/GpioController/Services/IGpioService.cs b/GpioController/Services/IGpioService.cs index 78b898b..6f585c9 100644 --- a/GpioController/Services/IGpioService.cs +++ b/GpioController/Services/IGpioService.cs @@ -7,5 +7,6 @@ public interface IGpioService { IEnumerable GetGpios(); Gpio GetGpioById(int chipsetId, int gpioId); + IEnumerable MapGpioNames(IEnumerable results); IEnumerable OrderResultsByFilter(IEnumerable results); } \ No newline at end of file diff --git a/GpioController/appsettings.json b/GpioController/appsettings.json index eb18778..087b950 100644 --- a/GpioController/appsettings.json +++ b/GpioController/appsettings.json @@ -17,6 +17,9 @@ "AllowOnlyTheseChipsets": [], "AllowOnlyTheseGpios": [] }, + "Mappings":{ + "GpioNames":[] + }, "Logging": { "LogLevel": { "Default": "Information", diff --git a/GpioControllerTests/Services/GpioServiceTests.cs b/GpioControllerTests/Services/GpioServiceTests.cs index f67fcd2..044fb8f 100644 --- a/GpioControllerTests/Services/GpioServiceTests.cs +++ b/GpioControllerTests/Services/GpioServiceTests.cs @@ -14,6 +14,7 @@ public class GpioServiceTests { private ICommandFactory commandFactory; private IOptions filterSettings; + private IOptions mappingSettings; private ICommand command; public GpioService GetSystemUnderTest() @@ -21,8 +22,9 @@ public GpioService GetSystemUnderTest() command = A.Fake>(); commandFactory = A.Fake(); filterSettings = A.Fake>(); + mappingSettings = A.Fake>(); - return new GpioService(commandFactory, filterSettings); + return new GpioService(commandFactory, filterSettings, mappingSettings); } [Fact] @@ -151,4 +153,94 @@ public void OrderResultsByFilter_WhenNoFilterIsPresent_ShouldStillReturnOriginal result[1].Id.Should().Be(80); result[2].Id.Should().Be(81); } + + [Fact] + public void MapGpioNames_WhenCustomMapsAreDefined_MapsNamesCorrectly() + { + var sut = GetSystemUnderTest(); + + A.CallTo(() => mappingSettings.Value).Returns(new MappingSettings + { + GpioNames = + [ + new Map + { + Id = 91, + Name = "VCC" + }, + new Map + { + Id = 92, + Name = "Common" + }, + new Map + { + Id = 81, + Name = "Zone 1" + } + ] + }); + + var data = new List + { + new() + { + Chipset = 1, + Id = 91, + Name = "Gpio 91" + }, + new() + { + Chipset = 1, + Id = 92, + Name = "Gpio 92" + }, + new() + { + Chipset = 1, + Id = 81, + Name = "Gpio 81" + } + }; + + var result = sut.MapGpioNames(data).ToArray(); + + result[0].Name.Should().Be("VCC"); + result[1].Name.Should().Be("Common"); + result[2].Name.Should().Be("Zone 1"); + } + + [Fact] + public void MapGpioNames_WhenNoMapsAreDefined_DoesNotError() + { + var sut = GetSystemUnderTest(); + + A.CallTo(() => mappingSettings.Value).Returns(new MappingSettings()); + + var data = new List + { + new() + { + Chipset = 1, + Id = 91, + Name = "Gpio 91" + }, + new() + { + Chipset = 1, + Id = 92, + Name = "Gpio 92" + }, + new() + { + Chipset = 1, + Id = 81, + Name = "Gpio 81" + } + }; + + var result = sut.MapGpioNames(data).ToArray(); + + result.Should().BeEquivalentTo(data); + } } \ No newline at end of file diff --git a/Installation/linux-arm64/pinpanda-api-1.4.deb b/Installation/linux-arm64/pinpanda-api-1.4.deb index 9c370f2..a7de0ab 100644 Binary files a/Installation/linux-arm64/pinpanda-api-1.4.deb and b/Installation/linux-arm64/pinpanda-api-1.4.deb differ diff --git a/Installation/linux-x64/pinpanda-api-1.4.deb b/Installation/linux-x64/pinpanda-api-1.4.deb index acc118d..0b589a0 100644 Binary files a/Installation/linux-x64/pinpanda-api-1.4.deb and b/Installation/linux-x64/pinpanda-api-1.4.deb differ diff --git a/README.md b/README.md index d00e6d9..46c38f7 100644 --- a/README.md +++ b/README.md @@ -297,6 +297,28 @@ To update your API settings, refer to the [AppSettings](https://github.com/Spark - `Filters:AllowOnlyTheseGpios` - `GET /sbc/chipsets/gpios` will usually return all GPIOs from your board. Some projects only require a subset of these. Adding IDs here filters results. - Example: `[91, 92, 81,95,80,79,94,93]` +### Mappings: + +- `Mappings:GpioNames` - `GET /sbc/chipsets/gpios` will usually return the names assigned by the SBC. If you'd like to return custom names in their place, use this setting. + - Example: ```[ + { + "Id": 91, + "Name": "VCC Power" + }, + { + "Id": 92, + "Name": "Common" + }, + { + "Id": 81, + "Name": "Zone 1" + }, + { + "Id": 85, + "Name": "Zone 2" + } +]``` + Important!
If you expose your IP and Port to the public (By adding a rule to your router / firewall) it is highly recommended to set `Authorization:Enabled` to `true`. Without it, anybody can call your API.