feat: add Organization/Sub-Organization CRUD cmdlets and Get-InfisicalSANList

Adds 8 cmdlets for Organization and Sub-Organization CRUD (Get/New/Update/Remove for each), targeting /api/v2/organizations and /api/v1/sub-organizations. Get cmdlets default to List parameter set and switch to Single when -OrganizationId or -SubOrganizationId is supplied. New/Update/Remove honor -WhatIf/-Confirm; Remove defaults to High ConfirmImpact and supports -PassThru. No project context required.

Adds Get-InfisicalSANList: emits a deduplicated SAN candidate set containing the local device name, the device name suffixed with each non-empty DNS suffix found across operational adapters and the system primary domain, every IPv4 unicast address falling within RFC 1918 or CGNAT, and the IPv4/IPv6 loopback addresses. Supports optional case-insensitive -InclusionExpression and -ExclusionExpression regex filters applied in fetch -> include -> exclude -> output order. Output is a single strongly-typed System.String[] array emitted non-enumerated so List<string>.AddRange consumes it directly.

Registers 10 new endpoints, adds InfisicalOrganization/InfisicalSubOrganization models with DTOs, mappers, and clients, full MAML help for all 9 new cmdlets, mapper unit tests, EndpointRegistry inline-data coverage, and docs/DesignSpec.md sections 16.7 and 16.8. build.ps1 CmdletsToExport and Test-ModuleImports expected list now contain 51 cmdlets. README updated with Organization/Sub-Organization tables, the new Get-InfisicalSANList entry, and an end-to-end certificate request example using splatted OrderedDictionary blocks.
This commit is contained in:
GraceSolutions
2026-06-06 20:17:49 -04:00
parent 15fadd01a4
commit 77cb03ec98
27 changed files with 1771 additions and 4 deletions
@@ -75,6 +75,16 @@ namespace PSInfisicalAPI.Tests
[InlineData(InfisicalEndpointNames.BulkUpdateSecret, "PATCH", "/api/v4/secrets/batch")]
[InlineData(InfisicalEndpointNames.BulkDeleteSecret, "DELETE", "/api/v4/secrets/batch")]
[InlineData(InfisicalEndpointNames.DuplicateSecret, "POST", "/api/v4/secrets/duplicate")]
[InlineData(InfisicalEndpointNames.ListOrganizations, "GET", "/api/v2/organizations")]
[InlineData(InfisicalEndpointNames.RetrieveOrganization, "GET", "/api/v1/organization/{organizationId}")]
[InlineData(InfisicalEndpointNames.CreateOrganization, "POST", "/api/v2/organizations")]
[InlineData(InfisicalEndpointNames.UpdateOrganization, "PATCH", "/api/v1/organization/{organizationId}")]
[InlineData(InfisicalEndpointNames.DeleteOrganization, "DELETE", "/api/v1/organization/{organizationId}")]
[InlineData(InfisicalEndpointNames.ListSubOrganizations, "GET", "/api/v1/sub-organizations")]
[InlineData(InfisicalEndpointNames.RetrieveSubOrganization, "GET", "/api/v1/sub-organizations/{subOrgId}")]
[InlineData(InfisicalEndpointNames.CreateSubOrganization, "POST", "/api/v1/sub-organizations")]
[InlineData(InfisicalEndpointNames.UpdateSubOrganization, "PATCH", "/api/v1/sub-organizations/{subOrgId}")]
[InlineData(InfisicalEndpointNames.DeleteSubOrganization, "DELETE", "/api/v1/sub-organizations/{subOrgId}")]
public void Registered_Endpoints_Have_Expected_Shape(string name, string method, string template)
{
InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(name);
@@ -0,0 +1,72 @@
using System.Reflection;
using PSInfisicalAPI.Models;
using Xunit;
namespace PSInfisicalAPI.Tests
{
public class OrganizationMapperTests
{
private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
.GetType("PSInfisicalAPI.Organizations.InfisicalOrganizationMapper", true);
private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
.GetType("PSInfisicalAPI.Organizations.InfisicalOrganizationResponseDto", true);
private static InfisicalOrganization InvokeMap(object dto)
{
MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static);
return (InfisicalOrganization)map.Invoke(null, new[] { dto });
}
[Fact]
public void Map_Null_Dto_Returns_Null()
{
Assert.Null(InvokeMap(null));
}
[Fact]
public void Map_Populates_Core_Fields()
{
object dto = System.Activator.CreateInstance(DtoType);
DtoType.GetProperty("Id").SetValue(dto, "org-001");
DtoType.GetProperty("Name").SetValue(dto, "Acme");
DtoType.GetProperty("Slug").SetValue(dto, "acme");
DtoType.GetProperty("CustomerId").SetValue(dto, "cust-9");
DtoType.GetProperty("AuthEnforced").SetValue(dto, true);
DtoType.GetProperty("ScimEnabled").SetValue(dto, true);
DtoType.GetProperty("CreatedAt").SetValue(dto, "2026-01-15T12:34:56Z");
DtoType.GetProperty("UpdatedAt").SetValue(dto, "2026-02-20T09:00:00Z");
InfisicalOrganization organization = InvokeMap(dto);
Assert.Equal("org-001", organization.Id);
Assert.Equal("Acme", organization.Name);
Assert.Equal("acme", organization.Slug);
Assert.Equal("cust-9", organization.CustomerId);
Assert.True(organization.AuthEnforced);
Assert.True(organization.ScimEnabled);
Assert.NotNull(organization.CreatedAtUtc);
Assert.NotNull(organization.UpdatedAtUtc);
}
[Fact]
public void Map_Falls_Back_To_InternalId()
{
object dto = System.Activator.CreateInstance(DtoType);
DtoType.GetProperty("InternalId").SetValue(dto, "internal-id-1");
InfisicalOrganization organization = InvokeMap(dto);
Assert.Equal("internal-id-1", organization.Id);
}
[Fact]
public void MapMany_Null_Returns_Empty()
{
MethodInfo mapMany = MapperType.GetMethod("MapMany", BindingFlags.Public | BindingFlags.Static);
InfisicalOrganization[] result = (InfisicalOrganization[])mapMany.Invoke(null, new object[] { null });
Assert.NotNull(result);
Assert.Empty(result);
}
}
}
@@ -0,0 +1,72 @@
using System.Reflection;
using PSInfisicalAPI.Models;
using Xunit;
namespace PSInfisicalAPI.Tests
{
public class SubOrganizationMapperTests
{
private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
.GetType("PSInfisicalAPI.SubOrganizations.InfisicalSubOrganizationMapper", true);
private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
.GetType("PSInfisicalAPI.SubOrganizations.InfisicalSubOrganizationResponseDto", true);
private static InfisicalSubOrganization InvokeMap(object dto)
{
MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static);
return (InfisicalSubOrganization)map.Invoke(null, new[] { dto });
}
[Fact]
public void Map_Null_Dto_Returns_Null()
{
Assert.Null(InvokeMap(null));
}
[Fact]
public void Map_Populates_Core_Fields()
{
object dto = System.Activator.CreateInstance(DtoType);
DtoType.GetProperty("Id").SetValue(dto, "sub-001");
DtoType.GetProperty("Name").SetValue(dto, "Platform Engineering");
DtoType.GetProperty("Slug").SetValue(dto, "platform-eng");
DtoType.GetProperty("OrganizationId").SetValue(dto, "org-001");
DtoType.GetProperty("IsAccessible").SetValue(dto, true);
DtoType.GetProperty("CreatedAt").SetValue(dto, "2026-01-15T12:34:56Z");
DtoType.GetProperty("UpdatedAt").SetValue(dto, "2026-02-20T09:00:00Z");
InfisicalSubOrganization subOrganization = InvokeMap(dto);
Assert.Equal("sub-001", subOrganization.Id);
Assert.Equal("Platform Engineering", subOrganization.Name);
Assert.Equal("platform-eng", subOrganization.Slug);
Assert.Equal("org-001", subOrganization.OrganizationId);
Assert.True(subOrganization.IsAccessible);
Assert.NotNull(subOrganization.CreatedAtUtc);
Assert.NotNull(subOrganization.UpdatedAtUtc);
}
[Fact]
public void Map_Falls_Back_To_InternalId_And_OrgId()
{
object dto = System.Activator.CreateInstance(DtoType);
DtoType.GetProperty("InternalId").SetValue(dto, "internal-id-1");
DtoType.GetProperty("OrgId").SetValue(dto, "org-fallback");
InfisicalSubOrganization subOrganization = InvokeMap(dto);
Assert.Equal("internal-id-1", subOrganization.Id);
Assert.Equal("org-fallback", subOrganization.OrganizationId);
}
[Fact]
public void MapMany_Null_Returns_Empty()
{
MethodInfo mapMany = MapperType.GetMethod("MapMany", BindingFlags.Public | BindingFlags.Static);
InfisicalSubOrganization[] result = (InfisicalSubOrganization[])mapMany.Invoke(null, new object[] { null });
Assert.NotNull(result);
Assert.Empty(result);
}
}
}
@@ -0,0 +1,47 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.Organizations;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalOrganization", DefaultParameterSetName = "List")]
[OutputType(typeof(InfisicalOrganization))]
public sealed class GetInfisicalOrganizationCmdlet : InfisicalCmdletBase
{
[Parameter(ParameterSetName = "Single", Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Alias("Id")]
public string OrganizationId { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
{
InfisicalOrganization organization = client.Retrieve(connection, OrganizationId);
if (organization != null)
{
WriteObject(organization);
}
return;
}
InfisicalOrganization[] organizations = client.List(connection);
foreach (InfisicalOrganization organization in organizations)
{
WriteObject(organization);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalOrganizationCmdlet", "GetOrganization", exception);
}
}
}
}
@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text.RegularExpressions;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalSANList")]
[OutputType(typeof(string[]))]
public sealed class GetInfisicalSANListCmdlet : InfisicalCmdletBase
{
private const string Component = "GetInfisicalSANListCmdlet";
[Parameter]
[ValidateNotNullOrEmpty]
public string InclusionExpression { get; set; }
[Parameter]
[ValidateNotNullOrEmpty]
public string ExclusionExpression { get; set; }
protected override void ProcessRecord()
{
try
{
Regex includeRegex = !string.IsNullOrEmpty(InclusionExpression) ? new Regex(InclusionExpression, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null;
Regex excludeRegex = !string.IsNullOrEmpty(ExclusionExpression) ? new Regex(ExclusionExpression, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) : null;
List<string> sans = new List<string>();
HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
string deviceName = Dns.GetHostName();
AddUnique(sans, seen, deviceName);
HashSet<string> suffixes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
string globalDomain = IPGlobalProperties.GetIPGlobalProperties().DomainName;
if (!string.IsNullOrEmpty(globalDomain)) { suffixes.Add(globalDomain); }
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adapter in adapters)
{
if (adapter.OperationalStatus != OperationalStatus.Up) { continue; }
if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback) { continue; }
IPInterfaceProperties props = adapter.GetIPProperties();
if (!string.IsNullOrEmpty(props.DnsSuffix)) { suffixes.Add(props.DnsSuffix); }
foreach (UnicastIPAddressInformation unicast in props.UnicastAddresses)
{
IPAddress ip = unicast.Address;
if (ip.AddressFamily == AddressFamily.InterNetwork && IsRfc1918OrCgnat(ip))
{
AddUnique(sans, seen, ip.ToString());
}
}
}
foreach (string suffix in suffixes)
{
string trimmed = suffix.Trim().TrimStart('.');
if (!string.IsNullOrEmpty(trimmed))
{
AddUnique(sans, seen, string.Concat(deviceName, ".", trimmed));
}
}
AddUnique(sans, seen, "127.0.0.1");
AddUnique(sans, seen, "::1");
List<string> filtered = new List<string>(sans.Count);
foreach (string san in sans)
{
if (includeRegex != null && !includeRegex.IsMatch(san)) { continue; }
if (excludeRegex != null && excludeRegex.IsMatch(san)) { continue; }
filtered.Add(san);
}
WriteObject(filtered.ToArray(), false);
}
catch (Exception exception)
{
ThrowTerminatingForException(Component, "GetSANList", exception);
}
}
private static bool IsRfc1918OrCgnat(IPAddress ip)
{
byte[] bytes = ip.GetAddressBytes();
if (bytes.Length != 4) { return false; }
if (bytes[0] == 10) { return true; }
if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) { return true; }
if (bytes[0] == 192 && bytes[1] == 168) { return true; }
if (bytes[0] == 100 && bytes[1] >= 64 && bytes[1] <= 127) { return true; }
return false;
}
private static void AddUnique(List<string> list, HashSet<string> seen, string value)
{
if (string.IsNullOrEmpty(value)) { return; }
if (seen.Add(value)) { list.Add(value); }
}
}
}
@@ -0,0 +1,59 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.SubOrganizations;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "InfisicalSubOrganization", DefaultParameterSetName = "List")]
[OutputType(typeof(InfisicalSubOrganization))]
public sealed class GetInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
{
[Parameter(ParameterSetName = "Single", Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Alias("Id")]
public string SubOrganizationId { get; set; }
[Parameter(ParameterSetName = "List")] public int? Limit { get; set; }
[Parameter(ParameterSetName = "List")] public int? Offset { get; set; }
[Parameter(ParameterSetName = "List")] public string Search { get; set; }
[Parameter(ParameterSetName = "List")] public string OrderBy { get; set; }
[Parameter(ParameterSetName = "List")]
[ValidateSet("asc", "desc")]
public string OrderDirection { get; set; }
[Parameter(ParameterSetName = "List")] public SwitchParameter IsAccessible { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
if (string.Equals(ParameterSetName, "Single", StringComparison.Ordinal))
{
InfisicalSubOrganization subOrganization = client.Retrieve(connection, SubOrganizationId);
if (subOrganization != null)
{
WriteObject(subOrganization);
}
return;
}
bool? isAccessible = MyInvocation.BoundParameters.ContainsKey("IsAccessible") ? (bool?)IsAccessible.IsPresent : null;
InfisicalSubOrganization[] subOrganizations = client.List(connection, Limit, Offset, Search, OrderBy, OrderDirection, isAccessible);
foreach (InfisicalSubOrganization subOrganization in subOrganizations)
{
WriteObject(subOrganization);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("GetInfisicalSubOrganizationCmdlet", "GetSubOrganization", exception);
}
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.Organizations;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.New, "InfisicalOrganization", SupportsShouldProcess = true)]
[OutputType(typeof(InfisicalOrganization))]
public sealed class NewInfisicalOrganizationCmdlet : InfisicalCmdletBase
{
[Parameter(Mandatory = true, Position = 0)] public string Name { get; set; }
[Parameter] public string Slug { get; set; }
protected override void ProcessRecord()
{
try
{
if (!ShouldProcess(Name, "Create Infisical organization"))
{
return;
}
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
InfisicalOrganization organization = client.Create(connection, Name, Slug);
if (organization != null)
{
WriteObject(organization);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("NewInfisicalOrganizationCmdlet", "CreateOrganization", exception);
}
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.SubOrganizations;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.New, "InfisicalSubOrganization", SupportsShouldProcess = true)]
[OutputType(typeof(InfisicalSubOrganization))]
public sealed class NewInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
{
[Parameter(Mandatory = true, Position = 0)] public string Name { get; set; }
[Parameter(Mandatory = true, Position = 1)] public string Slug { get; set; }
protected override void ProcessRecord()
{
try
{
if (!ShouldProcess(Name, "Create Infisical sub-organization"))
{
return;
}
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
InfisicalSubOrganization subOrganization = client.Create(connection, Name, Slug);
if (subOrganization != null)
{
WriteObject(subOrganization);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("NewInfisicalSubOrganizationCmdlet", "CreateSubOrganization", exception);
}
}
}
}
@@ -0,0 +1,42 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Organizations;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Remove, "InfisicalOrganization", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
public sealed class RemoveInfisicalOrganizationCmdlet : InfisicalCmdletBase
{
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Alias("Id")]
public string OrganizationId { get; set; }
[Parameter] public SwitchParameter PassThru { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
if (!ShouldProcess(OrganizationId, "Remove Infisical organization"))
{
return;
}
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
client.Delete(connection, OrganizationId);
if (PassThru.IsPresent)
{
WriteObject(OrganizationId);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("RemoveInfisicalOrganizationCmdlet", "DeleteOrganization", exception);
}
}
}
}
@@ -0,0 +1,42 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.SubOrganizations;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsCommon.Remove, "InfisicalSubOrganization", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
public sealed class RemoveInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
{
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Alias("Id")]
public string SubOrganizationId { get; set; }
[Parameter] public SwitchParameter PassThru { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
if (!ShouldProcess(SubOrganizationId, "Remove Infisical sub-organization"))
{
return;
}
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
client.Delete(connection, SubOrganizationId);
if (PassThru.IsPresent)
{
WriteObject(SubOrganizationId);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("RemoveInfisicalSubOrganizationCmdlet", "DeleteSubOrganization", exception);
}
}
}
}
@@ -0,0 +1,44 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.Organizations;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsData.Update, "InfisicalOrganization", SupportsShouldProcess = true)]
[OutputType(typeof(InfisicalOrganization))]
public sealed class UpdateInfisicalOrganizationCmdlet : InfisicalCmdletBase
{
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Alias("Id")]
public string OrganizationId { get; set; }
[Parameter] public string Name { get; set; }
[Parameter] public string Slug { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
if (!ShouldProcess(OrganizationId, "Update Infisical organization"))
{
return;
}
InfisicalOrganizationClient client = new InfisicalOrganizationClient(HttpClient, Logger);
InfisicalOrganization organization = client.Update(connection, OrganizationId, Name, Slug);
if (organization != null)
{
WriteObject(organization);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("UpdateInfisicalOrganizationCmdlet", "UpdateOrganization", exception);
}
}
}
}
@@ -0,0 +1,44 @@
using System;
using System.Management.Automation;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.SubOrganizations;
namespace PSInfisicalAPI.Cmdlets
{
[Cmdlet(VerbsData.Update, "InfisicalSubOrganization", SupportsShouldProcess = true)]
[OutputType(typeof(InfisicalSubOrganization))]
public sealed class UpdateInfisicalSubOrganizationCmdlet : InfisicalCmdletBase
{
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
[Alias("Id")]
public string SubOrganizationId { get; set; }
[Parameter] public string Name { get; set; }
[Parameter] public string Slug { get; set; }
protected override void ProcessRecord()
{
try
{
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
if (!ShouldProcess(SubOrganizationId, "Update Infisical sub-organization"))
{
return;
}
InfisicalSubOrganizationClient client = new InfisicalSubOrganizationClient(HttpClient, Logger);
InfisicalSubOrganization subOrganization = client.Update(connection, SubOrganizationId, Name, Slug);
if (subOrganization != null)
{
WriteObject(subOrganization);
}
}
catch (Exception exception)
{
ThrowTerminatingForException("UpdateInfisicalSubOrganizationCmdlet", "UpdateSubOrganization", exception);
}
}
}
}
@@ -44,6 +44,18 @@ namespace PSInfisicalAPI.Endpoints
public const string UpdateTag = "UpdateTag";
public const string DeleteTag = "DeleteTag";
public const string ListOrganizations = "ListOrganizations";
public const string RetrieveOrganization = "RetrieveOrganization";
public const string CreateOrganization = "CreateOrganization";
public const string UpdateOrganization = "UpdateOrganization";
public const string DeleteOrganization = "DeleteOrganization";
public const string ListSubOrganizations = "ListSubOrganizations";
public const string RetrieveSubOrganization = "RetrieveSubOrganization";
public const string CreateSubOrganization = "CreateSubOrganization";
public const string UpdateSubOrganization = "UpdateSubOrganization";
public const string DeleteSubOrganization = "DeleteSubOrganization";
public const string ListInternalCertificateAuthorities = "ListInternalCertificateAuthorities";
public const string RetrieveInternalCertificateAuthority = "RetrieveInternalCertificateAuthority";
public const string SearchCertificates = "SearchCertificates";
@@ -16,6 +16,8 @@ namespace PSInfisicalAPI.Endpoints
RegisterEnvironments(Candidates);
RegisterFolders(Candidates);
RegisterTags(Candidates);
RegisterOrganizations(Candidates);
RegisterSubOrganizations(Candidates);
RegisterPki(Candidates);
}
@@ -496,6 +498,112 @@ namespace PSInfisicalAPI.Endpoints
});
}
private static void RegisterOrganizations(Dictionary<string, List<InfisicalEndpointDefinition>> map)
{
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.ListOrganizations,
Resource = "Organizations",
Version = "v2",
Method = "GET",
Template = "/api/v2/organizations",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.RetrieveOrganization,
Resource = "Organizations",
Version = "v1",
Method = "GET",
Template = "/api/v1/organization/{organizationId}",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.CreateOrganization,
Resource = "Organizations",
Version = "v2",
Method = "POST",
Template = "/api/v2/organizations",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.UpdateOrganization,
Resource = "Organizations",
Version = "v1",
Method = "PATCH",
Template = "/api/v1/organization/{organizationId}",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.DeleteOrganization,
Resource = "Organizations",
Version = "v1",
Method = "DELETE",
Template = "/api/v1/organization/{organizationId}",
RequiresAuthorization = true
});
}
private static void RegisterSubOrganizations(Dictionary<string, List<InfisicalEndpointDefinition>> map)
{
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.ListSubOrganizations,
Resource = "SubOrganizations",
Version = "v1",
Method = "GET",
Template = "/api/v1/sub-organizations",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.RetrieveSubOrganization,
Resource = "SubOrganizations",
Version = "v1",
Method = "GET",
Template = "/api/v1/sub-organizations/{subOrgId}",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.CreateSubOrganization,
Resource = "SubOrganizations",
Version = "v1",
Method = "POST",
Template = "/api/v1/sub-organizations",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.UpdateSubOrganization,
Resource = "SubOrganizations",
Version = "v1",
Method = "PATCH",
Template = "/api/v1/sub-organizations/{subOrgId}",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.DeleteSubOrganization,
Resource = "SubOrganizations",
Version = "v1",
Method = "DELETE",
Template = "/api/v1/sub-organizations/{subOrgId}",
RequiresAuthorization = true
});
}
private static void RegisterPki(Dictionary<string, List<InfisicalEndpointDefinition>> map)
{
Add(map, new InfisicalEndpointDefinition
@@ -0,0 +1,21 @@
using System;
namespace PSInfisicalAPI.Models
{
public sealed class InfisicalOrganization
{
public string Id { get; set; }
public string Name { get; set; }
public string Slug { get; set; }
public string CustomerId { get; set; }
public bool AuthEnforced { get; set; }
public bool ScimEnabled { get; set; }
public DateTimeOffset? CreatedAtUtc { get; set; }
public DateTimeOffset? UpdatedAtUtc { get; set; }
public override string ToString()
{
return string.IsNullOrEmpty(Slug) ? (Name ?? Id) : Slug;
}
}
}
@@ -0,0 +1,20 @@
using System;
namespace PSInfisicalAPI.Models
{
public sealed class InfisicalSubOrganization
{
public string Id { get; set; }
public string Name { get; set; }
public string Slug { get; set; }
public string OrganizationId { get; set; }
public bool IsAccessible { get; set; }
public DateTimeOffset? CreatedAtUtc { get; set; }
public DateTimeOffset? UpdatedAtUtc { get; set; }
public override string ToString()
{
return string.IsNullOrEmpty(Slug) ? (Name ?? Id) : Slug;
}
}
}
@@ -0,0 +1,150 @@
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.Organizations
{
public sealed class InfisicalOrganizationClient
{
private const string Component = "OrganizationClient";
private readonly IInfisicalLogger _logger;
private readonly JsonInfisicalSerializer _serializer;
private readonly InfisicalApiInvoker _invoker;
public InfisicalOrganizationClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger)
{
if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); }
_logger = logger ?? NullInfisicalLogger.Instance;
_serializer = new JsonInfisicalSerializer();
_invoker = new InfisicalApiInvoker(httpClient);
}
public InfisicalOrganization[] List(InfisicalConnection connection)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
try
{
_logger.Information(Component, "Attempting to list Infisical organizations. Please Wait...");
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListOrganizations, "ListOrganizations", null, null, null);
InfisicalOrganizationListResponseDto dto = _serializer.Deserialize<InfisicalOrganizationListResponseDto>(response.Body);
response.Clear();
InfisicalOrganization[] mapped = InfisicalOrganizationMapper.MapMany(dto != null ? dto.Organizations : null);
_logger.Information(Component, "Infisical organization list retrieval was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical organization list retrieval failed.");
throw;
}
}
public InfisicalOrganization Retrieve(InfisicalConnection connection, string organizationId)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(organizationId)) { throw new InfisicalConfigurationException("OrganizationId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "organizationId", organizationId } };
try
{
_logger.Information(Component, string.Concat("Attempting to retrieve Infisical organization '", organizationId, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.RetrieveOrganization, "RetrieveOrganization", pathParameters, null, null);
InfisicalOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalOrganizationSingleResponseDto>(response.Body);
response.Clear();
InfisicalOrganization mapped = InfisicalOrganizationMapper.Map(dto != null ? dto.Organization : null);
_logger.Information(Component, "Infisical organization retrieval was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical organization retrieval failed.");
throw;
}
}
public InfisicalOrganization Create(InfisicalConnection connection, string name, string slug)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); }
InfisicalOrganizationCreateRequestDto request = new InfisicalOrganizationCreateRequestDto { Name = name, Slug = slug };
string body = _serializer.Serialize(request);
try
{
_logger.Information(Component, string.Concat("Attempting to create Infisical organization '", name, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateOrganization, "CreateOrganization", null, null, body);
InfisicalOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalOrganizationSingleResponseDto>(response.Body);
response.Clear();
InfisicalOrganization mapped = InfisicalOrganizationMapper.Map(dto != null ? dto.Organization : null);
_logger.Information(Component, "Infisical organization creation was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical organization creation failed.");
throw;
}
}
public InfisicalOrganization Update(InfisicalConnection connection, string organizationId, string name, string slug)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(organizationId)) { throw new InfisicalConfigurationException("OrganizationId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "organizationId", organizationId } };
InfisicalOrganizationUpdateRequestDto request = new InfisicalOrganizationUpdateRequestDto { Name = name, Slug = slug };
string body = _serializer.Serialize(request);
try
{
_logger.Information(Component, string.Concat("Attempting to update Infisical organization '", organizationId, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateOrganization, "UpdateOrganization", pathParameters, null, body);
InfisicalOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalOrganizationSingleResponseDto>(response.Body);
response.Clear();
InfisicalOrganization mapped = InfisicalOrganizationMapper.Map(dto != null ? dto.Organization : null);
_logger.Information(Component, "Infisical organization update was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical organization update failed.");
throw;
}
}
public void Delete(InfisicalConnection connection, string organizationId)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(organizationId)) { throw new InfisicalConfigurationException("OrganizationId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "organizationId", organizationId } };
try
{
_logger.Information(Component, string.Concat("Attempting to delete Infisical organization '", organizationId, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteOrganization, "DeleteOrganization", pathParameters, null, null);
response.Clear();
_logger.Information(Component, "Infisical organization deletion was successful.");
}
catch (Exception)
{
_logger.Error(Component, "Infisical organization deletion failed.");
throw;
}
}
}
}
@@ -0,0 +1,40 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PSInfisicalAPI.Organizations
{
internal sealed class InfisicalOrganizationResponseDto
{
[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("customerId")] public string CustomerId { get; set; }
[JsonProperty("authEnforced")] public bool AuthEnforced { get; set; }
[JsonProperty("scimEnabled")] public bool ScimEnabled { get; set; }
[JsonProperty("createdAt")] public string CreatedAt { get; set; }
[JsonProperty("updatedAt")] public string UpdatedAt { get; set; }
}
internal sealed class InfisicalOrganizationListResponseDto
{
[JsonProperty("organizations")] public List<InfisicalOrganizationResponseDto> Organizations { get; set; }
}
internal sealed class InfisicalOrganizationSingleResponseDto
{
[JsonProperty("organization")] public InfisicalOrganizationResponseDto Organization { get; set; }
}
internal sealed class InfisicalOrganizationCreateRequestDto
{
[JsonProperty("name")] public string Name { get; set; }
[JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; }
}
internal sealed class InfisicalOrganizationUpdateRequestDto
{
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; }
[JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; }
}
}
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using PSInfisicalAPI.Models;
namespace PSInfisicalAPI.Organizations
{
internal static class InfisicalOrganizationMapper
{
public static InfisicalOrganization Map(InfisicalOrganizationResponseDto dto)
{
if (dto == null)
{
return null;
}
return new InfisicalOrganization
{
Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId,
Name = dto.Name,
Slug = dto.Slug,
CustomerId = dto.CustomerId,
AuthEnforced = dto.AuthEnforced,
ScimEnabled = dto.ScimEnabled,
CreatedAtUtc = ParseTimestamp(dto.CreatedAt),
UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt)
};
}
public static InfisicalOrganization[] MapMany(IEnumerable<InfisicalOrganizationResponseDto> items)
{
if (items == null)
{
return Array.Empty<InfisicalOrganization>();
}
List<InfisicalOrganization> results = new List<InfisicalOrganization>();
foreach (InfisicalOrganizationResponseDto dto in items)
{
InfisicalOrganization mapped = Map(dto);
if (mapped != null)
{
results.Add(mapped);
}
}
return results.ToArray();
}
private static DateTimeOffset? ParseTimestamp(string value)
{
if (string.IsNullOrEmpty(value))
{
return null;
}
DateTimeOffset parsed;
if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out parsed))
{
return parsed;
}
return null;
}
}
}
@@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using PSInfisicalAPI.Connections;
using PSInfisicalAPI.Endpoints;
using PSInfisicalAPI.Errors;
using PSInfisicalAPI.Http;
using PSInfisicalAPI.Logging;
using PSInfisicalAPI.Models;
using PSInfisicalAPI.Serialization;
namespace PSInfisicalAPI.SubOrganizations
{
public sealed class InfisicalSubOrganizationClient
{
private const string Component = "SubOrganizationClient";
private readonly IInfisicalLogger _logger;
private readonly JsonInfisicalSerializer _serializer;
private readonly InfisicalApiInvoker _invoker;
public InfisicalSubOrganizationClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger)
{
if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); }
_logger = logger ?? NullInfisicalLogger.Instance;
_serializer = new JsonInfisicalSerializer();
_invoker = new InfisicalApiInvoker(httpClient);
}
public InfisicalSubOrganization[] List(InfisicalConnection connection, int? limit, int? offset, string search, string orderBy, string orderDirection, bool? isAccessible)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
List<KeyValuePair<string, string>> queryParameters = new List<KeyValuePair<string, string>>();
if (limit.HasValue) { queryParameters.Add(new KeyValuePair<string, string>("limit", limit.Value.ToString(CultureInfo.InvariantCulture))); }
if (offset.HasValue) { queryParameters.Add(new KeyValuePair<string, string>("offset", offset.Value.ToString(CultureInfo.InvariantCulture))); }
if (!string.IsNullOrEmpty(search)) { queryParameters.Add(new KeyValuePair<string, string>("search", search)); }
if (!string.IsNullOrEmpty(orderBy)) { queryParameters.Add(new KeyValuePair<string, string>("orderBy", orderBy)); }
if (!string.IsNullOrEmpty(orderDirection)) { queryParameters.Add(new KeyValuePair<string, string>("orderDirection", orderDirection)); }
if (isAccessible.HasValue) { queryParameters.Add(new KeyValuePair<string, string>("isAccessible", isAccessible.Value ? "true" : "false")); }
try
{
_logger.Information(Component, "Attempting to list Infisical sub-organizations. Please Wait...");
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListSubOrganizations, "ListSubOrganizations", null, queryParameters, null);
InfisicalSubOrganizationListResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationListResponseDto>(response.Body);
response.Clear();
InfisicalSubOrganization[] mapped = InfisicalSubOrganizationMapper.MapMany(dto != null ? dto.SubOrganizations : null);
_logger.Information(Component, "Infisical sub-organization list retrieval was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical sub-organization list retrieval failed.");
throw;
}
}
public InfisicalSubOrganization Retrieve(InfisicalConnection connection, string subOrganizationId)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(subOrganizationId)) { throw new InfisicalConfigurationException("SubOrganizationId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "subOrgId", subOrganizationId } };
try
{
_logger.Information(Component, string.Concat("Attempting to retrieve Infisical sub-organization '", subOrganizationId, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.RetrieveSubOrganization, "RetrieveSubOrganization", pathParameters, null, null);
InfisicalSubOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationSingleResponseDto>(response.Body);
response.Clear();
InfisicalSubOrganization mapped = InfisicalSubOrganizationMapper.Map(dto != null ? dto.SubOrganization : null);
_logger.Information(Component, "Infisical sub-organization retrieval was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical sub-organization retrieval failed.");
throw;
}
}
public InfisicalSubOrganization Create(InfisicalConnection connection, string name, string slug)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); }
if (string.IsNullOrEmpty(slug)) { throw new InfisicalConfigurationException("Slug is required."); }
InfisicalSubOrganizationCreateRequestDto request = new InfisicalSubOrganizationCreateRequestDto { Name = name, Slug = slug };
string body = _serializer.Serialize(request);
try
{
_logger.Information(Component, string.Concat("Attempting to create Infisical sub-organization '", name, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateSubOrganization, "CreateSubOrganization", null, null, body);
InfisicalSubOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationSingleResponseDto>(response.Body);
response.Clear();
InfisicalSubOrganization mapped = InfisicalSubOrganizationMapper.Map(dto != null ? dto.SubOrganization : null);
_logger.Information(Component, "Infisical sub-organization creation was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical sub-organization creation failed.");
throw;
}
}
public InfisicalSubOrganization Update(InfisicalConnection connection, string subOrganizationId, string name, string slug)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(subOrganizationId)) { throw new InfisicalConfigurationException("SubOrganizationId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "subOrgId", subOrganizationId } };
InfisicalSubOrganizationUpdateRequestDto request = new InfisicalSubOrganizationUpdateRequestDto { Name = name, Slug = slug };
string body = _serializer.Serialize(request);
try
{
_logger.Information(Component, string.Concat("Attempting to update Infisical sub-organization '", subOrganizationId, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateSubOrganization, "UpdateSubOrganization", pathParameters, null, body);
InfisicalSubOrganizationSingleResponseDto dto = _serializer.Deserialize<InfisicalSubOrganizationSingleResponseDto>(response.Body);
response.Clear();
InfisicalSubOrganization mapped = InfisicalSubOrganizationMapper.Map(dto != null ? dto.SubOrganization : null);
_logger.Information(Component, "Infisical sub-organization update was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical sub-organization update failed.");
throw;
}
}
public void Delete(InfisicalConnection connection, string subOrganizationId)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(subOrganizationId)) { throw new InfisicalConfigurationException("SubOrganizationId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "subOrgId", subOrganizationId } };
try
{
_logger.Information(Component, string.Concat("Attempting to delete Infisical sub-organization '", subOrganizationId, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteSubOrganization, "DeleteSubOrganization", pathParameters, null, null);
response.Clear();
_logger.Information(Component, "Infisical sub-organization deletion was successful.");
}
catch (Exception)
{
_logger.Error(Component, "Infisical sub-organization deletion failed.");
throw;
}
}
}
}
@@ -0,0 +1,40 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PSInfisicalAPI.SubOrganizations
{
internal sealed class InfisicalSubOrganizationResponseDto
{
[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("organizationId")] public string OrganizationId { get; set; }
[JsonProperty("orgId")] public string OrgId { get; set; }
[JsonProperty("isAccessible")] public bool IsAccessible { get; set; }
[JsonProperty("createdAt")] public string CreatedAt { get; set; }
[JsonProperty("updatedAt")] public string UpdatedAt { get; set; }
}
internal sealed class InfisicalSubOrganizationListResponseDto
{
[JsonProperty("subOrganizations")] public List<InfisicalSubOrganizationResponseDto> SubOrganizations { get; set; }
}
internal sealed class InfisicalSubOrganizationSingleResponseDto
{
[JsonProperty("subOrganization")] public InfisicalSubOrganizationResponseDto SubOrganization { get; set; }
}
internal sealed class InfisicalSubOrganizationCreateRequestDto
{
[JsonProperty("name")] public string Name { get; set; }
[JsonProperty("slug")] public string Slug { get; set; }
}
internal sealed class InfisicalSubOrganizationUpdateRequestDto
{
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; }
[JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; }
}
}
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using PSInfisicalAPI.Models;
namespace PSInfisicalAPI.SubOrganizations
{
internal static class InfisicalSubOrganizationMapper
{
public static InfisicalSubOrganization Map(InfisicalSubOrganizationResponseDto dto)
{
if (dto == null)
{
return null;
}
return new InfisicalSubOrganization
{
Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId,
Name = dto.Name,
Slug = dto.Slug,
OrganizationId = !string.IsNullOrEmpty(dto.OrganizationId) ? dto.OrganizationId : dto.OrgId,
IsAccessible = dto.IsAccessible,
CreatedAtUtc = ParseTimestamp(dto.CreatedAt),
UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt)
};
}
public static InfisicalSubOrganization[] MapMany(IEnumerable<InfisicalSubOrganizationResponseDto> items)
{
if (items == null)
{
return Array.Empty<InfisicalSubOrganization>();
}
List<InfisicalSubOrganization> results = new List<InfisicalSubOrganization>();
foreach (InfisicalSubOrganizationResponseDto dto in items)
{
InfisicalSubOrganization mapped = Map(dto);
if (mapped != null)
{
results.Add(mapped);
}
}
return results.ToArray();
}
private static DateTimeOffset? ParseTimestamp(string value)
{
if (string.IsNullOrEmpty(value))
{
return null;
}
DateTimeOffset parsed;
if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out parsed))
{
return parsed;
}
return null;
}
}
}