locked
How come the array stops storing when error occurs? RRS feed

  • Question

  • I have been working on the following code that supposedly retrieves all powerbi reports from the server, checks if they have refresh plans, if they dont, it outputs "No refresh pans exist..", and if it does have it, then it outputs refreshplan info like description.

    $webPortalURL = "https://server-pbi.domain.com/reports"
    
    $PBI_Reports_Array = @()
    $PBI_Reports_Array = $(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports"))
    
    $loopCount = 0 
    $refreshPlanArray = @()
    
    foreach ($reportPath in $PBI_Reports_Array.value.path) {
    
            $refreshPlanArray += $(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports(path='" + $reportPath + "')/CacheRefreshPlans")); 
    
            write-host "$($refreshPlanArray[$loopCount])" -foregroundcolor magenta; #testing output here to debug
    
            if ([string]::IsNullOrEmpty($($refreshPlanArray[$loopCount].value))) { 
                write-host "$loopCount | $reportPath | No Refresh Plan Exists for this report!"; 
            }
            else {
                write-host "$loopCount | $reportPath | $($refreshPlanArray[$loopCount].value.Description) | $($refreshPlanArray[$loopCount].value.ScheduleDescription)" -foregroundcolor magenta;
            }
    
        $loopCount++;
    }

    i am running into a weird bug. so i have 2 servers/portals, on one of the servers/portals, when i run this script, it retrieves all reports and does exactly what i am expecting it do as described above. when i thought i finished developing the script, i tested it on production portal/server and it wasnt working as expected! i debugged for many hours until i think i found whats happening, but idk what to do about it:

    basically, the reason why it worked on one server/portal but not the other is because the nonproduction portal/server didnt have this error:

    Invoke-RestMethod : The remote server returned an error: (404) Not Found.
    

    apparently, before that error happened on the production server/portal where this failed, this line write-host "$($refreshPlanArray[$loopCount])" i added for debugging purposes was printing the following odata contexts up until the error happened!

    @{@odata.context=https://server-pbi.domain.com/reports/api/v2.0/$metadata#CacheRefreshPlans; value=System.Object[]}
    

    then when the error occurred after iteration 4, it stopped printing the odata!

    why is that? it looks like $refreshPlanArray stops storing when the error happens, because as you can see from the image, it no longer prints out odata (purple lines) after the iterations after the error occurs...

    o

    Thursday, April 23, 2020 6:13 AM

Answers

  • Use a try/catch and an interim variable that you can append to the array.

    try { $goodresponse = $true $restreply = $(Invoke-RestMethod -erroraction stop -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports(path='" + $reportPath + "')/CacheRefreshPlans")); } catch { $goodresponse = $false # that call did not work # Add other diagnostic code here } if ($goodresponse){ # add isnull and other tests here $refreshPlanArray += $restreply # add the good response to the array }


    OMG that actually gave me a hint! i had a try catch statement before but it wasnt helping with debugging thats why i removed it for the mean time, but i added it again based on your suggestion, but you had one thing missing: the $loopCount++ had to be included in the if statement as well! i think what was happening is that when the error occurs, it doesnt save anything in the array because it was a null value, but the loop was still incrementing so it was messing up the indexing!

    in fact, i tested this with my original code (without the $goodresponse part) and i just had to put the $loopCount++ in the try statement, because anything in the try statement evaluates to true anyways so theres no need for the extra boolean step :)

    thank you very much, this is the correct way to do it :)

    $webPortalURL = "https://server-pbi.domain.com/reports"

    $PBI_Reports_Array = @() $PBI_Reports_Array = $(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports")) $loopCount = 0 $refreshPlanArray = @() foreach ($reportPath in $PBI_Reports_Array.value.path) { try { $goodresponse = $true $restreply = $(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports(path='" + $reportPath + "')/CacheRefreshPlans")); } catch { $goodresponse = $false # that call did not work } if ($goodresponse) { $refreshPlanArray += $restreply if ([string]::IsNullOrEmpty($($refreshPlanArray[$loopCount].value))) { write-host "$loopCount | $reportPath | No Refresh Plan Exists for this report!"; } else { write-host "$loopCount | $reportPath | $($refreshPlanArray[$loopCount].value.Description) | $($refreshPlanArray[$loopCount].value.ScheduleDescription)" -foregroundcolor magenta; } $loopCount++; #here } #remove $loopCount from here }




    • Edited by cataster Thursday, April 23, 2020 5:49 PM
    • Marked as answer by cataster Thursday, April 23, 2020 5:59 PM
    Thursday, April 23, 2020 5:47 PM

All replies

  • Use a try/catch and an interim variable that you can append to the array.

    try { $goodresponse = $true $restreply = $(Invoke-RestMethod -erroraction stop -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports(path='" + $reportPath + "')/CacheRefreshPlans")); } catch { $goodresponse = $false # that call did not work # Add other diagnostic code here } if ($goodresponse){ # add isnull and other tests here $refreshPlanArray += $restreply # add the good response to the array }


    • Proposed as answer by clayman2 Friday, April 24, 2020 12:06 PM
    Thursday, April 23, 2020 1:22 PM
  • Use a try/catch and an interim variable that you can append to the array.

    try { $goodresponse = $true $restreply = $(Invoke-RestMethod -erroraction stop -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports(path='" + $reportPath + "')/CacheRefreshPlans")); } catch { $goodresponse = $false # that call did not work # Add other diagnostic code here } if ($goodresponse){ # add isnull and other tests here $refreshPlanArray += $restreply # add the good response to the array }


    OMG that actually gave me a hint! i had a try catch statement before but it wasnt helping with debugging thats why i removed it for the mean time, but i added it again based on your suggestion, but you had one thing missing: the $loopCount++ had to be included in the if statement as well! i think what was happening is that when the error occurs, it doesnt save anything in the array because it was a null value, but the loop was still incrementing so it was messing up the indexing!

    in fact, i tested this with my original code (without the $goodresponse part) and i just had to put the $loopCount++ in the try statement, because anything in the try statement evaluates to true anyways so theres no need for the extra boolean step :)

    thank you very much, this is the correct way to do it :)

    $webPortalURL = "https://server-pbi.domain.com/reports"

    $PBI_Reports_Array = @() $PBI_Reports_Array = $(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports")) $loopCount = 0 $refreshPlanArray = @() foreach ($reportPath in $PBI_Reports_Array.value.path) { try { $goodresponse = $true $restreply = $(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports(path='" + $reportPath + "')/CacheRefreshPlans")); } catch { $goodresponse = $false # that call did not work } if ($goodresponse) { $refreshPlanArray += $restreply if ([string]::IsNullOrEmpty($($refreshPlanArray[$loopCount].value))) { write-host "$loopCount | $reportPath | No Refresh Plan Exists for this report!"; } else { write-host "$loopCount | $reportPath | $($refreshPlanArray[$loopCount].value.Description) | $($refreshPlanArray[$loopCount].value.ScheduleDescription)" -foregroundcolor magenta; } $loopCount++; #here } #remove $loopCount from here }




    • Edited by cataster Thursday, April 23, 2020 5:49 PM
    • Marked as answer by cataster Thursday, April 23, 2020 5:59 PM
    Thursday, April 23, 2020 5:47 PM
  • Can't you just reference $refreshPlanArray.count?
    Friday, April 24, 2020 3:03 AM
  • Can't you just reference $refreshPlanArray.count?
    Hmm, what do you mean
    Friday, April 24, 2020 1:44 PM
  • You're using loopcount as an index to keep track of the number of entries. 

    $refreshPlanArray = @()
    $refreshPlanArray.count
    $refreshPlanArray += "First response"
    $refreshPlanArray.count
    $refreshPlanArray += "Second response"
    $refreshPlanArray.count
    "The last response that we added was {0}" -f $refreshPlanArray[$refreshPlanArray.count -1]
    

    Produces:

    0
    1
    2
    The last response that we added was Second response

    Friday, April 24, 2020 2:23 PM
  • You're using loopcount as an index to keep track of the number of entries. 

    $refreshPlanArray = @()
    $refreshPlanArray.count
    $refreshPlanArray += "First response"
    $refreshPlanArray.count
    $refreshPlanArray += "Second response"
    $refreshPlanArray.count
    "The last response that we added was {0}" -f $refreshPlanArray[$refreshPlanArray.count -1]

    Produces:

    0
    1
    2
    The last response that we added was Second response

    ohhh fascinating...with -1

    i suppose i could...but $loopCount is much better in terms of editorial quality/visual first glance because its shorter than a statement like 

    $refreshPlanArray.count -1
     
    Friday, April 24, 2020 2:45 PM
  • If you're going to work from the back of the array (i.e. the 'Last-In') then why not use a stack instead of an array???

    "Stacks work like a plate dispenser in a cafeteria"
    "The one on top was the last on put on the stack -- or 'Last in, first out'"
    $refreshPlanStack = New-Object system.collections.stack
    $refreshPlanStack.Push(1)
    $refreshPlanStack.Push(2)
    $refreshPlanStack.Push(3)
    "Looks like an array, but backwards"
    $refreshPlanStack
    "Last in was '3' so it's first out. Then '2', and finally '1'"
    While ($refreshPlanStack.Count){
        $refreshPlanStack.Pop()
    }


    --- Rich Matheisen MCSE&I, Exchange Ex-MVP (16 years)

    Friday, April 24, 2020 3:26 PM
  • If you're going to work from the back of the array (i.e. the 'Last-In') then why not use a stack instead of an array???

    "Stacks work like a plate dispenser in a cafeteria"
    "The one on top was the last on put on the stack -- or 'Last in, first out'"
    $refreshPlanStack = New-Object system.collections.stack
    $refreshPlanStack.Push(1)
    $refreshPlanStack.Push(2)
    $refreshPlanStack.Push(3)
    "Looks like an array, but backwards"
    $refreshPlanStack
    "Last in was '3' so it's first out. Then '2', and finally '1'"
    While ($refreshPlanStack.Count){
        $refreshPlanStack.Pop()
    }


    --- Rich Matheisen MCSE&I, Exchange Ex-MVP (16 years)

    oh fascinating...i didnt think powershell had stacks. ive always been using arrays lol

    maybe itll be fun for me to try this out!

    btw, the ordering of the reports for me doesnt matter if they are accessed first or last from the array, as long as the functionality works (i.e. if report has no refresh plan, print so, if it does, print its information). so the accessing in this scenario matters not, which i suppose a stack would be a great case for this!

    Friday, April 24, 2020 3:32 PM
  • If you're going to work from the back of the array (i.e. the 'Last-In') then why not use a stack instead of an array???

    "Stacks work like a plate dispenser in a cafeteria"
    "The one on top was the last on put on the stack -- or 'Last in, first out'"
    $refreshPlanStack = New-Object system.collections.stack
    $refreshPlanStack.Push(1)
    $refreshPlanStack.Push(2)
    $refreshPlanStack.Push(3)
    "Looks like an array, but backwards"
    $refreshPlanStack
    "Last in was '3' so it's first out. Then '2', and finally '1'"
    While ($refreshPlanStack.Count){
        $refreshPlanStack.Pop()
    }


    --- Rich Matheisen MCSE&I, Exchange Ex-MVP (16 years)

    so in my code, how do i push the results from the rest response? is it the same way as the array appends each response?

    foreach ($reportPath in $PBI_Reports_Array.value.path) {
    
            $refreshPlanStack.push($(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports(path='" + $reportPath + "')/CacheRefreshPlans"))); 
    
    #other stuff...
    
    }


    like that?

    because the only way i see thats possible is if i add a foreach, but that would add on to the complexity dont you think?

    foreach ($p in $(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports(path='" + $reportPath + "')/CacheRefreshPlans"))) {$refreshPlanStack.push($p)}. 

    • Edited by cataster Friday, April 24, 2020 5:04 PM
    Friday, April 24, 2020 3:53 PM