Public/ExampleData/Invoke-JIMExampleDataTemplate.ps1

# Copyright (c) Tetron Limited. All rights reserved.
# Licensed under the Tetron Commercial License. See LICENSE file in the project root.

function Invoke-JIMExampleDataTemplate {
    <#
    .SYNOPSIS
        Executes a data generation template to create test data.
 
    .DESCRIPTION
        Queues a data generation template for execution by the JIM worker service to
        create identity objects in the Metaverse according to the template
        configuration. The task runs asynchronously and is tracked by an Activity;
        monitor progress and completion via Get-JIMActivity, or use the -Wait
        parameter to block until generation completes.
 
    .PARAMETER Id
        The unique identifier of the template to execute.
 
    .PARAMETER Name
        The name of the template to execute.
 
    .PARAMETER Wait
        If specified, waits for the data generation to complete before returning.
        Shows live progress while waiting (object counts, throughput and estimated
        time remaining), polling the lightweight Activity progress endpoint every
        2 seconds.
 
    .PARAMETER Timeout
        Maximum time in seconds to wait for completion when using -Wait.
        If not specified, waits indefinitely until completion.
 
    .PARAMETER PassThru
        If specified, returns information about the queued execution, including the
        ActivityId to follow via Get-JIMActivity.
 
    .OUTPUTS
        If -PassThru is specified, returns the execution information with TemplateId,
        ActivityId, TaskId, Status and Message properties.
 
    .EXAMPLE
        Invoke-JIMExampleDataTemplate -Id 1
 
        Executes the data generation template with ID 1.
 
    .EXAMPLE
        Invoke-JIMExampleDataTemplate -Name 'Test Users'
 
        Executes the data generation template named 'Test Users'.
 
    .EXAMPLE
        Invoke-JIMExampleDataTemplate -Id 1 -Wait
 
        Executes the template and waits for completion with progress display.
 
    .EXAMPLE
        Get-JIMExampleDataTemplate | Where-Object { $_.name -eq "Test Users" } | Invoke-JIMExampleDataTemplate
 
        Executes a template from the pipeline.
 
    .EXAMPLE
        Invoke-JIMExampleDataTemplate -Id 1 -Wait -Timeout 600
 
        Executes and waits up to 10 minutes for completion. If the timeout is
        exceeded, an error is thrown.
 
    .EXAMPLE
        Invoke-JIMExampleDataTemplate -Id 1 -PassThru
 
        Executes the template and returns the queued execution information,
        including the ActivityId.
 
    .LINK
        Get-JIMExampleDataTemplate
        Get-JIMExampleDataSet
        Get-JIMActivity
    #>

    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'ById')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, ParameterSetName = 'ById', ValueFromPipelineByPropertyName)]
        [int]$Id,

        [Parameter(Mandatory, ParameterSetName = 'ByName')]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [switch]$Wait,

        [ValidateRange(1, [int]::MaxValue)]
        [int]$Timeout,

        [switch]$PassThru
    )

    process {
        # Check connection first
        if (-not $script:JIMConnection) {
            Write-Error "You are not connected to JIM. Run Connect-JIM -Url <your JIM URL> to authenticate, then try again."
            return
        }

        # Resolve name to ID if using ByName parameter set
        if ($PSCmdlet.ParameterSetName -eq 'ByName') {
            try {
                $resolvedTemplate = Resolve-JIMExampleDataTemplate -Name $Name
                $Id = $resolvedTemplate.id
            }
            catch {
                Write-Error $_
                return
            }
        }

        $displayName = if ($Name) { $Name } else { "Template ID: $Id" }

        if ($PSCmdlet.ShouldProcess($displayName, "Execute Data Generation Template")) {
            Write-Verbose "Executing data generation template: $Id"

            try {
                $response = Invoke-JIMApi -Endpoint "/api/v1/example-data/templates/$Id/execute" -Method 'POST'

                Write-Verbose "Data generation queued. ActivityId: $($response.activityId), TaskId: $($response.taskId)"

                if ($Wait) {
                    $hasTimeout = $PSBoundParameters.ContainsKey('Timeout')
                    if ($hasTimeout) {
                        Write-Verbose "Waiting for data generation to complete (timeout: ${Timeout}s)"
                    } else {
                        Write-Verbose "Waiting for data generation to complete (no timeout)"
                    }

                    $startTime = Get-Date
                    $activityId = $response.activityId
                    $completed = $false
                    $lastStatus = ''

                    $consecutiveAuthFailures = 0
                    $maxAuthFailures = 3

                    while (-not $completed -and (-not $hasTimeout -or ((Get-Date) - $startTime).TotalSeconds -lt $Timeout)) {
                        Start-Sleep -Seconds 2

                        try {
                            # Lightweight progress endpoint (issue #202): status, counts, phase
                            # message, throughput and ETA without the cost of the full detail read.
                            $activityProgress = Invoke-JIMApi -Endpoint "/api/v1/activities/$activityId/progress"

                            # Reset auth failure counter on successful call
                            $consecutiveAuthFailures = 0

                            # Update progress
                            $elapsed = [int]((Get-Date) - $startTime).TotalSeconds
                            $status = $activityProgress.status ?? 'Running'

                            if ($status -ne $lastStatus) {
                                Write-Verbose "Status: $status"
                                $lastStatus = $status
                            }

                            $progressParams = Get-JIMActivityProgressDisplay -Progress $activityProgress -ActivityLabel "Executing Data Generation Template" -ElapsedSeconds $elapsed
                            Write-Progress @progressParams

                            # Check if completed (matches ActivityStatus enum names)
                            if ($status -in @('Complete', 'CompleteWithWarning', 'CompleteWithError', 'FailedWithError', 'Cancelled')) {
                                $completed = $true
                            }
                        }
                        catch {
                            $errorMsg = "$_"

                            # Detect authentication failures and stop polling rather than spamming
                            if ($errorMsg -match 'Authentication failed|session may have expired|API key may be invalid') {
                                $consecutiveAuthFailures++

                                if ($consecutiveAuthFailures -ge $maxAuthFailures) {
                                    Write-Progress -Activity "Executing Data Generation Template" -Completed
                                    throw "Authentication failed while monitoring activity $activityId. The operation was submitted successfully and may still be running on the server. Use Get-JIMActivity -Id $activityId to check its status after re-authenticating with Connect-JIM."
                                }

                                # Brief warning on first/second failure - Invoke-JIMApi may have already
                                # refreshed the token transparently, so give it another chance
                                Write-Warning "Authentication error while checking activity status (attempt $consecutiveAuthFailures of $maxAuthFailures). Retrying..."
                            }
                            else {
                                # Non-auth errors: warn but continue polling
                                Write-Warning "Error checking activity status: $errorMsg"
                            }
                        }
                    }

                    Write-Progress -Activity "Executing Data Generation Template" -Completed

                    if (-not $completed -and $hasTimeout) {
                        throw "Timeout waiting for data generation after $Timeout seconds. Activity ID: $activityId. The operation may still be running in the background."
                    }
                }

                if ($PassThru) {
                    [PSCustomObject]@{
                        TemplateId = $Id
                        ActivityId = $response.activityId
                        TaskId = $response.taskId
                        Status = 'Queued'
                        Message = $response.message
                    }
                }
            }
            catch {
                # Use throw to propagate as a terminating error so callers with
                # $ErrorActionPreference = "Stop" will see it immediately
                throw "Failed to execute data generation template: $_"
            }
        }
    }
}