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
@@ -59,12 +59,14 @@ namespace PSInfisicalAPI.Tests
object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchCreateRequestDto");
dto.GetType().GetProperty("WorkspaceId").SetValue(dto, "wks-1");
dto.GetType().GetProperty("ProjectId").SetValue(dto, "wks-1");
dto.GetType().GetProperty("Environment").SetValue(dto, "prod");
dto.GetType().GetProperty("SecretPath").SetValue(dto, "/db");
dto.GetType().GetProperty("Secrets").SetValue(dto, list);
JObject json = JObject.Parse(JsonConvert.SerializeObject(dto));
Assert.Equal("wks-1", (string)json["workspaceId"]);
Assert.Equal("wks-1", (string)json["projectId"]);
Assert.Equal("prod", (string)json["environment"]);
Assert.Equal("/db", (string)json["secretPath"]);
JArray secrets = (JArray)json["secrets"];
@@ -73,6 +75,31 @@ namespace PSInfisicalAPI.Tests
Assert.Equal("V1", (string)secrets[0]["secretValue"]);
}
[Fact]
public void BatchCreateRequest_Omits_Null_WorkspaceId_When_Only_ProjectId_Set()
{
object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchCreateRequestDto");
dto.GetType().GetProperty("ProjectId").SetValue(dto, "wks-1");
dto.GetType().GetProperty("Environment").SetValue(dto, "prod");
JObject json = JObject.Parse(JsonConvert.SerializeObject(dto));
Assert.False(json.ContainsKey("workspaceId"));
Assert.Equal("wks-1", (string)json["projectId"]);
}
[Fact]
public void BatchUpdateRequest_Includes_ProjectId_Alongside_WorkspaceId()
{
object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchUpdateRequestDto");
dto.GetType().GetProperty("WorkspaceId").SetValue(dto, "wks-1");
dto.GetType().GetProperty("ProjectId").SetValue(dto, "wks-1");
dto.GetType().GetProperty("Environment").SetValue(dto, "prod");
JObject json = JObject.Parse(JsonConvert.SerializeObject(dto));
Assert.Equal("wks-1", (string)json["workspaceId"]);
Assert.Equal("wks-1", (string)json["projectId"]);
}
[Fact]
public void BatchDeleteRequest_Serializes_With_Secret_Keys()
{
@@ -86,11 +113,13 @@ namespace PSInfisicalAPI.Tests
object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchDeleteRequestDto");
dto.GetType().GetProperty("WorkspaceId").SetValue(dto, "wks-1");
dto.GetType().GetProperty("ProjectId").SetValue(dto, "wks-1");
dto.GetType().GetProperty("Environment").SetValue(dto, "prod");
dto.GetType().GetProperty("Secrets").SetValue(dto, list);
JObject json = JObject.Parse(JsonConvert.SerializeObject(dto));
Assert.Equal("wks-1", (string)json["workspaceId"]);
Assert.Equal("wks-1", (string)json["projectId"]);
JArray secrets = (JArray)json["secrets"];
Assert.Single(secrets);
Assert.Equal("K1", (string)secrets[0]["secretKey"]);