- Fixed parameter conflicts in sf-data-export.ps1, sf-data-import.ps1, sf-retrieve.ps1, and sf-test-run.ps1 - Renamed conflicting \ parameter to \ to avoid conflicts with PowerShell's built-in -Verbose common parameter - Added proper parameter sets to ensure -Help parameter works correctly in all scripts - Added -hp aliases where needed for consistency across all scripts - All scripts now properly support help functionality without parameter conflicts This resolves issues where scripts would fail with 'A parameter with the name Verbose was defined multiple times' error.
373 lines
10 KiB
PowerShell
373 lines
10 KiB
PowerShell
#!/usr/bin/env pwsh
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Data import wrapper for Salesforce CLI with CSV and JSON support
|
|
|
|
.DESCRIPTION
|
|
A user-friendly wrapper around 'sf data import' that simplifies data import
|
|
to Salesforce orgs with CSV/JSON support, upsert operations, and intelligent validation.
|
|
|
|
.PARAMETER File
|
|
CSV or JSON file to import (alias: -fl)
|
|
|
|
.PARAMETER SObject
|
|
Target sObject type (alias: -so)
|
|
|
|
.PARAMETER Operation
|
|
Operation: insert, update, upsert (default: insert) (alias: -op)
|
|
|
|
.PARAMETER ExternalId
|
|
External ID field for upsert/update operations (alias: -ei)
|
|
|
|
.PARAMETER TargetOrg
|
|
Target org username or alias (alias: -to)
|
|
|
|
.PARAMETER Bulk
|
|
Use bulk API for large datasets (alias: -bk)
|
|
|
|
.PARAMETER Wait
|
|
Wait time in minutes (default: 10) (alias: -wt)
|
|
|
|
.PARAMETER BatchSize
|
|
Batch size for bulk operations (default: 10000) (alias: -bs)
|
|
|
|
.PARAMETER IgnoreErrors
|
|
Continue on errors (don't fail entire job) (alias: -ie)
|
|
|
|
.PARAMETER VerboseOutput
|
|
Enable verbose output (alias: -ve)
|
|
|
|
.PARAMETER Help
|
|
Show this help message (alias: -hp)
|
|
|
|
.EXAMPLE
|
|
.\sf-data-import.ps1 -fl accounts.csv -so Account
|
|
.\sf-data-import.ps1 -fl contacts.json -so Contact -op upsert -ei Email
|
|
.\sf-data-import.ps1 -fl leads.csv -so Lead -bk -bs 5000
|
|
.\sf-data-import.ps1 -fl updates.csv -so Account -op update -ei AccountNumber
|
|
|
|
.NOTES
|
|
This script automatically checks for Salesforce CLI installation and runs
|
|
diagnostics if the CLI is not found.
|
|
|
|
Supported formats:
|
|
- CSV files with header row
|
|
- JSON files (array of objects or newline-delimited JSON)
|
|
#>
|
|
|
|
param(
|
|
[Parameter(ParameterSetName="Import", Mandatory)]
|
|
[Alias("fl")]
|
|
[string]$File,
|
|
|
|
[Parameter(ParameterSetName="Import", Mandatory)]
|
|
[Alias("so")]
|
|
[string]$SObject,
|
|
|
|
[Parameter(ParameterSetName="Import")]
|
|
[ValidateSet("insert", "update", "upsert")]
|
|
[Alias("op")]
|
|
[string]$Operation = "insert",
|
|
|
|
[Parameter(ParameterSetName="Import")]
|
|
[Alias("ei")]
|
|
[string]$ExternalId,
|
|
|
|
[Parameter(ParameterSetName="Import")]
|
|
[Alias("to")]
|
|
[string]$TargetOrg,
|
|
|
|
[Parameter(ParameterSetName="Import")]
|
|
[Alias("bk")]
|
|
[switch]$Bulk,
|
|
|
|
[Parameter(ParameterSetName="Import")]
|
|
[Alias("wt")]
|
|
[int]$Wait = 10,
|
|
|
|
[Parameter(ParameterSetName="Import")]
|
|
[Alias("bs")]
|
|
[int]$BatchSize = 10000,
|
|
|
|
[Parameter(ParameterSetName="Import")]
|
|
[Alias("ie")]
|
|
[switch]$IgnoreErrors,
|
|
|
|
[Parameter(ParameterSetName="Import")]
|
|
[Alias("ve")]
|
|
[switch]$VerboseOutput,
|
|
|
|
[Parameter(ParameterSetName="Help", Mandatory=$true)]
|
|
[Alias("hp")]
|
|
[switch]$Help
|
|
)
|
|
|
|
# Show help if requested
|
|
if ($Help) {
|
|
Get-Help $MyInvocation.MyCommand.Path -Detailed
|
|
exit 0
|
|
}
|
|
|
|
# Function to check if Salesforce CLI is installed
|
|
function Test-SalesforceCLI {
|
|
try {
|
|
$null = Get-Command sf -ErrorAction Stop
|
|
return $true
|
|
} catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
# Function to run sf-check diagnostics
|
|
function Invoke-SalesforceCheck {
|
|
$checkScript = if (Test-Path "sf-check.ps1") {
|
|
".\sf-check.ps1"
|
|
} elseif (Test-Path "sf-check.sh") {
|
|
"bash sf-check.sh"
|
|
} else {
|
|
$null
|
|
}
|
|
|
|
if ($checkScript) {
|
|
Write-Host "Running Salesforce CLI diagnostics..." -ForegroundColor Yellow
|
|
Invoke-Expression $checkScript
|
|
} else {
|
|
Write-Host "Salesforce CLI not found and no diagnostic script available." -ForegroundColor Red
|
|
Write-Host "Please install the Salesforce CLI: https://developer.salesforce.com/tools/salesforcecli" -ForegroundColor Red
|
|
}
|
|
}
|
|
|
|
# Function to detect file format
|
|
function Get-FileFormat {
|
|
param([string]$FilePath)
|
|
|
|
$extension = [System.IO.Path]::GetExtension($FilePath).ToLower()
|
|
|
|
switch ($extension) {
|
|
".csv" { return "csv" }
|
|
".json" { return "json" }
|
|
default {
|
|
# Try to detect by content
|
|
$firstLine = Get-Content $FilePath -First 1
|
|
if ($firstLine -match '^\s*\{.*\}$|^\s*\[') {
|
|
return "json"
|
|
} else {
|
|
return "csv"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Function to validate CSV file
|
|
function Test-CSVFile {
|
|
param([string]$FilePath)
|
|
|
|
$lines = Get-Content $FilePath
|
|
if ($lines.Count -lt 2) {
|
|
Write-Host "Error: CSV file must have at least a header and one data row" -ForegroundColor Red
|
|
return $false
|
|
}
|
|
|
|
$headerFields = ($lines[0] -split ',').Count
|
|
$firstRowFields = ($lines[1] -split ',').Count
|
|
|
|
if ($headerFields -ne $firstRowFields) {
|
|
Write-Host "Warning: Header field count ($headerFields) differs from first row ($firstRowFields)" -ForegroundColor Yellow
|
|
}
|
|
|
|
return $true
|
|
}
|
|
|
|
# Function to validate JSON file
|
|
function Test-JSONFile {
|
|
param([string]$FilePath)
|
|
|
|
try {
|
|
$null = Get-Content $FilePath -Raw | ConvertFrom-Json
|
|
return $true
|
|
} catch {
|
|
Write-Host "Error: Invalid JSON format in file" -ForegroundColor Red
|
|
return $false
|
|
}
|
|
}
|
|
|
|
# Function to show file preview
|
|
function Show-FilePreview {
|
|
param([string]$FilePath, [string]$Format)
|
|
|
|
Write-Host "📄 File Preview ($Format):" -ForegroundColor Yellow
|
|
Write-Host "----------------------------------------" -ForegroundColor Gray
|
|
|
|
switch ($Format) {
|
|
"csv" {
|
|
$lines = Get-Content $FilePath
|
|
Write-Host "Header: $($lines[0])" -ForegroundColor Gray
|
|
if ($lines.Count -gt 1) {
|
|
Write-Host "Sample: $($lines[1])" -ForegroundColor Gray
|
|
}
|
|
Write-Host "Records: $($lines.Count - 1)" -ForegroundColor Gray
|
|
}
|
|
"json" {
|
|
try {
|
|
$content = Get-Content $FilePath -Raw | ConvertFrom-Json
|
|
if ($content -is [array]) {
|
|
Write-Host "Array format with $($content.Count) records" -ForegroundColor Gray
|
|
if ($content.Count -gt 0) {
|
|
$keys = ($content[0] | Get-Member -MemberType NoteProperty).Name -join ", "
|
|
Write-Host "Sample keys: $keys" -ForegroundColor Gray
|
|
}
|
|
} else {
|
|
$recordCount = (Get-Content $FilePath).Count
|
|
Write-Host "NDJSON format" -ForegroundColor Gray
|
|
Write-Host "Records: $recordCount" -ForegroundColor Gray
|
|
}
|
|
} catch {
|
|
Write-Host "Unable to parse JSON preview" -ForegroundColor Gray
|
|
}
|
|
}
|
|
}
|
|
|
|
$fileInfo = Get-Item $FilePath
|
|
$fileSize = if ($fileInfo.Length -gt 1MB) {
|
|
"{0:N1} MB" -f ($fileInfo.Length / 1MB)
|
|
} elseif ($fileInfo.Length -gt 1KB) {
|
|
"{0:N1} KB" -f ($fileInfo.Length / 1KB)
|
|
} else {
|
|
"$($fileInfo.Length) bytes"
|
|
}
|
|
|
|
Write-Host "File size: $fileSize" -ForegroundColor Gray
|
|
Write-Host "----------------------------------------" -ForegroundColor Gray
|
|
}
|
|
|
|
# Silently check for Salesforce CLI
|
|
if (-not (Test-SalesforceCLI)) {
|
|
Invoke-SalesforceCheck
|
|
exit 1
|
|
}
|
|
|
|
# Validate file exists
|
|
if (-not (Test-Path $File)) {
|
|
Write-Host "Error: File not found: $File" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
# Validate external ID for upsert/update operations
|
|
if (($Operation -eq "upsert" -or $Operation -eq "update") -and -not $ExternalId) {
|
|
Write-Host "Error: External ID field is required for $Operation operations" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
# Detect and validate file format
|
|
$fileFormat = Get-FileFormat $File
|
|
Write-Host "Using file: $File" -ForegroundColor Green
|
|
Write-Host "Detected format: $fileFormat" -ForegroundColor Cyan
|
|
|
|
# Validate file content
|
|
switch ($fileFormat) {
|
|
"csv" {
|
|
if (-not (Test-CSVFile $File)) {
|
|
exit 1
|
|
}
|
|
}
|
|
"json" {
|
|
if (-not (Test-JSONFile $File)) {
|
|
exit 1
|
|
}
|
|
}
|
|
}
|
|
|
|
# Show file preview if verbose
|
|
if ($VerboseOutput) {
|
|
Show-FilePreview $File $fileFormat
|
|
}
|
|
|
|
# Build the sf command
|
|
$sfArgs = @("data", $Operation, "--file", $File, "--sobject", $SObject)
|
|
|
|
# Add optional parameters
|
|
if ($TargetOrg) {
|
|
$sfArgs += "--target-org"
|
|
$sfArgs += $TargetOrg
|
|
Write-Host "Target org: $TargetOrg" -ForegroundColor Cyan
|
|
}
|
|
|
|
if ($ExternalId) {
|
|
$sfArgs += "--external-id"
|
|
$sfArgs += $ExternalId
|
|
Write-Host "External ID field: $ExternalId" -ForegroundColor Cyan
|
|
}
|
|
|
|
if ($Bulk) {
|
|
$sfArgs += "--bulk"
|
|
Write-Host "Using Bulk API" -ForegroundColor Yellow
|
|
}
|
|
|
|
if ($Wait -ne 10) {
|
|
$sfArgs += "--wait"
|
|
$sfArgs += $Wait.ToString()
|
|
}
|
|
|
|
if ($Bulk -and $BatchSize -ne 10000) {
|
|
$sfArgs += "--batch-size"
|
|
$sfArgs += $BatchSize.ToString()
|
|
Write-Host "Batch size: $BatchSize" -ForegroundColor Cyan
|
|
}
|
|
|
|
if ($IgnoreErrors) {
|
|
$sfArgs += "--ignore-errors"
|
|
Write-Host "Ignoring individual record errors" -ForegroundColor Yellow
|
|
}
|
|
|
|
# Add verbose flag if requested
|
|
if ($VerboseOutput) {
|
|
$sfArgs += "--verbose"
|
|
}
|
|
|
|
# Display import information
|
|
Write-Host ""
|
|
Write-Host "📥 Starting Data Import" -ForegroundColor Blue
|
|
Write-Host "=======================" -ForegroundColor Blue
|
|
Write-Host "Operation: $Operation" -ForegroundColor Cyan
|
|
Write-Host "sObject: $SObject" -ForegroundColor Cyan
|
|
|
|
# Display the command being run
|
|
Write-Host ""
|
|
Write-Host "Executing: sf $($sfArgs -join ' ')" -ForegroundColor Gray
|
|
Write-Host ""
|
|
|
|
# Execute the command
|
|
try {
|
|
& sf @sfArgs
|
|
$exitCode = $LASTEXITCODE
|
|
|
|
Write-Host ""
|
|
if ($exitCode -eq 0) {
|
|
Write-Host "✅ Data import completed successfully!" -ForegroundColor Green
|
|
|
|
switch ($Operation) {
|
|
"insert" {
|
|
Write-Host "📊 Records inserted into $SObject" -ForegroundColor Cyan
|
|
}
|
|
"update" {
|
|
Write-Host "📊 Records updated in $SObject" -ForegroundColor Cyan
|
|
}
|
|
"upsert" {
|
|
Write-Host "📊 Records upserted in $SObject (using $ExternalId as external ID)" -ForegroundColor Cyan
|
|
}
|
|
}
|
|
|
|
if ($VerboseOutput) {
|
|
Write-Host "💡 Check the output above for detailed results and any warnings" -ForegroundColor Yellow
|
|
}
|
|
} else {
|
|
Write-Host "❌ Data import failed with exit code: $exitCode" -ForegroundColor Red
|
|
Write-Host "💡 Check data format, field mappings, and validation rules" -ForegroundColor Yellow
|
|
exit $exitCode
|
|
}
|
|
} catch {
|
|
Write-Host "Error executing sf command: $($_.Exception.Message)" -ForegroundColor Red
|
|
exit 1
|
|
}
|