Badgerati / Pode

Pode is a Cross-Platform PowerShell web framework for creating REST APIs, Web Sites, and TCP/SMTP servers
https://badgerati.github.io/Pode
MIT License
830 stars 92 forks source link

Improve Function Handling of Pipeline Input and Array Blocking #1345

Open mdaneri opened 2 months ago

mdaneri commented 2 months ago

Background: In the PowerShell module Pode, it is essential to ensure that functions correctly handle pipeline input to provide predictable and reliable behavior. The process block plays a crucial role in processing each item from the pipeline individually, which is a common requirement in many PowerShell scripts. Additionally, there are scenarios where functions should explicitly block arrays from being passed through the pipeline to prevent unintended behavior and ensure data integrity.

Issue 1: Importance of the process Block Functions without a process block may exhibit unexpected behavior when dealing with pipeline input. Specifically, such functions will only process the first item from the pipeline and ignore subsequent items. This behavior can lead to confusion and bugs, especially in scripts that rely on processing multiple items from the pipeline.

Example:

Function Get-Number {
    [CmdletBinding()]
    Param(
        [Parameter(ValueFromPipeline)]
        [int]
        $Number
    )

    $Number
}

When used with pipeline input, this function will only return the first item:

1..5 | Get-Number
# Output: 1

Recommended Solution: Include the process block in functions to ensure each pipeline item is processed individually. Improved Function:

Function Get-Number {
    [CmdletBinding()]
    Param(
        [Parameter(ValueFromPipeline)]
        [int]
        $Number
    )

    process {
        $Number
    }
}

With this enhancement, the function will handle each pipeline item as expected:

1..5 | Get-Number
# Output: 1 2 3 4 5

Issue 2: Blocking Arrays from Pipeline Input In some scenarios, it is necessary to prevent functions from accepting arrays as pipeline input to ensure they process only individual items. Allowing arrays can lead to unintended behavior, especially when functions are designed to handle single items.

Example:

Function Get-Number {
    [CmdletBinding()]
    Param(
        [Parameter(ValueFromPipeline)]
        [int]
        $Number
    )

    Begin {
        $inputNumbers = @()
    }

    Process {
        $inputNumbers += $Number
    }

    End {
        if ($inputNumbers.Count -gt 1) {
            throw "Input should not be an array."
        } 
        $inputNumbers
    }
}

With this validation, the function will throw an error if an array is passed through the pipeline:

1,2,3 | Get-Number
Input should not be an array.
At line:19 char:13
+             throw "Input should not be an array."
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (Input should not be an array.:String) [], RuntimeException
    + FullyQualifiedErrorId : Input should not be an array.

Recommended Solution: Implement validation to block arrays from being passed through the pipeline, ensuring functions handle only single items.

Proposed Actions:

  1. Review and update existing functions to include the process block where appropriate.
  2. Implement validation to block arrays from being passed through the pipeline for functions that should handle only single items.
Badgerati commented 2 months ago

In principal, all sounds good to me; I can't think of anything else to suggest.

It seems #1344 will help to cover off some of this work, and for the array blocking you've eluded to one set of functions which will need this: Lock-PodeObject and Enter-PodeLockable - these accept a piped "lockable" Object from Get-PodeLockable, but it should only be a single item, not an array.