functions/update-fscpsisvsource.ps1


<#
    .SYNOPSIS
        Installation of Nuget CLI
         
    .DESCRIPTION
        Download latest Nuget CLI
         
    .PARAMETER MetadataPath
        Path to the local Metadata folder
         
    .PARAMETER Url
        Url/Uri to zip file contains code/package/axmodel
         
    .PARAMETER FileName
        The name of the file should be downloaded by the url. Use if the url doesnt contain the filename.
         
    .EXAMPLE
        PS C:\> Update-FSCPSISVSource MetadataPath "C:\temp\PackagesLocalDirectory" -Url "https://ciellosarchive.blob.core.windows.net/test/Main-Extension-10.0.39_20240516.263.zip?sv=2023-01-03&st=2024-05-21T14%3A26%3A41Z&se=2034-05-22T14%3A26%3A00Z&sr=b&sp=r&sig=W%2FbS1bQrr59i%2FBSHWsftkfNsE1HvFXTrICwZSFiUItg%3D""
         
        This will update the local metadata with the source from the downloaded zip archive.
         
    .NOTES
        Author: Oleksandr Nikolaiev (@onikolaiev)
#>

function Update-FSCPSISVSource {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param (
        [Parameter(HelpMessage = "The path to the metadata", Mandatory = $true)]
        [string] $MetadataPath,
        [Parameter(HelpMessage = "The url to the file contains the D365FSC axmodel/modelSourceCode/deployablePackage", Mandatory = $true)]
        [string] $Url,
        [Parameter(HelpMessage = "The name of the downloading file", Mandatory = $false)]
        [string] $FileName
    )
    begin
    {
        try 
        {
            if([string]::IsNullOrEmpty($FileName))
            {
                $_tmpUrlPart = ([uri]$Url).Segments[-1]
                if($_tmpUrlPart.Contains("."))
                {
                    $FileName = $_tmpUrlPart
                }            
            }

            if([string]::IsNullOrEmpty($FileName))
            {
                throw "FileName is empty or cannot be parsed from the url. Please specify the FileName parameter."
            }
            
            if( (-not $FileName.Contains(".zip")) -and (-not $FileName.Contains(".axmodel")) )
            {
                throw "Only a zip or axmodel file can be processed."
            }

            if(Test-Path "$($MetadataPath)/PackagesLocalDirectory")
            {
                $MetadataPath = (Join-Path $($MetadataPath) "/PackagesLocalDirectory")
            }
            elseif(Test-Path "$($MetadataPath)/Metadata")
            {
                $MetadataPath = (Join-Path $($MetadataPath) "/Metadata")
            }

            #$script:DefaultTempPath
            $tempPath = Join-Path -Path $script:DefaultTempPath -ChildPath "updateSource"

            #Cleanup existing temp folder
            Remove-Item -Path $tempPath -Recurse -Force -ErrorAction SilentlyContinue -Confirm:$false        
            $downloadPath = Join-Path -Path $tempPath -ChildPath $fileName
            if (-not (Test-PathExists -Path $tempPath -Type Container -Create)) { return }
        }
        catch {
            Write-PSFMessage -Level Host -Message "Error: " -Exception $PSItem.Exception
            Stop-PSFFunction -Message "Stopping because of errors" -EnableException $true
            return
        }
    }
    process
    {
        if (Test-PSFFunctionInterrupt) { return }
        $helperPath = Join-Path -Path $($Script:ModuleRoot) -ChildPath "\internal\scripts\helpers.ps1" -Resolve
        . ($helperPath)    
        try {
            
            Write-PSFMessage -Level Important -Message "Downloading $($FileName)" -Target $downloadPath
            [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
            #[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

            Write-PSFMessage -Level Important -Message "Source: $Url"
            Write-PSFMessage -Level Important -Message "Destination $downloadPath"

            Start-BitsTransfer -Source $Url -Destination $downloadPath


            #check is archive contains few archives
            $packagesPaths = [System.Collections.ArrayList]@()
            $sourceCodePaths = [System.Collections.ArrayList]@()
            $axmodelsPaths = [System.Collections.ArrayList]@()

            if($downloadPath.EndsWith(".zip"))
            {            
                Unblock-File $downloadPath
                Expand-7zipArchive -Path $downloadPath -DestinationPath "$tempPath/archives"

                $ispackage = Get-ChildItem -Path "$tempPath/archives" -Filter 'AXUpdateInstaller.exe' -ErrorAction SilentlyContinue -Force    

                if($ispackage)
                {
                    $null = $packagesPaths.Add($downloadPath)
                }
                else
                {   
                    Get-ChildItem "$tempPath/archives" -Filter '*.zip' -Recurse  -ErrorAction SilentlyContinue -Force | ForEach-Object{
                        $archive = $_.FullName
                        
                        $tmpArchivePath = Join-Path "$tempPath/archives" $_.BaseName
                        
                        Unblock-File $archive
                        Expand-7zipArchive -Path $archive -DestinationPath $tmpArchivePath
                        $ispackage = Get-ChildItem -Path $tmpArchivePath -Filter 'AXUpdateInstaller.exe' -Recurse -ErrorAction SilentlyContinue -Force    
    
                        if($ispackage)
                        {
                            $null = $packagesPaths.Add($_.FullName)
                        }
                        else
                        {   
                            if($_.FullName -notlike "*dynamicsax-*.zip")
                            {
                                $null = $sourceCodePaths.Add($_.FullName)
                            }
                        }
                            
                    } 
                    #check axmodel files inside and add to list if found
                    Get-ChildItem "$tempPath/archives" -Filter '*.axmodel' -Recurse -ErrorAction SilentlyContinue -Force | ForEach-Object {
                        $null = $axmodelsPaths.Add($_.FullName)
                    }           
                } 
            }
            if($downloadPath.EndsWith(".axmodel"))
            {
                $null = $axmodelsPaths.Add($_.FullName)
            }

            foreach($package in $packagesPaths)
            {
                try {
                    $package = Get-ChildItem $package
                    Write-PSFMessage -Level Important -Message "The package $($package.BaseName) importing..."
                    $tmpPackagePath = Join-Path "$tempPath/packages" $package.BaseName
                    Unblock-File $package
                    Expand-7zipArchive -Path $package -DestinationPath $tmpPackagePath
                    $models = Get-ChildItem -Path $tmpPackagePath -Filter "dynamicsax-*.zip" -Recurse -ErrorAction SilentlyContinue -Force
                    foreach($model in $models)
                    {            
                        Write-PSFMessage -Level Important -Message "$($model.BaseName) processing..."
                        $zipFile = [IO.Compression.ZipFile]::OpenRead($model.FullName)
                        $zipFile.Entries | Where-Object {$_.FullName.Contains(".xref")} | ForEach-Object{
                            $modelName = $_.Name.Replace(".xref", "")
                            $targetModelPath = (Join-Path $MetadataPath "$modelName/")   
                            if(Test-Path $targetModelPath)
                            {
                                Remove-Item $targetModelPath -Recurse -Force
                            }      
                            Write-PSFMessage -Level Important -Message "'$($model.FullName)' to the $($targetModelPath)..."
                            Expand-7zipArchive -Path $model.FullName -DestinationPath $targetModelPath
                        }            
                        $zipFile.Dispose()
                    }
                    Write-PSFMessage -Level Important -Message "The package $($package) imported"
                }
                catch {
                    Write-PSFMessage -Level Host -Message "Error:" -Exception $PSItem.Exception
                    Write-PSFMessage -Level Important -Message "The package $($package) is not imported"
                }               
            }

            if(($axmodelsPaths.Count -gt 0) -and ($PSVersionTable.PSVersion.Major -gt 5)) {
                Write-PSFMessage -Level Warning -Message "The axmodel cannot be imported. Current PS version is $($PSVersionTable.PSVersion). The latest PS major version acceptable to import the axmodel is 5."
            }
            else {

                $PlatformVersion = (Get-FSCPSVersionInfo -Version 10.0.38).data.PlatformVersion
                $nugetsPath = Join-Path $tempPath "NuGets"
                $compilerNugetPath = Join-Path $nugetsPath "Microsoft.Dynamics.AX.Platform.CompilerPackage.$PlatformVersion.nupkg"
                $compilerPath = Join-Path $tempPath "Microsoft.Dynamics.AX.Platform.CompilerPackage.$PlatformVersion"

                $null = Test-PathExists -Path $compilerPath -Type Container -Create
                $null = Test-PathExists -Path $nugetsPath -Type Container -Create
                Write-PSFMessage -Level Important -Message "The $PlatformVersion Platform Version used."
                Get-FSCPSNuget -Version $PlatformVersion -Type PlatformCompilerPackage -Path $nugetsPath
                Write-PSFMessage -Level Important -Message "The PlatformCompiler NuGet were downloaded at $nugetsPath."
                Expand-7zipArchive -Path $compilerNugetPath -DestinationPath $compilerPath
                $curLocation = Get-Location
                Set-Location $compilerPath
                                
                try {
                    $miscPath = Join-Path -Path $($Script:ModuleRoot) -ChildPath "\internal\misc"
                    Copy-Item -Path "$miscPath\Microsoft.TeamFoundation.Client.dll" -Destination $compilerPath -Force
                    Copy-Item -Path "$miscPath\Microsoft.TeamFoundation.Common.dll" -Destination $compilerPath -Force               
                    Copy-Item -Path "$miscPath\Microsoft.TeamFoundation.Diff.dll" -Destination $compilerPath -Force
                    Copy-Item -Path "$miscPath\Microsoft.TeamFoundation.VersionControl.Client.dll" -Destination $compilerPath -Force
                    Copy-Item -Path "$miscPath\Microsoft.TeamFoundation.VersionControl.Common.dll" -Destination $compilerPath -Force
                }
                catch {
                    Write-PSFMessage -Level Important -Message $_.Exception.Message
                }

                foreach($axModel in $axmodelsPaths)
                {
                    try {
                        Write-PSFMessage -Level Important -Message "The axmodel $($axModel) importing..."
                        Enable-D365Exception
                        
                        #Import-D365Model -Path $axModel -MetaDataDir $MetadataPath -BinDir $compilerPath -Replace

                        Invoke-ModelUtil -Path $axModel -MetaDataDir $MetadataPath -BinDir $compilerPath -Command Replace


                        Disable-D365Exception
                        Write-PSFMessage -Level Important -Message "The axmodel $($axModel) imported."
                    }
                    catch {
                        Disable-D365Exception
                        Write-PSFMessage -Level Host -Message "Error:" -Exception $PSItem.Exception
                        Write-PSFMessage -Level Important -Message "The axmodel $($axModel) is not imported."
                    }                   
                }
                Set-Location $curLocation
            }            

            foreach($sourceCode in $sourceCodePaths)
            {
                try {
                    Write-PSFMessage -Level Important -Message "The source code $($sourceCode) importing..."
                    $zipFile = [IO.Compression.ZipFile]::OpenRead($sourceCode)
                        $zipFile.Entries | Where-Object {$_.FullName.Contains(".xref")} | ForEach-Object{
                            $modelName = $_.Name.Replace(".xref", "")
                            $targetModelPath = (Join-Path $MetadataPath "$modelName/")   
                            Remove-Item $targetModelPath -Recurse -Force 
                            Expand-7zipArchive -Path $($sourceCode) -DestinationPath $targetModelPath
                        }            
                        $zipFile.Dispose()
                    Write-PSFMessage -Level Important -Message "The source code $($sourceCode) imported"
                }
                catch {
                    Write-PSFMessage -Level Host -Message "Error:" -Exception $PSItem.Exception
                    Write-PSFMessage -Level Important -Message "The source code $($sourceCode) is not imported"
                }
            }

            ## Cleanup XppMetadata
            Get-ChildItem -Path $MetadataPath -Directory -Filter "*XppMetadata" -Recurse | ForEach-Object { Remove-Item -Path $_.FullName -Recurse -Force }
        }
        catch {
            Write-PSFMessage -Level Host -Message "Error:" -Exception $PSItem.Exception
            Stop-PSFFunction -Message "Stopping because of errors" -EnableException $true
            return
        }
    }
    end{

    } 
}