Cloudship is a Clojure toolkit to explore and manipulate your salesforce instances. It’s designed to work from the repl against different orgs at he same time and supports
-
Auth Data from different Sources
-
Username + Password or Session in Configuration
-
sfdx
-
OAuth via web-server flow
-
-
Extensions to Configuration
-
get Username + Password from Keepass
-
connect via CipherCloud
-
your own extension
-
-
Convenient methods taking and returning Clojure Datastructures via
-
SOAP-Api
-
Bulk-Api
-
Meta-Api (describe and read for now)
-
csv-Files (cast with the describe-data of an existing org)
-
Include cloudship in your project or clone this project and start a repl using lein repl
(you will need Leiningen for this).
(require '[cloudship.data :as data])
(data/q :mycon.auth:web "Account" [:Id] {:limit 10})The second call should open a browser and starts an OAuth flow. You can then login into your Developer Org and when the flow is finished, cloudship will use the session the query the Ids of the first 10 Accounts on your org and return them.
Also the session is cached for one hour and every subsequent call using the key :mycon.auth:web will use the
cached session.
You are up and running to explore the api further.
Every function that talks to a Salesforce Org (cloudship.data and cloudship.metadata) takes a cloudship
as first argument (usally a keyword as in the quickstart).
While working with your data and metadata when you have an established connection is pretty straight forward, (and you can probably stop reading here if you only have one dev-org to toy around) the configuration and connection resolving is a bit sophisticated.
This is because cloudship is developed to easily access many orgs/sandboxes from the repl fast and
at the same time. For example if :myprod is configured to connect to your production org, :myprod:mydev
automaticly resolves to the sandbox mydev of your org and :myprod.v:40 connects to your production using
api-version 40.
So here is a explanation how the resolval works.
When cloudship resolves a keyword or a basic config map to a connection there are four steps:
-
parse keyword into basic config map (when starting with a keyword)
-
expand into a full config map
-
run an auth mechanism
-
cache this information (including the resulting session) for fast reuse
A keyword that describes a cloudship has the following syntax:
:org-name[:sandbox-name][.flag|.flag:opt]*where the org-name can be choosen freely, sandbox-name is the name of the sandbox you want to connect.
Given this keyword, cloudship will do the following:
-
check if there is a cached
CloudshipClientRecord for this keyword -
if not, build a property map from the keyword by
-
parsing the keyword into a initial prop map (with
:org-name:sandbox:flagsand:cache-name(the full keyword) as keys) -
continue with the [From Property Map]
-
To see this in action run
(cloudship.connection.props.keyword/kw->props :your:keyword)`Given a property map, cloudship will try to create an CloudshipClient Record by
-
deep-merges all found
cloudship.edninresource-folder,%user-home/.cloudship,current folder(in this order) to a big config map. -
deep-merges to a final prop map
-
all entries (except
:orgs) of config map (1.) -
all entries in
[:orgs :org-name]of config map -
all entries in
[:orgs :org-name :sandboxes "sandbox-name"]of config-map -
the initial prop map
-
-
add or coerce api-version (currently default is
53.0) -
build base-url from
:sandbox,:instanceand:my-domain-
providing nothing will result in
login.salesforce.com -
only sandbox will result in
test.salesforce.com -
only my-domain will result in
<my-domain>.my.salesforce.com -
my-domain, sandbox needs the instance (e.g. "86") as this cannot be resolved and will result in
<my-domain>--<sandbox>.cs<instance>.my.salesforce.com
-
-
add
https://to url if missing -
add proxy if there is a system default proxy
-
add
.<sandbox>to username if target is sandbox and it’s missing
To see 1. and 2. in action run
(cloudship.connection.props.load/find-and-merge-props "props from kw->props"})To see the final property map you can run
(cloudship.connection.props.core/->props :your:keyword:or:map)With the resulting property map, cloudship tries to auth against your org.
Currently there are three auth methods (you can configure with :auth-method in your config).
-
:soap- uses theloginmethod of the soap client (default)-
needs
:usernameand:password(supports:security-token) -
or just an existing
:session
-
-
:sfdx- gets session by callingsfdx:force:org:display -u ${:username or :org}] -
:web- uses the web server OAuth flow
Given the cloudship.edn
{:api-version "51.0"
:orgs {:org1 {:username "[email protected]"
:password "very-secret1!"
:sandboxes {"new" {:api-version "52.0"}}}
:my-dev {:kppath ["mydev" "login"]
:kpdb "dir/to/db.kdbx"
:my-domain "cloudship"}}}the keywords will result in the following property maps
(->props :org1) =>
{:api-version "51.0",
:full :org1,
:org "org1",
:password "very-secret1!",
:url "https://login.salesforce.com",
:username "[email protected]"}
(->props :org1:new) =>
; api-version is overwritten in config
; username and url are adjusted for the sandbox
{:api-version "52.0",
:base-username "[email protected]",
:full :org1:new,
:org "org1",
:password "very-secret1!",
:sandbox "new",
:url "https://test.salesforce.com",
:username "[email protected]"}
(->props :org1:other) =>
; nothing is overwritten, but username/url are adjusted for the sandbox
{:api-version "51.0",
:base-username "[email protected]",
:full :org1:other,
:org "org1",
:password "very-secret1!",
:sandbox "other",
:url "https://test.salesforce.com",
:username "[email protected]"}
(->props :org1.v:39) =>
; version is adjusted by the flag
{:api-version "39.0",
:full :org1.v:39,
:org "org1",
:password "very-secret1!",
:resolved-flags [{:flag-name "v", :opt "39"}],
:url "https://login.salesforce.com",
:username "[email protected]"}Flags are instructions to modify the connection properties before trying to
create the CloudshipClient Record.
A Flag can either be a simple String or a Map with {:flag-name string? …​}.
The Multimethod cloudship.connection.props.flags/resolve-flag is called
on the flag and needs to return a function Property Map → Property Map.
All Flags are resolved and applied in order until there are no more left.
The following flags are included:
-
v- Version flag that sets the version to it’s opt e.g.v:39. -
kp- Keepass, reads username and password from a keepass file.-
needs
:kpdb(path to keepath db) and:kppath(vector of path in keepath db). -
:kppasscan be set, otherwise will be prompted.
-
-
authshort to set the auth-method e.g.auth:sfdx -
sfdxshort forauth:sfdx-
needs
sfdxexecutable in path -
uses
:usernameor:orgas$usernameOrAlias
-
-
webshort forauth:web -
cc- CipherCloud, changes the url to a CipherCloud url.-
needs
:cc-domainand:cc-alias. -
you probably won’t need this.
-
-
There a methods the SOAP/Metadata API provides that are not available in the cloudship API. If you need one of them you can directly call the methods you need against the
PartnerConnection/MetadataConnectionwhich are included in the(info [])call (path:[$client-type :base :connection]).
-
As the Bulk-API is build with the
csvcontent type, inner queries are not supported -
Also because of this, it’s impossible to know wether
SELECT Account.Phone FROM ContactreturnsAccount.PhoneasnilbecausePhoneisnilor because there is no Account attached to the Contact. In this case{:type "Contact" :Account nil}will be returned. To avoid this, make sure to query a related field that can’t benil(likeId).