Salesforce Canvas + JSforceでレコードの作成・検索をやってみる

# はじめに

https://qiita.com/takaHAL/items/6fa0d76a4056045d76bc

https://qiita.com/takaHAL/items/9150fae5dce87c90ff3a

キャンバスアプリケーションの作成、イベントの作成を前回まで行いました。
今回はキャンバスアプリケーションに渡ってきた認証情報を使って
JSforceでSalesforceの取引先レコードを作成・検索をしようと思います。

:::note
検証につかう環境は、trailheadのハンズオン環境です。
出てくるデータも初期のデモデータになります。
:::

# 前提条件

- キャンバスアプリケーションの設定をした接続アプリケーションがあること
- キャンバスアプリケーションとして呼び出すWebサイトがあること

# JSforceとは

https://jsforce.github.io/start/#salesforce-canvas

JavaScriptからSalesforceのAPIを簡易的に実行できるライブラリです。
npmとしても公開されているので、AWS Lambda（Node） + JSforceで利用できたりと非常に便利です。

https://www.npmjs.com/package/jsforce

# 今日使うコードの完成系はこちら

https://github.com/jsforce/jsforce/blob/master/build/jsforce.min.js

jsforce.min.jsをダウロードして配置しています。

コードはGitHubにもおいてます。

https://github.com/takaHAL/canvasAppTest



```html: index.html

<html>
  <head>
    <script src="./js/canvas-all.js"></script>
    <script src="./js/jsforce.min.js"></script>
  </head>
  <body>
    <h1>埋め込みテスト</h1>
    <div id="js-canvasArea">表示エリア</div>
    <button d="js-insertBtn">取引先作成ボタン</button>
    <script>
      Sfdc.canvas.client.refreshSignedRequest(function(data) {
        if (data.status === 200) {

          const signedRequest =  data.payload.response;
          const part = signedRequest.split('.')[1];
          const obj = JSON.parse(Sfdc.canvas.decode(part));
          const parseSignedRequest = Sfdc.canvas.decode(part);
          const conn = new jsforce.Connection({ signedRequest: parseSignedRequest });
           // 作成
          const insertBtn = document.getElementById('js-insertBtn');
          insertBtn.addEventListener('click', function () {
            conn.sobject('Account').create({ Name : '株式会社 Salesforce Canvas' }, function(err, ret) {
              if (err || !ret.success) { return console.error(err, ret); }
              console.log("Created record id : " + ret.id);
            });
          });

          // 検索
          conn.query('SELECT Id, Name FROM Account LIMIT 1', function(err, res) {
            if (err) {
              console.error(err)
            }
            const canvasArea = document.getElementById('js-canvasArea');
            canvasArea.innerHTML = JSON.stringify(res);
          });
        }
      });
    </script>
  </body>
</html>
```

# やってみる

https://jsforce.github.io/start/#salesforce-canvas

JSforceでは、公式でCanvasを使ったサンプルが紹介されているのでそれベースで今回進めていきます。

## JSforceの準備

今回は、npmを利用しないのでGitHubから`jsforce.min.js`をダウンロードします。

https://github.com/jsforce/jsforce/blob/master/build/jsforce.min.js

ダウンロードしたものをプロジェクトに配置し、スクリプト読み込みを追加しました。


