Salesforce → Salesforceのデータ転送を楽にする？脱データローダー？？ SFDMUを試してみる

はじめまして Salesforce現在勉強中の@takaHAL と言います。

この記事は、Salesforce Advent Calendar 2022の15日目の記事です
よろしくお願いします:santa_tone1:

https://qiita.com/advent-calendar/2022/salesforce

# これはなに？
[SFDMU(SFDX-Data-Move-Utility)](https://github.com/forcedotcom/SFDX-Data-Move-Utility)というSalesforce CLIのプラグインを試してみます。
デスクトップアプリも存在しますが、日本語対応していないのとUIがSFDMUを完全に理解した！という感じでないと使いにくいため
今回はコマンドでの実行を試していこうと思います。

SFDMUを使用することで、`force:data:tree`やデータローダを使用している人の不満の解決策になるかもしれません。

# 対象者
- データローダでのデータ転送で、手元のCSV加工に疲れた人
- Salesforceの開発環境への初期データ投入をどうしようか考えている人

# SFDMUてなに？

Salesforce CLIのプラグインで、Salesforceへの転送ができるものです。
既存の他システムのCSVを読み取ってSalesforceに転送したり、Salesforce（本番）からSalesforce（sandbox）への転送も可能です。
みなさんも開発環境に本番に近いデータがなくて困ったことはありませんでしょうか？
そんな時にデータローダーやforce:data:treeコマンドを使っていますでしょうか？
その際に、データローダーの使いにくさで困ってませんか？
今回SFDMUを使ってみることで脱データローダーをしてみましょう。

https://github.com/forcedotcom/SFDX-Data-Move-Utility

SFDMUを利用することで今までデータローダーを使って以下のような内容を行うパターンなどに非常に楽になります。

- 本番Salesforceから、AccountとContactの親子関係を保持したままデータを開発環境Salesforceに転送したい
- 複数のオブジェクトのデータを一回のコマンドでまとめて他のSalesforceに転送したい
- Salesforceに転送する際に、個人情報はマスキングしたい

では早速試してみましょう

# どんな感じに実行するのか？

最終的にどういうことをするのかのざっくりのイメージ

以下の２ステップで実行できます。

- 1\. export.jsonを作成し転送したい設定ファイルを書く
- 2\. `sfdx sfdmu:run`コマンドで特定の環境にデータを転送

送るデータは、取引先オブジェクトと取引先責任者で互いに外部ID項目はなしです。

![無題の図形描画 (21).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445600/616ffc78-28d1-6085-b64d-3748c183abff.jpeg)


外部ID項目はありませんが、SFDMUでは外部ID項目　でなくとも外部IDのようにUpsertKeyにすることができます。

```json: export.json
{
  "objects": [
    {
      "query": "SELECT all, RecordTypeId FROM Account",
      "operation": "Upsert",
      "externalId": "Name",
      "mockFields": [
        {
          "name": "Name",
          "pattern": "c_seq_number('テスト取引先', 0, 1)"
        }
      ],
      "updateWithMockData": true
    },
    {
      "query": "SELECT all, RecordTypeId FROM Contact",
      "operation": "Upsert",
      "externalId": "Name",
      "mockFields": [
        {
          "name": "LastName",
          "pattern": "c_seq_number('テスト姓', 0, 1)"
        },
        {
          "name": "FirstName",
          "pattern": "c_seq_number('テスト名', 0, 1)"
        }
      ],
      "updateWithMockData": true
    }
  ]
}
```

実行することでAccountとContactの親子関係を保った状態で、データを転送することができます。

```bash
# sourceusername　データ転送元環境
# targetusername データ転送先環境
$ sfdx sfdmu:run --sourceusername SOURCEUSERNAME --targetusername TARGETUSERNAME
```

このようにマスキングされた状態で、取引先と取引先責任者を紐づけて挿入してくれます。

![スクリーンショット 2022-12-15 0.39.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445600/222337b8-1220-516c-b87b-27c1985a2361.png)


元データはこんな感じです。

![スクリーンショット 2022-12-15 0.41.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445600/c66ec973-d855-59d1-79ed-7094a898ab6b.png)

それでは、export.jsonのオプション等の説明とセットアップをこの後やります。


# セットアップ

## Salesforce CLI

SFDMUは、Salesforce CLIのプラグインである為
Salesforce CLIのセットアップが必要です。

詳しくはこの記事では説明しませんので、公式のドキュメントをご参照ください。

https://developer.salesforce.com/docs/atlas.ja-jp.sfdx_setup.meta/sfdx_setup/sfdx_setup_install_cli.htm

## SFDMUのインストール

```bash
$ sfdx plugins:install sfdmu
```

## Salesforceの環境について

今回使用するSalesforce環境は、Trailheadのハンズオン環境を２つ使用します。

https://trailhead.salesforce.com/ja/users/profiles/orgs


**production**と**development**の環境を作成しました。

下記の画像のように、**production**のハンズオン環境のデータを**development**のハンズオン環境に転送しようと思います。

![無題の図形描画 (18).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445600/4a83aac3-7b44-8029-41ab-84a3b4d0cd04.jpeg)

### 組織を認証する

https://developer.salesforce.com/docs/atlas.ja-jp.234.0.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_web_flow.htm

```bash
$ sfdx auth:web:login --setalias production   # productionへのログイン
$ sfdx auth:web:login --setalias sandbox      # developmentへのログイン
```
２つの環境の認証が完了したら準備OKです

# 使い方

Salesforce → Salesforceの転送の仕方と、各種オプションの説明を書きます。

## 公式ドキュメント

https://help.sfdmu.com/

## export.jsonファイルを作成する

以下のファイルでAccount と Contactを全件取得し、
転送先のSalesforceに参照関係を保持した状態で、データが入ります

- オブジェクトの指定
- 取得するためのSOQLを記述
- マスキング等のオプションの追記

送るデータは、取引先オブジェクトに外部ID項目があるパターンです。

![無題の図形描画 (20).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445600/13ed7208-9758-4eff-a357-af697ef17aa3.jpeg)


このような要素でファイルが構成されています。

```json: export.json
{
  "objects": [
    {
      "query": "SELECT all, RecordTypeId FROM Account",
      "operation": "Upsert",
      "externalId": "ExternalId__c",
      "mockFields": [
        {
          "name": "Name",
          "pattern": "c_seq_number('テスト取引先', 0, 1)"
        }
      ],
      "updateWithMockData": true
    },
    {
      "query": "SELECT all, RecordTypeId FROM Contact",
      "operation": "Upsert",
      "externalId": "Account.ExternalId__c",
      "mockFields": [
        {
          "name": "LastName",
          "pattern": "c_seq_number('テスト姓', 0, 1)"
        },
        {
          "name": "FirstName",
          "pattern": "c_seq_number('テスト名', 0, 1)"
        }
      ],
      "updateWithMockData": true
    }
  ]
}

```

### よく使いそうなjsonの項目の説明について

| 項目      |   説明  | 
|:-----------------|:------------------|
| query             |     取得するSOQLを各場所 |  
| operation             |   **Insert, Update, Upsert, Delete** の基本的な挿入時の挙動と、**Readonly**というデータは入れなくてもいいけど参照する必要がある場合に指定するオプションがある     |  
| externalId             |    外部キー　外部キーになっていないフィールドも指定可能（取引先の名前とか） |  
|  updateWithMockData |  **true**にするとmockFieldsに記載したルールでデータがマスキングされて挿入されます。 |

#### queryについて

https://help.sfdmu.com/full-documentation/advanced-features/field-multiselect

通常のSOQLの記述に加えて以下のような形式もサポートしています。


| 項目      |   説明  | 
|:-----------------|:------------------|
| all             |     RecordType, Group, User, DandBCompany を除くすべての項目|  
| readonly_true             |   読み取り専用のフィールドのみ（自動採番の項目とか）   |  
| readonly_false             |    読み取り専用のフィールド以外の項目 |  
|  custom_true |  カスタム項目のみ |
|  custom_false |  標準項目のみ |
|  createable_true |  作成可能なフィールドのみ |
|  createable_false |  作成可能なフィールド以外 |
|  updateable_true |  更新可能なフィールドのみ |
|  updateable_false |  更新可能なフィールド以外 |
|  lookup_true |  ルックアップ項目すべて |
|  lookup_false | ルックアップ項目以外 |
|  type_... | type_string だと テキスト項目のフィールドのみを選択 型名は[こちら](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_enum_Schema_DisplayType.htm) |

#### mockFieldsについて

https://help.sfdmu.com/full-documentation/advanced-features/data-anonymization

```bash
 "mockFields": [
    {
      "name": "LastName",
      "pattern": "c_seq_number('テスト姓', 0, 1)"
     },
     {
       "name": "FirstName",
       "pattern": "c_seq_number('テスト名', 0, 1)"
      }
  ],
```

| 項目      |   説明  | 
|:-----------------|:------------------|
| name             |     マスキングする項目名|  
| pattern             |   上書きする値：(※1)     |  
| externalId             |    外部キー　外部キーになっていないフィールドも指定可能（取引先の名前とか） |  
|  updateWithMockData |  **true**にするとmockFieldsに記載したルールでデータがマスキングされて挿入されます。 |

patternについて(※1)

https://www.npmjs.com/package/casual#embedded-generators

| 項目      |   説明  | 
|:-----------------|:------------------|
| ids             |    レコードのIDで上書きされます|  
| c_seq_number(prefix, from, step)             |   c_seq_number('テスト姓', 0, 1) で　テスト姓1,テスト姓2 のようなデータで上書きされます。     |  
| c_seq_date(from, step)             |    c_seq_date('2019-01-01', 'd') で　*"2019-01-01", "2019-01-02", "2019-01-03"* のように日付データが入ります。 |  
|  casualの値 |  "pattern": "name"とか"pattern": "email"で対応したダミーデータを作ってくれるhttps://www.npmjs.com/package/casual#embedded-generators  |

:::
casualに関しては、検証が出来次第追記します。
:::

## Salesforce → Salesforce データを転送する

export.json がある場所で以下を実行

```bash
$ sfdx sfdmu:run --sourceusername production --targetusername development
``` 

もしくは

例）`/sfdx-out`に `export.json `があるとすると以下のように実行も可能

```bash
$ sfdx sfdmu:run --sourceusername production --targetusername development --path ./sfdx-out
```

## CSVをデータソースにする

SFDMUは、Salesforce → Salesforceだけではなく
CSV → Salesforceも実行可能です。

まずは、Trailheadをのデモデータを元に簡単なCSVを用意しました。

```csvs: Account.csv
Name,Phone
テスト取引先500,(503) 421-7800
```

export.jsonと同一階層にCSVを設置しています。

![スクリーンショット 2022-12-15 1.46.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445600/7129d8e8-2f3b-57d9-7d1b-d686ef9e43b7.png)


CSVのデータが入ったとわかりやすいように、マスキングはせずexport.jsonを作成。

```json: export.json
{
  "objects": [
    {
      "query": "SELECT Name, Phone FROM Account",
      "operation": "Upsert",
      "externalId": "Name"
    }
  ]
}
```

## CSV→Salesforce データを転送する

https://help.sfdmu.com/full-documentation/reports/issues-with-the-source-csv-files

https://help.sfdmu.com/full-documentation/reports/the-target-csv-files

export.json がある場所で以下を実行

CSVをデータソースにする場合は、--sourceusername を csvfileにしてください。
--targetusernameを csvfileにした場合は取得したデータをCSVとして出力できます。

```bash
$ sfdx sfdmu:run --sourceusername csvfile --targetusername development
``` 

もしくは

例）`/sfdx-out`に `export.json `があるとすると以下のように実行も可能

```bash
$ sfdx sfdmu:run --sourceusername csvfile --targetusername development --path ./sfdx-out
```

![スクリーンショット 2022-12-15 1.53.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445600/a71bd5c3-c5ed-1647-ba89-1d0df5e42133.png)

CSVのデータも取り扱うことができました。こうなってくると
外部データ連携システムが、SFDMUのCLIをAWS上とかで実行すると完成しますね。

# 最後に

Salesforce開発を進めると問題になってくる開発環境のデータ問題を解決する策としても、データローダーの利用しんどい問題を解決する策としてもSFDMUは候補にあがるのではないでしょうか？
私が所属するチームでは、SalesforceのAdminもエンジニアもSalesforceDXを活用するので
SFDMUはかなり重宝しております。
CLIなので、データ連携システムの一部として使えそうだったりと非常に面白いものでした。

今回は、アドベントカレンダーの完走賞を目指しているので
SalesforceやWeb関連の記事をガンガンアウトプットしていきます！！
気になるものがあれば是非コメントでアドバイス等いただけると幸いです。

https://qiita.com/advent-calendar/2022/takahal

# 参考にさせていただいた記事

https://qiita.com/kaku10/items/fe879883ad40bb12b510