Developer

SandboxPostCopyで業務効率化

Developer
この記事は約6分で読めます。

こんにちは、アンダーソンです。
今回はSandboxPostCopyを使った活用方法についてご説明していきたいと思います。

今回なぜこれをするに至ったかというと、
本番のデータは見せずにSandで開発をして欲しい
でもSandだとデータが入っておらず、本番のエラーを起こすまでに時間がかかる。

こんな感じの流れで、それだったらSandbox作成時にデータを準備しておこうとなりました。

スポンサーリンク

第一段階

まずはメインとなるオブジェクトを準備するところからはじめました。
SandboxPostCopyインターフェースを実装したApexはSandBox作成時に実行するApexのところから呼ぶことができます。

ここ入れておくことでOKです。
これまでの経験ですと、管理者以外の有効を全て外しておくとかはあったんですが、
初期データの投入は僕自身初めてでした。
ちなみにソースはこんな感じで書いていきました。

global with sharing class WorkerSandboxCopy implements SandboxPostCopy {
    
    private ServicePrepareSandoboxDao dao = new ServicePrepareSandoboxDao();
    global void runApexClass(SandboxContext context) {
        this.doCreateSandboxCopy();
    }
    @TestVisible
    private void doCreateSandboxCopy(){
        dao.callCreateMethod();
    }
}
public with sharing class ServicePrepareSandoboxDao {
    public void callCreateMethod(){
        List<Account> accList = this.createAccountRecord();
        this.createContactRecord(accList);
    }

    @TestVisible
    private List<Account> createAccountRecord(){

        List<Account> insertList = new List<Account>();

        insertList.add(new Account(Name = 'TEST1株式会社'));
        insertList.add(new Account(Name = 'Test2株式会社'));
        insertList.add(new Account(Name = 'Test3株式会社'));

        insert insertList;

        return insertList;
    }

    @TestVisible
    private void createContactRecord(List<Account> accList){

        List<Contact> insertList = new List<Contact>();

        insertList.add(new Contact(
            FirstName = '太郎',
            LastName = 'Test',
            AccountId = accList[0].Id));

        insertList.add(new Contact(
            FirstName = 'たろう',
            LastName = 'Test',
            AccountId = accList[1].Id));

        insertList.add(new Contact(
            FirstName = 'Tarou',
            LastName = 'テスト',
            AccountId = accList[2].Id));

        insert insertList;
    }
}

これでデータはうまく入るんですが、かなりコードが冗長化してしまい、
汎用性もないコードになってしまいました。。

第二段階

冗長化してしまうコードに加えてさらにマスタデータも入れたいというご要望もあり、
直接書いてinsertする方法は諦めました。

で、目をつけたのが静的リソース。
Sandboxコピー時には、DevだろうがPartialだろうがFullだろうが同じものが引き継がれます。

そこにcsvファイルを入れておき、Sandboxコピー時に読み込んで
データを入れる仕組みにしました。
ざっくりのソースはこんな感じです。

@Testvisible
    private Map<String,Id> createAccountServiceCode(){
        
        StaticResource sr = [SELECT Id,Body FROM StaticResource WHERE Name = 'TestCsv' LIMIT 1];
        
        String body = sr.Body.toString();
        List<String> csvStrings = body.split('\r\n');

        List<Test1__c> insertList = new List<Test1__c>();
        List<String> strList = new List<String>();
        for ( String str : csvStrings ) {
            strList = Utility.csvSplit(str);
            Test1__c record = new Test1__c(
                Name = strList[0]
            );
            insertList.add(code);
        }
        insert codeList;
    }

csvの分割はUtilityクラス側にうまく分割するメソッドを作っておいて毎回呼び出すようにしました。

参照関係はどうしたか

今回は参照関係については、Name項目が一意の命名規則に基づいていたので、
Mapを使ってデータ作成時にMap<String,Id>でNameを元にIdを引っ張ってくるって形にしました。

Map<String,Id> retunrMap = new Map<String,Id>();
for ( KanjoBugyoAccountCode__c code : codeList ) {
    retunrMap.put(code.Name,code.Id);
}
return retunrMap;

csv自体に一意のIdを振っておくとすればもっと汎用的に使えるかと思いました。

引き続き。。。

実はまだこれで終わりではなく、まだまだデータを準備しておきたいというご要望があります。ので、今後の問題点はcsvデータのカラムチェックや型の確認とかも入れておかないと、コピー自体失敗に終わる。。なんてこともあるかもしれません。

いろんなケースを想定してまた今後も実装を進めていきたいと思います。