```diff_html: index.html

<html>
  <head>
    <script src="./js/canvas-all.js"></script>
+   <script src="./js/jsforce.min.js"></script>
  </head>
  <body>
    <h1>埋め込みテスト</h1>
    <div id="js-canvasArea">表示エリア</div>
    <button d="js-insertBtn">取引先作成ボタン</button>
    <script>
      Sfdc.canvas.client.refreshSignedRequest(function(data) {
        if (data.status === 200) {

          const signedRequest =  data.payload.response;
          const part = signedRequest.split('.')[1];
          const parseSignedRequest = Sfdc.canvas.decode(part);
          const conn = new jsforce.Connection({ signedRequest: parseSignedRequest });
           // 作成
          const insertBtn = document.getElementById('js-insertBtn');
          insertBtn.addEventListener('click', function () {
            conn.sobject('Account').create({ Name : '株式会社 Salesforce Canvas' }, function(err, ret) {
              if (err || !ret.success) { return console.error(err, ret); }
              console.log("Created record id : " + ret.id);
            });
          });

          // 検索
          conn.query('SELECT Id, Name FROM Account LIMIT 1', function(err, res) {
            if (err) {
              console.error(err)
            }
            const canvasArea = document.getElementById('js-canvasArea');
            canvasArea.innerHTML = JSON.stringify(res);
          });
        }
      });
    </script>
  </body>
</html>
```

# 認証

SalesforceのAPIは基本
- 接続アプリケーションのキーや、ユーザのセキュリティトークンで認証
- 認証でトークンを取得
- 取得したトークンでAPIを実行

というステップになっているのですが

`接続アプリケーションのキーや、ユーザのセキュリティトークンで認証`ここの部分をCanvas + JSforceを使っていることで
意識することなくキャンバスアプリケーション側でAPIを実行できるのはいいと思います。

```diff_html: index.html

<html>
  <head>
    <script src="./js/canvas-all.js"></script>
    <script src="./js/jsforce.min.js"></script>
  </head>
  <body>
    <h1>埋め込みテスト</h1>
    <div id="js-canvasArea">表示エリア</div>
    <button d="js-insertBtn">取引先作成ボタン</button>
    <script>
      Sfdc.canvas.client.refreshSignedRequest(function(data) {
        if (data.status === 200) {

          const signedRequest =  data.payload.response;
          const part = signedRequest.split('.')[1];
+         const parseSignedRequest = Sfdc.canvas.decode(part);
+         const conn = new jsforce.Connection({ signedRequest: parseSignedRequest });
        }
      });
    </script>
  </body>
</html>
```


# 取引先レコードの作成

`取引先作成ボタン`というボタンを作成し、押すと`株式会社 Salesforce Canvas`というレコードが取引先に作成されるようにしました。

```diff_html: index.html

<html>
  <head>
    <script src="./js/canvas-all.js"></script>
    <script src="./js/jsforce.min.js"></script>
  </head>
  <body>
    <h1>埋め込みテスト</h1>
    <div id="js-canvasArea">表示エリア</div>
+   <button d="js-insertBtn">取引先作成ボタン</button>
    <script>
      Sfdc.canvas.client.refreshSignedRequest(function(data) {
        if (data.status === 200) {

          const signedRequest =  data.payload.response;
          const part = signedRequest.split('.')[1];
          const parseSignedRequest = Sfdc.canvas.decode(part);
          const conn = new jsforce.Connection({ signedRequest: parseSignedRequest });
          // 作成
+         const insertBtn = document.getElementById('js-insertBtn');
+         insertBtn.addEventListener('click', function () {
+         conn.sobject('Account').create({ Name : '株式会社 Salesforce Canvas' }, function(err, ret) {
+           if (err || !ret.success) { return console.error(err, ret); }
+             console.log("Created record id : " + ret.id);
+           });
+         });
        }
      });
    </script>
  </body>
</html>
```

今回は作成するレコードが１件でしたが、複数件作成する場合は公式サンプルにもあるように

`create`に複数のレコード情報を渡したり

https://jsforce.github.io/document/#create

```js
// Multiple records creation
conn.sobject("Account").create([
  { Name : 'My Account #1' },
  { Name : 'My Account #2' }
],
function(err, rets) {
  if (err) { return console.error(err); }
  for (var i=0; i < rets.length; i++) {
    if (rets[i].success) {
      console.log("Created record id : " + rets[i].id);
    }
  }
  // ...
});
```

大量のレコードを作成したい場合は、BulkAPIが有効で

https://jsforce.github.io/document/#bulk-api

