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
50 changes: 50 additions & 0 deletions command/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func init() {
recordCmd.AddCommand(recordGetCmd)
recordCmd.AddCommand(recordCreateCmd)
recordCmd.AddCommand(recordUpdateCmd)
recordCmd.AddCommand(recordUpsertCmd)
recordCmd.AddCommand(recordDeleteCmd)
recordCmd.AddCommand(recordMergeCmd)
recordCmd.AddCommand(recordUndeleteCmd)
Expand Down Expand Up @@ -65,6 +66,33 @@ var recordUpdateCmd = &cobra.Command{
},
}

var recordUpsertCmd = &cobra.Command{
Use: "upsert <object> <extid>:<value> [<field>:<value>...]",
Short: "Upsert record using external ID",
Long: `
Upsert (insert or update) a record using an external ID field.

If a record with the given external ID value exists, it will be updated.
Otherwise, a new record will be created.

Usage:

force record upsert <object> <extid>:<value> [<field>:<value>...]
`,
Example: `
force record upsert Account External_Id__c:ABC123 Name:"Acme Corp" Industry:Technology
force record upsert Contact Email:john@example.com FirstName:John LastName:Doe
`,
Args: cobra.MinimumNArgs(2),
DisableFlagsInUseLine: true,
Run: func(cmd *cobra.Command, args []string) {
object := args[0]
extIdPair := args[1]
fields := args[2:]
runRecordUpsert(object, extIdPair, fields)
},
}

var recordDeleteCmd = &cobra.Command{
Use: "delete <object> <id>",
Short: "Delete record",
Expand Down Expand Up @@ -115,6 +143,7 @@ Usage:
force record create <object> [<fields>]
force record update <object> <id> [<fields>]
force record update <object> <extid>:<value> [<fields>]
force record upsert <object> <extid>:<value> [<fields>]
force record delete <object> <id>
force record merge <object> <masterId> <duplicateId>
force record undelete <id>
Expand All @@ -125,6 +154,7 @@ Usage:
force record create User Name:"David Dollar" Phone:0000000000
force record update User 00Ei0000000000 State:GA
force record update User username:user@name.org State:GA
force record upsert Account External_Id__c:ABC123 Name:"Acme Corp"
force record delete User 00Ei0000000000
force record merge Contact 0033c00002YDNNWAA5 0033c00002YDPqkAAH
force record undelete 0033c00002YDNNWAA5
Expand Down Expand Up @@ -158,6 +188,26 @@ func runRecordUpdate(object string, id string, fields []string) {
fmt.Println("Record updated")
}

func runRecordUpsert(object string, extIdPair string, fields []string) {
split := strings.SplitN(extIdPair, ":", 2)
if len(split) != 2 {
ErrorAndExit("Invalid external ID format. Use <extid>:<value>")
}
extIdField := split[0]
extIdValue := split[1]

attrs := parseArgumentAttrs(fields)
result, err := force.UpsertRecord(object, extIdField, extIdValue, attrs)
if err != nil {
ErrorAndExit("Failed to upsert record: %s", err.Error())
}
if result.Created {
fmt.Printf("Record created: %s\n", result.Id)
} else {
fmt.Println("Record updated")
}
}

func runRecordMerge(object, masterId, duplicateId string) {
err := force.Partner.Merge(object, masterId, duplicateId)
if err != nil {
Expand Down
25 changes: 25 additions & 0 deletions lib/force.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ type ForceCreateRecordResult struct {
Success bool
}

type ForceUpsertResult struct {
Id string
Created bool
Success bool
}

type ForceLimits map[string]ForceLimit

type ForceLimit struct {
Expand Down Expand Up @@ -1340,6 +1346,25 @@ func (f *Force) UpdateRecord(sobject string, id string, attrs map[string]string)
return
}

func (f *Force) UpsertRecord(sobject string, externalIdField string, externalIdValue string, attrs map[string]string) (result ForceUpsertResult, err error) {
url := fmt.Sprintf("%s/services/data/%s/sobjects/%s/%s/%s", f.Credentials.InstanceUrl, apiVersion, sobject, externalIdField, externalIdValue)
body, err := f.httpPatch(url, attrs)
if err != nil {
return
}
// If body is empty, it was an update (HTTP 204)
if len(body) == 0 {
result.Created = false
result.Success = true
return
}
// Otherwise it was a create (HTTP 201) with the new Id
json.Unmarshal(body, &result)
result.Created = true
result.Success = true
return
}

func (f *Force) DeleteRecord(sobject string, id string) (err error) {
url := fmt.Sprintf("%s/services/data/%s/sobjects/%s/%s", f.Credentials.InstanceUrl, apiVersion, sobject, id)
_, err = f.httpDelete(url)
Expand Down
Loading