Bulk v4 batch routes + strongly-typed -Secrets IDictionary[string,string][]

- Endpoint registry: register POST/PATCH/DELETE /api/v4/secrets/batch as preferred candidates for BulkCreate/Update/Delete; v3 raw routes retained as automatic fallback.
- DTOs: add projectId (required for v4) alongside workspaceId on the three batch request envelopes; both serialized when set, both ignored when null.
- SecretsClient: populate ProjectId in CreateBatch/UpdateBatch/DeleteBatch so v4 succeeds on first attempt.
- Cmdlets: -Secrets on New/Update-InfisicalSecret changed from Hashtable[] to IDictionary<string,string>[] for stronger typing and tab-completion; converter rewritten to accept IEnumerable<IDictionary<string,string>>. TagIds parsed from comma-separated string; nested Metadata dropped from bulk hashtable surface (still settable programmatically on bulk items).
- Tests: 166 passing (was 161). Bulk endpoints now resolve to v4 primary with v3 fallback; new tests verify projectId envelope serialization, dual-key omission, and TagIds trimming.
This commit is contained in:
GraceSolutions
2026-06-03 20:06:13 -04:00
parent e0a6ef02df
commit 211fbcf34d
12 changed files with 158 additions and 114 deletions
@@ -1,4 +1,4 @@
using System.Collections;
using System.Collections.Generic;
using PSInfisicalAPI.Errors;
using PSInfisicalAPI.Secrets;
using Xunit;
@@ -10,16 +10,16 @@ namespace PSInfisicalAPI.Tests
[Fact]
public void ToCreateItems_Maps_Standard_Keys()
{
Hashtable entry = new Hashtable
Dictionary<string, string> entry = new Dictionary<string, string>
{
{ "SecretName", "API_KEY" },
{ "SecretValue", "abc" },
{ "SecretComment", "primary" },
{ "SkipMultilineEncoding", true },
{ "TagIds", new[] { "tag-1", "tag-2" } }
{ "SkipMultilineEncoding", "true" },
{ "TagIds", "tag-1,tag-2" }
};
InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new[] { entry });
InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new IDictionary<string, string>[] { entry });
Assert.Single(items);
Assert.Equal("API_KEY", items[0].SecretName);
Assert.Equal("abc", items[0].SecretValue);
@@ -31,13 +31,13 @@ namespace PSInfisicalAPI.Tests
[Fact]
public void ToCreateItems_Accepts_Name_Alias_For_SecretName()
{
Hashtable entry = new Hashtable
Dictionary<string, string> entry = new Dictionary<string, string>
{
{ "Name", "API_KEY" },
{ "Value", "abc" }
};
InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new[] { entry });
InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new IDictionary<string, string>[] { entry });
Assert.Single(items);
Assert.Equal("API_KEY", items[0].SecretName);
Assert.Equal("abc", items[0].SecretValue);
@@ -46,23 +46,23 @@ namespace PSInfisicalAPI.Tests
[Fact]
public void ToCreateItems_Without_SecretName_Throws()
{
Hashtable entry = new Hashtable { { "Value", "abc" } };
Dictionary<string, string> entry = new Dictionary<string, string> { { "Value", "abc" } };
Assert.Throws<InfisicalConfigurationException>(() =>
InfisicalBulkSecretConverter.ToCreateItems(new[] { entry }));
InfisicalBulkSecretConverter.ToCreateItems(new IDictionary<string, string>[] { entry }));
}
[Fact]
public void ToUpdateItems_Maps_NewSecretName()
{
Hashtable entry = new Hashtable
Dictionary<string, string> entry = new Dictionary<string, string>
{
{ "SecretName", "API_KEY" },
{ "NewSecretName", "RENAMED" },
{ "SecretValue", "new-value" }
};
InfisicalBulkUpdateSecretItem[] items = InfisicalBulkSecretConverter.ToUpdateItems(new[] { entry });
InfisicalBulkUpdateSecretItem[] items = InfisicalBulkSecretConverter.ToUpdateItems(new IDictionary<string, string>[] { entry });
Assert.Single(items);
Assert.Equal("API_KEY", items[0].SecretName);
Assert.Equal("RENAMED", items[0].NewSecretName);
@@ -70,20 +70,17 @@ namespace PSInfisicalAPI.Tests
}
[Fact]
public void ToCreateItems_Maps_Metadata_Dictionary()
public void ToCreateItems_Trims_TagId_Whitespace_And_Skips_Empty()
{
Hashtable meta = new Hashtable { { "owner", "platform" }, { "tier", "1" } };
Hashtable entry = new Hashtable
Dictionary<string, string> entry = new Dictionary<string, string>
{
{ "SecretName", "API_KEY" },
{ "SecretValue", "abc" },
{ "Metadata", meta }
{ "TagIds", " tag-1 , , tag-2 " }
};
InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new[] { entry });
Assert.NotNull(items[0].SecretMetadata);
Assert.Equal("platform", items[0].SecretMetadata["owner"]);
Assert.Equal("1", items[0].SecretMetadata["tier"]);
InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new IDictionary<string, string>[] { entry });
Assert.Equal(new[] { "tag-1", "tag-2" }, items[0].TagIds);
}
[Fact]