The project runs existing salesforce reports, adding date filters to chunk the report into smaller portions. The data is then reassembled and returned.
- create a report in salesforce, selecting which fields you want exported
- in the report specify your constraints (filters) - basically the report as you want it, without the date filters
- save the report and take it's report-id (easiest way is to use the URL)
- get a security token for API SOAP logins from salesforce for your user
type GetReportInput struct {
Debug bool // if set, will print what it's doing
ReportId string // sfdc report ID
DateFieldName string // name of the field to use for date chunks
IncrementDays int // number of days to do at once each time
StartDate time.Time // date from which to get report
EndDate time.Time // date to which to get report
}
type GetReportOutput struct {
Rows []GetReportRow `json:"rows"` // a list of rows with their colums in each row
ColumnNames []string `json:"columnNames"` // a list of column names present; does not guarantee all columns exist in each row, user must check this for each row when parsing
}
type GetReportRow map[string]string // definition of a row: map[column-name]column-value
Create reporting object with login details
reports := &sfdc.Reporting{
Sandbox: false,
User: "...",
Password: "...",
SecToken: "...",
}
Login
err := reports.Login()
if err != nil {
log.Fatal(err)
}
fmt.Println(reports.String())
Get the report - example - get last month, but do not get today (isn't full day finished yet), chunk by 7-day increments NOTE: the start/end dates are INCLUSIVE, so the day present in endDate will also be exported
out, err := reports.GetReport(&sfdc.GetReportInput{
ReportId: "...",
DateFieldName: "...",
IncrementDays: 7,
StartDate: time.Now().AddDate(0, -1, -1),
EndDate: time.Now().AddDate(0, 0, -1),
Debug: true,
})
if err != nil {
log.Fatal(err)
}
Just print the result
fmt.Println(out.String())
Basic example for getting a report for a selected date range, chunking it into 7-day increments for each API call, and storing the result in a CSV file.
package main
import (
"encoding/csv"
"log"
"os"
"time"
"github.com/bestmethod/sfdc"
)
func main() {
// create object with login details
reports := &sfdc.Reporting{
Sandbox: false,
User: "...",
Password: "...",
SecToken: "...",
}
// login
err := reports.Login()
if err != nil {
log.Fatalf("sfdc login failed: %s", err)
}
// create csv file for writing
fd, err := os.OpenFile("out.csv", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
log.Fatalf("failed to open csv for writing: %s", err)
}
defer fd.Close()
// open csv writer
csvWriter := csv.NewWriter(fd)
defer csvWriter.Flush()
// get the report - example - get last month - do not get today (isn't full day), chunk by 7-day increments
// NOTE: the start/end dates are INCLUSIVE, so the day present in endDate will also be exported
out, err := reports.GetReport(&sfdc.GetReportInput{
ReportId: "...",
DateFieldName: "...",
IncrementDays: 7,
StartDate: time.Now().AddDate(0, -1, -1),
EndDate: time.Now().AddDate(0, 0, -1),
Debug: true,
})
if err != nil {
log.Fatalf("failed to get report: %s", err)
}
// write out csv header row
err = csvWriter.Write(out.ColumnNames)
if err != nil {
log.Fatalf("failed to write csv header row: %s", err)
}
// write data rows
for _, row := range out.Rows {
col := []string{}
for _, colName := range out.ColumnNames {
colVal, ok := row[colName]
if !ok {
colVal = ""
}
col = append(col, colVal)
}
err = csvWriter.Write(col)
if err != nil {
log.Fatalf("failed to write csv rows: %s", err)
}
}
}
Example includes a yaml configuration file, support for multiple reports and date ranges, storing to csv files.
connect:
user: "some@email.com"
password: "user-password"
secToken: "wefhhuih934"
sandbox: false
reports:
- fileName: "cases.csv"
reportId: "00O1x000001z1xDIEF"
dateFieldName: "Last Modified"
incrementDays: 28
startDate: "2021-07-01"
endDate: "2021-07-31"
debug: true
- fileName: "caseHistory.csv"
reportId: "00O1x000001z1xEIGE"
dateFieldName: "Edit Date"
incrementDays: 28
startDate: "2021-07-01"
endDate: "2021-07-31"
debug: true
package main
import (
"encoding/csv"
"io/ioutil"
"log"
"os"
"time"
"gopkg.in/yaml.v2"
"github.com/bestmethod/sfdc"
)
type Salesforce struct {
Connect struct {
User string `yaml:"user"`
Password string `yaml:"password"`
SecToken string `yaml:"secToken"`
Sandbox bool `yaml:"sandbox"`
} `yaml:"connect"`
Reports []struct {
FileName string `yaml:"fileName"`
ReportID string `yaml:"reportId"`
DateFieldName string `yaml:"dateFieldName"`
IncrementDays int `yaml:"incrementDays"`
StartDate string `yaml:"startDate"`
EndDate string `yaml:"endDate"`
Debug bool `yaml:"debug"`
startDate time.Time
endDate time.Time
} `yaml:"reports"`
reports *sfdc.Reporting
}
func main() {
s := new(Salesforce)
conf, err := ioutil.ReadFile("config.yml")
if err != nil {
log.Printf("ERROR opening config.yml: %s", err)
os.Exit(1)
}
err = yaml.Unmarshal(conf, s)
if err != nil {
log.Printf("ERROR loading config.yml: %s", err)
os.Exit(1)
}
s.reports = &sfdc.Reporting{
Sandbox: false,
User: s.Connect.User,
Password: s.Connect.Password,
SecToken: s.Connect.SecToken,
}
err = s.reports.Login()
if err != nil {
log.Printf("sfdc login failed: %s", err)
os.Exit(1)
}
for i := range s.Reports {
s.Reports[i].startDate, err = time.Parse("2006-01-02", s.Reports[i].StartDate)
if err != nil {
log.Printf("start date wrong format: %s", err)
os.Exit(1)
}
s.Reports[i].endDate, err = time.Parse("2006-01-02", s.Reports[i].EndDate)
if err != nil {
log.Printf("end date wrong format: %s", err)
os.Exit(1)
}
err = s.getReport(i)
if err != nil {
log.Printf("sfdc reporting failed: %s", err)
os.Exit(1)
}
}
}
func (s *Salesforce) getReport(reportNo int) error {
fd, err := os.OpenFile(s.Reports[reportNo].FileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer fd.Close()
csvWriter := csv.NewWriter(fd)
defer csvWriter.Flush()
out, err := s.reports.GetReport(&sfdc.GetReportInput{
ReportId: s.Reports[reportNo].ReportID,
DateFieldName: s.Reports[reportNo].DateFieldName,
IncrementDays: s.Reports[reportNo].IncrementDays,
StartDate: s.Reports[reportNo].startDate,
EndDate: s.Reports[reportNo].endDate,
Debug: s.Reports[reportNo].Debug,
})
if err != nil {
return err
}
err = csvWriter.Write(out.ColumnNames)
if err != nil {
return err
}
for _, row := range out.Rows {
col := []string{}
for _, colName := range out.ColumnNames {
colVal, ok := row[colName]
if !ok {
colVal = ""
}
col = append(col, colVal)
}
err = csvWriter.Write(col)
if err != nil {
return err
}
}
return nil
}