Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
```



### <a id="history"></a>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

Expand All @@ -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

Expand Down
3 changes: 2 additions & 1 deletion psCommandService.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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);
Expand Down
54 changes: 54 additions & 0 deletions test/unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ const commandRegistry = {
command: "Get-Content {{{arguments}}}",
arguments: {
'Path': {},
'Filter': {
empty: true,
},
},
return: {
type: "text",
Expand Down Expand Up @@ -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);
}
});
});