Translate

2011年5月3日火曜日

JavaScriptでSalesforce上のApexコード(SOAP API)を呼び出す

Force.com上のApexコードをSOAP APIとして公開するのは
とても簡単だ。

以下のコードは
Force.com開発者コースでもらった
サンプルコードですが、
見てもらって分かるとおり
すでにApexコードが実装されているのであれば
あとはWSDLをダウンロードできる状態にするために
クラス委定義にglobalキーワードを加え
公開するwebserviceキーワードを追加するだけだ。

こうすれば
設定画面のApexクラス一覧からWSDLがダウンロードできるようになる。




// Apex Code 演習8-1 カスタムSOAP Webサービス(1)
// EmployeeReferralタブのVisualforce画面から入力しsaveボタン押下
// →CandidateKeyWebService#submitEmployeeReferral()をSOAP API呼び出し
//   →CandidateレコードとJobApplicationレコードを格納する
//
// Webサービスとして呼び出すクラスはglobalでなくてはならない
global class CandidateKeyWebService {

 // Webサービス呼び出しメソッドは webServiceキーワードをつける
 // EmployeeReferralタブ上のページから呼び出され
 // 応募者(Candidate__c)レコードと申込(JobApplication__c)レコードを
 // 新規追加する
 // ただしすでに存在する応募者の場合は応募者レコードは追加しない
 //
 // 引数: posId 申込レコード上の募集職種ID(参照項目)
 //  c 応募者レコード
 // 戻り値: 真:成功、偽:失敗
 webService static Boolean submitEmployeeReferral(
 String posId, Candidate__c c){

  // 応募者レコード作成フラグ
  boolean cCreate = true;

  // 項目Emailが設定されている場合、重複チェックをかける
  if (c.Email__c != null){
   // LastNameとEmailを連結した文字列を作成
   String uKey =
    c.Last_Name__c.toLowerCase() +
    c.Email__c.toLowerCase();

   // unique_key(自動計算される項目)と突き合わせ、
   // 1件以上存在すれば重複
   if ([select count()
    from Candidate__c
    where unique_key__c = :uKey] >= 1){

    // 既に存在する→作成フラグを偽に変更
    cCreate=false;
    // 引数cを既存レコードのForce.com IDに変更
    // limit 1を付けると最初の1件をとることができる
    c = [select Id
     from Candidate__c
     where unique_key__c = :uKey limit 1];
   }
  }

  // エラーフラグ
  boolean err = false;

  // 応募者レコードを新規作成する必要がある場合
  if (cCreate){
   try{
    // 新規追加
    insert c;
    // DBエラーの場合
   } catch (System.DmlException e) {
    // エラーフラグを真にする
    err = true;
    // デバッグログへ例外情報を記述
    System.debug(
     'error bulk inserting new candidate record');
    for (Integer k = 0; k < e.getNumDml(); k++) {
     System.debug(e.getDmlMessage(k));
    }
   }
  }

  // 応募者レコード新規追加に成功、もしくは既存レコードが存在し
  // 新規追加操作をしていない場合
  //  応募者レコード新規格納失敗の場合に申込レコードのみを追加
  //  してしまうとDBの整合性が荒れてしまうので
  if (!err){
   // 新規申込レコードを作成
   Job_Application__c j = new Job_Application__c();

   j.Status__c = 'Open'; // Status値をOpenに設定
   j.Stage__c = 'New'; // Stage値をNewに設定
   j.Position__c = posId; // Position値を引数posIdに設定
   j.Candidate__c = c.Id; // Candidate値を応募者IDに設定

   try{
    // 申込レコードを新規追加する
    insert j;
   // DBエラーの場合
   } catch (System.DmlException e) {
    // デバッグログに例外情報を格納
    System.debug(
     'error bulk inserting new job application');
    for (Integer k = 0; k < e.getNumDml(); k++) {
     System.debug(e.getDmlMessage(k));
    }

    // 本来はエラーフラグを立て後続のハンドリング
    // 処理判定に使用する
   }
  }

  // エラーがない場合
  if (!err) {
   // 真を返却
   return true;

  // エラーが発生した場合
  } else {
   // 本来はエラーハンドリング処理をここに書く
   // たとえばErrorLogレコードに格納する、など

   // 偽を返却
   return false;
  }
 }
}




できあがったWSDLをAXIS2のwsdl2javaへかませば
Javaから呼び出せるし
VisualStudioにかませれば
そっくりAPI呼び出し準備が完了する。

ただこの研修では
SOAPクライアント側をSalesforceが用意する
JavaScriptライブラリを利用して使用している
サンプルを紹介している。

Visualforceページになっているが
呼び出ししている
SOAP API呼び出しは
まるまるJavaScriptなので
apexタグを知らなくても読めると思う。




<!-- Apex Code 演習8-1 カスタムSOAP Webサービス(1) -->
<!-- EmployeeReferralタブのVisualforce画面から入力しsaveボタン押下 -->
<!-- →CandidateKeyWebService#submitEmployeeReferral()をSOAP API呼び出し -->
<!-- →CandidateレコードとJobApplicationレコードを格納する -->

<!-- controllerは定義されていない→JavaScriptからSOAP APIを呼び出している -->
<apex:page >
//Note that if the org password is NOT 'password1' that you will need to change it twice below!

<!-- Visualforceは終了タグを明記しないとエラーになる -->
<script type="text/javascript" src="/js/functions.js"></script>
<script src="/soap/ajax/17.0/connection.js"></script>
<script src="/soap/ajax/17.0/apex.js" type="text/javascript"></script>

<script type="text/javascript">
 <!-- 入力チェックを実施、すべてOkならばsave()を実行 -->
 function validate(){
  <!-- connection.js上のメソッドを呼び出し -->
  <!-- $Api/$APIはSalesforce上のメタデータを入手する際に -->
  <!-- 使用するキーワード、ほかにも$Referenceなどがある -->
  sforce.connection.init(
   "{!$API.Session_ID}", "{!$Api.Partner_Server_URL_140}");
  <!-- 判定結果格納用変数 -->
  var ok2Go = true;

  <!-- preSelectorリストボックスの選択値を取得 -->
  var ps = document.getElementById("posSelector");
  if (ps.options.length != 0){
   var posId = ps.options[ps.selectedIndex].value;
  }

  <!-- 選択値がnullもしくは空文字の場合 -->
  if ((posId == null)||(posId == "")) {
   <!-- ダイアログでアラート表示し判定結果をNGに -->
   alert("A position must be selected");
   ok2Go = false;
  }

  <!-- form"refForm"内のID値"lastname"の値を取得 -->
  var ln = document.forms["refForm"].elements["lastname"].value;
  <!-- lastname値が空文字もしくはnullの場合 -->
  if ((ln == "")||(ln == null)){
   <!-- ダイアログでアラート表示し判定結果をNGに -->
   alert("Last name is required");
   ok2Go = false;
  }

  // form"refForm"内のID値"email"の値を取得 -->
  var email = document.forms["refForm"].elements["email"].value; 
  <!-- email値が空文字もしくはnullの場合 -->
  if ((email == "")||(email == null)){
   <!-- ダイアログでアラート表示し判定結果をNGに -->
   alert("Email is required");
   ok2Go = false;
  }

  <!-- 判定結果OKの場合 -->
  if (ok2Go) {
   <!-- save()を呼び出しCandidateレコードを追加 -->
   <!-- 引数posIdは-->
   save(posId);

  <!-- 判定結果NGの場合 -->
  } else {
   <!-- 戻り値falseを返す -->
   return false;
  }
 }

 <!-- ※使用されていない※ -->
 <!-- グローバルオブジェクトの情報を入手する -->
 <!-- apexドキュメントをdescribeGlobalで検索→サンプルコード参考の事 -->
 function doDescribeGlobal(){
  try{
   var dgResults = sforce.connection.describeGlobal();
  } catch (e) {
   sforce.debug.open();
   sforce.debug.log(e);
  }

  return dgResults;
 }

 <!-- ※使用されていない※ -->
 <!-- グローバルSObjectの情報を入手する -->
 <!-- apexドキュメントをdescribeSObject検索→サンプルコード参考の事 -->
 function doDescribeSObject(entity){
  try{
   var dso = sforce.connection.describeSObject(entity);
  }catch (e) {
   sforce.debug.open();
   sforce.debug.log(e);
  }
  return dso;
 }

 <!-- 引数で与えられたIDのリストボックス内のオプションをクリア -->
 function clearSelect(name){
  <!-- IDからオブジェクトを入手 -->
  var sel = document.getElementById(name);

  <!-- 空になるまでループ -->
  while (sel.length > 0){
   <!-- 先頭のオプションを削除 -->
   sel.remove(0);
  }
 }

 <!-- Department欄が選択されたら呼び出される -->
 <!-- Department欄の状態によってposSelectorリストボックスを更新する -->
 function deptChanged(department){
  <!-- connection.jsを使用してコネクションを張る -->
  sforce.connection.init("{!$API.Session_ID}", "{!$Api.Partner_Server_URL_140}");

  <!-- IDが"posSelector"のリストボックス(Open Positions)を -->
  <!-- クリア -->
  clearSelect("posSelector");

  <!-- 選択された部門(Department)に合致する募集職種レコード -->
  <!-- を取得するクエリ文字列構築 -->
  var qStr = "select Id, Name, Location__c, Department__c, Type__c, Status__c from Position__c where Status__c='Open' and Department__c = '" + department + "'";

  try{ 
   <!-- クエリを実行し結果を取得 -->
   var queryResults = sforce.connection.query(qStr);
   <!-- 1件以上取得できたら'records'レコード情報を -->
   <!-- 配列として取得 -->
   if (queryResults != null){
    if (queryResults.size > 0){ 
     var records = queryResults.getArray('records'); 
    }
   }

  <!-- 例外発生時処理 -->
  } catch (e){
   <!-- Salesforceデバッグログへ例外情報を書き込む -->
   sforce.debug.open();
   sforce.debug.log(e);
  }

  <!-- IDが"posSelector"であるオブジェクトを取得 -->
  var ps = document.getElementById("posSelector");

  <!-- 結果が存在する場合 -->
  if (records != null){
   <!-- 結果レコードの全件ループ -->
   for (var i=0; i<records.length; i++) {
    <!-- JavaScriptのDOMを使ってoptionタグを -->
    <!-- 新規生成 -->
    var optNew = document.createElement('option');

    <!-- value属性値としてPosition__cの -->
    <!-- Force.com ID値を設定 -->
    optNew.value = records[i].Id;

    <!-- text属性値としてPosition__cのNameと -->
    <!-- Location__c、Type__cを連結した文字列 -->
    <!-- を設定 -->
    optNew.text = records[i].Name + " : " + records[i].Location__c + " : " + records[i].Type__c;

    <!-- IEのDOM方言対応のためのtry-catch句 -->
    try {
     <!-- posSelectorリストボックスの -->
     <!-- オプションとして格納 -->
     ps.add(optNew, null); // IE以外の場合
    <!-- 例外発生時(IE)処理 -->
    } catch(ex) {
     <!-- IEの場合は引数1つらしい -->
     ps.add(optNew); // IE の場合
    }
   }
  }
 }

 <!-- 応募者レコードを新規格納する関数 -->
 <!-- 引数posId: 応募者レコードに紐づける募集職種レコードID -->
 function save(posId){

  <!-- Candidate__c(応募者)レコードを新規生成
  var candidate = new sforce.SObject("Candidate__c");
  <!-- firstname値を応募者レコードのFirst_Name__cへ設定 -->
  candidate.First_Name__c = document.forms["refForm"].elements["firstname"].value; 
  <!-- lastname値を応募者レコードのLast_Name__cへ設定 -->
  candidate.Last_Name__c = document.forms["refForm"].elements["lastname"].value;
  <!-- phone値を応募者レコードのPhone__cへ設定 -->
  candidate.Phone__c = document.forms["refForm"].elements["phone"].value;
  <!-- mobile値を応募者レコードのMobile__cへ設定 -->
  candidate.Mobile__c = document.forms["refForm"].elements["mobile"].value;
  <!-- email値を応募者レコードのEmail__cへ設定 -->
  candidate.Email__c = document.forms["refForm"].elements["email"].value;

  <!-- Webサービスを呼び出し、応募者レコードを格納している -->
  try {
   <!-- Apexクラス(WebService): CandidateKeyWebService -->
   <!-- 呼び出すメソッド名:    submitEmployeeReferral -->
   <!-- 渡す引数:              募集職種レコードID -->
   <!--                         応募者レコード -->
   <!-- 戻り値:                成功→true -->
   var success = sforce.apex.execute("CandidateKeyWebService","submitEmployeeReferral",{a:posId,b:candidate});

   <!-- 値が返ってきた場合 -->
   if (success != null) {
    <!-- DB格納成功の場合 -->
    if (success == "true") {
     <!-- DOMを使ってBODYタグ内を下記 -->
     <!-- HTMLに書き換える-->
     document.body.innerHTML = "<h1>Referral Successfully Submitted. Thank You!</h1><br/><br/><br/><br/>";

    <!-- DB格納失敗の場合 -->
    } else {
     <!-- DOMを使ってBODYタグ内を下記 -->
     <!-- HTMLに書き換える-->
     document.body.innerHTML = "<h1>Temporarily unable to submit referrals. Please try again later.</h1><br/><br/><br/><br/>";
    }
   <!-- SOAP API呼び出し失敗の場合 -->
   <!-- デバッグログのトレースをtrueに変更 -->
   } else { sforce.debug.trace = true; }

  <!-- 例外発生時 -->
  } catch(e) {
   <!-- デバッグログのトレースをtrueに変更 -->
   sforce.debug.trace = true;
   <!-- デバッグログに例外情報を書き込む -->
   sforce.debug.open();
   sforce.debug.log(e);
  }
 }
</script>

<!-- formタグ相当 -->
<form id="refForm" name="refForm">

<!-- 申込レコードに設定するための既存のオープンされた募集職種 -->
<!-- レコードを選択させるためのテーブル -->
<table ID="Table1">
<tr>
<th colspan="2">Department</th>
<th colspan="2">Open Positions</th>
</tr>

<tr>
<td>Choose:</td>
<td>
<!-- Department欄のリストボックス -->
<!-- 選択状態変更→deptChanged関数呼び出し -->
<!-- 引数には選択ぽうしょんのテキスト値が渡される -->
<select id="deptSelector" type="select-one" size="1" NAME="deptSelector" onchange="javascript:deptChanged(this.options[this.selectedIndex].text);">
<!-- ここは決め打ちリストボックスとして定義されている -->
<option value="none">-- None --</option>
<option value="Engineering">Engineering</option>
<option value="IT">IT</option>
<option value="Finance">Finance</option>
<option value="Support">Support</option>
<option value="Sales">Sales</option>
</select>
</td>
<td>Choose:</td>
<td>
<!-- Open Positions欄のリストボックス -->
<!-- Department欄の値によりオプションが変わる -->
<select id="posSelector" NAME="posSelector" type="select-one" size="1">
<!-- 初期状態ではoptionタグは存在しない -->
</select>
</td>
</tr>

