NewRelicPS.Dashboards.Common.psm1

# Unique String to replace the GUID that can be found and removed.
$PLACEHOLDER_GUID = 'NO_DESTINATION_GUID_FOUND_THIS_WILL_BE_REMOVED'

Function Update-NRDashboardWidgetAccountID {
  [CMDLetBinding(SupportsShouldProcess)]
  Param (
    [Parameter (Mandatory = $true)]
    [int] $AccountId,
    [Parameter (Mandatory = $true)]
    [PSCustomObject] $DashboardConfig
  )
  Write-Debug 'Updating widget Account IDs'
  $dashboardConfigJSON = $DashboardConfig | ConvertTo-JSON -Depth 20 -Compress

  # Old Replacement for string based format - Not sure if this is still in use for old dashboards but kept for backwards compatability
  $stringsToReplace = ($dashboardConfigJSON | select-string -pattern '"AccountId":([0-9]*),' -AllMatches).matches.value | Select-Object -unique

  # New Replacement for array based format - Was added on 5/4/2023 - Not sure when NR moved to this format
  $arraysToReplace = ($dashboardConfigJSON | select-string -pattern '"accountIds":\[([0-9]*)\],' -AllMatches).matches.value | Select-Object -unique

  If ($stringsToReplace -or $arraysToReplace -and $PSCmdlet.ShouldProcess('Update Dashboard Widget Account Numbers')) {
    Write-Debug 'Found strings to replace with Account IDs'
    # Replace account Id strings
    Foreach ($string in $stringsToReplace) {
      $DashboardConfigJSON = $DashboardConfigJSON.Replace($string, "`"accountId`": $AccountId,")
    }

    # Replace account Id arrays
    Foreach ($arrayString in $arraysToReplace) {
      $DashboardConfigJSON = $DashboardConfigJSON.Replace($arrayString, "`"accountIds`": [$AccountId],")
    }
  }

  Return $DashboardConfigJSON | ConvertFrom-JSON -Depth 20
}

Function Update-NRDashboardLinkedEntityID {
  [CMDLetBinding(SupportsShouldProcess)]
  Param (
    [Parameter (Mandatory = $true)]
    [PSCustomObject] $SourceConfig,
    [Parameter (Mandatory = $true)]
    [PSCustomObject] $DestinationConfig
  )
  Write-Debug "Updating Dashboard Linked Entity IDs"
  $payload = $SourceConfig | ConvertTo-Json -Depth 50

  # Process each Page
  foreach ($page in $SourceConfig.pages) {
    # Find existing matching page
    $destinationGUID = ($DestinationConfig.pages | Where-Object {$_.name -eq $page.name}).guid

    # Swap GUID
    if($destinationGUID){
      Write-Verbose "Updating guid: $($page.guid) to: $destinationGUID"
      If ($PSCmdlet.ShouldProcess("Update guid: $($page.guid) to: $destinationGUID")) {
        $payload = $payload.replace($page.guid, $destinationGUID)
      }
    }else{
      Write-Verbose "No page found to match guid: $($page.guid)"
      # Replace with random known GUID - will be removed in Update call...
      If ($PSCmdlet.ShouldProcess("Update guid: $($page.guid) to a stock value to be removed later")) {
        $payload = $payload.replace($page.guid, $PLACEHOLDER_GUID)
      }
    }
  }

  return ($payload | ConvertFrom-Json -Depth 50)
}

Function ConvertTo-NRDashboardCreatePayload {
  Param (
    [Parameter (Mandatory = $true)]
    [PSCustomObject] $Config
  )

  # Deep clone the object
  $payload = $Config | ConvertTo-Json -Depth 50 | ConvertFrom-Json -Depth 50

  # Loop through each page and rebuild object
  for ($pageIndex = 0; $pageIndex -lt $payload.pages.count; $pageIndex++) {
    $page = $payload.pages[$pageIndex];

    $payload.pages[$pageIndex] = [PSCustomObject]@{ # Remove Page GUID
      name = $page.name
      widgets = $page.widgets
    }

    for ($widgetIndex = 0; $widgetIndex -lt $payload.pages[$pageIndex].widgets.count; $widgetIndex++) {
      $widget = $payload.pages[$pageIndex].widgets[$widgetIndex]

      if($widget){
        # Replace with new Widget Object excluding linkedEntities
        $payload.pages[$pageIndex].widgets[$widgetIndex] = [PSCustomObject]@{
          layout = $widget.layout
          rawConfiguration = $widget.rawConfiguration
          title = $widget.title
          visualization = $widget.visualization
        }
      }
    }
  }

  Return $payload
}

Function ConvertTo-NRDashboardUpdatePayload {
  Param (
    [Parameter (Mandatory = $true)]
    [PSCustomObject] $Config
  )

  # Deep clone the object
  $payload = $Config | ConvertTo-Json -Depth 50 | ConvertFrom-Json -Depth 50

  # Loop through each page and rebuild object
  for ($pageIndex = 0; $pageIndex -lt $payload.pages.count; $pageIndex++) {
    $page = $payload.pages[$pageIndex];

    if($page.guid -eq $PLACEHOLDER_GUID){
      $payload.pages[$pageIndex] = [PSCustomObject]@{
        name = $page.name
        widgets = $page.widgets
      }
    }

    for ($widgetIndex = 0; $widgetIndex -lt $payload.pages[$pageIndex].widgets.count; $widgetIndex++) {
      $widget = $payload.pages[$pageIndex].widgets[$widgetIndex]

      if($widget){
        # Replace with new Widget Object excluding linkedEntities
        if($widget.linkedEntities.guid -and $widget.linkedEntities.guid -ne $PLACEHOLDER_GUID){
          $payload.pages[$pageIndex].widgets[$widgetIndex] = [PSCustomObject]@{
            layout = $widget.layout
            linkedEntityGuids = $widget.linkedEntities[0].guid
            rawConfiguration = $widget.rawConfiguration
            title = $widget.title
            visualization = $widget.visualization
          }
        }else{
          $payload.pages[$pageIndex].widgets[$widgetIndex] = [PSCustomObject]@{
            layout = $widget.layout
            rawConfiguration = $widget.rawConfiguration
            title = $widget.title
            visualization = $widget.visualization
          }
        }
      }
    }
  }

  Return $payload
}