Published on

Making SOQL Queries from Emacs

Authors

Need to make a SOQL query, but hate leaving Emacs?

Prerequisites

Request

While raw curl calls as entirely possible to do from Emacs, the request package makes this much easier.

After using whatever package manager of your liking to download it, let's ensure it's loaded.

  (require 'request)

Connected App

A username and password isn't enough to request a token programmatically, so we're going to need to create a Connected App.

Log in as an organization administrator, and assuming you're in the Lightning UI.

  1. Setup > Apps > App Manager > New Connected App Org Babel
  2. Enable OAuth settings, Device Flow, and give full OAuth scopes Org Babel
  3. Obtain the Consumer Key and Secret from Consumer Details Org Babel Org Babel

Assign the variables required for our request

Now that we have our Consumer Key and Secret, we're mostly there. Provided below is a org-babel source block to assign all the required variables for our subsequent request.

  #+begin_src emacs-lisp
          (setq sfdc.orgid "00D************")
          (setq sfdc.loginurl "https://*****************.salesforce.com/")
          (setq sfdc.url "https://***********************.lightning.force.com/")
          (setq sfdc.casecret "********")
          (setq sfdc.cakey "********")
          (setq sfdc.username "my@user.com")
          (setq sfdc.password "*********")  
#+end_src

sfdc.loginurl is the instance url of your Salesforce org, and sfdc.url is the My Domain url.

Once you've updated the values in accordance with your org, evaluate the source block.

Requesting the token

We're ready to request our token. You can either evaluate this org-babel code block or otherwise make the refresh-token functional available to Emacs.

#+begin_src emacs-lisp
  (defun refresh-token ()
    "Obtain a current Salesforce session id token"
    (request (concatenate 'string sfdc.loginurl "/services/oauth2/token")
      :type "POST"
      :params `(("client_id" . ,sfdc.cakey) ("client_secret" . ,sfdc.casecret) ("username" . ,sfdc.username) ("password" . ,sfdc.password) (grant_type . "password") )
      :parser 'json-read
      :sync t
      :success (cl-function
                (lambda (&key data &allow-other-keys)
                  (setq sfdc.token (assoc-default 'access_token data)

  )))))
#+end_src

Finally execute refresh-token and ensure sfdc.token is now defined. (describe-variable)

SOQL Query

We're finally ready to use our token to execute a SOQL query.

I'm determining the latest api version of the Salesforce instance from /services/data, and then make a SOQL query against it. An error code of 401 should call refresh-token, so while your request may fail with an expired token, you can just rerun it.

#+begin_src emacs-lisp :results verbatim :wrap src json 
  (request (concatenate 'string sfdc.loginurl "/services/data")
  :parser 'json-read
  :sync t
  :success (cl-function
  (lambda (&key data &allow-other-keys)
  ( setq sfdc.endpoint (concatenate 'string sfdc.loginurl (cdr (assoc 'url (elt (reverse data) 0 ))))))))


  (defun sfdc-query(query)
  (setq sfdc.auth (concatenate 'string "Bearer " sfdc.token ))

  (request-response-data (request (concatenate 'string sfdc.endpoint "/query")
  :type "GET"
  :headers `( ("Content-Type" . "application/json") (grant_type . "password") ("Authorization" . ,sfdc.auth ) )
  :params `(("q" . ,query) )
  :parser 'json-read
  :sync t
  :success (cl-function
  (lambda (&key data &allow-other-keys)
  (print (assoc-default `records data))))
  :status-code '((401 . (lambda (&rest _) (refresh-token)))))))

  (sfdc-query "select id from contact limit 10")
#+end_src
Org Babel Using org-babel, we see the output placed directly into our org file.