SFDCのデータを抽出してAWSのS3にCSVを出力する(その3)

今回はapexの単体プログラムがメインです。apex用エディタの起動はつまづかないと思いますが念のため、管理コンソールにログインして歯車マークをクリックして[開発者]コンソールを選択します。デバッグ方法などはググってみてください。





ちなみにSFDCでapexを使って何かしようとするとリリース時にはテストで75%のカバレッジが必要という落とし穴があります。なのでがっつりある程度処理を網羅するテストを書く必要があります。自分はテストなど書いたことなかったので非常に苦労しました。

◆大枠の手順

1.AWSでS3を設定する
 1)S3バケット作成
 2)IAMポリシー設定
 3)IAMユーザ追加
2.SFDC設定
 1)環境変数関連設定
 2)パラメータ入力
 3)リモートサイト許可
3.プログラム作成(単体レベル)
 1)環境変数抽出
 2)SOQLを使用してデータ抽出
 3)S3にCSVファイルをアップロード
4.バッチ設定

◆プログラム群

1)環境変数抽出

GetEnvironment
--------------------
public class GetEnvironment {
    // 環境変数をカスタム設定から取得する
    public Map GetEnv(){
        try {
            Map settingMap = EnvironmentVars__c.getAll();
            Map MapEnv = new Map();
            for (String key : settingMap.keySet()) {
                EnvironmentVars__c env = settingMap.get(key);
                MapEnv.put(key,env.EnvParam__c);
            }
            return MapEnv;
        } catch(Exception e) {
          System.debug('Exception caught: ' + e.getMessage());
          system.debug(e.getLineNumber());
          return null;
        }
   }
}
--------------------
GetEnvironmentTest
--------------------
@isTest(SeeAllData=true)
private class GetEnvironmentTest {
 @isTest static void testGetEnv() {
        Map m1 = new Map();
        GetEnvironment ge = new GetEnvironment();
        m1 = ge.GetEnv();
        for (String sKey:m1.keySet()){
               if(sKey == 'S3Key'){
                 System.assertEquals(sKey ,'S3Key');
               }
               if(sKey == 'S3Token'){
                 System.assertEquals(sKey ,'S3Token');
               }
               if(sKey == 'S3Bucket'){
                 System.assertEquals(sKey ,'S3Bucket');
               }
               if(sKey == 'S3Domain'){
                 System.assertEquals(sKey ,'S3Domain');
            }
        }
        System.debug(m1);
    }
}
--------------------

2)SOQLを使用してデータ抽出

SoqlOperate
--------------------
public class SoqlOperate{
    //SOQLでデータを抽出
    public List GetLeads(){
        try{
            String queryString = 'SELECT ' +
                                 'LastName,FirstName,Phone,Email ' + 
                                 'FROM Lead ' +
                                 'WHERE send_stat__c != \'SEND\'';
            List leads = Database.query(queryString);
            if(leads.isEmpty()){
                leads = new List();
            }
            return leads;
        }catch(Exception e){
            System.debug('Exception caught: ' + e.getMessage());
            System.debug(e.getLineNumber());
            return null;
        }
    }
    //送信ステータス更新
    public Boolean UpdateLeadsStatus(List ids){
        try{
            List leads = [SELECT Id FROM Lead WHERE Id IN :ids];
            for (Lead ld : leads){
                ld.send_stat__c = 'SEND';
            }
            if(!leads.isEmpty()){
                //一括更新
                List srList = Database.update(leads, false);
                //エラー処理
                for (Integer i = 0; i < leads.size(); i++) {
                    Lead ld = leads.get(i);
                    Database.SaveResult sr = srList.get(i);
                    if (!sr.isSuccess()) {
                        System.debug('**** エラー発生:updateが失敗しました ****');
                        System.debug('Id:' + ld.Id);
                        for (Database.Error err : sr.getErrors()) {
                            System.debug(err.getStatusCode());
                            System.debug(err.getFields() + ' ' + err.getMessage());
                        }
                    }
                }
            }
            return true;
        }catch(Exception e){
            System.debug('Exception caught: ' + e.getMessage());
            System.debug(e.getLineNumber());
            return false;
        }
    }
    //抽出データをCSVに変換
    public String RecordsTransferCsv(List OriginCsv){
        try{
            String str_body='';
            Integer cnt = OriginCsv.size();
            if(cnt==0){
                throw new QueryException();
            }
            Integer i = 0;            
            for(Lead l : OriginCsv){
                if(i==cnt-1){
                    str_body += '"'+l.LastName+'"'+','+'"'+l.FirstName +'"' +','+
                        '"'+l.Phone+'"' + ','+ '"'+l.Email+'"';
                }else{
                    str_body += '"'+l.LastName+'"'+','+'"'+l.FirstName +'"' +','+
                        '"'+l.Phone+'"' + ',' + '"'+l.Email+'"'+'\r\n';
                }
                i+=1;
            }
            return str_body;
        }catch(Exception e){
            System.debug('Exception caught: ' + e.getMessage());
            System.debug(e.getLineNumber());
            return null;
        }
    }
    //抽出データからIDを取得してリストにする
    public List ReturnKeyId(List OriginData){
        try{
            List id_list = new List();
            Integer cnt = OriginData.size();
            if(cnt==0){
                throw new QueryException();
            }
            for(Lead l : OriginData){
               id_list.add(l.Id);
            }
            return id_list;
        }catch(Exception e){
            System.debug('Exception caught: ' + e.getMessage());
            System.debug(e.getLineNumber());
            return null;
        }
    }
}
--------------------
SoqlOperateTest
--------------------
@isTest(SeeAllData=true)
private class SoqlOperateTest {
 @isTest static void GetLeadsTest(){
        SoqlOperate sq = new SoqlOperate();
        List li = sq.GetLeads();
    }
    @isTest  static void RecordsTransferCsvTest(){
        SoqlOperate sq = new SoqlOperate();
        List leads = [SELECT LastName,FirstName,Phone,Email FROM Lead];
        String s11 = sq.RecordsTransferCsv(leads);
        System.debug(s11);
        List dm;
        String s12 = sq.RecordsTransferCsv(dm);
    }
    @isTest  static void  ReturnKeyIdTest(){
        SoqlOperate sq = new SoqlOperate();
        List leads_id = [SELECT LastName,FirstName,Phone,Email FROM Lead];
        List s21 = sq.ReturnKeyId(leads_id);
        for (String s_id : s21){
            System.debug(s_id);
        }
    }
    @isTest  static void UpdateLeadsStatusTest(){
        SoqlOperate sq = new SoqlOperate();
        List leads = [SELECT Id FROM Lead WHERE send_stat__c != 'SEND'];
        List ids = new List();
        for (Lead l : leads) {
             ids.add(l.Id);
        }
        Boolean b = sq.UpdateLeadsStatus(ids);
        System.debug(b);
    }
}
--------------------

