session/session.go (102 lines of code) (raw):
// Package session provides handles creation of a Salesforce session
package session
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/elastic/go-sfdc"
"github.com/elastic/go-sfdc/credentials"
)
// Session is the authentication response. This is used to generate the
// authroization header for the Salesforce API calls.
type Session struct {
response *sessionPasswordResponse
config sfdc.Configuration
}
// Clienter interface provides the HTTP client used by the
// the resources.
type Clienter interface {
Client() *http.Client
}
// InstanceFormatter is the session interface that
// formaters the session instance information used
// by the resources.
//
// InstanceURL will return the Salesforce instance.
//
// AuthorizationHeader will add the authorization to the
// HTTP request's header.
type InstanceFormatter interface {
InstanceURL() string
AuthorizationHeader(*http.Request)
Clienter
}
// ServiceFormatter is the session interface that
// formats the session for service resources.
//
// ServiceURL provides the service URL for resources to
// user.
type ServiceFormatter interface {
InstanceFormatter
ServiceURL() string
}
type sessionPasswordResponse struct {
AccessToken string `json:"access_token"`
InstanceURL string `json:"instance_url"`
ID string `json:"id"`
TokenType string `json:"token_type"`
IssuedAt string `json:"issued_at"`
Signature string `json:"signature"`
}
const oauthEndpoint = "/services/oauth2/token"
// Open is used to authenticate with Salesforce and open a session. The user will need to
// supply the proper credentials and a HTTP client.
func Open(config sfdc.Configuration) (*Session, error) {
if config.Credentials == nil {
return nil, errors.New("session: configuration crendentials can not be nil")
}
if config.Client == nil {
return nil, errors.New("session: configuration client can not be nil")
}
if config.Version <= 0 {
return nil, errors.New("session: configuration version can not be less than zero")
}
request, err := passwordSessionRequest(config.Credentials)
if err != nil {
return nil, err
}
response, err := passwordSessionResponse(request, config.Client)
if err != nil {
return nil, err
}
session := &Session{
response: response,
config: config,
}
return session, nil
}
func passwordSessionRequest(creds *credentials.Credentials) (*http.Request, error) {
oauthURL := creds.URL() + oauthEndpoint
body, err := creds.Retrieve()
if err != nil {
return nil, err
}
request, err := http.NewRequest(http.MethodPost, oauthURL, body)
if err != nil {
return nil, err
}
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
request.Header.Add("Accept", "application/json")
return request, nil
}
func passwordSessionResponse(request *http.Request, client *http.Client) (*sessionPasswordResponse, error) {
response, err := client.Do(request)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("session response error: %d %s", response.StatusCode, response.Status)
}
decoder := json.NewDecoder(response.Body)
defer response.Body.Close()
var sessionResponse sessionPasswordResponse
err = decoder.Decode(&sessionResponse)
if err != nil {
return nil, err
}
return &sessionResponse, nil
}
// InstanceURL will retuern the Salesforce instance
// from the session authentication.
func (session *Session) InstanceURL() string {
return session.response.InstanceURL
}
// ServiceURL will return the Salesforce instance for the
// service URL.
func (session *Session) ServiceURL() string {
return fmt.Sprintf("%s/services/data/v%d.0", session.response.InstanceURL, session.config.Version)
}
// AuthorizationHeader will add the authorization to the
// HTTP request's header.
func (session *Session) AuthorizationHeader(request *http.Request) {
auth := fmt.Sprintf("%s %s", session.response.TokenType, session.response.AccessToken)
request.Header.Add("Authorization", auth)
}
// Client returns the HTTP client to be used in APIs calls.
func (session *Session) Client() *http.Client {
return session.config.Client
}