Salesforce DXでAPIからスクラッチ組織を作成する

## Salesforce DX
[SalesforceDX](https://developer.salesforce.com/platform/dx)がついに Open Betaになりました。一口にDXといってもいろいろあるみたいですが、とりあえず新規組織をプログラムから作成することができるようになったのが一番大きいでしょうか。これにより、まっさらな組織を簡単に手に入れることができるため、同じ組織を使い続けることで発生しがちな環境依存のテスト成功／失敗などが発生しにくくなるという利点があります。

公式でSalesforce DX CLI (sfdx)が提供されているので、そちらをダウンロードするのが本筋かもしれませんが、ちょっとセットアップに癖があるのでCI環境などではあまりどうかな、という感じです。特にフロントエンド開発が絡んでくるとやっぱりできればnpmなどで完結したい、というのはよくある話ではないかと思います。

## APIからのスクラッチ組織作成

少し中身を調べてみたところ、組織を作るには普通に通常のSObjectにレコードを作ればオッケーみたいな感じなので、組織作成をトリガするクライアントはJavaでもRubyでも、あるいはおそらくApexでも、まあなんでもありっぽいですね。

なので少しNode.js + jsforceでやってみた例がこちら(async/awaitを使っています)。

```js
import fs from 'fs';
import jsforce from 'jsforce';

/**
 *
 */
async function startScratchOrg(username, password, options) {
  // establish oauth2 connection to dev hub org using jsforce
  const hubConn = new jsforce.Connection({ ...options, version: '40.0' });
  const hubUserInfo = await hubConn.login(username, password);
  const hubUser = await hubConn.sobject('User').findOne({ Id: hubUserInfo.id }, 'Id,Username,Email');
  console.log(`Connected to developer hub org: username = ${hubUser.Username}`);
  const ScratchOrgInfo = hubConn.sobject('ScratchOrgInfo');
  try {
    await ScratchOrgInfo.describe();
  } catch (e) {
    throw new Error('Dev hub org is not enabled for this connection');
  }

  // create scratch org
  console.log('Creating scratch org....');
  const { id, success, errors } = await ScratchOrgInfo.create({
    Country: 'JP',
    Edition: 'Developer',
    OrgName: 'testorg',
    AdminEmail: hubUser.Email,
    ConnectedAppConsumerKey: options.clientId,
    ConnectedAppCallbackUrl: options.redirectUri,
  });
  if (!success) {
    console.error(errors);
    throw new Error('Error occurred while creating scratch org');
  }
  const orgInfo = await ScratchOrgInfo.retrieve(id);
  console.log(`New scratch org has been created. ${orgInfo.Name}`);

  // establish connection
  const loginUrl = `https://${orgInfo.SignupInstance}.salesforce.com`;
  const conn = new jsforce.Connection({ ...options, loginUrl });
  const userInfo = await conn.authorize(orgInfo.AuthCode);
  const user = await conn.sobject('User').findOne({ Id: userInfo.id });
  console.log(`Connected with scratch org: username = ${user.Username}`);

  // package deployment / data loading tasks from here
  // ...
}

startScratchOrg('admin@devhub.example.org', 'passw0rd', {
  // you should obtain client id / secret by registering a connected application 
  clientId: 'yyyyyyyyyyyyy',
  clientSecret: 'xxxxxx',
  redirectUri: 'http://localhost/callback',
});

```

Scratch Org作成後に生成されるAuth Codeは、OAuth2のauthorization code flowで得られる物と同じで有効期限が短いので、作成したらすぐにtokenリクエストまで行ってしまってrefresh tokenを取得するべきだと思います。取得したrefresh tokenを保存しておくことで次回以降も接続は可能になりますが、保存を忘れてしまうとその組織はおそらく二度と接続できないごみになってしまいます。まあそんなときもあせらず組織削除してしまえばいいかもしれないですが。

もしtokenの取得にJWT Bearerを使うのであれば、そのあたりおそらく問題ないでしょう。クレデンシャル情報の管理を考えるとJWT Bearerは証明書一つで済むので明らかに楽です。とりあえずはそちらを検討した方がいいでしょう。ちなみに以前[JWT Bearerを使ったOAuthのトークン取得について記事にしています](http://qiita.com/stomita/items/4542ce1b48e5fa849ef1)ので、そちらも参考にして下さい。

作成できるスクラッチ組織数にはリミットがあるので、いらなくなった組織はどんどん削除した方がいいと思います。削除するには、`ActiveScratchOrg`内のレコードエントリを削除します。`ActiveScratchOrg`のレコードを削除すると、ScratchOrgInfoのStatusが`Deleted`となります。`ScratchOrgInfo`のレコード自体を削除することでも組織削除はできる模様ですが、もしかしたらなにか悪影響あるかもしれません。

## 感想

実際、新規スクラッチ組織の作成にたかだか10秒もかからないので、そこはかなり頑張ったのではないでしょうかね。