3)S3にCSVファイルをアップロード

PutFiletoS3
--------------------
public class PutFiletoS3 {
    /* CSV抽出 */
    private static String SaveCSV(String str_body){
        try{
            String csv_header = '"姓","名","電話番号","Email"\r\n';
            //引数チェック
            if(str_body == null){
                throw new NullPointerException();
            }
            String csv_body = csv_header + str_body;
            return csv_body;
        }catch(QueryException e){
            System.debug('Exception caught: ' + e.getMessage());
            system.debug(e.getLineNumber());
            return null;
     }
    }
    
    /* S3にアップロード */
    @future(callout=true)
    public static void PutS3(String csv_body, String file_name,String s3access,String s3secret,
                             String s3bucketname,String s3domain, List ids){
          try {
               //引数チェック
              if((csv_body == null)||(file_name == null)||(s3access == null)||(s3secret == null)||
                 (s3bucketname == null)||(s3domain == null)||(ids==null)){
                 throw new NullPointerException();
              }
              String csv = SaveCSV(csv_body);
          //S3へのファイルアップロード
              String dateString = Datetime.now().formatGmt('EEE, dd MMM yyyy HH:mm:ss Z');
              String stringToSign = 'PUT\n\ntext/csv; charset=Shift_JIS\n' + dateString +'\n/' + s3bucketname + '/' + file_name;
              Blob mac = Crypto.generateMac('hmacSHA1',  Blob.valueOf(stringToSign), Blob.valueOf(s3secret));
              String signature = EncodingUtil.base64Encode(mac);
              //httpリクエスト        
              HttpRequest req = new HttpRequest();
              req.setEndPoint('https://' + s3bucketname + '.' + s3domain + '/' + file_name);
              req.setHeader('Content-Type', 'text/csv; charset=Shift_JIS');
              req.setHeader('Content-Length', String.valueOf(csv.length()));
              req.setHeader('Host', s3bucketName + '.' + s3domain);
              req.setHeader('Date', dateString);
              req.setHeader('Authorization','AWS ' + s3access + ':' + signature);
              req.setBody(csv);
              req.setMethod('PUT');
              Http httpConnection = new Http();
              HTTPResponse res = httpConnection.send(req);
              if(res.getStatusCode()==200){
               System.debug(res);
                  //ステータス更新
                  SoqlOperate sq = new SoqlOperate();
                  Boolean b = sq.UpdateLeadsStatus(ids);
              }
          } catch(Exception e) {
          System.debug('Exception caught: ' + e.getMessage());
          system.debug(e.getLineNumber());
          //return null;
        }          
    }
}
--------------------
PutFiletoS3Test
--------------------
@isTest
private class PutFiletoS3Test {
 @isTest static void testPutS3() {
        //正常パターン
        //PutFiletoS3 ps3 = new PutFiletoS3();
        String body = '"テスト1","電話1"\r\n"テスト2","電話2"\r\n"テスト3","電話3"';
        String s;
        String S3Key= 'AWSのキー';
        String S3Token='AWSのトークン';
        String S3Bucket = 'バケット名'; 
        String S3Domain = 's3-ap-northeast-1.amazonaws.com';
        String fn = 'sample1.csv';
        List ids = new List{
            'value1', 'value2', 'value3'        
        };
     PutFiletoS3.PutS3(body,fn,S3Key,S3Token,S3Bucket,S3Domain,ids);
        System.debug('**DEBUG** ' + s);
        //異常パターン
        S3Key= '';
        S3Token='';
        PutFiletoS3.PutS3(body,fn,S3Key,S3Token,S3Bucket,S3Domain,ids);
        System.debug('**DEBUG** ' + s);
    }
}
--------------------

