M4: Folders CRUD - model, DTOs, mapper, client, 5 cmdlets + tests
This commit is contained in:
@@ -113,7 +113,12 @@ function Write-Manifest {
|
|||||||
'Get-InfisicalEnvironment',
|
'Get-InfisicalEnvironment',
|
||||||
'New-InfisicalEnvironment',
|
'New-InfisicalEnvironment',
|
||||||
'Update-InfisicalEnvironment',
|
'Update-InfisicalEnvironment',
|
||||||
'Remove-InfisicalEnvironment'
|
'Remove-InfisicalEnvironment',
|
||||||
|
'Get-InfisicalFolders',
|
||||||
|
'Get-InfisicalFolder',
|
||||||
|
'New-InfisicalFolder',
|
||||||
|
'Update-InfisicalFolder',
|
||||||
|
'Remove-InfisicalFolder'
|
||||||
)
|
)
|
||||||
AliasesToExport = @()
|
AliasesToExport = @()
|
||||||
VariablesToExport = @()
|
VariablesToExport = @()
|
||||||
@@ -173,7 +178,7 @@ if (`$null -eq `$manifest) {
|
|||||||
|
|
||||||
Import-Module -Name '$($ModuleDirectory.FullName)' -Force
|
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','Get-InfisicalEnvironments','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment')
|
`$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','Get-InfisicalFolders','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder')
|
||||||
foreach (`$c in `$cmds) {
|
foreach (`$c in `$cmds) {
|
||||||
if (-not (Get-Command -Name `$c -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) {
|
if (-not (Get-Command -Name `$c -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) {
|
||||||
throw "Cmdlet not found: `$c"
|
throw "Cmdlet not found: `$c"
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Tests
|
||||||
|
{
|
||||||
|
public class FolderMapperTests
|
||||||
|
{
|
||||||
|
private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
|
||||||
|
.GetType("PSInfisicalAPI.Folders.InfisicalFolderMapper", true);
|
||||||
|
|
||||||
|
private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly
|
||||||
|
.GetType("PSInfisicalAPI.Folders.InfisicalFolderResponseDto", true);
|
||||||
|
|
||||||
|
private static InfisicalFolder InvokeMap(object dto, string fallbackProjectId, string fallbackEnvironment)
|
||||||
|
{
|
||||||
|
MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
return (InfisicalFolder)map.Invoke(null, new object[] { dto, fallbackProjectId, fallbackEnvironment });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map_Null_Returns_Null()
|
||||||
|
{
|
||||||
|
Assert.Null(InvokeMap(null, "proj-x", "dev"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map_Populates_Fields_With_Explicit_ProjectId()
|
||||||
|
{
|
||||||
|
object dto = System.Activator.CreateInstance(DtoType);
|
||||||
|
DtoType.GetProperty("Id").SetValue(dto, "fld-001");
|
||||||
|
DtoType.GetProperty("Name").SetValue(dto, "config");
|
||||||
|
DtoType.GetProperty("Path").SetValue(dto, "/app/config");
|
||||||
|
DtoType.GetProperty("ParentId").SetValue(dto, "fld-root");
|
||||||
|
DtoType.GetProperty("Environment").SetValue(dto, "prod");
|
||||||
|
DtoType.GetProperty("ProjectId").SetValue(dto, "proj-001");
|
||||||
|
|
||||||
|
InfisicalFolder folder = InvokeMap(dto, "fallback-proj", "fallback-env");
|
||||||
|
|
||||||
|
Assert.Equal("fld-001", folder.Id);
|
||||||
|
Assert.Equal("config", folder.Name);
|
||||||
|
Assert.Equal("/app/config", folder.Path);
|
||||||
|
Assert.Equal("fld-root", folder.ParentId);
|
||||||
|
Assert.Equal("prod", folder.Environment);
|
||||||
|
Assert.Equal("proj-001", folder.ProjectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map_Uses_WorkspaceId_When_ProjectId_Empty()
|
||||||
|
{
|
||||||
|
object dto = System.Activator.CreateInstance(DtoType);
|
||||||
|
DtoType.GetProperty("Id").SetValue(dto, "fld-002");
|
||||||
|
DtoType.GetProperty("WorkspaceId").SetValue(dto, "wks-002");
|
||||||
|
|
||||||
|
InfisicalFolder folder = InvokeMap(dto, "fallback-proj", "fallback-env");
|
||||||
|
Assert.Equal("wks-002", folder.ProjectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map_Uses_Fallback_When_No_ProjectId_Or_Environment()
|
||||||
|
{
|
||||||
|
object dto = System.Activator.CreateInstance(DtoType);
|
||||||
|
DtoType.GetProperty("Id").SetValue(dto, "fld-003");
|
||||||
|
|
||||||
|
InfisicalFolder folder = InvokeMap(dto, "fallback-proj", "fallback-env");
|
||||||
|
Assert.Equal("fallback-proj", folder.ProjectId);
|
||||||
|
Assert.Equal("fallback-env", folder.Environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map_Falls_Back_To_InternalId_For_Id()
|
||||||
|
{
|
||||||
|
object dto = System.Activator.CreateInstance(DtoType);
|
||||||
|
DtoType.GetProperty("InternalId").SetValue(dto, "internal-fld");
|
||||||
|
|
||||||
|
InfisicalFolder folder = InvokeMap(dto, "p", "e");
|
||||||
|
Assert.Equal("internal-fld", folder.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Folders;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.Get, "InfisicalFolder")]
|
||||||
|
[OutputType(typeof(InfisicalFolder))]
|
||||||
|
public sealed class GetInfisicalFolderCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
|
[Alias("Name", "Id")]
|
||||||
|
public string FolderNameOrId { get; set; }
|
||||||
|
|
||||||
|
[Parameter] public string ProjectId { get; set; }
|
||||||
|
[Parameter] public string Environment { get; set; }
|
||||||
|
[Parameter] public string Path { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger);
|
||||||
|
InfisicalFolder folder = client.Retrieve(connection, ProjectId, Environment, Path, FolderNameOrId);
|
||||||
|
if (folder != null)
|
||||||
|
{
|
||||||
|
WriteObject(folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("GetInfisicalFolderCmdlet", "RetrieveFolder", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Folders;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.Get, "InfisicalFolders")]
|
||||||
|
[OutputType(typeof(InfisicalFolder))]
|
||||||
|
public sealed class GetInfisicalFoldersCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter] public string ProjectId { get; set; }
|
||||||
|
[Parameter] public string Environment { get; set; }
|
||||||
|
[Parameter] public string Path { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger);
|
||||||
|
InfisicalFolder[] folders = client.List(connection, ProjectId, Environment, Path);
|
||||||
|
foreach (InfisicalFolder folder in folders)
|
||||||
|
{
|
||||||
|
WriteObject(folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("GetInfisicalFoldersCmdlet", "ListFolders", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Folders;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.New, "InfisicalFolder", SupportsShouldProcess = true)]
|
||||||
|
[OutputType(typeof(InfisicalFolder))]
|
||||||
|
public sealed class NewInfisicalFolderCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(Mandatory = true, Position = 0)] public string Name { get; set; }
|
||||||
|
[Parameter] public string ProjectId { get; set; }
|
||||||
|
[Parameter] public string Environment { get; set; }
|
||||||
|
[Parameter] public string Path { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!ShouldProcess(Name, "Create Infisical folder"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger);
|
||||||
|
InfisicalFolder folder = client.Create(connection, ProjectId, Environment, Name, Path);
|
||||||
|
if (folder != null)
|
||||||
|
{
|
||||||
|
WriteObject(folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("NewInfisicalFolderCmdlet", "CreateFolder", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Folders;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsCommon.Remove, "InfisicalFolder", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||||
|
public sealed class RemoveInfisicalFolderCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
|
[Alias("Id")]
|
||||||
|
public string FolderId { get; set; }
|
||||||
|
|
||||||
|
[Parameter] public string ProjectId { get; set; }
|
||||||
|
[Parameter] public string Environment { get; set; }
|
||||||
|
[Parameter] public string Path { get; set; }
|
||||||
|
[Parameter] public SwitchParameter PassThru { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!ShouldProcess(FolderId, "Remove Infisical folder"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger);
|
||||||
|
client.Delete(connection, ProjectId, Environment, FolderId, Path);
|
||||||
|
|
||||||
|
if (PassThru.IsPresent)
|
||||||
|
{
|
||||||
|
WriteObject(FolderId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("RemoveInfisicalFolderCmdlet", "DeleteFolder", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
|
using PSInfisicalAPI.Folders;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Cmdlets
|
||||||
|
{
|
||||||
|
[Cmdlet(VerbsData.Update, "InfisicalFolder", SupportsShouldProcess = true)]
|
||||||
|
[OutputType(typeof(InfisicalFolder))]
|
||||||
|
public sealed class UpdateInfisicalFolderCmdlet : InfisicalCmdletBase
|
||||||
|
{
|
||||||
|
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
|
[Alias("Id")]
|
||||||
|
public string FolderId { get; set; }
|
||||||
|
|
||||||
|
[Parameter(Mandatory = true, Position = 1)] public string Name { get; set; }
|
||||||
|
[Parameter] public string ProjectId { get; set; }
|
||||||
|
[Parameter] public string Environment { get; set; }
|
||||||
|
[Parameter] public string Path { get; set; }
|
||||||
|
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!ShouldProcess(FolderId, "Update Infisical folder"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger);
|
||||||
|
InfisicalFolder folder = client.Update(connection, ProjectId, Environment, FolderId, Name, Path);
|
||||||
|
if (folder != null)
|
||||||
|
{
|
||||||
|
WriteObject(folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
ThrowTerminatingForException("UpdateInfisicalFolderCmdlet", "UpdateFolder", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
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.Folders
|
||||||
|
{
|
||||||
|
public sealed class InfisicalFolderClient
|
||||||
|
{
|
||||||
|
private const string Component = "FolderClient";
|
||||||
|
|
||||||
|
private readonly IInfisicalLogger _logger;
|
||||||
|
private readonly JsonInfisicalSerializer _serializer;
|
||||||
|
private readonly InfisicalApiInvoker _invoker;
|
||||||
|
|
||||||
|
public InfisicalFolderClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger)
|
||||||
|
{
|
||||||
|
if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); }
|
||||||
|
_logger = logger ?? NullInfisicalLogger.Instance;
|
||||||
|
_serializer = new JsonInfisicalSerializer();
|
||||||
|
_invoker = new InfisicalApiInvoker(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalFolder[] List(InfisicalConnection connection, string projectId, string environment, string path)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId);
|
||||||
|
string resolvedEnvironment = FirstNonEmpty(environment, connection.Environment);
|
||||||
|
string resolvedPath = FirstNonEmpty(path, connection.DefaultSecretPath, "/");
|
||||||
|
if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); }
|
||||||
|
if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); }
|
||||||
|
|
||||||
|
List<KeyValuePair<string, string>> queryParameters = new List<KeyValuePair<string, string>>
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("workspaceId", resolvedProjectId),
|
||||||
|
new KeyValuePair<string, string>("environment", resolvedEnvironment),
|
||||||
|
new KeyValuePair<string, string>("path", resolvedPath)
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, "Attempting to list Infisical folders. Please Wait...");
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListFolders, "ListFolders", null, queryParameters, null);
|
||||||
|
InfisicalFolderListResponseDto dto = _serializer.Deserialize<InfisicalFolderListResponseDto>(response.Body);
|
||||||
|
response.Clear();
|
||||||
|
|
||||||
|
InfisicalFolder[] mapped = InfisicalFolderMapper.MapMany(dto != null ? dto.Folders : null, resolvedProjectId, resolvedEnvironment);
|
||||||
|
_logger.Information(Component, "Infisical folder list retrieval was successful.");
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical folder list retrieval failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalFolder Retrieve(InfisicalConnection connection, string projectId, string environment, string path, string folderNameOrId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(folderNameOrId)) { throw new InfisicalConfigurationException("Folder name or id is required."); }
|
||||||
|
|
||||||
|
InfisicalFolder[] all = List(connection, projectId, environment, path);
|
||||||
|
foreach (InfisicalFolder folder in all)
|
||||||
|
{
|
||||||
|
if (string.Equals(folder.Id, folderNameOrId, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals(folder.Name, folderNameOrId, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalFolder Create(InfisicalConnection connection, string projectId, string environment, string name, string path)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId);
|
||||||
|
string resolvedEnvironment = FirstNonEmpty(environment, connection.Environment);
|
||||||
|
string resolvedPath = FirstNonEmpty(path, connection.DefaultSecretPath, "/");
|
||||||
|
if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); }
|
||||||
|
if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); }
|
||||||
|
if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); }
|
||||||
|
|
||||||
|
InfisicalFolderCreateRequestDto request = new InfisicalFolderCreateRequestDto
|
||||||
|
{
|
||||||
|
WorkspaceId = resolvedProjectId,
|
||||||
|
Environment = resolvedEnvironment,
|
||||||
|
Name = name,
|
||||||
|
Path = resolvedPath
|
||||||
|
};
|
||||||
|
string body = _serializer.Serialize(request);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, string.Concat("Attempting to create Infisical folder '", name, "'. Please Wait..."));
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateFolder, "CreateFolder", null, null, body);
|
||||||
|
InfisicalFolderSingleResponseDto dto = _serializer.Deserialize<InfisicalFolderSingleResponseDto>(response.Body);
|
||||||
|
response.Clear();
|
||||||
|
|
||||||
|
InfisicalFolder mapped = InfisicalFolderMapper.Map(dto != null ? dto.Folder : null, resolvedProjectId, resolvedEnvironment);
|
||||||
|
_logger.Information(Component, "Infisical folder creation was successful.");
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical folder creation failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfisicalFolder Update(InfisicalConnection connection, string projectId, string environment, string folderId, string name, string path)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId);
|
||||||
|
string resolvedEnvironment = FirstNonEmpty(environment, connection.Environment);
|
||||||
|
string resolvedPath = FirstNonEmpty(path, connection.DefaultSecretPath, "/");
|
||||||
|
if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); }
|
||||||
|
if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); }
|
||||||
|
if (string.IsNullOrEmpty(folderId)) { throw new InfisicalConfigurationException("FolderId is required."); }
|
||||||
|
if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); }
|
||||||
|
|
||||||
|
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "folderId", folderId } };
|
||||||
|
InfisicalFolderUpdateRequestDto request = new InfisicalFolderUpdateRequestDto
|
||||||
|
{
|
||||||
|
WorkspaceId = resolvedProjectId,
|
||||||
|
Environment = resolvedEnvironment,
|
||||||
|
Name = name,
|
||||||
|
Path = resolvedPath
|
||||||
|
};
|
||||||
|
string body = _serializer.Serialize(request);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, string.Concat("Attempting to update Infisical folder '", folderId, "'. Please Wait..."));
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateFolder, "UpdateFolder", pathParameters, null, body);
|
||||||
|
InfisicalFolderSingleResponseDto dto = _serializer.Deserialize<InfisicalFolderSingleResponseDto>(response.Body);
|
||||||
|
response.Clear();
|
||||||
|
|
||||||
|
InfisicalFolder mapped = InfisicalFolderMapper.Map(dto != null ? dto.Folder : null, resolvedProjectId, resolvedEnvironment);
|
||||||
|
_logger.Information(Component, "Infisical folder update was successful.");
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical folder update failed.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(InfisicalConnection connection, string projectId, string environment, string folderId, string path)
|
||||||
|
{
|
||||||
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId);
|
||||||
|
string resolvedEnvironment = FirstNonEmpty(environment, connection.Environment);
|
||||||
|
string resolvedPath = FirstNonEmpty(path, connection.DefaultSecretPath, "/");
|
||||||
|
if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); }
|
||||||
|
if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); }
|
||||||
|
if (string.IsNullOrEmpty(folderId)) { throw new InfisicalConfigurationException("FolderId is required."); }
|
||||||
|
|
||||||
|
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "folderId", folderId } };
|
||||||
|
List<KeyValuePair<string, string>> queryParameters = new List<KeyValuePair<string, string>>
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("workspaceId", resolvedProjectId),
|
||||||
|
new KeyValuePair<string, string>("environment", resolvedEnvironment),
|
||||||
|
new KeyValuePair<string, string>("path", resolvedPath)
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Information(Component, string.Concat("Attempting to delete Infisical folder '", folderId, "'. Please Wait..."));
|
||||||
|
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteFolder, "DeleteFolder", pathParameters, queryParameters, null);
|
||||||
|
response.Clear();
|
||||||
|
_logger.Information(Component, "Infisical folder deletion was successful.");
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_logger.Error(Component, "Infisical folder 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Folders
|
||||||
|
{
|
||||||
|
internal sealed class InfisicalFolderResponseDto
|
||||||
|
{
|
||||||
|
[JsonProperty("id")] public string Id { get; set; }
|
||||||
|
[JsonProperty("_id")] public string InternalId { get; set; }
|
||||||
|
[JsonProperty("name")] public string Name { get; set; }
|
||||||
|
[JsonProperty("path")] public string Path { get; set; }
|
||||||
|
[JsonProperty("parentId")] public string ParentId { get; set; }
|
||||||
|
[JsonProperty("envId")] public string EnvId { get; set; }
|
||||||
|
[JsonProperty("environment")] public string Environment { get; set; }
|
||||||
|
[JsonProperty("projectId")] public string ProjectId { get; set; }
|
||||||
|
[JsonProperty("workspaceId")] public string WorkspaceId { get; set; }
|
||||||
|
[JsonProperty("createdAt")] public string CreatedAt { get; set; }
|
||||||
|
[JsonProperty("updatedAt")] public string UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalFolderListResponseDto
|
||||||
|
{
|
||||||
|
[JsonProperty("folders")] public List<InfisicalFolderResponseDto> Folders { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalFolderSingleResponseDto
|
||||||
|
{
|
||||||
|
[JsonProperty("folder")] public InfisicalFolderResponseDto Folder { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalFolderCreateRequestDto
|
||||||
|
{
|
||||||
|
[JsonProperty("workspaceId")] public string WorkspaceId { get; set; }
|
||||||
|
[JsonProperty("environment")] public string Environment { get; set; }
|
||||||
|
[JsonProperty("name")] public string Name { get; set; }
|
||||||
|
[JsonProperty("path", NullValueHandling = NullValueHandling.Ignore)] public string Path { get; set; }
|
||||||
|
[JsonProperty("directory", NullValueHandling = NullValueHandling.Ignore)] public string Directory { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InfisicalFolderUpdateRequestDto
|
||||||
|
{
|
||||||
|
[JsonProperty("workspaceId")] public string WorkspaceId { get; set; }
|
||||||
|
[JsonProperty("environment")] public string Environment { get; set; }
|
||||||
|
[JsonProperty("name")] public string Name { get; set; }
|
||||||
|
[JsonProperty("path", NullValueHandling = NullValueHandling.Ignore)] public string Path { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using PSInfisicalAPI.Models;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Folders
|
||||||
|
{
|
||||||
|
internal static class InfisicalFolderMapper
|
||||||
|
{
|
||||||
|
public static InfisicalFolder Map(InfisicalFolderResponseDto dto, string fallbackProjectId, string fallbackEnvironment)
|
||||||
|
{
|
||||||
|
if (dto == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string projectId = !string.IsNullOrEmpty(dto.ProjectId)
|
||||||
|
? dto.ProjectId
|
||||||
|
: (!string.IsNullOrEmpty(dto.WorkspaceId) ? dto.WorkspaceId : fallbackProjectId);
|
||||||
|
|
||||||
|
string environment = !string.IsNullOrEmpty(dto.Environment) ? dto.Environment : fallbackEnvironment;
|
||||||
|
|
||||||
|
return new InfisicalFolder
|
||||||
|
{
|
||||||
|
Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId,
|
||||||
|
Name = dto.Name,
|
||||||
|
Path = dto.Path,
|
||||||
|
ParentId = dto.ParentId,
|
||||||
|
Environment = environment,
|
||||||
|
ProjectId = projectId,
|
||||||
|
CreatedAtUtc = ParseTimestamp(dto.CreatedAt),
|
||||||
|
UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InfisicalFolder[] MapMany(IEnumerable<InfisicalFolderResponseDto> items, string fallbackProjectId, string fallbackEnvironment)
|
||||||
|
{
|
||||||
|
if (items == null)
|
||||||
|
{
|
||||||
|
return Array.Empty<InfisicalFolder>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<InfisicalFolder> results = new List<InfisicalFolder>();
|
||||||
|
foreach (InfisicalFolderResponseDto dto in items)
|
||||||
|
{
|
||||||
|
InfisicalFolder mapped = Map(dto, fallbackProjectId, fallbackEnvironment);
|
||||||
|
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,21 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PSInfisicalAPI.Models
|
||||||
|
{
|
||||||
|
public sealed class InfisicalFolder
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Path { get; set; }
|
||||||
|
public string ParentId { get; set; }
|
||||||
|
public string Environment { get; set; }
|
||||||
|
public string ProjectId { get; set; }
|
||||||
|
public DateTimeOffset? CreatedAtUtc { get; set; }
|
||||||
|
public DateTimeOffset? UpdatedAtUtc { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(Path) ? (Name ?? Id) : Path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user