ラベル apex の投稿を表示しています。 すべての投稿を表示
ラベル apex の投稿を表示しています。 すべての投稿を表示

2016年7月23日土曜日

Sitesの落とし穴再び / Salesforce

Salesforceのことをここに書き込むのは何年ぶりでしょ。

勤務先でSalesforceを導入するかも?ってんで、やらせてもらってます。で、Sitesを使うんですが…初期段階で動いていたものが、いつの間にか毎度おなじみの「認証が必要です」画面が出てしまうように。

当然ApexもVisualforceも登録済です。そして数日後、Apexのコンストラクタを見て気付きました。

public FooClass() {
    Id userId = UserInfo.getUserId();
    ....
}

…そりゃ認証しろって言われますよね・・・これに気づかなかった自分がいや。本来ここでidを取得する必要はまったくないのですが、社内デモ向けにSitesではなく普通のVisialforceとして見せるためにちょこっと追加したコードでした。

あー、でも動いてよかった。

なお、この「認証が必要です」について「salesforce sites 認証が必要です。」でぐぐったら2010年3月にまさにこのわたくしがdeveloper forumで同じ質問をしていることが判明いたしましたorz この時は違う原因だったんですが…。

これコンサル取得する前かなー…いやはや…なんとも…。

2013年5月26日日曜日

SalesforceからiPhoneへPush notificationを送る

とんかつ専門店のご飯が妙に美味いのはなぜ?
上の写真はまったく関係ありません。

週末、何かネタを探していたら、www.parse.comというサービスに関する記事が見つかりました。最近ではPush Notification代行サーバもいろいろあるのですが、これは余計な機能がないぶんシンプルで安い。

そんなわけで、chatter上に「$go message」と書くと、triggerが作動してForce.comのhttp calloutからparse.comのAPIを叩き手元のiPhoneにメッセージが届く、というのを作ってみました。

■まず、Push Notificationを試す■

試すと言いますか、こちらの記事を参考に、いや、そのまま実行してみます。

Remote Push Notification ASPサービスを試す

Parse、一発で動いてPushされました。いやー、いままでこんなに簡単にPushできたのって初めてかもしれない。10分か15分くらいで動いてしまいました。



■FeedItemのTriggerからParseのREST APIを呼ぶ■

さて、ここからはForce.com上での作業。TriggerからREST APIを呼ぶことはできないので、Triggerからバッチを起動、バッチからcall outする作戦。

忘れないうちに、まずはお約束のリモートサイトの設定。
  管理者設定>セキュリティのコントロール>リモートサイトの設定
これでhttps://api.parse.com/を登録しておきます。

■Batchを作る■

Developer ConsoleからClass CallParseを定義、IterableなBatchApexでParse.comのAPIを叩きます(コードは末尾)。

■Triggerを作る■

例によってDeveloper ConsoleからFeedItem上のキーワード $go を検出してBatch Apexを呼び出すコードを書きます(コードは末尾)。

■動かない■

一応、Salesforce上で
  管理者設定>監視>デバッグログ
を設定してから、Chatter上にメッセージを書いてみる。

…動かない。デバッグログを見ると、

