internal/functions/invoke-adoclassificationnodemigrationbytype.ps1


<#
    .SYNOPSIS
        Migrates classification nodes (Areas or Iterations) from source to target Azure DevOps organization by specified type.
         
    .DESCRIPTION
        Migrates either Area or Iteration nodes from a source Azure DevOps project to a target project,
        preserving the hierarchy structure and attributes (like start/finish dates for iterations).
         
    .PARAMETER SourceOrganization
        The name of the source Azure DevOps organization.
    .PARAMETER TargetOrganization
        The name of the target Azure DevOps organization.
    .PARAMETER SourceToken
        Personal Access Token for the source Azure DevOps organization with work item tracking permissions.
    .PARAMETER TargetToken
        Personal Access Token for the target Azure DevOps organization with work item tracking permissions.
    .PARAMETER SourceProjectName
        The name of the source Azure DevOps project.
    .PARAMETER TargetProjectName
        The name of the target Azure DevOps project.
    .PARAMETER StructureGroup
        The classification node type to migrate: 'Areas' or 'Iterations'.
    .PARAMETER ApiVersion
        The version of the Azure DevOps REST API to use. Default is '7.2-preview.2'.
    .EXAMPLE
        Invoke-ADOClassificationNodeMigrationByType -SourceOrganization "sourceorg" -TargetOrganization "targetorg" -SourceToken $sourcePat -TargetToken $targetPat -SourceProjectName "SourceProject" -TargetProjectName "TargetProject" -StructureGroup 'Areas' -ApiVersion '7.2-preview.2'
         
        Migrates all Area nodes from SourceProject to TargetProject.
         
    .EXAMPLE
        Invoke-ADOClassificationNodeMigrationByType -SourceOrganization "sourceorg" -TargetOrganization "targetorg" -SourceToken $sourcePat -TargetToken $targetPat -SourceProjectName "SourceProject" -TargetProjectName "TargetProject" -StructureGroup 'Areas' -ApiVersion '7.2-preview.2'
         
        Migrates all Area nodes from SourceProject to TargetProject.
         
    .NOTES
        This function is part of the ADO Tools module and adheres to the conventions used in
        the module for logging, error handling, and API interaction.
         
        Author: Oleksandr Nikolaiev (@onikolaiev)
#>


function Invoke-ADOClassificationNodeMigrationByType {
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param(
        [Parameter(Mandatory)][string]$SourceOrganization,
        [Parameter(Mandatory)][string]$TargetOrganization,
        [Parameter(Mandatory)][string]$SourceToken,
        [Parameter(Mandatory)][string]$TargetToken,
        [Parameter(Mandatory)][string]$SourceProjectName,
        [Parameter(Mandatory)][string]$TargetProjectName,
        [Parameter(Mandatory)][ValidateSet('Areas', 'Iterations')][string]$StructureGroup,
        [Parameter(Mandatory)][string]$ApiVersion
    )

    $migratedCount = 0
    $skippedCount = 0
    $errorCount = 0

    try {
        # Get source classification nodes (returns both Areas and Iterations)
        $sourceRoots = Get-ADOClassificationNodeRoot -Organization $SourceOrganization -Token $SourceToken -Project $SourceProjectName -ApiVersion $ApiVersion -Depth 99
        
        # Filter to get the specific structure group (Areas or Iterations)
        $structureTypeFilter = if ($StructureGroup -eq 'Areas') { 'area' } else { 'iteration' }
        $sourceRoot = $sourceRoots | Where-Object { $_.structureType -eq $structureTypeFilter } | Select-Object -First 1
        
        if (-not $sourceRoot -or -not $sourceRoot.children) {
            Write-PSFMessage -Level Verbose -Message "No $StructureGroup nodes found in source project."
            return @{ Migrated = 0; Skipped = 0; Errors = 0 }
        }

        # Get target classification nodes for comparison
        $targetRoots = Get-ADOClassificationNodeRoot -Organization $TargetOrganization -Token $TargetToken -Project $TargetProjectName -ApiVersion $ApiVersion -Depth 99
        $targetRoot = $targetRoots | Where-Object { $_.structureType -eq $structureTypeFilter } | Select-Object -First 1
        
        if (-not $targetRoot) {
            Write-PSFMessage -Level Warning -Message "No target $StructureGroup root found. This is unexpected."
            return @{ Migrated = 0; Skipped = 0; Errors = 1 }
        }

        # Migrate nodes recursively
        foreach ($sourceNode in $sourceRoot.children) {
            $result = Invoke-ADOClassificationNodeMigrationRecursive -SourceOrganization $SourceOrganization -TargetOrganization $TargetOrganization -SourceToken $SourceToken -TargetToken $TargetToken -TargetProjectName $TargetProjectName -SourceNode $sourceNode -TargetParentNodes $targetRoot.children -StructureGroup $StructureGroup -ApiVersion $ApiVersion
            $migratedCount += $result.Migrated
            $skippedCount += $result.Skipped
            $errorCount += $result.Errors
        }

    } catch {
        Write-PSFMessage -Level Error -Message "Failed to migrate $StructureGroup nodes: $($_.Exception.Message)"
        $errorCount++
    }

    return @{
        Migrated = $migratedCount
        Skipped = $skippedCount
        Errors = $errorCount
    }
}