diff --git a/README.md b/README.md index e4b1084..01d5f7f 100644 --- a/README.md +++ b/README.md @@ -62,10 +62,48 @@ There is also option to run Docker based tests. You need to configure `environme Exchange online tests will be skipped if the connection is not available. +### Empty Argument Values Support + +As of version 1.1.5, the module now supports passing empty string values to PowerShell command arguments when explicitly configured. This is useful for optional parameters that need to be passed as empty values rather than omitted entirely. + +To enable empty value support for a command argument, set the `empty` property to `true` in the argument configuration: + +```javascript +const commandRegistry = { + 'myCommand': { + command: "Get-Content {{{arguments}}}", + arguments: { + 'Path': {}, + 'Filter': { + empty: true, // Allow empty string values + }, + }, + return: { + type: "text", + } + } +}; +``` + +When `empty: true` is set, the argument will accept empty string values and include them in the generated PowerShell command: + +```javascript +// This will generate: Get-Content -Path './test.txt' -Filter '' +await psCommandService.execute("myCommand", { + Path: "./test.txt", + Filter: "" // Empty string value is now allowed +}); +``` + + ### History ``` +v1.1.5 - 2025-09-19 + - Added support for empty argument values in commands via 'empty' property + - Fixed argument value bleed into the next empty argument + v1.1.4 - 2024-11-22 - Extended testing and fixed escaping reserved variables and special characters in commands @@ -75,6 +113,7 @@ v1.1.3 - 2024-11-14 v1.1.2 - 2022-07-06 - Added support for usage of reserved powershell variables in commands [$null, $true, $false] + v1.1.1 - 2020-12-07 - Fixed bug import of custom commands if provided for certificate based auth diff --git a/psCommandService.js b/psCommandService.js index 12cd4ef..05aee60 100644 --- a/psCommandService.js +++ b/psCommandService.js @@ -282,6 +282,7 @@ PSCommandService.prototype._generateCommand = function(commandConfig, argument2V if ((argument.hasOwnProperty('valued') ? argument.valued : true)) { var isQuoted = (argument.hasOwnProperty('quoted') ? argument.quoted : true); + var isEmpty = (argument.hasOwnProperty('empty') ? argument.empty : false); var passedArgValues = argument2ValueMap[argumentName]; if (!(passedArgValues instanceof Array)) { @@ -314,7 +315,7 @@ PSCommandService.prototype._generateCommand = function(commandConfig, argument2V } // append the value - if (valueToSet && valueToSet.trim().length > 0) { + if (valueToSet !== null && valueToSet !== undefined && (isEmpty || valueToSet.trim().length > 0)) { // sanitize valueToSet = this._sanitize(valueToSet,isQuoted); diff --git a/test/unit.js b/test/unit.js index 9638f9f..8eeb25d 100644 --- a/test/unit.js +++ b/test/unit.js @@ -96,6 +96,9 @@ const commandRegistry = { command: "Get-Content {{{arguments}}}", arguments: { 'Path': {}, + 'Filter': { + empty: true, + }, }, return: { type: "text", @@ -652,4 +655,55 @@ describe("test PSCommandService w/ o365CommandRegistry", function () { }, 5000); } }); + it("Should test empty value support", async function () { + this.timeout(10000); + const statefulProcessCommandProxy = new StatefulProcessCommandProxy({ + name: "Powershell pool", + max: 1, + min: 1, + idleTimeoutMS: 30000, + + logFunction: logFunction, + processCommand: "pwsh", + processArgs: ["-Command", "-"], + processRetainMaxCmdHistory: 30, + processCwd: null, + processEnvMap: null, + processUid: null, + processGid: null, + initCommands: initCommands, + validateFunction: (processProxy) => processProxy.isValid(), + }); + + const psCommandService = new PSCommandService( + statefulProcessCommandProxy, + commandRegistry, + myLogFunction + ); + try { + const newResult = await psCommandService.execute("setContent", { + Path: "./test.txt", + Value: "Test", + Filter: "" + }); + assert.equal(newResult.command.trim(), "Set-Content -Path './test.txt' -Value 'Test'"); + assert.equal(newResult.stderr, ""); + const getResult = await psCommandService.execute("getContent", { + Path: "./test.txt", + Filter: "" + }); + assert.equal(getResult.command.trim(), "Get-Content -Path './test.txt' -Filter ''"); + assert.equal(getResult.stderr, ""); + assert.equal(getResult.stdout, "Test"); + } catch (e) { + assert.fail(e); + } finally { + await psCommandService.execute("removeItem", { + Path: "./test.txt", + }); + setTimeout(() => { + statefulProcessCommandProxy.shutdown(); + }, 5000); + } + }); });