公式サンプルの以下のように操作したり、CSVを読み込んでCSVの内容を、BulkAPIに利用することもできます。

```js
conn.bulk.pollTimeout = 25000; // Bulk timeout can be specified globally on the connection object
conn.bulk.load("Account", "insert", accounts, function(err, rets) {
  if (err) { return console.error(err); }
  for (var i=0; i < rets.length; i++) {
    if (rets[i].success) {
      console.log("#" + (i+1) + " loaded successfully, id = " + rets[i].id);
    } else {
      console.log("#" + (i+1) + " error occurred, message = " + rets[i].errors.join(', '));
    }
  }
  // ...
});
```

## 動かしてみる

作成ボタンを押す

![Edge-Communications-Salesforce.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445600/f9862098-a280-3562-12a6-4c6d48bbfae6.png)

コンソールに作成したレコードIDがでていることを確認

![スクリーンショット 2022-12-14 1.12.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445600/4dd59212-1e59-2457-884b-ba653fd592f4.png)

レコードIDをもとに取引先詳細ページにアクセス！

目的のレコードが作成されていました！

![株式会社-Salesforce-Canvas-Salesforce.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445600/f85860d8-4d0d-4e82-7b30-21c5634d3004.png)

# 取引先レコードの検索

https://jsforce.github.io/document/#query

SOQLを実行して、取引先レコードを一件取得し画面の`表示エリア`という要素に表示させます。


```diff_html: index.html

<html>
  <head>
    <script src="./js/canvas-all.js"></script>
    <script src="./js/jsforce.min.js"></script>
  </head>
  <body>
    <h1>埋め込みテスト</h1>
    <div id="js-canvasArea">表示エリア</div>
    <button d="js-insertBtn">取引先作成ボタン</button>
    <script>
      Sfdc.canvas.client.refreshSignedRequest(function(data) {
        if (data.status === 200) {

          const signedRequest =  data.payload.response;
          const part = signedRequest.split('.')[1];
          const parseSignedRequest = Sfdc.canvas.decode(part);
          const conn = new jsforce.Connection({ signedRequest: parseSignedRequest });
           // 作成
          const insertBtn = document.getElementById('js-insertBtn');
          insertBtn.addEventListener('click', function () {
            conn.sobject('Account').create({ Name : '株式会社 Salesforce Canvas' }, function(err, ret) {
              if (err || !ret.success) { return console.error(err, ret); }
              console.log("Created record id : " + ret.id);
            });
          });

          // 検索
+         conn.query('SELECT Id, Name FROM Account LIMIT 1', function(err, res) {
+           if (err) {
+             console.error(err)
+           }
+           const canvasArea = document.getElementById('js-canvasArea');
+           canvasArea.innerHTML = JSON.stringify(res);
+         });
        }
      });
    </script>
  </body>
</html>
```

## 動かしてみる

無事１件レコードが取得できました。

![株式会社-Salesforce-Canvas-Salesforce (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445600/4210ea5c-f174-8589-e260-10c353d85bd1.png)

今回はSOQLでしたが、SalesforceのレコードIDがキャンバス側でわかっているのであれば

https://jsforce.github.io/document/#retrieve

公式のサンプルにもあるように、SOQLを書かずにレコードを取得することができます。

```js
// Single record retrieval
conn.sobject("Account").retrieve("0017000000hOMChAAO", function(err, account) {
  if (err) { return console.error(err); }
  console.log("Name : " + account.Name);
  // ...
});
```

# 最後に

JSforceでのレコードの操作、キャンバスアプリケーションでのイベント操作をいままでやってきました。
やってみると非常に強力で
Salesforceは使ってるけど、一部請求系のような複雑なシステムは外部の自前のシステムを使っている
みたいな場合に Salesforce の Canvasは活躍するのではないでしょうか？

次回はAuraでのCanvasの使い方を書いてみようと思います。

# 参考

https://jsforce.github.io/start/#salesforce-canvas