cloudship
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)
-
Quickstart
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.
How it works
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.
Getting a Connection
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
Start with a Keyword
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
CloudshipClient
Record 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
:flags
and: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)`
Expand Property Map
Given a property map, cloudship will try to create an CloudshipClient
Record by
-
deep-merges all found
cloudship.edn
inresource-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
43.0
) -
build base-url from
:sandbox
,:instance
and: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)
Run Auth
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 thelogin
method of the soap client (default)-
needs
:username
and: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-
you can provide
:consumer-key
,:consumer-secret
,callback-port
,callback-timeout
-
if you connect to an own app, make sure to set the
callback-url
tohttps://localhost[:port]
.
-
Resolve Examples
Given the cloudship.edn
{:api-version "40.0"
:orgs {:org1 {:username "my@username.de"
:password "very-secret1!"
:sandboxes {"new" {:api-version "41.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 "40.0",
:full :org1,
:org "org1",
:password "very-secret1!",
:url "https://login.salesforce.com",
:username "my@username.de"}
(->props :org1:new) =>
; api-version is overwritten in config
; username and url are adjusted for the sandbox
{:api-version "41.0",
:base-username "my@username.de",
:full :org1:new,
:org "org1",
:password "very-secret1!",
:sandbox "new",
:url "https://test.salesforce.com",
:username "my@username.de.new"}
(->props :org1:other) =>
; nothing is overwritten, but username/url are adjusted for the sandbox
{:api-version "40.0",
:base-username "my@username.de",
:full :org1:other,
:org "org1",
:password "very-secret1!",
:sandbox "other",
:url "https://test.salesforce.com",
:username "my@username.de.other"}
(->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 "my@username.de"}
Resolving Flags
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). -
:kppass
can be set, otherwise will be prompted.
-
-
auth
short to set the auth-method e.g.auth:sfdx
-
sfdx
short forauth:sfdx
-
needs
sfdx
executable in path -
uses
:username
or:org
as$usernameOrAlias
-
-
web
short forauth:web
-
cc
- CipherCloud, changes the url to a CipherCloud url.-
needs
:cc-domain
and:cc-alias
. -
you probably won’t need this.
-
Advanced Topics
Missing API Methods
-
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
/MetadataConnection
which are included in the(info [])
call (path:[$client-type :base :connection]
).
Known Issues
Java SDK Bulk API
-
As the Bulk-API is build with the
csv
content type, inner queries are not supported -
Also because of this, it’s impossible to know wether
SELECT Account.Phone FROM Contact
returnsAccount.Phone
asnil
becausePhone
isnil
or 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
).
License
Copyright © 2019 Albrecht Schmidt
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.