Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ $ source <(force completion bash)
apiversion Display/Set current API version
bigobject Manage big objects
bulk Load csv file or query data using Bulk API
bulk2 Load csv file or query data using Bulk API 2.0
completion Generate the autocompletion script for the specified shell
create Creates a new, empty Apex Class, Trigger, Visualforce page, or Component.
datapipe Manage DataPipes
Expand Down Expand Up @@ -176,6 +177,51 @@ Export allows you to fetch all codes from your org to local machine. This comman
Includes notification library, [beeep](https://github.com/gen2brain/beeep), that displays desktop notifications across platforms. Currently, only the `push` and `test` methods are displaying notifications.


### bulk2
Bulk API 2.0 provides a REST-based interface for data loading and querying with automatic batch management. Unlike Bulk API 1.0, it handles batch splitting automatically and uses CSV format.

# Insert records
force bulk2 insert Account accounts.csv --wait

# Update records
force bulk2 update Account updates.csv --wait

# Upsert records using external ID
force bulk2 upsert -e External_Id__c Account data.csv --wait

# Delete records
force bulk2 delete Account deletes.csv --wait

# Hard delete records
force bulk2 hardDelete Account harddeletes.csv --wait

# Query records
force bulk2 query "SELECT Id, Name FROM Account" --wait

# Query including deleted and archived records
force bulk2 query "SELECT Id, Name FROM Account" --query-all --wait

# Check job status
force bulk2 job <jobId>

# List all ingest jobs
force bulk2 jobs

# List all query jobs
force bulk2 jobs --query

# Get job results
force bulk2 results <jobId>

# Get only failed results
force bulk2 results <jobId> --failed

# Abort a job
force bulk2 abort <jobId>

# Delete a job
force bulk2 delete-job <jobId>

### limits
Limits will display limits information for your organization.
- Max is the limit total for the organization
Expand Down
113 changes: 113 additions & 0 deletions bubbles/bulk2job.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package bubbles

import (
"fmt"

force "github.com/ForceCLI/force/lib"
"github.com/charmbracelet/bubbles/progress"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)

var (
bulk2InfoStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#4E4E4E")).TabWidth(lipgloss.NoTabConversion)
bulk2StatusStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#89D5C9")).TabWidth(lipgloss.NoTabConversion)
)

type Bulk2JobModel struct {
force.Bulk2IngestJobInfo
progress progress.Model
}

type NewBulk2JobStatusMsg struct {
force.Bulk2IngestJobInfo
}

func NewBulk2JobModel() Bulk2JobModel {
return Bulk2JobModel{
progress: progress.New(progress.WithDefaultGradient()),
}
}

func (m Bulk2JobModel) Init() tea.Cmd {
return nil
}

func (m Bulk2JobModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.progress.Width = min(msg.Width-padding*2-4, maxWidth)
return m, nil

case NewBulk2JobStatusMsg:
m.Bulk2IngestJobInfo = msg.Bulk2IngestJobInfo
var cmd tea.Cmd
if m.IsTerminal() {
cmd = m.progress.SetPercent(1.0)
} else if m.State == force.Bulk2JobStateInProgress {
cmd = m.progress.SetPercent(0.5)
} else {
cmd = m.progress.SetPercent(0.1)
}
return m, cmd

case tea.KeyMsg:
switch msg.String() {
case "q", "ctrl+c":
return m, tea.Quit
}

case progress.FrameMsg:
progressModel, cmd := m.progress.Update(msg)
m.progress = progressModel.(progress.Model)
return m, cmd

case QuitMsg:
return m, tea.Quit
}
return m, nil
}

func (m Bulk2JobModel) View() string {
header := headerStyle.Render("Bulk API 2.0 Job Status")

var infoMsg = `
Id %s
State %s
Operation %s
Object %s
Api Version %.1f

Created By Id %s
Created Date %s
Content Type %s
`

var statusMsg = `
Number Records Processed %d
Number Records Failed %d
Retries %d

Total Processing Time %d ms
Api Active Processing Time %d ms
Apex Processing Time %d ms
`

components := []string{
lipgloss.JoinVertical(lipgloss.Top, header,
bulk2InfoStyle.Render(fmt.Sprintf(infoMsg,
m.Id, m.State, m.Operation, m.Object, m.ApiVersion,
m.CreatedById, m.CreatedDate, m.ContentType)),
m.progress.View(),
bulk2StatusStyle.Render(fmt.Sprintf(statusMsg,
m.NumberRecordsProcessed, m.NumberRecordsFailed, m.Retries,
m.TotalProcessingTime, m.ApiActiveProcessingTime, m.ApexProcessingTime))),
}

if m.ErrorMessage != "" {
errorMsg := failureStyle.Render(fmt.Sprintf("Error: %s", m.ErrorMessage))
components = append(components, errorMsg)
}

return lipgloss.JoinVertical(lipgloss.Top, components...)
}
Loading
Loading