added addl wrappers
This commit is contained in:
345
sf-data-import.ps1
Normal file
345
sf-data-import.ps1
Normal file
@@ -0,0 +1,345 @@
|
||||
#!/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
|
||||
|
||||
.PARAMETER SObject
|
||||
Target sObject type
|
||||
|
||||
.PARAMETER Operation
|
||||
Operation: insert, update, upsert (default: insert)
|
||||
|
||||
.PARAMETER ExternalId
|
||||
External ID field for upsert/update operations
|
||||
|
||||
.PARAMETER TargetOrg
|
||||
Target org username or alias
|
||||
|
||||
.PARAMETER Bulk
|
||||
Use bulk API for large datasets
|
||||
|
||||
.PARAMETER Wait
|
||||
Wait time in minutes (default: 10)
|
||||
|
||||
.PARAMETER BatchSize
|
||||
Batch size for bulk operations (default: 10000)
|
||||
|
||||
.PARAMETER IgnoreErrors
|
||||
Continue on errors (don't fail entire job)
|
||||
|
||||
.PARAMETER Verbose
|
||||
Enable verbose output
|
||||
|
||||
.PARAMETER Help
|
||||
Show this help message
|
||||
|
||||
.EXAMPLE
|
||||
.\sf-data-import.ps1 -File accounts.csv -SObject Account
|
||||
.\sf-data-import.ps1 -File contacts.json -SObject Contact -Operation upsert -ExternalId Email
|
||||
.\sf-data-import.ps1 -File leads.csv -SObject Lead -Bulk -BatchSize 5000
|
||||
.\sf-data-import.ps1 -File updates.csv -SObject Account -Operation update -ExternalId 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(Mandatory)]
|
||||
[string]$File,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$SObject,
|
||||
|
||||
[ValidateSet("insert", "update", "upsert")]
|
||||
[string]$Operation = "insert",
|
||||
|
||||
[string]$ExternalId,
|
||||
[string]$TargetOrg,
|
||||
[switch]$Bulk,
|
||||
[int]$Wait = 10,
|
||||
[int]$BatchSize = 10000,
|
||||
[switch]$IgnoreErrors,
|
||||
[switch]$Verbose,
|
||||
[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 ($Verbose) {
|
||||
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 ($Verbose) {
|
||||
$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 ($Verbose) {
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user