From 6eab0713b53c3726528e201c3f8c8f4df8bd8560 Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 17:23:11 -0400 Subject: [PATCH] M3: Environments CRUD - model, DTOs, mapper, client, 5 cmdlets + tests --- build.ps1 | 9 +- .../EnvironmentMapperTests.cs | 77 ++++++++ .../Cmdlets/GetInfisicalEnvironmentCmdlet.cs | 37 ++++ .../Cmdlets/GetInfisicalEnvironmentsCmdlet.cs | 33 ++++ .../Cmdlets/NewInfisicalEnvironmentCmdlet.cs | 41 +++++ .../RemoveInfisicalEnvironmentCmdlet.cs | 42 +++++ .../UpdateInfisicalEnvironmentCmdlet.cs | 45 +++++ .../InfisicalEnvironmentClient.cs | 166 ++++++++++++++++++ .../Environments/InfisicalEnvironmentDtos.cs | 47 +++++ .../InfisicalEnvironmentMapper.cs | 50 ++++++ .../Models/InfisicalEnvironment.cs | 16 ++ 11 files changed, 561 insertions(+), 2 deletions(-) create mode 100644 src/PSInfisicalAPI.Tests/EnvironmentMapperTests.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs create mode 100644 src/PSInfisicalAPI/Environments/InfisicalEnvironmentClient.cs create mode 100644 src/PSInfisicalAPI/Environments/InfisicalEnvironmentDtos.cs create mode 100644 src/PSInfisicalAPI/Environments/InfisicalEnvironmentMapper.cs create mode 100644 src/PSInfisicalAPI/Models/InfisicalEnvironment.cs diff --git a/build.ps1 b/build.ps1 index 8171355..7ba58a5 100644 --- a/build.ps1 +++ b/build.ps1 @@ -108,7 +108,12 @@ function Write-Manifest { 'Get-InfisicalProject', 'New-InfisicalProject', 'Update-InfisicalProject', - 'Remove-InfisicalProject' + 'Remove-InfisicalProject', + 'Get-InfisicalEnvironments', + 'Get-InfisicalEnvironment', + 'New-InfisicalEnvironment', + 'Update-InfisicalEnvironment', + 'Remove-InfisicalEnvironment' ) AliasesToExport = @() VariablesToExport = @() @@ -168,7 +173,7 @@ if (`$null -eq `$manifest) { Import-Module -Name '$($ModuleDirectory.FullName)' -Force -`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProjects','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject') +`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProjects','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironments','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment') foreach (`$c in `$cmds) { if (-not (Get-Command -Name `$c -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) { throw "Cmdlet not found: `$c" diff --git a/src/PSInfisicalAPI.Tests/EnvironmentMapperTests.cs b/src/PSInfisicalAPI.Tests/EnvironmentMapperTests.cs new file mode 100644 index 0000000..302daae --- /dev/null +++ b/src/PSInfisicalAPI.Tests/EnvironmentMapperTests.cs @@ -0,0 +1,77 @@ +using System.Reflection; +using PSInfisicalAPI.Models; +using Xunit; + +namespace PSInfisicalAPI.Tests +{ + public class EnvironmentMapperTests + { + private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly + .GetType("PSInfisicalAPI.Environments.InfisicalEnvironmentMapper", true); + + private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly + .GetType("PSInfisicalAPI.Environments.InfisicalEnvironmentResponseDto", true); + + private static InfisicalEnvironment InvokeMap(object dto, string fallbackProjectId) + { + MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static); + return (InfisicalEnvironment)map.Invoke(null, new object[] { dto, fallbackProjectId }); + } + + [Fact] + public void Map_Null_Returns_Null() + { + Assert.Null(InvokeMap(null, "proj-x")); + } + + [Fact] + public void Map_Populates_Fields_With_Explicit_ProjectId() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "env-001"); + DtoType.GetProperty("Name").SetValue(dto, "Production"); + DtoType.GetProperty("Slug").SetValue(dto, "prod"); + DtoType.GetProperty("Position").SetValue(dto, 1); + DtoType.GetProperty("ProjectId").SetValue(dto, "proj-001"); + + InfisicalEnvironment env = InvokeMap(dto, "fallback-proj"); + + Assert.Equal("env-001", env.Id); + Assert.Equal("Production", env.Name); + Assert.Equal("prod", env.Slug); + Assert.Equal(1, env.Position); + Assert.Equal("proj-001", env.ProjectId); + } + + [Fact] + public void Map_Uses_WorkspaceId_When_ProjectId_Empty() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "env-002"); + DtoType.GetProperty("WorkspaceId").SetValue(dto, "wks-002"); + + InfisicalEnvironment env = InvokeMap(dto, "fallback-proj"); + Assert.Equal("wks-002", env.ProjectId); + } + + [Fact] + public void Map_Uses_Fallback_When_No_ProjectId_Or_WorkspaceId() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "env-003"); + + InfisicalEnvironment env = InvokeMap(dto, "fallback-proj"); + Assert.Equal("fallback-proj", env.ProjectId); + } + + [Fact] + public void Map_Falls_Back_To_InternalId_For_Id() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("InternalId").SetValue(dto, "internal-env"); + + InfisicalEnvironment env = InvokeMap(dto, "p"); + Assert.Equal("internal-env", env.Id); + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs new file mode 100644 index 0000000..e7f3d59 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs @@ -0,0 +1,37 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Environments; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Get, "InfisicalEnvironment")] + [OutputType(typeof(InfisicalEnvironment))] + public sealed class GetInfisicalEnvironmentCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Slug", "Id", "Environment")] + public string EnvironmentSlugOrId { get; set; } + + [Parameter] public string ProjectId { get; set; } + + protected override void ProcessRecord() + { + try + { + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); + InfisicalEnvironment env = client.Retrieve(connection, ProjectId, EnvironmentSlugOrId); + if (env != null) + { + WriteObject(env); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("GetInfisicalEnvironmentCmdlet", "RetrieveEnvironment", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs new file mode 100644 index 0000000..23879d9 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs @@ -0,0 +1,33 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Environments; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Get, "InfisicalEnvironments")] + [OutputType(typeof(InfisicalEnvironment))] + public sealed class GetInfisicalEnvironmentsCmdlet : InfisicalCmdletBase + { + [Parameter] public string ProjectId { get; set; } + + protected override void ProcessRecord() + { + try + { + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); + InfisicalEnvironment[] envs = client.List(connection, ProjectId); + foreach (InfisicalEnvironment env in envs) + { + WriteObject(env); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("GetInfisicalEnvironmentsCmdlet", "ListEnvironments", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs new file mode 100644 index 0000000..bcda139 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs @@ -0,0 +1,41 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Environments; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.New, "InfisicalEnvironment", SupportsShouldProcess = true)] + [OutputType(typeof(InfisicalEnvironment))] + public sealed class NewInfisicalEnvironmentCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, Position = 0)] public string Name { get; set; } + [Parameter(Mandatory = true, Position = 1)] public string Slug { get; set; } + [Parameter] public string ProjectId { get; set; } + [Parameter] public int? Position { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(Slug, "Create Infisical environment")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); + InfisicalEnvironment env = client.Create(connection, ProjectId, Name, Slug, Position); + if (env != null) + { + WriteObject(env); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("NewInfisicalEnvironmentCmdlet", "CreateEnvironment", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs new file mode 100644 index 0000000..54bd9ff --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs @@ -0,0 +1,42 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Environments; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Remove, "InfisicalEnvironment", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] + public sealed class RemoveInfisicalEnvironmentCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Id")] + public string EnvironmentId { get; set; } + + [Parameter] public string ProjectId { get; set; } + [Parameter] public SwitchParameter PassThru { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(EnvironmentId, "Remove Infisical environment")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); + client.Delete(connection, ProjectId, EnvironmentId); + + if (PassThru.IsPresent) + { + WriteObject(EnvironmentId); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("RemoveInfisicalEnvironmentCmdlet", "DeleteEnvironment", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs new file mode 100644 index 0000000..ade169e --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs @@ -0,0 +1,45 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Environments; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsData.Update, "InfisicalEnvironment", SupportsShouldProcess = true)] + [OutputType(typeof(InfisicalEnvironment))] + public sealed class UpdateInfisicalEnvironmentCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Id")] + public string EnvironmentId { get; set; } + + [Parameter] public string ProjectId { get; set; } + [Parameter] public string Name { get; set; } + [Parameter] public string Slug { get; set; } + [Parameter] public int? Position { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(EnvironmentId, "Update Infisical environment")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); + InfisicalEnvironment env = client.Update(connection, ProjectId, EnvironmentId, Name, Slug, Position); + if (env != null) + { + WriteObject(env); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("UpdateInfisicalEnvironmentCmdlet", "UpdateEnvironment", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Environments/InfisicalEnvironmentClient.cs b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentClient.cs new file mode 100644 index 0000000..3a917bc --- /dev/null +++ b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentClient.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Serialization; + +namespace PSInfisicalAPI.Environments +{ + public sealed class InfisicalEnvironmentClient + { + private const string Component = "EnvironmentClient"; + + private readonly IInfisicalLogger _logger; + private readonly JsonInfisicalSerializer _serializer; + private readonly InfisicalApiInvoker _invoker; + + public InfisicalEnvironmentClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger) + { + if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); } + _logger = logger ?? NullInfisicalLogger.Instance; + _serializer = new JsonInfisicalSerializer(); + _invoker = new InfisicalApiInvoker(httpClient); + } + + public InfisicalEnvironment[] List(InfisicalConnection connection, string projectId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId } }; + + try + { + _logger.Information(Component, "Attempting to list Infisical environments. Please Wait..."); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListEnvironments, "ListEnvironments", pathParameters, null, null); + InfisicalEnvironmentWorkspaceWrapperDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalEnvironmentWorkspaceDto workspace = dto != null ? (dto.Workspace ?? dto.Project) : null; + List envs = workspace != null ? workspace.Environments : null; + InfisicalEnvironment[] mapped = InfisicalEnvironmentMapper.MapMany(envs, resolvedProjectId); + _logger.Information(Component, "Infisical environment list retrieval was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical environment list retrieval failed."); + throw; + } + } + + public InfisicalEnvironment Retrieve(InfisicalConnection connection, string projectId, string environmentSlugOrId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(environmentSlugOrId)) { throw new InfisicalConfigurationException("Environment is required."); } + + InfisicalEnvironment[] all = List(connection, resolvedProjectId); + foreach (InfisicalEnvironment env in all) + { + if (string.Equals(env.Id, environmentSlugOrId, StringComparison.OrdinalIgnoreCase) || + string.Equals(env.Slug, environmentSlugOrId, StringComparison.OrdinalIgnoreCase)) + { + return env; + } + } + + return null; + } + + public InfisicalEnvironment Create(InfisicalConnection connection, string projectId, string name, string slug, int? position) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); } + if (string.IsNullOrEmpty(slug)) { throw new InfisicalConfigurationException("Slug is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId } }; + InfisicalEnvironmentCreateRequestDto request = new InfisicalEnvironmentCreateRequestDto { Name = name, Slug = slug, Position = position }; + string body = _serializer.Serialize(request); + + try + { + _logger.Information(Component, string.Concat("Attempting to create Infisical environment '", slug, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateEnvironment, "CreateEnvironment", pathParameters, null, body); + InfisicalEnvironmentSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalEnvironment mapped = InfisicalEnvironmentMapper.Map(dto != null ? dto.Environment : null, resolvedProjectId); + _logger.Information(Component, "Infisical environment creation was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical environment creation failed."); + throw; + } + } + + public InfisicalEnvironment Update(InfisicalConnection connection, string projectId, string environmentId, string name, string slug, int? position) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(environmentId)) { throw new InfisicalConfigurationException("EnvironmentId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId }, { "environmentId", environmentId } }; + InfisicalEnvironmentUpdateRequestDto request = new InfisicalEnvironmentUpdateRequestDto { Name = name, Slug = slug, Position = position }; + string body = _serializer.Serialize(request); + + try + { + _logger.Information(Component, string.Concat("Attempting to update Infisical environment '", environmentId, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateEnvironment, "UpdateEnvironment", pathParameters, null, body); + InfisicalEnvironmentSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalEnvironment mapped = InfisicalEnvironmentMapper.Map(dto != null ? dto.Environment : null, resolvedProjectId); + _logger.Information(Component, "Infisical environment update was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical environment update failed."); + throw; + } + } + + public void Delete(InfisicalConnection connection, string projectId, string environmentId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(environmentId)) { throw new InfisicalConfigurationException("EnvironmentId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId }, { "environmentId", environmentId } }; + + try + { + _logger.Information(Component, string.Concat("Attempting to delete Infisical environment '", environmentId, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteEnvironment, "DeleteEnvironment", pathParameters, null, null); + response.Clear(); + _logger.Information(Component, "Infisical environment deletion was successful."); + } + catch (Exception) + { + _logger.Error(Component, "Infisical environment deletion failed."); + throw; + } + } + + private static string FirstNonEmpty(params string[] values) + { + if (values == null) { return null; } + foreach (string value in values) { if (!string.IsNullOrEmpty(value)) { return value; } } + return null; + } + } +} diff --git a/src/PSInfisicalAPI/Environments/InfisicalEnvironmentDtos.cs b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentDtos.cs new file mode 100644 index 0000000..de52268 --- /dev/null +++ b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentDtos.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace PSInfisicalAPI.Environments +{ + internal sealed class InfisicalEnvironmentResponseDto + { + [JsonProperty("id")] public string Id { get; set; } + [JsonProperty("_id")] public string InternalId { get; set; } + [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("slug")] public string Slug { get; set; } + [JsonProperty("position")] public int? Position { get; set; } + [JsonProperty("projectId")] public string ProjectId { get; set; } + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + } + + internal sealed class InfisicalEnvironmentSingleResponseDto + { + [JsonProperty("environment")] public InfisicalEnvironmentResponseDto Environment { get; set; } + } + + internal sealed class InfisicalEnvironmentWorkspaceWrapperDto + { + [JsonProperty("workspace")] public InfisicalEnvironmentWorkspaceDto Workspace { get; set; } + [JsonProperty("project")] public InfisicalEnvironmentWorkspaceDto Project { get; set; } + } + + internal sealed class InfisicalEnvironmentWorkspaceDto + { + [JsonProperty("id")] public string Id { get; set; } + [JsonProperty("environments")] public List Environments { get; set; } + } + + internal sealed class InfisicalEnvironmentCreateRequestDto + { + [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("slug")] public string Slug { get; set; } + [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)] public int? Position { get; set; } + } + + internal sealed class InfisicalEnvironmentUpdateRequestDto + { + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } + [JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; } + [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)] public int? Position { get; set; } + } +} diff --git a/src/PSInfisicalAPI/Environments/InfisicalEnvironmentMapper.cs b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentMapper.cs new file mode 100644 index 0000000..03e9200 --- /dev/null +++ b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentMapper.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Environments +{ + internal static class InfisicalEnvironmentMapper + { + public static InfisicalEnvironment Map(InfisicalEnvironmentResponseDto dto, string fallbackProjectId) + { + if (dto == null) + { + return null; + } + + string projectId = !string.IsNullOrEmpty(dto.ProjectId) + ? dto.ProjectId + : (!string.IsNullOrEmpty(dto.WorkspaceId) ? dto.WorkspaceId : fallbackProjectId); + + return new InfisicalEnvironment + { + Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId, + Name = dto.Name, + Slug = dto.Slug, + Position = dto.Position, + ProjectId = projectId + }; + } + + public static InfisicalEnvironment[] MapMany(IEnumerable items, string fallbackProjectId) + { + if (items == null) + { + return Array.Empty(); + } + + List results = new List(); + foreach (InfisicalEnvironmentResponseDto dto in items) + { + InfisicalEnvironment mapped = Map(dto, fallbackProjectId); + if (mapped != null) + { + results.Add(mapped); + } + } + + return results.ToArray(); + } + } +} diff --git a/src/PSInfisicalAPI/Models/InfisicalEnvironment.cs b/src/PSInfisicalAPI/Models/InfisicalEnvironment.cs new file mode 100644 index 0000000..d92f035 --- /dev/null +++ b/src/PSInfisicalAPI/Models/InfisicalEnvironment.cs @@ -0,0 +1,16 @@ +namespace PSInfisicalAPI.Models +{ + public sealed class InfisicalEnvironment + { + public string Id { get; set; } + public string Name { get; set; } + public string Slug { get; set; } + public int? Position { get; set; } + public string ProjectId { get; set; } + + public override string ToString() + { + return string.IsNullOrEmpty(Slug) ? (Name ?? Id) : Slug; + } + } +}