05:50:02.039 (39869000)|CALLOUT_REQUEST|[29]|System.HttpRequest[Endpoint=https://api.parse.com/1/push, Method=POST]
05:50:02.175 (175302000)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:661
05:50:02.175 (175393000)|CALLOUT_RESPONSE|[29]|System.HttpResponse[Status=Bad Request, StatusCode=400]
05:50:02.175 (175420000)|HEAP_ALLOCATE|[29]|Bytes:130
05:50:02.175 (175438000)|SYSTEM_METHOD_EXIT|[29]|System.Http.send(ANY)

と出ていました。ちくしょう、どうせ一発で動くと思ってねぇですよ←涙目 なので、Apex側にコードを入れて
    System.debug(res.getBody());
    System.debug(res.getStatus());
    System.debug(res.getStatusCode());
検証してみたところ
    16:43:08.153 (153194000)|USER_DEBUG|[36]|DEBUG|{"code":107,"error":"This endpoint only supports Content-Type: application/json requests, not : application/json."}
という結果が。

ソースを改めて見なおしたら、

    req.setHeader('Content-Type : ', 'application/json');

なんてバカなコーディングを発見。直したら無事動作。




以下コードを張っておきます。iPhone側はparse.comのサンプルを貼っただけです。

■Trigger■


trigger KickFromChatter on FeedItem (before insert) {

    FeedItem[] feeds = Trigger.new;
    List<FeedItem> arrayFeedItem = new List<FeedItem>();
    
    for (FeedItem feed : feeds) {
        String strBody = feed.Body;
        if (strBody != null && strBody.contains('$go')) {
            arrayFeedItem.add(feed);
        }
    }
    
    if (arrayFeedItem.size() > 0) {
        CallParse batch = new CallParse();
        batch.arrayFeedItem = arrayFeedItem;
        
        Database.executeBatch(batch);
    }
}


■Batch Apex■


global class CallParse implements Database.batchable<FeedItem>, Database.AllowsCallouts {
    
    global List<FeedItem> ArrayFeedItem;
    
    global Iterable<FeedItem> start(Database.BatchableContext info) {
        Iterable<FeedItem> i = arrayFeedItem;
        
        return i;
    }
    
    global void execute(Database.BatchableContext info, List<FeedItem> feeds) {
        for (FeedItem feed : feeds) {
            String str = feed.Body;
            String strPayload = '{"channels" : [""], "data" : {"alert" : "' + str + '"}}';
            Blob blobPayload = Blob.valueOf(strPayload);
            
            // Instantiate a new http object
            Http h = new Http();
            
            HttpRequest req = new HttpRequest();
            req.setEndpoint('https://api.parse.com/1/push');
            req.setMethod('POST');
            
            req.setHeader('X-Parse-Application-Id', 
                                   'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
            req.setHeader('X-Parse-REST-API-Key',
                                   'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb');
            req.setHeader('Content-Type',
                                   'application/json');
            
            req.setBody(strPayload);
            
            // Send the request, and return a response
            HttpResponse res = h.send(req);
            
            System.debug(res.getBody());
            System.debug(res.getStatus());
            System.debug(res.getStatusCode());
        }
    }
    
    global void finish(Database.BatchableContext info) {
    }
    
}


2012年6月9日土曜日

Salesforce季節の変わり目にありがちなこと:APIが違う

さてもうすぐSummer '12です。

新規に取得したDev環境はSummer '12になってますね。その上で一部リリースされた新機能を試したりチャットのポップアップがうぜぇwなどと言いつつ作業し、できあがったパッケージを本番環境に移そうとしたのですが…「API違うから駄目っすw」と言われました。

パッケージもForce.com IDEも駄目。同じことをSpring '12の時期にもやっていたような気がする…でも、Dev環境取得する時点でAPI選べないのでどうにもなりません。

で、エラーを細かくチェックしていくと、

  • カスタムオブジェクトやレイアウトに関しては移植可能
  • エラーが出るのはVisualforce, Apex, Triggerなどのメタファイル

という状態でした。

ということでwork around。

  1. 稼働環境用サンドボックスへカスタムオブジェクトやレイアウトをすべて移植
  2. 同サンドボックスへ手動でVisualforce, Apex, Triggerをコピペ
  3. サンドボックスから本番環境へDeployment
まぁ当たり前の手順ですけども、ハマった方のご参考になれば幸いです。

なお、今回ソース数本だったので手動で対応できましたが、本数が多いと依存関係もあって大変です。とりあえずは以下の順番でコピペしてみます。
  1. Trigger
  2. 通常のApex
  3. Visualforce
  4. テストコード
ひっかかったら適宜入れ替えます。また、どうしても依存関係が解消できない場合には、先にクラス名定義だけコピーしておいて、あとから実装部分をコピーするという方法もあります。最後の手段として、ソースの数だけブラウザにタブを開いておいて、依存関係のないところから片っ端からQuick Saveして少しずつ埋めていく、なんてのもあります。

申し遅れましたが、この手の作業はForce.com IDEよりもブラウザ経由の方がレスポンスが良くて作業しやすいような気がします。

2012年1月18日水曜日

Batch Apex / Mailでハマった件

香港にて
お疲れ様です。

Batch Apexに限らないんだけど…arrayに入れたSingleMailMessageをまとめてどーんと送る時、その中に1通でも不達アドレスが含まれていると一通も送られないでエラーになりますね。

昨日はそれでハマりました。

Contactに関連付けたemailアドレスにメールを送って、それが正常に配信されないとそのアドレスには「確認しろよコラ」ってフラグが立ちます。その状態で同じアドレスにメールを送ろうとすると送信前にエラーになる。まぁそれは当たり前。問題は、不達アドレスだと検出されるタイミングが前回送信から数十分〜数時間かかるってところで、いつ判定されるかわかんない。なので大量に送信してたりすると、エラーがたまたま次回の送信Batchにひっかかってコケてました。

報告をするのにエラーの経緯を再現しなきゃいけないんだけど、そんな神業なタイミングをどーしろとw

それにしても、疲れてるとレアケースの推測がダメだわ。

今日は、送信前にそのContact / emailアドレスが不達だと判断する方法を探さなければ…。


2012-01-29追記:
EmailBouncedDate / EmailBouncedReasonがnullじゃなかったら不達ですね…。