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

Piping off Invoke-RestMethod not expanding as expected #15272

Closed
JAK1047 opened this issue Apr 19, 2021 · 7 comments
Closed

Piping off Invoke-RestMethod not expanding as expected #15272

JAK1047 opened this issue Apr 19, 2021 · 7 comments
Labels
Needs-Triage The issue is new and needs to be triaged by a work group. WG-Cmdlets-Utility cmdlets in the Microsoft.PowerShell.Utility module

Comments

@JAK1047
Copy link

JAK1047 commented Apr 19, 2021

Steps to reproduce

This may not be an issue at all and just something in Powershell I am unaware of. While making a call to an internal REST API I noticed a difference when piping to a ForEach-Object vs saving the output to a variable first and running in a ForEach loop. If I do not either expand a property on the call or save it to a variable first then the output gets piped over to the foreach a single time as the full array. Does it have to serialize the data first or something? I've tried finding any relevant documentation for this exact scenario but haven't had any luck so far.

$APIKey = 'APIKeyHere'
$Address = 'https://nagiosxi.contoso.com/nagiosxi/api/v1'

$Count1 = 0
$Count2 = 0

Invoke-RestMethod -Method GET -Uri "$Address/Config/contact?apikey=$APIKey" | ForEach-Object {$Count1++}

$Contacts = Invoke-RestMethod -Method GET -Uri "$Address/Config/contact?apikey=$APIKey"
ForEach ($Contact in $Contacts) {$Count2++}

Write-Output "Object count was: $Count1"
Write-Output "Object count was: $Count2"

Expected behavior

Object count was: 274
Object count was: 274

Actual behavior

Object count was: 1
Object count was: 274

Environment data

Name                           Value
----                           -----
PSVersion                      7.1.3
PSEdition                      Core
GitCommitId                    7.1.3
OS                             Microsoft Windows 10.0.17763
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
@JAK1047 JAK1047 added the Needs-Triage The issue is new and needs to be triaged by a work group. label Apr 19, 2021
@vexx32
Copy link
Collaborator

vexx32 commented Apr 19, 2021

This is probably due to how the API returns the result, it can depend on the endpoint, but it is noticeably different at times to how Windows PowerShell handled it, I think. There's been a lot of discussion over it, and at the moment I don't recall if there was ever an agreement to change the behaviour or not.

The quick workaround is just to pipe the result through Write-Output to force it to enumerate -- Invoke-RestMethod ...| Write-Output | ...

@JAK1047
Copy link
Author

JAK1047 commented Apr 19, 2021

That's interesting. I imagine any change to the behavior would probably be a breaking change, so if it's something already at least somewhat known it's certainly easy enough for me to work around. Was just the first time I had ever encountered it and my 15 minutes of Google didn't return me anything that seemed to match documentation wise.

@mklement0
Copy link
Contributor

mklement0 commented Apr 20, 2021

Yes, when Invoke-RestMethod receives an array, it sends that as a whole to the pipeline, which contravenes PowerShell's usual behavior of enumerating arrays, i.e. sending their elements one by one.

ConvertFrom-Json used to behave this way, but the behavior was changed in v7.0, and a -NoEnumerate switch was added as an opt-in to the old non-enumeration behavior - see #3424 for the original discussion.

It would make sense to me to change Invoke-RestMethod in the same fashion, which would (again) be a breaking change, however.

A simpler alternative to the Write-Output workaround mentioned by @vexx32 is to simply enclose the Invoke-RestMethod in (...), which also forces enumeration.

(Using (...) generally collects the output from the enclosed command in memory in full first, but that's not a problem here, given that the data in question already is in memory in full, as an array.)

@JAK1047
Copy link
Author

JAK1047 commented Apr 20, 2021

Order of operations type deal I got it. From a user perspective I can say the behavior I expected was enumeration like most every other command you send down the pipeline, but if the trade-off of changing the functionally does more harm then good then it may not be worth it in the grand scheme of things. (I don't know the consensus on why ConvertFrom-JSON was changed and if this checks similar boxes)

It would definitely make sense to me however to add a footnote on this objectively, nonstandard behavior to the Microsoft docs page for the cmdlet since I feel that's where most users, like myself, would turn to for guidance.

@mklement0
Copy link
Contributor

It would definitely make sense to me however to add a footnote on this objectively, nonstandard behavior to the Microsoft docs page for the cmdlet since I feel that's where most users, like myself, would turn to for guidance.

Good idea, please see MicrosoftDocs/PowerShell-Docs#7504

I don't know the consensus on why ConvertFrom-JSON was changed and if this checks similar boxes

To me, definitely - if the change was worth it for ConvertFrom-JSON, then it's also worth it for the same functionality that is built into Invoke-RestMethod.

@iSazonov iSazonov added the WG-Cmdlets-Utility cmdlets in the Microsoft.PowerShell.Utility module label Apr 20, 2021
@iSazonov
Copy link
Collaborator

I don't know the consensus on why ConvertFrom-JSON was changed and if this checks similar boxes

To me, definitely - if the change was worth it for ConvertFrom-JSON, then it's also worth it for the same functionality that is built into Invoke-RestMethod.

If it is true behavior please open new clear issue with the request.

@mklement0
Copy link
Contributor

Good idea, @iSazonov; please see #15280

JasonRobertson added a commit to JasonRobertson/PS-Okta that referenced this issue Aug 14, 2023
PowerShell doesn't enumerate objects in an array before routing
through the pipeline. This creates the issue where the properties
that are selected are not seen as the enitire array is passed.
To correct this I added encapsulation which forces enumeration.

Reference:
PowerShell/PowerShell#15272
#issuecomment-822620722

Shorten URL: https://rb.gy/67s9d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs-Triage The issue is new and needs to be triaged by a work group. WG-Cmdlets-Utility cmdlets in the Microsoft.PowerShell.Utility module
Projects
None yet
Development

No branches or pull requests

4 participants