Jean-Philippe Monette

How to query Salesforce APIs in Go

jeudi 7 janvier 2016

·

4 minutes de lecture

Interacting with the Salesforce APIs can be tricky if your favorite language doesn't have modules or packages available to handle the complexity. Of course, you can build one from scratch. However, chances you'll hit a couple of roadblocks along the way.

A couple of weeks ago, I started working on a package to interact with the Salesforce APIs in Go, currently available on Github. My main motivations were to use it for my personal projects, to leverage existing packages such as golang.org/x/oauth2 (since all the other packages seem to rebuild this part of the logic) and to improve my understanding of both the Salesforce APIs and Go.


Here's a quick example on how to execute an SOQL query using the package.

First of, you will have to create an http client implementing the OAuth2 specification. To achieve this, github.com/jpmonette/force is leveraging the oauth2 package built by Google.

conf := &oauth2.Config{ ClientID: "CLIENT-ID", ClientSecret: "CLIENT-SECRET", Endpoint: oauth2.Endpoint{ TokenURL: "TOKEN-URL", }, } token, \_ := conf.PasswordCredentialsToken(oauth2.NoContext, "USERNAME", "PASSWORD") instanceUrl, \_ := token.Extra("instance_url").(string) client := conf.Client(oauth2.NoContext, token)

Here are the steps to obtain a functional http client:

  1. Instantiating an oauth2.Config required to query an access_token from Salesforce - details are provided when configuring a Connected App;
  2. Requesting an access_token using the Username-Password OAuth Flow;
  3. Storing the instance_url from the reponse, required by the github.com/jpmonette/force package to query the correct instance;
  4. Generating a http client implementing the OAuth2 specification.

Now that we have a functional http client, here's how to use it with the force package:

c, \_ := force.NewClient(client, instanceUrl) var data QueryReponse c.Query("SELECT Id, Name FROM Account LIMIT 10", &data) fmt.Println("Total size: "+data.TotalSize) fmt.Println("First ID: "+data.Records[0].Id)

In the previous snippet, we are:

  1. Generating a force client;
  2. Creating a data variable using the type QueryReponse (defined later)
  3. Executing an SOQL query using the func (c *Client) Query(query string, v interface{}) (err error) function;
  4. Printing the amount of records returned;
  5. Printing the Id of the first record found.

At last, you have to define the QueryReponse struct:

type QueryReponse struct { TotalSize int `json:"totalSize"` Records []struct { ID string `json:"Id"` Name string `json:"Name"` Attributes struct { Type string `json:"type"` URL string `json:"url"` } `json:"attributes"` } `json:"records"` }

Now, here's the whole example:

package main import ( "os" "fmt" "github.com/jpmonette/force" "golang.org/x/oauth2" ) func main() { conf := &oauth2.Config{ ClientID: os.Getenv("SFCLIENTID"), ClientSecret: os.Getenv("SFCLIENTSECRET"), Endpoint: oauth2.Endpoint{ TokenURL: os.Getenv("SFTOKENURL"), }, } token, _ := conf.PasswordCredentialsToken(oauth2.NoContext, os.Getenv("SFUSERNAME"), os.Getenv("SFPASSWORD")) instanceUrl, _ := token.Extra("instance_url").(string) client := conf.Client(oauth2.NoContext, token) c, _ := force.NewClient(client, instanceUrl) var data QueryReponse c.Query("SELECT Id, Name FROM Account LIMIT 10", &data) fmt.Println("Total size: " + data.TotalSize) fmt.Println("First ID: " + data.Records[0].Id) } // QueryReponse is used to Unmarshal the SOQL query response type QueryReponse struct { TotalSize int `json:"totalSize"` Records []struct { ID string `json:"Id"` Name string `json:"Name"` Attributes struct { Type string `json:"type"` URL string `json:"url"` } `json:"attributes"` } `json:"records"` }

Now - if you go get github.com/jpmonette/force and go run main.go, the console should output something similar to:

~ » go run main.go Total size: 10 First ID: 001b000002T9ak4

Notes: For simplicity, error checking has been omitted in the example.


Please note that this package is inspired by the go-github package. Since it is a work in progress, you are more than welcomed to send pull requests to support additional features.

© jpmonette.net

TwitterGithubTelegram