Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG][PowerShell] Parameter of type array passed in pipeline: only first element is processed #19663

Open
4 of 6 tasks
condorcorde opened this issue Sep 24, 2024 · 3 comments
Open
4 of 6 tasks

Comments

@condorcorde
Copy link
Contributor

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

The issue affects functions New-PSUsersWithArrayInput and New-PSUsersWithListInput in PSPetStore API.
The parameter $User has type PsCustomObject[] and has the attribute ValueFromPipeline = $true.
The body of the function consists of a single Process {} block that ends with a return statement.

In PowerShell the Process {} block is executed once for each input object that reaches the function, so the actual behaviour is the serialization the first element, the execution of Invoke-PSApiClient and a return to the caller.

openapi-generator version
$  openapi-generator --version
openapi-generator-cli 7.8.0
  commit : 6bdc452
  built  : -999999999-01-01T00:00:00+18:00
  source : https://github.com/openapitools/openapi-generator
  docs   : https://openapi-generator.tech/
OpenAPI declaration file content or url

user.yml

Generation Details

Standard generation of PSPetStore client library

Steps to reproduce
Import-Module -Name PSPetstore
$a = Initialize-PSUser -Id 1 -Username foo
$b = Initialize-PSUser -Id 2 -Username bar
$c = @($a, $b)
$c.GetType()
$DebugPreference = 'Continue'
$c | New-PSUsersWithArrayInput

Both New-PSUsersWithArrayInput and Invoke-PSApiClient show a single object being processed (*):

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

DEBUG: Calling method: New-PSUsersWithArrayInput
DEBUG: Parameter Value
DEBUG: --------- -----
DEBUG: User      @{id=1; username=foo; firstName=; lastName=; email=; password=; phone=; userStatus=;
DEBUG:           objectWithNoDeclaredProps=; objectWithNoDeclaredPropsNullable=; anyTypeProp=; anyTypePropNullable=}
DEBUG: Calling method: Invoke-PSApiClient
DEBUG: Parameter        Value
DEBUG: ---------        -----
DEBUG: Method           POST
DEBUG: Uri              /user/createWithArray
DEBUG: Accepts
DEBUG: ContentTypes     application/json
DEBUG: Body             [
DEBUG:                    {
DEBUG:                      "id": 1,
DEBUG:                      "username": "foo",
DEBUG:                      "firstName": "",
DEBUG:                      "lastName": "",
DEBUG:                      "email": "",
DEBUG:                      "password": "",
DEBUG:                      "phone": "",
DEBUG:                      "userStatus": null,
DEBUG:                      "objectWithNoDeclaredProps": null,
DEBUG:                      "objectWithNoDeclaredPropsNullable": null,
DEBUG:                      "anyTypeProp": null,
DEBUG:                      "anyTypePropNullable": null
DEBUG:                    }
DEBUG:                  ]
DEBUG: HeaderParameters {}
DEBUG: QueryParameters  {}
DEBUG: FormParameters   {}
DEBUG: CookieParameters {}
DEBUG: ReturnType
DEBUG: IsBodyNullable   False

(*) The function Out-DebugParameter was modified by adding the parameter -Wrap to Format-Table in order to show the full body.

Related issues/PRs

Array context not maintained when converting a parameter to JSON #18427

Suggest a fix

The main product of the function produced by api.mustache is a JSON serialization of the input, and this requires the whole input object to be passed at once to ConvertTo-JSON. Actually, in PowerShell 5 the behavior of ConvertTo-JSON is such that the output may not be correct when the input object is passed from the pipeline.

IMHO there are two options:

  1. Remove the ValueFromPipeline attribute when the main parameter is an array type.
  2. Use the Process {}block to reconstruct the input array, and pass this variable to ConvertTo-Json in the End {}block of the function. This option shall ensure that the serialization of the reconstructed collection is the same as the original one.
@condorcorde
Copy link
Contributor Author

This implementation of New-PSUsersWithArrayInput seems to produce the expected serialization in PowerShell 5.

    Begin {
        'Calling method: New-PSUsersWithArrayInput' | Write-Debug
        $PSBoundParameters | Out-DebugParameter | Write-Debug

        $LocalVarAccepts = @()
        $LocalVarContentTypes = @()
        $LocalVarQueryParameters = @{}
        $LocalVarHeaderParameters = @{}
        $LocalVarFormParameters = @{}
        $LocalVarPathParameters = @{}
        $LocalVarCookieParameters = @{}
        $LocalVarBodyParameter = $null
        $LocalVarJsonInput = [System.Collections.ArrayList]::new()

        $Configuration = Get-PSConfiguration
        # HTTP header 'Content-Type'
        $LocalVarContentTypes = @('application/json')

        $LocalVarUri = '/user/createWithArray'
    }

    Process {
        $LocalVarJsonInput.Add($User[0]) | Out-Null
    }

    End {
        $LocalVarBodyParameter =  ConvertTo-Json -InputObject $LocalVarJsonInput -Depth 100

        $LocalVarResult = Invoke-PSApiClient -Method 'POST' `
                                -Uri $LocalVarUri `
                                -Accepts $LocalVarAccepts `
                                -ContentTypes $LocalVarContentTypes `
                                -Body $LocalVarBodyParameter `
                                -HeaderParameters $LocalVarHeaderParameters `
                                -QueryParameters $LocalVarQueryParameters `
                                -FormParameters $LocalVarFormParameters `
                                -CookieParameters $LocalVarCookieParameters `
                                -ReturnType "" `
                                -IsBodyNullable $false

        if ($WithHttpInfo.IsPresent) {
            return $LocalVarResult
        } else {
            return $LocalVarResult["Response"]
        }
    }

@condorcorde
Copy link
Contributor Author

... only when the parameter is passed in the pipeline. If it is specified as a named parameter the process block should rather be

    Process {
        $LocalVarJsonInput = $User
    }

@condorcorde
Copy link
Contributor Author

@wing328
A discriminator - compatible with both PS5 and PS7 - could be obtained via $MyInvocation.ExpectingInput:

Process {
    if ($MyInvocation.ExpectingInput) {
        Process-FromPipeline
    } else {
        Process-FromNamedParameter
   }
}

However this is going to cause a major change to the mustache file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant