Public/Datascripts.ps1

## TM Datascripts

$DataTypeConfig = @{
    MatchPattern             = '\.(json)|(csv)|(xlsx?)'
    BadExtensionErrorMessage = 'The file you attempted to upload has an invalid type. Valid types are JSON, CSV, and Excel files.'
    FileNotFoundError        = 'The file you specified cannot be found.'
}
Function Get-TMDatascript {
    [alias("Get-TMETLScript")]
    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param(
        [Parameter(Mandatory = $false)]
        [PSObject]$TMSession = 'Default',

        [Parameter(Mandatory = $false)]
        [String]$Name,

        [Parameter(Mandatory = $false)]
        [String[]]$ProviderName,

        [Parameter(Mandatory = $false)]
        [Switch]$ResetIDs,

        [Parameter(Mandatory = $false, ParameterSetName = 'SaveCode')]
        [String]$SaveCodePath,

        [Parameter(Mandatory = $false, ParameterSetName = 'SaveCode')]
        [Switch]$Passthru
    )

    ## Get Session Configuration
    $TMSession = Get-TMSession $TMSession

    #Honor SSL Settings
    $TMCertSettings = $TMSession.AllowInsecureSSL ? @{SkipCertificateCheck = $true } : @{SkipCertificateCheck = $false }

    # Format the uri
    $Instance = $TMSession.TMServer.Replace('/tdstm', '').Replace('https://', '').Replace('http://', '')
    $uri = "https://$instance/tdstm/ws/dataScript/list?status=ALL"

    try {
        $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings
    } catch {
        return $_
    }

    if ($response.StatusCode -in @(200, 204)) {
        $Result = ($response.Content | ConvertFrom-Json).data
        if ($Result.Count -eq 0) { return $false }
    } else {
        return "Unable to collect Datascripts."
    }

    ## Get each Datascript's Source Code in the list
    for ($i = 0; $i -lt $Result.Count; $i++) {

        $uri = "https://"
        $uri += $instance
        $uri += '/tdstm/ws/dataScript/' + $Result[$i].id
        try {
            $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings
        } catch {
            return $_
        }

        if ($response.StatusCode -eq 200) {
            $Result[$i] = ($response.Content | ConvertFrom-Json).data.datascript
        } else {
            return "Unable to collect Datascripts."
        }
    }

    if ($ResetIDs) {
        for ($i = 0; $i -lt $Result.Count; $i++) {
            $Result[$i].id = $null
            $Result[$i].provider.id = $null
        }
    }

    ## Return the details -- Filter based on passed parameters
    if ($ProviderName) {
        $Result = $Result | Where-Object { $_.provider.name -in $ProviderName }
    } elseif ($Name) {
        $Result = $Result | Where-Object { $_.name -eq $Name }
    }

    ## Save the Code Files to a folder
    if ($SaveCodePath) {

        ## Save Each of the Script Source Data
        foreach ($Item in $Result) {

            ## Get a FileName safe version of the Provider Name
            $SafeProviderName = Get-FilenameSafeString -String $Item.provider.name
            $SafeScriptName = Get-FilenameSafeString -String $Item.name

            $Item.semVer = ($Item.semVer.trim().Length -gt 0) ? $Item.semVer.trim() : '1.0.0'
            $SafeScriptName += " - $($Item.semVer)"

            ## Create the Provider Action Folder path
            $ProviderPath = Join-Path $SaveCodePath $SafeProviderName
            Test-FolderPath -FolderPath $ProviderPath

            ## Create a File ame for the Action
            $ProviderScriptConfig = Join-Path $ProviderPath ($SafeScriptName + '.json')
            $ProviderScriptPath = Join-Path $ProviderPath ($SafeScriptName + '.groovy')

            ## Copy the code from the Datascript
            $DatascriptCode = $Item.etlSourceCode ? $Item.etlSourceCode.toString() : ""

            ## Remove the ETL Code fro the Item, so it can be converted to Json
            $Item.PSObject.Properties.Remove('etlSourceCode')

            ## Start Writing the Content of the Script (Force to overwrite any existing files)
            Set-Content -Path $ProviderScriptConfig -Force -Value (ConvertTo-Json -InputObject $Item -Depth 5)
            Set-Content -Path $ProviderScriptPath -Force -Value $DatascriptCode
        }
    }

    if ($Passthru -or !$SaveCodePath) {
        return $Result
    }
}