◆参考サイト

・Developersサイト
 https://developer.salesforce.com/jpblogs/2016/04/developer-edition-signup/

・apex基本
 https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_dev_guide.htm
 https://tyoshikawa1106.hatenablog.com/entry/2019/06/08/133844
 http://tyoshikawa1.rssing.com/chan-55762271/all_p55.html
http://fioa.site/2019/04/14/apex%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E3%81%AEhello-world/
 https://tyoshikawa1106.hatenablog.com/entry/2019/05/04/073119
 https://keneloper.com/2020/01/16/how-to-get-a-user-debug-log/
 https://deferloader.blog.uhuru.co.jp/2185/
  https://medium.com/nyle-engineering-blog/salesforce%E9%96%8B%E7%99%BA%E5%88%9D%E5%AD%A6%E8%80%85%E3%81%8C%E3%81%A4%E3%81%BE%E3%81%A5%E3%81%8D%E3%82%84%E3%81%99%E3%81%84%E3%81%84%E3%81%8F%E3%81%A4%E3%81%8B%E3%81%AE%E7%BD%A0-cbae46ae2ae1
  https://tyoshikawa1106.hatenablog.com/entry/2014/12/10/195900
  https://www.xgeek.net/ja/salesforce/kiss-principle-for-apex-code-with-list-and-map-in-salesforce/

・例外テスト関連
 https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_classes_exception_methods.htm
 https://tyoshikawa1106.hatenablog.com/entry/2013/06/23/120049
 https://qiita.com/s_hayashida/items/b0e9da04833fd316d9d2
 https://qiita.com/TaigarAndDragon/items/343dad5ed06fd538a711
 https://trailhead.salesforce.com/ja/content/learn/modules/apex_testing/apex_testing_data
  https://gist.github.com/tyoshikawa1106/162c07053c37d5313fc2

・CSV出力
http://daeheui.com/2019/07/05/salesforcevisualforce%E3%81%8B%E3%82%89csv%E5%87%BA%E5%8A%9B/

・apex、SOQL取得
 https://developer.salesforce.com/docs/atlas.ja-jp.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_relationships_and_polymorph_keys.htm
 https://qiita.com/yoshimatsu11/items/e84e20d97151b1124c7c
 http://blog.flect.co.jp/salesforce/2011/02/soql-7d09.html
 https://qiita.com/TaigarAndDragon/items/5932966e18cc4459da70
 https://tyoshikawa1106.hatenablog.com/entry/2019/09/19/075843
 https://qiita.com/yoshimatsu11/items/983b15e621d3238ca8dc
 https://savepo.com/soql_summary
 https://www.mg6.jp/archives/tag/salesforce
 https://nontitle.xyz/archives/199
 https://qiita.com/lalato/items/87107fee1fbd3dee4b8d
 https://gawatari.com/soql-date-bind/
 https://web.plus-idea.net/2019/04/salesforce-record-tracking-history/
   http://nodding-off-programmer.blogspot.com/2017/06/blog-post_30.html
   https://hub.appirio.jp/tech-blog/try-apex-like-java-dev
   http://www30304u.sakura.ne.jp/blog/?p=532

・SOSL
https://trailhead.salesforce.com/ja/content/learn/modules/apex_database/apex_database_sosl
 https://developer.salesforce.com/docs/atlas.ja-jp.soql_sosl.meta/soql_sosl/sforce_api_calls_sosl_syntax.htm
 https://qiita.com/hankboe/items/8526edaedfd6fa9ae698
 https://www.cnblogs.com/dlywang0411/articles/11458804.html
 https://salesforce.oikeru.com/entry/querySOQL_SOSL
 https://esp-labo.com/sosl_loop/
 http://kayakuguri.github.io/blog/2016/05/13/database-upsert/

・CSV出力→S3アップロード
 http://kayakuguri.github.io/blog/2017/04/10/put-s3-csv-file/
 https://kayakuguri.github.io/blog/2016/02/01/custom-setting/
 https://qiita.com/daichi_fukui/items/77c046f3d6115057f40c

コメント

このブログの人気の投稿

証券外務員1種勉強(計算式暗記用メモ)

GASでGoogleDriveのサブフォルダとファイル一覧を出力する

マクロ経済学(IS-LM分析)