PowerShell / Crescendo

a module for wrapping native applications in a PowerShell function and module
MIT License
399 stars 37 forks source link

Increase UX of Crescendo-generated cmdlets #191

Closed steviecoaster closed 1 year ago

steviecoaster commented 1 year ago

Summary of the new feature / enhancement

Crescendo is a fantastic tool for quickly generating PowerShell code for a native command, however there are some long-standing PowerShell user-experience considerations that need to be addressed. Most of this has to do with adding the ability to do filtering within the generated Cmdlet, without the need to pipe to something like Where-Object or Select-Object.

Take for example the Get-Service Cmdlet. I don't have to do something like Get-Service | Where-Object $_.Name -eq 'BITS', I can simply call Get-Service and provide either the service name or DisplayName. This is just one of a very long line of examples of native functionality that exists today.

This same user experience does not exist in Crescendo for a generated native command wrapper. What I would propose is that we add something to the Schema that allows for parameters added to the Native Command's function be passed to a handler. If no handler has been provided, or you have opted to skip an OutputHandler via your json configuration then we should prevent the filter parameters from being extended from the schema onto the generated command. This could be provided potentially by a flag applied in schema (maybe, the implementation details here are something to discuss and figure out what works best; both from an authoring perspective as well as from a end user point of view).

As an example, here's a handler I have for parsing vagrant box list:

function parseVagrantBox {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory)]
        [Object[]]
        $Lines,

        [Parameter()]
        [String]
        $Name
    )
    process {

        $Boxes = [System.Collections.Generic.List[pscustomobject]]::new()
        $Lines | Foreach-Object {
            $null = $_ -match '(?<name>([\w\/]+))\s+\((?<provider>(\w+)),\s(?<version>(\d.+))\)'

           $b = [pscustomObject]@{
                Name     = $matches.name
                Provider = $matches.provider
                Version  = [version]$matches.version
            }

            $Boxes.Add($b)
        }

        if(-not $Name){
            return $Boxes
        }

        else {
           $r = $Boxes | Where-Object { $_.Name -eq $Name}
           if(-not $r){
               Write-Warning "Box not found!"
           }
           else {
               return $r
           }
        }
    }
}

On a command-line outside of my module I can see that things work as expected:

$lines = vagrant box list
. ./parseVagrantBox.ps1
parseVagrantBox -Lines $lines

Name                       Provider   Version
----                       --------   -------
StefanScherer/windows_2019 virtualbox 2021.5.15
ubuntu/trusty64            virtualbox 20190514.0.0
ubuntu/xenial64            virtualbox 20211001.0.0
Additionally, I can filter this:
parseVagrantBox -Lines $lines -Name 'ubuntu/xenial64'

Name            Provider   Version
----            --------   -------
ubuntu/xenial64 virtualbox 20211001.0.0

My error handling works as well:

 parseVagrantBox -Lines $lines -Name 'ubuntu/wefwefewf'
WARNING: Box not found!

However, when I run this in the context of the generated Crescendo module, my filter/error logic falls apart:

Get-VagrantBox

Name                       Provider   Version
----                       --------   -------
StefanScherer/windows_2019 virtualbox 2021.5.15
ubuntu/trusty64            virtualbox 20190514.0.0
ubuntu/xenial64            virtualbox 20211001.0.0

Get-VagrantBox -Name 'ubuntu/xenial64'

Name                       Provider   Version
----                       --------   -------
StefanScherer/windows_2019 virtualbox 2021.5.15
ubuntu/trusty64            virtualbox 20190514.0.0
ubuntu/xenial64            virtualbox 20211001.0.0

Get-VagrantBox -Name 'uowejfi'        

Name                       Provider   Version
----                       --------   -------
StefanScherer/windows_2019 virtualbox 2021.5.15
ubuntu/trusty64            virtualbox 20190514.0.0
ubuntu/xenial64            virtualbox 20211001.0.0

I've put the full Crescendo module code in a gist if anyone finds it useful to see exactly how I've configured things.

Proposed technical implementation details (optional)

No response

steviecoaster commented 1 year ago

@theJasonHelmick per our conversation at PowerShell Summit 2023, I've put this issue in here for now. I will update with what I think would be a good example of a Crescendo schema that would support this flow later once I have a bit more time to think on it a bit.

JamesWTruher commented 1 year ago

There might be a couple of different possible approaches here. One is as you suggest and pass values to the output handler, another would be to just have a parameter type which is not passed to the executable but still available to the handler. For example, if you have a [parameter()]$Name which is not destined for the native tool as a parameter, $Name is still defined and you could reference $Name in your handler.

in either case, we'll need to add a property to the parameter (in the parameter map) which basically says "don't give this to the native exe". I have a feeling that the later is going to be less complicated then adding parameters to the call of the output handler.

steviecoaster commented 1 year ago

I agree that I believe the latter is going to be better approach here. Thanks for your input @JamesWTruher!

theJasonHelmick commented 1 year ago

Thank you @steviecoaster for the suggestion - let us investigate and discuss this more.

theJasonHelmick commented 1 year ago

@steviecoaster thank you for this suggestion - we are looking to include this in a future release.

JamesWTruher commented 1 year ago

should be addressed in commit f2a1208d

theJasonHelmick commented 1 year ago

Thank you! Fixed/closed - in release: https://www.powershellgallery.com/packages/Microsoft.PowerShell.Crescendo/1.1.0-RC1