Function New-TMDatascript {
    [alias("New-TMETLScript")]
    param(
        [Parameter(Mandatory = $false)][PSObject]$TMSession = 'Default',
        [Alias("ETLScript")]
        [Parameter(Mandatory = $true)][PSObject]$Datascript,
        [Parameter(Mandatory = $false)][switch]$Update

    )
    ## Get Session Configuration
    $TMSession = Get-TMSession $TMSession

    #Honor SSL Settings
    $TMCertSettings = @{SkipCertificateCheck = $TMSession.AllowInsecureSSL }

    ## Look for an existing Script
    $DatascriptId = 0
    $DatascriptName = $Datascript.name
    $ExistingDatascript = Get-TMDatascript -Name $DatascriptName -TMSession $TMSession

    ## If the Script should not be updated, return it
    if ($ExistingDatascript) {

        $DataScriptId = $ExistingDatascript.id

        ## End the function if there is not an update to be made
        if ($Update) {

            $PostBodyJSON = @{
                name                = $Datascript.name ?? $ExistingDatascript.name
                description         = $Datascript.description ?? $ExistingDatascript.description
                mode                = $Datascript.mode ?? $ExistingDatascript.mode ?? 'IMPORT'
                semVer              = $Datascript.semVer ?? $ExistingDatascript.semVer ?? ''
                branchId            = $Datascript.branchId ?? $ExistingDatascript.branchId ?? 0
                branchedFromId      = $Datascript.branchedFromId ?? $ExistingDatascript.branchedFromId ?? 0
                branchedFromVersion = $Datascript.branchedFromVersion ?? $ExistingDatascript.branchedFromVersion ?? ''
                sortVersion         = $Datascript.sortVersion ?? $ExistingDatascript.sortVersion ?? ''
                status              = $Datascript.status ?? $ExistingDatascript.status ?? 'Development'
                dataSourceType      = $Datascript.dataSourceType ?? $ExistingDatascript.dataSourceType ?? 'External'
                contextDomains      = $Datascript.contextDomains ?? $ExistingDatascript.contextDomains ?? @()
                affectedDomains     = $Datascript.affectedDomains ?? $ExistingDatascript.affectedDomains ?? @()
                formSpec            = $Datascript.formSpec ?? $ExistingDatascript.formSpec ?? @()
                formModel           = $Datascript.formModel ?? $ExistingDatascript.formModel ?? @()
                type                = $Datascript.type ?? $ExistingDatascript.type ?? "Transformation"
                provider            = $ProviderID
                fromExtension       = $true

            } | ConvertTo-Json -Compress -Depth 100

            $uri = "https://"
            $uri += $TMSession.TMServer
            $uri += '/tdstm/ws/dataScript'

            ## Post the Datascript Object to the server
            Set-TMHeaderContentType -ContentType JSON -TMSession $TMSession
            $response = Invoke-WebRequest -Method 'POST' -Uri $uri -WebSession $TMSession.TMWebSession -Body $PostBodyJSON @TMCertSettings
            if ($response.StatusCode -eq 200) {
                $responseContent = $response.Content | ConvertFrom-Json
                if ($responseContent.status -eq "success") {

                    ## Now that the shell exists, get it's ID
                    $DatascriptID = $responseContent.data.datascript.id
                    $UpdatedDatascript = $responseContent.data.datascript
                }
            }


        } else {

            ## If $PassThru is present, return the Existing Datascript script.
            if ($PassThru) {
                return $Datascript
            } else {
                return
            }
        }
    } else {

        ## Lookup Provider ID
        if ([String]::IsNullOrWhiteSpace($Datascript.provider.name)) {
            Write-Error -Message "Provider name is blank for Datascript: $($Datascript.name)"
            return
        }
        $ProviderID = (Get-TMProvider -Name $Datascript.provider.name -TMSession $TMSession).id
        if (!$ProviderID) {
            # Create the provider if it doesn't exist
            $NowFormatted = (Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ' -AsUTC).ToString()
            $Provider = [PSCustomObject]@{
                id          = $null
                name        = $Datascript.provider.name
                description = ""
                comment     = ""
                dateCreated = $NowFormatted
                lastUpdated = $NowFormatted
            }
            $ProviderID = (New-TMProvider -Provider $Provider -PassThru -TMSession $TMSession).id
        }

        try {

            ## There is not an existing Datascript, Create the basic
            ## Create the shell Datascript object, which must be created first
            $PostBodyJSON = @{
                name                = $Datascript.name
                description         = $Datascript.description
                mode                = $Datascript.mode
                provider            = $ProviderID
                fromExtension       = $true
                semVer              = $Datascript.semVer ?? ''
                branchId            = $Datascript.branchId ?? 0
                branchedFromId      = $Datascript.branchedFromId ?? 0
                branchedFromVersion = $Datascript.branchedFromVersion ?? ''
                sortVersion         = $Datascript.sortVersion ?? ''
                status              = $Datascript.status ?? 'Development'
                dataSourceType      = $Datascript.dataSourceType ?? 'External'
                contextDomains      = $Datascript.contextDomains ?? @()
                affectedDomains     = $Datascript.affectedDomains ?? @()
                formSpec            = $Datascript.formSpec ?? @()
                formModel           = $Datascript.formModel ?? @()
                type                = $Datascript.type ?? "Transformation"

            } | ConvertTo-Json -Compress -Depth 100

            $uri = "https://"
            $uri += $TMSession.TMServer
            $uri += '/tdstm/ws/dataScript'

            ## Post the Datascript Object to the server
            Set-TMHeaderContentType -ContentType JSON -TMSession $TMSession
            $response = Invoke-WebRequest -Method 'POST' -Uri $uri -WebSession $TMSession.TMWebSession -Body $PostBodyJSON @TMCertSettings
            if ($response.StatusCode -eq 200) {
                $responseContent = $response.Content | ConvertFrom-Json
                if ($responseContent.status -eq "success") {

                    ## Now that the shell exists, get it's ID
                    $DatascriptID = $responseContent.data.datascript.id
                    $UpdatedDatascript = $responseContent.data.datascript
                }
            }
        } catch {
            Write-Host "Unable to create Datascript."
            return $_
        }

    }

    ## Now that we have an ID for the Datascript, send the source code content
    $PostBodyJSON = @{
        id     = $DatascriptID
        script = $Datascript.etlSourceCode.toString()
    } | ConvertTo-Json -Depth 100

    try {

        ## Post the Source to be saved for this script.
        $uri = "https://"
        $uri += $TMSession.TMServer
        $uri += '/tdstm/ws/dataScript/saveScript'
        $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSession.TMWebSession -Body $PostBodyJSON @TMCertSettings
        if ($response.StatusCode -eq 200) {
            $responseContent = $response.Content | ConvertFrom-Json
            if ($responseContent.status -eq 'success') {
                $DatascriptID = $responseContent.data.datascript.id
                $UpdatedDatascript = $responseContent.data.datascript
            }
        } elseif ($response.StatusCode -eq 204) {
            Write-Host "Updating the source code responded with a 204..." -ForegroundColor Yellow
        } else {
            throw "There was an issue saving the source code to the datascript"
        }
    } catch {
        Write-Host "Unable to save Datascript Source to server"
        return $_
    }

    ## Post updates to the Form Spec (version 6.5.0 and higher)
    if ($Datascript.formSpec -and ([System.version]$TMSession.TMVersion -ge [System.Version]"6.5.0")) {
        $PostBodyJSON = @{
            spec  = $Datascript.formSpec ?? $ExistingDatascript.formSpec ?? @()
            model = $Datascript.formModel ?? $ExistingDatascript.formModel ?? @()
        } | ConvertTo-Json -Depth 100

        try {
            ## Post the Source to be saved for this script.
            $uri = "https://"
            $uri += $TMSession.TMServer
            $uri += "/tdstm/ws/dataScript/$($DatascriptID)/form"
            $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSession.TMWebSession -Body $PostBodyJSON @TMCertSettings
            if ($response.StatusCode -eq 200) {
                $responseContent = $response.Content | ConvertFrom-Json
                if ($responseContent.status -eq 'success') {
                    if ($PassThru) {
                        $UpdatedDatascript = $responseContent.data.dataScript
                    }
                }
            } elseif ($response.StatusCode -eq 204) {
                ##
            } else {
                throw "There was an issue saving the source code to the datascript"
            }
        } catch {
            Write-Host "Unable to create Datascript."
            return $_
        }
    }

    ## Return the object if required
    if ($Passthru) {
        return $UpdatedDatascript
    }
}

Function Invoke-TMDatascript {
    [alias("Invoke-TMETLScript")]
    param(
        [Parameter(Mandatory = $false)][PSObject]$TMSession = 'Default',
        [Alias("ETLScriptName")]
        [Parameter(Mandatory = $true)][String]$DatascriptName,
        [Parameter(Mandatory = $false)][bool]$QueueBatches = $false,
        [Parameter(Mandatory = $false)][bool]$MonitorBatches = $false,
        [Parameter(Mandatory = $false)]$Data,
        [Parameter(Mandatory = $false)][String]$FilePath,
        [Parameter(Mandatory = $false)][String]$FileName,
        [Parameter(Mandatory = $false)][Int16]$ActivityId,
        [Parameter(Mandatory = $false)][Int16]$ParentActivityId = -1,
        [Parameter(Mandatory = $false)][switch]$isAutoPost,
        [Switch]$SendNotification
    )

    ## Get Session Configuration
    $TMSession = Get-TMSession $TMSession

    if ($FilePath) {
        if ( -not (Test-Path -Path $FilePath -PathType Leaf) ) {
            throw $DataTypeConfig.FileNotFoundError
        }

        ## If a File Path was passed and there is no data, Read the file
        if ( -not $Data ) {
            $Data = Get-Content $FilePath -Raw
            $FileName = (Get-Item $FilePath).Name
        }

        ## If a File Path was passed and there is no File Name, get the file name
        if ( -not $FileName ) {
            $FileName = (Get-Item $FilePath).Name
        }
    }

    #Honor SSL Settings
    $TMCertSettings = @{SkipCertificateCheck = $TMSession.AllowInsecureSSL }

    if ($ActivityId) {
        ## Parent ID is only used on the root 'TransitionManager Data Import' Activity
        ## ParentID + 1 = Transform Data
        ## ParentID + 2 = Import Batches
        ## ParentID + 3 + n for each batch

        ## Add the Datascript Processing Progress Indicators
        $ProgressIndicators = @()
        $ProgressIndicators += @{ Id = $ActivityId; Activity = 'TransitionManager Data Import'; ParentId = $ParentActivityId }
        $ProgressIndicators += @{ Id = ($ActivityId + 1); Activity = 'Datascript Data Transformation'; ParentId = $ActivityId }
        $ProgressIndicators += @{ Id = ($ActivityId + 2); Activity = 'Import Batches'; ParentId = $ActivityId }
        $ProgressIndicators += @{ Id = ($ActivityId + 3); Activity = 'Monitor Batches'; ParentId = $ActivityId }

        #Write Progress Indicators for each in the Array
        $ProgressIndicators | ForEach-Object {
            Write-Progress @_ -CurrentOperation 'Queued' -PercentComplete 0
        }
    }

    ## Fix the Server URL
    $Instance = $TMSession.TMServer.Replace('/tdstm', '')
    $instance = $instance.Replace('https://', '')
    $instance = $instance.Replace('http://', '')

    $Boundary = '----------------------------540299933173025267350719'

    ## First Test to see if the Datascript Script exists or not
    if ($ActivityId) {

        Write-Progress -Id ($ActivityId + 1) -ParentId $ActivityId -Activity 'Datascript Data Transformation' -CurrentOperation 'Validating Datascript Script' -Status 'Confirming Datascript Script exists in TransitionManager' -PercentComplete 5
    }
    Write-Host 'Validating Datascript Script: ' -NoNewline
    Write-Host $DatascriptName -ForegroundColor Yellow

    $Datascript = Get-TMDatascript -TMSession $TMSession -Name $DatascriptName
    if (-Not $Datascript) { Throw  'The Datascript [' + $DatascriptName + '] does not exist' }

    ## If there is a file to upload to storage, upload it
    if ($FileName) {

        ## Build the Data Post
        $uri = "https://"
        $uri += $instance
        $uri += "/tdstm/ws/fileSystem/uploadFileETLAssetImport"

        ## Determine the data type of the file to be uploaded
        if ($FilePath) {
            $DataType = (Get-Item $FilePath).Extension
            if ($DataType -and $DataType -notmatch $DataTypeConfig.MatchPattern) {
                throw $DataTypeConfig.BadExtensionErrorMessage
            }
        } else {
            $DataType = [string]::Empty
        }

        ## FileName
        if (-not $FileName) {
            $FileName = ('DataForETL_' + (Get-Date -Format FileDateTime ) + $DataType)
        }

        ## Construct the PostBody and Form Data for the request
        $CRLF = "`r`n";
        $PostBody = ("-----------------------------$boundary",
            'Content-Disposition: form-data; name="uploadType"',
            '',
            'assetImport',
            "-----------------------------$boundary",
            ''
        ) -join $CRLF

        $FileUpload = @{
            Form = @{
                uploadType = 'assetImport'
                file       = Get-Item -Path $FilePath
                fileName   = $FileName
            }
        }
        Write-Host "Uploading File: " -NoNewline
        Write-Host $FileName -ForegroundColor Yellow

        ## Upload the Data File
        if ($ActivityId) {
            Write-Progress -Id ($ActivityId + 1) -ParentId $ActivityId -Activity 'Datascript Data Transformation' -CurrentOperation 'Uploading Data' -Status ('Uploading ' + $FileName) -PercentComplete 5
        }

        try {
            ## If the file is being uploaded, append the fileupload to the command
            if ($FileName) {
                $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSession.TMWebSession -ContentType ('multipart/form-data; boundary=---------------------------' + $boundary) @TMCertSettings @FileUpload

            } else {

                $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSession.TMWebSession -ContentType ('multipart/form-data; boundary=---------------------------' + $boundary) @TMCertSettings
            }

        } catch {
            throw $_
        }
        if ($response.StatusCode -eq 200) {
            $responseContent = $response.Content | ConvertFrom-Json
            if ($responseContent.status -eq "success") {
                $DatascriptdataFileName = $responseContent.data.filename
            } else {
                Throw "Unable to upload data to TM Datascript pre-transform storage."
            }
        }
    }

    ## With the file uploaded, Initiate the Datascript script on the server
    $uri = "https://"
    $uri += $instance
    $uri += "/tdstm/ws/assetImport/initiateTransformData"
    $uri += "?dataScriptId=" + $Datascript.id

    ## Attach the file if there was one
    if ($Filename) {
        $uri += "&filename=" + $DatascriptdataFileName
        $uri += "&originalFilename=" + $FileName
    }

    ## Deal with Version specific items
    $uri += "&sendNotification=" + $SendNotification.IsPresent.toString().ToLower()
    if ($isAutoPost.IsPresent) {
        $uri += '&isAutoPost=true'
    }

    Set-TMHeaderContentType -ContentType JSON -TMSession $TMSession

    ## Post the data starting the Datascript process. A progress key is provided. This progress key is what can be used to poll for the status of the Datascript script
    if ($ActivityId) {
        Write-Progress -Id ($ActivityId + 1) -ParentId $ActivityId -Activity 'Datascript Data Transformation' -CurrentOperation 'Starting Datascript Transformation' -PercentComplete 5
    }
    Write-Host 'TransitionManager Data Import: ' -NoNewline
    Write-Host 'Starting Datascript Transformation' -ForegroundColor Yellow

    $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings
    if ($response.StatusCode -eq 200) {
        $responseContent = $response.Content | ConvertFrom-Json
        if ($responseContent.status -eq "success") {
            $DatascriptProgressKey = $responseContent.data.progressKey
        } else {
            throw $responseContent.errors
        }
    } else {
        Throw $response
    }


    ## Supply Progress from Datascript Transformation Progress
    if ($ActivityId) {
        Write-Progress -Id $($ActivityId + 1) -Activity 'Datascript Data Transformation' -CurrentOperation 'Running Datascript Transformation' -PercentComplete 0 -ParentId $ActivityId
    }

    ## The Datascript is underway, Setup a progress URL
    $uri = "https://"
    $uri += $instance
    $uri += "/tdstm/ws/progress/" + $DatascriptProgressKey
    Set-TMHeaderContentType -ContentType JSON -TMSession $TMSession

    ## Poll for the status of the Datascript engine
    ## TODO: This should be converted to a function for polling the job engine. It's nearly dupilcated now in the Import Batch watching.
    $Completed = $false
    ## $ReportedLogItems = [System.Collections.ArrayList]@()
    while ($Completed -eq $false) {

        ## Post the data starting the Datascript process. A progress key is provided. This progress key is what can be used to poll for the status of the Datascript script
        $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings
        if ($response.StatusCode -eq 200) {
            $responseContent = $response.Content | ConvertFrom-Json
            if ($responseContent.status -eq "success") {
                $DatascriptProgress = $responseContent.data

                switch ($DatascriptProgress.status) {
                    "Queued" {

                        $CurrentOperation = 'Datascript Queued'
                        $Status = 'Queued'
                        $ProgressString = 'Status - Queued: ' + $DatascriptProgress.percentComp + '%'
                        $PercentComplete = $DatascriptProgress.percentComp
                        $SleepSeconds = 2
                        Break

                    }
                    "Pending" {
                        $CurrentOperation = 'Datascript Pending'
                        $Status = 'Pending'
                        $ProgressString = 'Status - Pending: ' + $DatascriptProgress.percentComp + '%'
                        $PercentComplete = $DatascriptProgress.percentComp
                        $SleepSeconds = 2
                        Break
                    }
                    "RUNNING" {
                        $CurrentOperation = 'Datascript Running'
                        $Status = 'Transforming Data'
                        $ProgressString = 'Status - Running: ' + $DatascriptProgress.percentComp + '%'
                        $PercentComplete = $DatascriptProgress.percentComp -gt 99 ? 99 : $DatascriptProgress.percentComp
                        $SleepSeconds = 2
                        Break
                    }
                    "COMPLETED" {
                        # $DatascriptOutputKey = $DatascriptProgress.detail
                        $CurrentOperation = 'Datascript Processing Complete'
                        $Status = 'Creating Import Batches'
                        $Completed = $true
                        $SleepSeconds = 0
                        $PercentComplete = 99
                        $ProgressString = "Status - Datascript Processing Complete, Creating Import Batches."
                        Break
                    }
                    "Failed" {
                        $CurrentOperation = 'Failed'
                        $Status = $DatascriptProgress.status
                        Write-Host "Datascript Processing Failed "$DatascriptProgress.detail
                        Throw $DatascriptProgress.detail
                    }
                    Default {
                        $CurrentOperation = 'State Unknown'
                        $Status = 'Unknown. Sleeping to try again.'
                        $ProgressString = "Unknown Status: " + $DatascriptProgress.status
                        $PercentComplete = 99
                        $SleepSeconds = 2
                        Break
                    }
                }

                ## Notify the user of the Datascript Progress
                if ($ActivityId) {
                    Write-Progress -Id ($ActivityId + 1) `
                        -ParentId $ActivityId `
                        -Activity 'Datascript Data Transformation' `
                        -CurrentOperation $CurrentOperation `
                        -Status $Status `
                        -PercentComplete ($PercentComplete ?? 0)
                } else {
                    Write-Host $ProgressString
                }
                Start-Sleep -Seconds $SleepSeconds
            }
        }
    }

    ## Update Progress and transition to Activity + 2
    Write-Progress -Id ($ActivityId + 1) -ParentId $ActivityId -Activity 'Datascript Data Transformation' -CurrentOperation 'Transformation Complete' -PercentComplete 100 -Completed
    Write-Progress -Id ($ActivityId + 2) -ParentId $ActivityId -Activity 'Importing Batches' -CurrentOperation 'Beginning Import' -PercentComplete 5

    Write-Host "Datascript Transformation is complete, Loading to Batch Import"

    ## TODO, this is where a Job Log could be pulled. With the DatascriptOutputKey now provided, the API like this could be used:
    ## https://tmad60.transitionmanager.net/tdstm/ws/assetImport/viewData?filename=DatascriptOutputData_BSItLiP036AOsI0cr8e1g2WmipbdtN0c.json and
    ## https://tmad60.transitionmanager.net/tdstm/ws/assetImport/viewData?filename=DatascriptOutputData_BSItLiP036AOsI0cr8e1g2WmipbdtN0c_Application.json
    ## Check the Datascript Script Editor Web Dev tools to see more

    ## Ensure the Batches loaded and the job is finished
    ## The Datascript is underway, Setup a progress URL
    $uri = "https://"
    $uri += $instance
    $uri += "/tdstm/ws/progress/" + $DatascriptProgressKey
    Set-TMHeaderContentType -ContentType JSON -TMSession $TMSession

    ## Poll for the status of the Datascript engine
    ## TODO: This should be converted to a function for polling the job engine. It's nearly dupilcated now in the Import Batch watching.
    $Completed = $false
    while ($Completed -eq $false) {

        ## Post the data starting the Datascript process. A progress key is provided. This progress key is what can be used to poll for the status of the Datascript script
        $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings
        if ($response.StatusCode -eq 200) {
            $responseContent = $response.Content | ConvertFrom-Json
            if ($responseContent.status -eq "success") {
                $DatascriptProgress = $responseContent.data

                switch ($DatascriptProgress.status) {
                    "Queued" {
                        $CurrentOperation = 'Loading Batches Queued'
                        $Status = 'Queued'
                        $ProgressString = 'Status - Queued: ' + $DatascriptProgress.percentComp + '%'
                        $PercentComplete = $DatascriptProgress.percentComp
                        $SleepSeconds = 2
                        Break
                    }
                    "Pending" {
                        $CurrentOperation = 'Loading Batches Pending'
                        $Status = 'Pending Batch Loading'
                        $ProgressString = 'Status - Pending: ' + $DatascriptProgress.percentComp + '%'
                        $PercentComplete = $DatascriptProgress.percentComp
                        $SleepSeconds = 2
                        Break
                    }
                    "RUNNING" {
                        $CurrentOperation = 'Loading Batches'
                        $Status = 'Loading Batches'
                        $ProgressString = 'Status - Running: ' + $DatascriptProgress.percentComp + '%'
                        $PercentComplete = $DatascriptProgress.percentComp
                        $SleepSeconds = 2
                        Break
                    }
                    "COMPLETED" {
                        $BatchGroupGuid = $DatascriptProgress.data.groupGuid
                        $CurrentOperation = 'Batch Loading Complete'
                        $Status = 'Loaded Import Batches'
                        $Completed = $true
                        $SleepSeconds = 0
                        $PercentComplete = 99
                        $ProgressString = "Status - Completed Loading Import Batches."
                        Break
                    }
                    "Failed" {
                        $CurrentOperation = 'Failed'
                        $Status = $DatascriptProgress.status
                        Write-Host "Batch Loading Processing Failed "$DatascriptProgress.detail
                        Throw $DatascriptProgress.detail
                    }
                    Default {
                        $CurrentOperation = 'State Unknown'
                        $Status = 'Unknown. Sleeping to try again.'
                        $ProgressString = "Unknown Status: " + $DatascriptProgress.status
                        $PercentComplete = 99
                        $SleepSeconds = 2
                        Break
                    }
                }

                ## Notify the user of the Datascript Progress
                if ($ActivityId) {
                    $ProgressOptions = @{
                        Id               = ($ActivityId + 2)
                        ParentId         = $ActivityId
                        Activity         = 'Import Batch Loading'
                        CurrentOperation = $CurrentOperation
                        Status           = $Status
                        PercentComplete  = $PercentComplete
                    }
                    if ($Completed) {
                        $ProgressOptions.Completed = $True
                    }
                    Write-Progress @ProgressOptions
                } else {
                    Write-Host $ProgressString
                }
                if (-Not $Completed) {
                    Start-Sleep -Seconds $SleepSeconds
                }
            }
        }
    }

    ## Assemble the batch list object. Dependencies are deliberately moved to the end
    $BatchesToProcess = [System.Collections.ArrayList]@()

    ## Get the Batches that were created during the import
    ## With the Datascript converted, Use it to create Import Batches
    $uri = "https://"
    $uri += $instance
    $uri += "/tdstm/ws/import/batches?groupGuid="
    $uri += $BatchGroupGuid

    Set-TMHeaderContentType -ContentType JSON -TMSession $TMSession

    ## Post the Transformed data filename to the Datascript engine to Import the Batches.
    $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings
    if ($response.StatusCode -eq 200) {
        $responseContent = $response.Content | ConvertFrom-Json
        if ($responseContent.status -eq "success") {
            $Batches = $responseContent.data
        } else {

        }
    } else {

    }

    if ($Batches.Count) {
        Write-Host $Batches.Length -ForegroundColor Yellow -NoNewline
        Write-Host ' Batches have been created'
    }

    ## Update Progress and transition to Activity + 3
    Write-Progress -Id ($ActivityId + 2) -ParentId $ActivityId -Activity 'Batches Imported' -CurrentOperation 'Importing Batches Complete' -PercentComplete 100 -Completed
    Write-Progress -Id ($ActivityId + 3) -ParentId $ActivityId -Activity 'Monitoring Batches' -CurrentOperation 'Monitoring Batches' -PercentComplete 5

    ## OPTIONAL - If the switch was set to Queue the batches
    if ($QueueBatches) {

        ## Sort the batches to a desired order
        $Batches | Where-Object { $_.domainClass -ne 'Dependency' } | ForEach-Object { $BatchesToProcess.Add($_) | Out-Null }
        $Batches | Where-Object { $_.domainClass -eq 'Dependency' } | ForEach-Object { $BatchesToProcess.Add($_) | Out-Null }

        ## Write Progress to the Monitor Import Batches
        if ($ActivityId) {
            Write-Progress -Id ($ActivityId + 3) `
                -ParentId $ActivityId `
                -Activity 'Manage Batches' `
                -CurrentOperation 'Queueing Batches' `
                -Status ('Queueing ' + [string]$Batches.Length + ' batches.') `
                -PercentComplete 5
        }
        Write-Host "Queueing Batches: " -NoNewline
        Write-Host $Batches.Length -ForegroundColor Yellow

        ## Add a Progress Activity for each of the Import Batches
        for ($i = 0; $i -lt $BatchesToProcess.Count; $i++) {

            ## Set Variable for Batch
            $Batch = $BatchesToProcess[$i]

            ## Create Progress Results for the batches that were created
            if ($ActivityId) {

                ## Activity Explained: The $ActivityId is considered the (Root) + 2 (to move to Monitoring Batches) + I for the looping + 1 to add a new layer
                Write-Progress -Id ($ActivityId + 3 + $i + 1) `
                    -ParentId ($ActivityId + 3) `
                    -Activity ($Batch.domainClassName + ' batch: ' + $Batch.id + ' | Total: ' + $Batch.recordsSummary.count) `
                    -CurrentOperation 'Queueing Batch' `
                    -Status ([String]$Batch.recordsSummary.count + ' ' + $Batch.domainClassName + ' records') `
                    -PercentComplete 10
            }
            Write-Host "Batch (id $($Batch.id)) Created with $([String]$Batch.recordsSummary.count) $($Batch.domainClassName) records"
        }

        ## Queue each Batch
        for ($i = 0; $i -lt $BatchesToProcess.Count; $i++) {

            ## Set Variable for Batch
            $Batch = $BatchesToProcess[$i]
            if ($Batch.autoProcess -eq 0) {

                ## With the file uploaded, Initiate the Datascript script on the server
                $uri = "https://"
                $uri += $instance
                $uri += "/tdstm/ws/import/batches"

                Set-TMHeaderContentType -ContentType JSON -TMSession $TMSession

                $PostBody = @{
                    action = 'QUEUE'
                    ids    = $Batch.id
                } | ConvertTo-Json -Depth 100


                ## Post this batch to begin it's Queueing
                $response = Invoke-WebRequest -Method Patch -Uri $uri -WebSession $TMSession.TMWebSession -Body $PostBody @TMCertSettings
                if ($response.StatusCode -eq 200) {
                    $responseContent = $response.Content | ConvertFrom-Json
                    if ($responseContent.status -eq "success") {

                        ## Create Progress Results for the batches that were created
                        if ($ActivityId) {

                            ## Notify the Manage Batches Activity of a queued batch
                            Write-Progress -Id ($ActivityId + 3) `
                                -ParentId $ActivityId `
                                -Activity 'Monitor Batches' `
                                -CurrentOperation 'Queueing Batches' `
                                -Status ('Queued ' + $Batch.domainClass + ' Batch') `
                                -PercentComplete 5

                            ## Activity (Root) + 2 (to move to Monitoring Batches) + I for the looping + 1 to add a new layer
                            Write-Progress -Id ($ActivityId + 3 + $i + 1) `
                                -ParentId ($ActivityId + 3) `
                                -Activity ($Batch.domainClassName + ' batch: ' + $Batch.id + ' | Total: ' + $Batch.recordsSummary.count) `
                                -CurrentOperation 'Batch Queued'  `
                                -Status 'Queued' `
                                -PercentComplete 0
                        }
                        Write-Host "Batch Queued: " -NoNewline
                        Write-Host $Batch.domainClassName -ForegroundColor Yellow
                    } else {
                        Throw 'Failed to Queue Batch'
                    }
                } else {
                    Throw 'Failed to Queue Batch.'
                }
            }
        }

        ## If Monitoring isn't going to occur, status should be complete here
        if ($MonitorBatches -ne $true) {
            Write-Progress -Id ($ActivityId + 3) -ParentId $ActivityId -Activity 'Batches Queued' -CurrentOperation 'Batches have been queued' -PercentComplete 100 -Completed
        }
    }

    ## Allow the batch to be monitored and reported on in the UI
    if ($MonitorBatches) {
        $BatchStatus = @{
            CompletedBatches = 0
            TotalBatches     = $BatchesToProcess.Count
        }

        ## Monitor the batches to completion
        for ($i = 0; $i -lt $BatchesToProcess.Count; $i++) {

            ## Set Variable for Batch
            $Batch = $BatchesToProcess[$i]
            if ($Batch.recordsSummary.count -gt 0) {
                ## Build the URL
                $uri = "https://"
                $uri += $instance
                $uri += "/tdstm/ws/import/batch/"
                $uri += $Batch.id
                $uri += "/progress"

                Set-TMHeaderContentType -ContentType JSON -TMSession $TMSession

                ## Poll for the status of the Import Batch
                if ($ActivityId) {

                    ## Activity (Root) + 3 (to move to Monitoring Batches) + I for the looping + 1 to add a new layer
                    Write-Progress -Id ($ActivityId + 3 + $i + 1) `
                        -ParentId ($ActivityId + 3) `
                        -Activity ($Batch.domainClassName + ' batch: ' + $Batch.id + ' | Total: ' + $Batch.recordsSummary.count) `
                        -CurrentOperation 'Monitoring Progress'  `
                        -Status 'Importing' `
                        -PercentComplete 1
                }
                Write-Host 'Monitoring Batch Status for ' -NoNewline
                Write-Host $Batch.domainClassName -ForegroundColor Yellow

                ## Start a loop to run while the batch is not complete.
                $Completed = $false
                while ($Completed -eq $false) {

                    ## Post the data starting the Datascript process. A progress key is provided. This progress key is what can be used to poll for the status of the Datascript script
                    $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings
                    if ($response.StatusCode -eq 200) {
                        $responseContent = $response.Content | ConvertFrom-Json
                        if ($responseContent.status -eq "success") {

                            $BatchProgress = $responseContent.data

                            switch ($BatchProgress.status.code) {
                                "RUNNING" {
                                    $CurrentOperation = 'Import Running'
                                    $Status = 'Importing Batch Data'
                                    $ProgressString = $DomainClass + ' Status: Running - ' + $BatchProgress.progress + "%"
                                    $PercentComplete = $BatchProgress.progress
                                    $SleepSeconds = 2
                                    break
                                }
                                "QUEUED" {
                                    $CurrentOperation = 'Batch Queued'
                                    $Status = 'Batch Queued'
                                    $ProgressString = $DomainClass + ' Status: Queued'
                                    $PercentComplete = $BatchProgress.progress
                                    $SleepSeconds = 2
                                    break

                                }
                                "PENDING" {
                                    $CurrentOperation = 'Batch Pending'
                                    $Status = 'Batch Pending'
                                    $ProgressString = $DomainClass + ' Status: Pending'
                                    $PercentComplete = $BatchProgress.progress
                                    $SleepSeconds = 2
                                    break

                                }
                                "COMPLETED" {
                                    $Completed = $true
                                    $CurrentOperation = 'Complete'
                                    $Status = 'Complete'
                                    $ProgressString = ($batch.domainClassName + " Status: Complete")
                                    $PercentComplete = 100
                                    $SleepSeconds = 0
                                    break

                                }
                                Default {
                                    $CurrentOperation = 'Status Unknown. Retrying'
                                    $Status = 'Retrying'
                                    $ProgressString = ($batch.domainClassName + "Status Unkown. Retrying")
                                    $PercentComplete = 1
                                    $SleepSeconds = 2
                                    break
                                }
                            }

                            ## Display the Status of this loop
                            if ($ActivityId) {

                                $ProgressOptions = @{
                                    Id               = ($ActivityId + 3 + $i + 1)
                                    ParentId         = ($ActivityId + 3)
                                    Activity         = ($Batch.domainClassName + ' batch: ' + $Batch.id + ' | Total: ' + $Batch.recordsSummary.count)
                                    CurrentOperation = $CurrentOperation
                                    Status           = $Status
                                    PercentComplete  = ($PercentComplete ?? 0)
                                }
                                ## Activity (Root) + 3 (to move to Monitoring Batches) + I for the looping + 1 to add a new layer
                                if ($Completed) {
                                    $ProgressOptions.Completed = $true
                                }
                                Write-Progress @ProgressOptions

                            } else {
                                Write-Host $ProgressString
                            }

                            ## Sleep the expected duration
                            Start-Sleep -Seconds $SleepSeconds
                        }
                    }
                }
            }
            ## Now that this batch is done, increment the Completed Batches counter
            $BatchStatus.CompletedBatches++
            Write-Host 'Batch Import Complete for ' -NoNewline
            Write-Host $Batch.domainClassName -ForegroundColor Yellow
        }

        ## Mark the Manage Batches Activity Complete
        if ($ActivityId) {
            Write-Progress -Id ($ActivityId + 3) `
                -ParentId $ActivityId `
                -Activity 'Batches Posted' `
                -CurrentOperation 'Complete'  `
                -Status 'All Batches Imported' `
                -PercentComplete 100 `
                -Completed
        }
    }


    ## Mark the TM Import Activity complete
    if ($ActivityId) {
        Write-Progress -Id $ActivityId `
            -ParentId $ParentActivityId `
            -Activity 'TransitionManager Data Import' `
            -CurrentOperation 'Complete'  `
            -Status 'All Data Imported' `
            -PercentComplete 100 `
            -Complete
    }

    if ($Batches.Count) {
        Write-Host 'All Batches have Imported successfully'
    }
}

Function Test-TMDatascript {
    [alias("Test-TMETLScript")]
    param(
        [Parameter(Mandatory = $false)][PSObject]$TMSession = 'Default',
        [Alias("ETLScriptName")]
        [Parameter(Mandatory = $true)][String]$DatascriptName,
        [Parameter(Mandatory = $false)][bool]$QueueBatches = $false,
        [Parameter(Mandatory = $false)][bool]$MonitorBatches = $false,
        [Parameter(Mandatory = $false)]$Data,
        [Parameter(Mandatory = $false)][String]$FilePath,
        [Parameter(Mandatory = $false)][String]$FileName,
        [Parameter(Mandatory = $false)][Int16]$ActivityId,
        [Parameter(Mandatory = $false)][Int16]$ParentActivityId = -1
    )

    ## Get Session Configuration
    $TMSession = Get-TMSession $TMSession

    if ($FilePath) {
        if ( -not (Test-Path -Path $FilePath -PathType Leaf) ) {
            throw $DataTypeConfig.FileNotFoundError
        }

        ## If a File Path was passed and there is no data, Read the file
        if ( -not $Data ) {
            $Data = Get-Content $FilePath -Raw
            $FileName = (Get-Item $FilePath).Name
        }

        ## If a File Path was passed and there is no File Name, get the file name
        if ( -not $FileName ) {
            $FileName = (Get-Item $FilePath).Name
        }
    }

    #Honor SSL Settings
    $TMCertSettings = @{SkipCertificateCheck = $TMSession.AllowInsecureSSL }

    if ($ActivityId) {
        ## Parent ID is only used on the root 'TransitionManager Data Import' Activity
        ## ParentID + 1 = Transform Data
        ## ParentID + 2 = Import Batches
        ## ParentID + 3 + n for each batch

        ## Add the Datascript Processing Progress Indicators
        $ProgressIndicators = @()
        $ProgressIndicators += @{ Id = $ActivityId; Activity = 'Test Datascript Data Transformation'; ParentId = $ParentActivityId }

    }

    ## Fix the Server URL
    $Instance = $TMSession.TMServer.Replace('/tdstm', '')
    $instance = $instance.Replace('https://', '')
    $instance = $instance.Replace('http://', '')

    $Boundary = '----------------------------540299933173025267350719'

    ## First Test to see if the Datascript exists or not
    if ($ActivityId) {

        Write-Progress -Id ($ActivityId + 1) -ParentId $ActivityId -Activity 'Datascript Data Transformation' -CurrentOperation 'Validating Datascript' -Status 'Confirming Datascript exists in TransitionManager' -PercentComplete 5
    }
    $Datascript = Get-TMDatascript -TMSession $TMSession -Name $DatascriptName
    if (-Not $Datascript) { Throw  'The Datascript [' + $DatascriptName + '] does not exist' }

    ## Determine the data type of the file to be uploaded
    if ($FilePath) {

        ## Build the Data Post
        $uri = "https://"
        $uri += $instance
        $uri += "/tdstm/ws/fileSystem/uploadFileETLAssetImport"

        $DataType = (Get-Item $FilePath).Extension
        if ($DataType -notmatch $DataTypeConfig.MatchPattern) {
            throw $DataTypeConfig.BadExtensionErrorMessage
        }


        ## FileName
        $FileName = ('DataForETL_' + (Get-Date -Format FileDateTime ) + $DataType)

        ## Construct the PostBody and Form Data for the request
        $CRLF = "`r`n";
        $PostBody = ("-----------------------------$boundary",
            'Content-Disposition: form-data; name="uploadType"',
            '',
            'assetImport',
            "-----------------------------$boundary",
            ''
        ) -join $CRLF
        $FileUpload = @{
            Form = @{
                uploadType = 'assetImport'
                file       = Get-Item -Path $FilePath
                fileName   = $FileName
            }
        }
        ## Upload the Data File
        if ($ActivityId) {
            Write-Progress -Id ($ActivityId + 1) -ParentId $ActivityId -Activity 'Testing Datascript Data Transformation' -CurrentOperation 'Uploading Data' -Status ('Uploading ' + $FileName) -PercentComplete 5
        }

        try {
            ## If the file is being uploaded, append the fileupload to the command
            if ($FileUpload) {
                $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSession.TMWebSession -ContentType ('multipart/form-data; boundary=---------------------------' + $boundary) @TMCertSettings @FileUpload
            } else {
                $response = Invoke-WebRequest -Method Post -Uri $uri -WebSession $TMSession.TMWebSession -ContentType ('multipart/form-data; boundary=---------------------------' + $boundary) @TMCertSettings
            }

        } catch {
            throw $_
        }

        if ($response.StatusCode -eq 200) {
            $responseContent = $response.Content | ConvertFrom-Json
            if ($responseContent.status -eq "success") {
                $DatascriptdataFileName = $responseContent.data.filename
            } else {
                Throw "Unable to upload data to TM Datascript pre-transform storage."
            }
        }
    }

    ## With the file uploaded, Initiate the Datascript script on the server
    $uri = "https://"
    $uri += $instance
    $uri += "/tdstm/ws/dataScript/initiateTestScript"

    ## Create a post body
    if ($FilePath) {
        $PostBody = [PSCustomObject]@{
            dataScriptId = $Datascript.id
            filename     = $DatascriptdataFileName
            script       = ($Datascript.etlSourceCode -replace $CRLF, '\n')
        } | ConvertTo-Json -Compress
    } else {
        $PostBody = [PSCustomObject]@{
            dataScriptId = $Datascript.id
            script       = ($Datascript.etlSourceCode)
        } | ConvertTo-Json -Compress
    }

    Set-TMHeaderContentType -ContentType JSON -TMSession $TMSession

    ## Post the data starting the Datascript process. A progress key is provided. This progress key is what can be used to poll for the status of the Datascript
    if ($ActivityId) {
        Write-Progress -Id ($ActivityId + 1) -ParentId $ActivityId -Activity 'Testing Datascript Data Transformation' -CurrentOperation 'Starting Datascript Testing' -PercentComplete 5
    } else {
        Write-Host 'TransitionManager Data Import: Starting Datascript Test'
    }

    $response = Invoke-WebRequest -Method Post -Uri $uri -Body $PostBody -WebSession $TMSession.TMWebSession @TMCertSettings
    if ($response.StatusCode -eq 200) {
        $responseContent = $response.Content | ConvertFrom-Json
        if ($responseContent.status -eq "success") {
            $DatascriptProgressKey = $responseContent.data.progressKey
        } else {
            throw $responseContent.errors
        }
    } else {
        Throw $response
    }


    ## Supply Progress from Datascript Transformation Progress
    if ($ActivityId) {
        Write-Progress -Id $($ActivityId + 1) -Activity 'Testing Datascript Data Transformation' -CurrentOperation 'Running Datascript Transformation' -PercentComplete 0 -ParentId $ActivityId
    } else {
        Write-Host 'Testing Datascript Data Transformation: Starting Datascript Processing'
    }

    ## The Datascript is underway, Setup a progress URL
    $uri = "https://"
    $uri += $instance
    $uri += "/tdstm/ws/progress/" + $DatascriptProgressKey
    Set-TMHeaderContentType -ContentType JSON -TMSession $TMSession

    ## Poll for the status of the Datascript engine
    ## TODO: This should be converted to a function for polling the job engine. It's nearly dupilcated now in the Import Batch watching.
    $Completed = $false
    while ($Completed -eq $false) {

        ## Post the data starting the Datascript process. A progress key is provided. This progress key is what can be used to poll for the status of the Datascript script
        $response = Invoke-WebRequest -Method Get -Uri $uri -WebSession $TMSession.TMWebSession @TMCertSettings
        if ($response.StatusCode -eq 200) {
            $responseContent = $response.Content | ConvertFrom-Json
            if ($responseContent.status -eq "success") {
                $DatascriptProgress = $responseContent.data

                switch ($DatascriptProgress.status) {
                    "Queued" {

                        $CurrentOperation = 'Datascript Queued'
                        $Status = 'Queued'
                        $ProgressString = 'Status - Queued: ' + $DatascriptProgress.percentComp + '%'
                        $PercentComplete = $DatascriptProgress.percentComp
                        $SleepSeconds = 2
                        Break

                    }
                    "Pending" {
                        $CurrentOperation = 'Datascript Pending'
                        $Status = 'Pending'
                        $ProgressString = 'Status - Pending: ' + $DatascriptProgress.percentComp + '%'
                        $PercentComplete = $DatascriptProgress.percentComp
                        $SleepSeconds = 2
                        Break
                    }
                    "RUNNING" {
                        $CurrentOperation = 'Datascript Running'
                        $Status = 'Transforming Data'
                        $ProgressString = 'Status - Running: ' + $DatascriptProgress.percentComp + '%'
                        $PercentComplete = $DatascriptProgress.percentComp
                        $SleepSeconds = 2
                        Break
                    }
                    "COMPLETED" {
                        $CurrentOperation = 'Datascript Testing Complete'
                        $Status = 'Creating Import Batches'
                        $Completed = $true
                        $SleepSeconds = 0
                        $PercentComplete = 99
                        $ProgressString = "Status - Datascript Testing Complete"
                        Break
                    }
                    "Failed" {
                        $CurrentOperation = 'Failed'
                        $Status = $DatascriptProgress.status
                        Write-Host "Datascript Testing Failed "$DatascriptProgress.detail
                        Throw $DatascriptProgress.detail
                    }
                    Default {
                        $CurrentOperation = 'State Unknown'
                        $Status = 'Unknown. Sleeping to try again.'
                        $ProgressString = "Unknown Status: " + $DatascriptProgress.status
                        $PercentComplete = 99
                        $SleepSeconds = 2
                        Break
                    }
                }

                ## Notify the user of the Datascript Progress
                if ($ActivityId) {
                    Write-Progress -Id ($ActivityId + 1) `
                        -ParentId $ActivityId `
                        -Activity 'Testing Datascript Data Transformation' `
                        -CurrentOperation $CurrentOperation `
                        -Status $Status `
                        -PercentComplete $PercentComplete
                } else {
                    Write-Host $ProgressString
                }
                Start-Sleep -Seconds $SleepSeconds
            }
        }
    }

    ## Update Progress and transition to Activity + 2
    Write-Progress -Id ($ActivityId + 1) -ParentId $ActivityId -Activity 'Tested Datascript Data Transformation' -CurrentOperation 'Testing Complete' -PercentComplete 100 -Completed

    ## Mark the TM Import Activity complete
    if ($ActivityId) {
        Write-Progress -Id $ActivityId `
            -ParentId $ParentActivityId `
            -Activity 'TransitionManager Data Import' `
            -CurrentOperation 'Complete'  `
            -Status 'All Data Imported' `
            -PercentComplete 100 `
            -Complete
    }
}

Function Read-TMDatascriptFile {
    param(
        [Parameter(Mandatory = $true)]$Path
    )

    ## First order of business is to determine if this Script file has a counterpart Json file
    $DatascriptConfigJsonPath = $Path -Replace '.groovy', '.json'
    $DatascriptConfigFile = Get-Item -Path $DatascriptConfigJsonPath -ErrorAction 'SilentlyContinue'

    ## Check if there is a config JSON file, if so, get the Action from the 2 files
    if ($DatascriptConfigFile) {

        ## Get the Recipe Object that doesn't have the source code
        $TMDatascript = Get-Content -Path $DatascriptConfigFile | ConvertFrom-Json
        Add-Member -InputObject $TMDatascript -NotePropertyName etlSourceCode -NotePropertyValue (Get-Content -Path $Path -Raw)
    }

    ## Read the ReferenceDesign (Combined) file
    else {

        ## Name the Input File
        $Content = Get-Content -Path $Path -Raw

        ## Ignore Empty Files
        if (-Not $Content) {
            return
        }

        $ContentLines = Get-Content -Path $Path

        ## Create Automation Token Variables Parse the Script File
        New-Variable astTokens -Force
        New-Variable astErr -Force
        $ast = [System.Management.Automation.Language.Parser]::ParseInput($Content, [ref]$astTokens, [ref]$astErr)

        ##
        ## Assess the Script Parts to get delineating line numbers
        ##

        ## Locate the Delimiting line
        $ConfigBlockStartLine = $astTokens | `
                Where-Object { $_.Text -like '/*********TransitionManager-ETL-Script*********' } |`
                Select-Object -First 1 | `
                Select-Object -ExpandProperty Extent | `
                Select-Object -ExpandProperty StartLineNumber

        ## Test if the file has been written with MetaData
        if (-Not $ConfigBlockStartLine) {

            ## This file does not have metadata. Create the minimum data for the constructor below
            $DatascriptConfig = @{
                DatascriptName = (Get-Item -Path $Path).BaseName
                Description    = ""
                ProviderName   = (Get-Item -Path $Path).Directory.BaseName
                IsAutoProcess  = $false
                Target         = $null
                Mode           = 'Import'
            }

            ## Add Variables that will be used in the construction
            $DatascriptCode = $ContentLines
            $ConfigBlockEndLine = -1
        } else {

            ## This File has metadata - Work to parse it

            $ConfigBlockEndLine = $astTokens | `
                    Where-Object { $_.Text -like '*********TransitionManager-ETL-Script*********/' } |`
                    Select-Object -First 1 | `
                    Select-Object -ExpandProperty Extent | `
                    Select-Object -ExpandProperty StartLineNumber

            ## Adjust the Line Numbers to capture just the JSON
            $JsonConfigBlockStartLine = $ConfigBlockStartLine + 1
            $JsonConfigBlockEndLine = $ConfigBlockEndLine - 1


            ##
            ## Read the Script Header to gather the configurations
            ##

            ## Get all of the lines in the header comment
            $DatascriptConfigJson = $JsonConfigBlockStartLine..$JSONConfigBlockEndLine | ForEach-Object {

                ## Return the line for collection
                $ContentLines[$_ - 1]

            } | Out-String

            ## Convert the JSON string to an Object
            if ($DatascriptConfigJson) {
                $DatascriptConfig = $DatascriptConfigJson | ConvertFrom-Json
            } else {
                $DatascriptConfig = @{
                    DatascriptName = (Get-Item -Path $Path).BaseName
                    Description    = ""
                    ProviderName   = (Get-Item -Path $Path).Directory.Parent.Parent.BaseName
                    IsAutoProcess  = $false
                    Target         = $null
                    Mode           = 'Import'
                }
            }
        }

        ##
        ## Read the Script Block
        ##

        ## Note where the Configuration Code is located
        $StartCodeBlockLine = $ConfigBlockEndLine + 1
        $EndCodeBlockLine = $ast[-1].Extent.EndLineNumber

        ## Create a Text StrinBuilder to collect the Script into
        $DatascriptStringBuilder = New-Object System.Text.StringBuilder

        ## For each line in the Code Block, add it to the Datascript Script Code StringBuilder
        $StartCodeBlockLine..$EndCodeBlockLine | ForEach-Object {
            $DatascriptStringBuilder.AppendLine($ContentLines[$_]) | Out-Null
        }

        ## Convert the StringBuilder to a Multi-Line String
        $DatascriptCode = $DatascriptStringBuilder.ToString()

        ## Assemble the Action Object
        $TMDatascript = [pscustomobject]@{

            ## Primary Information
            id            = $null
            name          = $DatascriptConfig.DatascriptName
            description   = $DatascriptConfig.Description

            ## Source Code
            etlSourceCode = $DatascriptCode

            ## Provider
            provider      = @{
                id   = $null
                name = $DatascriptConfig.ProviderName
            }

            ## Other details for the Datascript Script
            dateCreated   = Get-Date
            lastUpdated   = Get-Date
        }
    }

    ## Return the Action Object
    return $TMDatascript
}