sf-industry-commoncore-callable-apex について
Salesforce Industries Common Core の callable Apex を生成、レビュー、評価する専門スキルです。安全で明示的なアクション契約と、OmniStudio 拡張ポイントとの互換性を重視します。
主な機能
- 01System.Callable クラスの生成
- 02callable 実装のレビュー
- 03120 点ルーブリックでの評価
- 04OmniStudio 拡張ポイントへの対応
- 05Open Interface 移行のガイド
活用シーン
- 01OmniScript のエントリーポイント用 callable の作成
- 02CRUD/FLS とアクション振り分けの監査
- 03VlocityOpenInterface から System.Callable への移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jaganpro/ sf-skills --skill sf-industry-commoncore-callable-apexインストール後、Claude Code で/sf-industry-commoncore-callable-apexを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CREDITS.mdREADME.mdexamples/Test_QuoteByProductCallable/Industries_QuoteByProductCallable.clsIndustries_QuoteByProductCallableTest.clsIndustriesCallableException.clsTRANSCRIPT.mdexamples/Test_VlocityOpenInterface2Conversion/IndustriesCallableException.clsMyCustomCallable.clsMyCustomCallableTest.clsMyCustomRemoteClass.clsTRANSCRIPT.mdexamples/Test_VlocityOpenInterfaceConversion/IndustriesCallableException.clsMyCustomCallable.clsMyCustomCallableTest.clsMyCustomVlocityOpenInterface2.clsTRANSCRIPT.md詳細
sf-industry-commoncore-callable-apex:Salesforce Industries Common Core 向け Callable Apex
Salesforce Industries Common Core の Callable Apex 実装を専門とするエージェントです。OmniStudio および Industries の拡張ポイントとスムーズに統合できる、安全で決定的かつ設定可能な Apex を作成します。
主な役割
- Callable の生成:安全なアクションディスパッチを備えた
System.Callableクラスを構築する - Callable のレビュー:既存の Callable 実装の正確性とリスクを監査する
- 検証と採点:120 点満点のルーブリックに照らして評価する
- Industries への適合性:OmniStudio/Industries の拡張ポイントとの互換性を確保する
ワークフロー(4 フェーズのパターン)
フェーズ 1:要件の収集
以下を確認します:
- エントリーポイント(OmniScript、Integration Procedure、DataRaptor、その他の Industries フック)
- アクション名(
callに渡される文字列) - 入力/出力の契約(必須キー、型、レスポンス形式)
- データアクセスの要件(オブジェクト/フィールド、CRUD/FLS ルール)
- 副作用(DML、コールアウト、非同期処理の要件)
次に:
- 既存の Callable クラスを検索します:
Glob: **/*Callable*.cls - Industries 拡張で使用されている共通ユーティリティまたは基底クラスを特定します
- タスクリストを作成します
フェーズ 2:設計と契約の定義
Callable の契約を定義します:
- アクション一覧(明示的で、バージョン管理された文字列)
- 入力スキーマ(必須キーと型)
- 出力スキーマ(一貫したレスポンスエンベロープ)
推奨するレスポンスエンベロープ:
{
"success": true|false,
"data": {...},
"errors": [ { "code": "...", "message": "..." } ]
}
アクションディスパッチのルール:
switch on actionを使用します- デフォルトケースでは型付き例外をスローします
- 動的なメソッド呼び出しやリフレクションは使用しません
VlocityOpenInterface/VlocityOpenInterface2 契約のマッピング:
レガシーな Open Interface 拡張向け(または Callable と Open Interface の両方をサポートする場合)に設計する際は、シグネチャを次のようにマッピングします:
invokeMethod(String methodName, Map<String, Object> inputMap, Map<String, Object> outputMap, Map<String, Object> options)
| パラメーター | 役割 | Callable での対応 |
|---|---|---|
methodName | アクションセレクター(action と同じ意味) | call(action, args) の action |
inputMap | 主な入力データ(必須キー、型) | args.get('inputMap') |
outputMap | 結果を書き込む可変マップ(参照渡しによる出力) | 戻り値。Callable では代わりにエンベロープを返します |
options | 追加のコンテキスト(親 DataRaptor/OmniScript のコンテキスト、呼び出しメタデータ) | args.get('options') |
Open Interface 契約の設計ルール:
inputMapとoptionsを組み合わせた入力スキーマとして扱います- アクションごとに、
outputMapに書き込む必要があるキー(成功時とエラー時)を定義します methodNameの文字列は Callable のactionの文字列と一致するよう、そのまま保持します- アクションごとに、
optionsが必須か、任意か、未使用かを文書化します
Phase 3: 実装パターン
標準の System.Callable(引数はフラット形式、Open Interface との結合なし):
public with sharing class Industries_OrderCallable implements System.Callable {
public Object call(String action, Map<String, Object> args) {
switch on action {
when 'createOrder' {
return createOrder(args != null ? args : new Map<String, Object>());
}
when else {
throw new IndustriesCallableException('Unsupported action: ' + action);
}
}
}
private Map<String, Object> createOrder(Map<String, Object> args) {
// Validate input (e.g. args.get('orderId')), run business logic, return response envelope
return new Map<String, Object>{ 'success' => true };
}
}
呼び出し側がフラットな引数を渡し、VlocityOpenInterface との連携が不要な場合は、標準パターンを使用します。
Callable のひな形(VlocityOpenInterface と同じ入力):
Open Interface と連携する場合、または呼び出し側がその構造で引数を渡す場合は、args 内の inputMap と options のキーを使用します。
public with sharing class Industries_OrderCallable implements System.Callable {
public Object call(String action, Map<String, Object> args) {
Map<String, Object> inputMap = (args != null && args.containsKey('inputMap'))
? (Map<String, Object>) args.get('inputMap') : (args != null ? args : new Map<String, Object>());
Map<String, Object> options = (args != null && args.containsKey('options'))
? (Map<String, Object>) args.get('options') : new Map<String, Object>();
if (inputMap == null) { inputMap = new Map<String, Object>(); }
if (options == null) { options = new Map<String, Object>(); }
switch on action {
when 'createOrder' {
return createOrder(inputMap, options);
}
when else {
throw new IndustriesCallableException('Unsupported action: ' + action);
}
}
}
private Map<String, Object> createOrder(Map<String, Object> inputMap, Map<String, Object> options) {
// Validate input, run business logic, return response envelope
return new Map<String, Object>{ 'success' => true };
}
}
入力形式:呼び出し側は、args を { 'inputMap' => Map<String, Object>, 'options' => Map<String, Object> } として渡します。フラット形式の呼び出し側との後方互換性を保つため、args に 'inputMap' がない場合は、args 自体を inputMap として扱い、options には空のマップを使用します。
実装ルール:
call()は薄く保ち、プライベートメソッドまたはサービスクラスに処理を委譲します- 入力の型は早い段階で検証し、適切な型に変換します(null セーフにします)
- CRUD/FLS と共有ルールを適用します(
with sharing、Security.stripInaccessible()) - 引数にレコードのコレクションが含まれる場合は、一括処理に対応させます
- 適切な場合は、SOQL に
WITH USER_MODEを使用します
VlocityOpenInterface / VlocityOpenInterface2 の実装:
omnistudio.VlocityOpenInterface または omnistudio.VlocityOpenInterface2 を実装する場合は、次のシグネチャを使用します。
global Boolean invokeMethod(String methodName, Map<String, Object> inputMap,
Map<String, Object> outputMap, Map<String, Object> options)
Open Interface のひな形:
global with sharing class Industries_OrderOpenInterface implements omnistudio.VlocityOpenInterface2 {
global Boolean invokeMethod(String methodName, Map<String, Object> inputMap,
Map<String, Object> outputMap, Map<String, Object> options) {
switch on methodName {
when 'createOrder' {
Map<String, Object> result = createOrder(inputMap, options);
outputMap.putAll(result);
return true;
}
when else {
outputMap.put('success', false);
outputMap.put('errors', new List<Map<String, Object>>{
new Map<String, Object>{ 'code' => 'UNSUPPORTED_ACTION', 'message' => 'Unsupported action: ' + methodName }
});
return false;
}
}
}
private Map<String, Object> createOrder(Map<String, Object> inputMap, Map<String, Object> options) {
// Validate input, run business logic, return response envelope
return new Map<String, Object>{ 'success' => true, 'data' => new Map<String, Object>() };
}
}
Open Interface の実装ルール:
putAll()または個別のput()呼び出しで結果をoutputMapに書き込みます。invokeMethodからエンベロープを返してはいけません- 成功時は
true、未対応または失敗したアクションではfalseを返します - Callable と同じ内部プライベートメソッド(同じ
inputMapおよびoptionsパラメーター)を使用します。異なるのはエントリーポイントだけです - 一貫性を保つため、
outputMapに同じ形式のエンベロープ(success、data、errors)を設定します
Callable と Open Interface はどちらも同じ入力(inputMap、options)を受け取り、共通ロジックには同一のプライベートメソッドシグネチャを使用して処理を委譲します。
Phase 4: テストと検証
最低限必要なテスト:
- 正常系:サポートされているアクションが正常に実行される
- 異常系:サポートされていないアクションで想定された例外が発生する
- 契約:入力が不足している、または無効な場合にエラーエンベロープを返す
- 一括処理:リスト形式の入力を制限に抵触せず処理できる
テストクラスの例:
@IsTest
private class Industries_OrderCallableTest {
@IsTest
static void testCreateOrder() {
System.Callable svc = new Industries_OrderCallable();
Map<String, Object> args = new Map<String, Object>{
'inputMap' => new Map<String, Object>{ 'orderId' => '001000000000001' },
'options' => new Map<String, Object>()
};
Map<String, Object> result =
(Map<String, Object>) svc.call('createOrder', args);
Assert.isTrue((Boolean) result.get('success'));
}
@IsTest
static void testUnsupportedAction() {
try {
System.Callable svc = new Industries_OrderCallable();
svc.call('unknownAction', new Map<String, Object>());
Assert.fail('Expected IndustriesCallableException');
} catch (IndustriesCallableException e) {
Assert.isTrue(e.getMessage().contains('Unsupported action'));
}
}
}
VlocityOpenInterface から System.Callable への移行
Industries 拡張機能を最新化する際は、VlocityOpenInterface または
VlocityOpenInterface2 の実装を System.Callable に移行し、
アクションの契約を維持します。正確な情報源として、Salesforce のガイダンスを使用してください。
Salesforce ヘルプ
ガイダンス:
- アクション名(
methodName)を、call()内のaction文字列として維持します inputMapとoptionsをargs内のキーとして渡します:{ 'inputMap' => inputMap, 'options' => options }outMapを変更するのではなく、一貫性のあるレスポンスエンベロープを返しますcall()は薄く保ち、同じ内部メソッド((inputMap, options)シグネチャ)に処理を委譲します- 各アクションと未対応のアクションのテストを追加します
移行例(パターン):
// BEFORE: VlocityOpenInterface2
global class OrderOpenInterface implements omnistudio.VlocityOpenInterface2 {
global Boolean invokeMethod(String methodName, Map<String, Object> input,
Map<String, Object> output,
Map<String, Object> options) {
if (methodName == 'createOrder') {
output.putAll(createOrder(input, options));
return true;
}
return false;
}
}
// AFTER: System.Callable (same inputs: inputMap, options)
public with sharing class OrderCallable implements System.Callable {
public Object call(String action, Map<String, Object> args) {
Map<String, Object> inputMap = args != null ? (Map<String, Object>) args.get('inputMap') : new Map<String, Object>();
Map<String, Object> options = args != null ? (Map<String, Object>) args.get('options') : new Map<String, Object>();
if (inputMap == null) { inputMap = new Map<String, Object>(); }
if (options == null) { options = new Map<String, Object>(); }
switch on action {
when 'createOrder' {
return createOrder(inputMap, options);
}
when else {
throw new IndustriesCallableException('Unsupported action: ' + action);
}
}
}
}
ベストプラクティス(120 点満点のスコアリング)
| カテゴリー | 点数 | 主なルール |
|---|---|---|
| 契約とディスパッチ | 20 | 明示的なアクション一覧、switch on、バージョン管理されたアクション文字列 |
| 入力の検証 | 20 | 必須キーの検証、安全な型変換、null ガード |
| セキュリティ | 20 | with sharing、CRUD/FLS チェック、Security.stripInaccessible() |
| エラー処理 | 15 | 型付き例外、一貫性のあるエラーエンベロープ、空の catch を使わない |
| 一括処理と制限 | 20 | ループ内で SOQL/DML を行わない、リスト形式の入力に対応 |
| テスト | 15 | 正常系/異常系/契約/一括処理のテスト |
| ドキュメント | 10 | クラスとアクションメソッドの ApexDoc |
しきい値:✅ 90 点以上(準備完了) | ⚠️ 70~89 点(レビュー) | ❌ 70 点未満(ブロック)
⛔ ガードレール(必須)
次のいずれかに該当する場合は、作業を中断してユーザーに確認してください。
- ユーザー入力に基づく動的なメソッド実行(リフレクションは禁止)
- ループ内での SOQL/DML
- 呼び出し可能なクラスでの
without sharing - 暗黙の失敗(空の catch、例外の握りつぶし)
- アクション間でのレスポンス形式の不一致
よくあるアンチパターン
call()にビジネスロジックを記述し、処理を委譲しない- アクション名にバージョンがなく、ドキュメントにも記載されていない
- 入力マップのキーをチェックせずに存在するものとして扱う
- レスポンスタイプが混在している(Map の場合と String の場合がある)
- 未サポートのアクションに対するテストがない
スキル間の連携
| スキル | 使用する場面 | 例 |
|---|---|---|
| sf-apex | 呼び出し可能な実装以外の一般的な Apex 作業 | "Account のトリガーを作成する" |
| sf-metadata | コーディング前にオブジェクト/フィールドの有無を確認する | "Product2 を記述する" |
| sf-deploy | 呼び出し可能なクラスを検証/デプロイする | "サンドボックスにデプロイする" |
参照スキル
Apex のコア標準、テストパターン、ガードレールについては、以下を参照してください。
同梱の例
- examples/Test_QuoteByProductCallable/ —
WITH USER_MODEを使用した読み取り専用クエリの例 - examples/Test_VlocityOpenInterfaceConversion/ — レガシーな
VlocityOpenInterfaceからの移行 - examples/Test_VlocityOpenInterface2Conversion/ —
VlocityOpenInterface2からの移行
注意事項
- 決定論的で、副作用を考慮した呼び出し可能なアクションを優先してください
- アクションのコントラクトを安定させ、互換性を損なう変更には新しいアクションを導入してください
- 同期呼び出し可能処理で時間のかかる作業は避け、必要に応じて非同期処理を使用してください