<tr>
<td colspan="4"> <br /></td>
</tr>

<td colspan="1"><h3>Candidate Info:</h3></td>
<td colspan="3"> </td>

<tr>
</tr>

</table>

<!-- 応募者レコードの属性値を入力させるためのテーブル -->
<table id="candidate">

<tr>
<!-- First_Name__cを格納するためのテキストフィールド -->
<td>First Name:</td><td>
<input type="text" id="firstname" /></td>
<!-- Phone__cを格納するためのテキストフィールド -->
<td>Phone:</td>
<td><input type="text" id="phone" /></td>
</tr>

<tr>
<!-- Last_Name__cを格納するためのテキストフィールド -->
<td><font color="#ff2222">Last Name:</font></td>
<td><input type="text" id="lastname" /></td>
<!-- Mobile__cを格納するためのテキストフィールド -->
<td>Mobile:</td>
<td><input type="text" id="mobile" /></td>
</tr>

<tr>
<!-- Email__cを格納するためのテキストフィールド -->
<td><font color="#ff2222">Email:</font></td>
<td><input type="text" id="email" /></td>
<td colspan="2"> </td>
</tr>

<tr>
<!-- レジュメ(ファイル)を格納するためのファイル入力フィールド -->
<!-- ※使用していない※ -->
<td>Resume:</td><td>
<input type="file" id="resume" name="resume" /></td>
<td colspan="2"> </td>
</tr>

<tr>
<td colspan="4"><br /></td>
</tr>

<table cellpadding="0" cellspacing="0" border="0" ID="Table2">
<tr>
<td class="pbTitle"><img src="/s.gif" alt="" width="1" height="1" class="minWidth" title="" /> </td>
<td class="pbButtonb">
<!-- saveボタン:押下→validate()呼び出し -->
<input value=" Save " class="btn" type="button" title="Save" name="save" onclick='javascript:validate();' ID="Button1" />
</td>
</tr>
</table>
</table>
</form>
</apex:page>




このサンプルでは
SOAP APIへ接続するために
Salesforceが用意している
・function.js
・connection.js
・apex.js
を使っている。
これだとWSDLをかませて云々の行程が不要だ。

SOAP APIのほかに
SOQLを文字列加工して実行する方法、
それとDepartment欄が変更された際
即時にForce.com上のDBをアクセスしてとなりの
リストボックス内容を書き換える
なんちゃってAjax(完全非同期ではないので..)の
コードなども読むことが出来る
おいしいつくりになっている。

ご参考まで。

0 件のコメント:

ClaudeをOpenAI O1のように思考させるDifyサンプルを試す

 Difyの「探索」タブにはさまざまなサンプルが載っており、その1つに「Thinking Claude (OpenAI O1 Alternative)」というものがある。   このサンプルがどういうものか、未だに自分でも解けない以下の問題をためしに聞いてみることにした。 『人類の...