mcpField
S
sf-industry-commoncore-callable-apex

sf-industry-commoncore-callable-apex

github.com/jaganpro開発424950 回インストール26 週間前に更新
リポジトリ

sf-industry-commoncore-callable-apex について

Salesforce Industries Common Core の callable Apex を生成、レビュー、評価する専門スキルです。安全で明示的なアクション契約と、OmniStudio 拡張ポイントとの互換性を重視します。

主な機能

  1. 01System.Callable クラスの生成
  2. 02callable 実装のレビュー
  3. 03120 点ルーブリックでの評価
  4. 04OmniStudio 拡張ポイントへの対応
  5. 05Open Interface 移行のガイド

活用シーン

  1. 01OmniScript のエントリーポイント用 callable の作成
  2. 02CRUD/FLS とアクション振り分けの監査
  3. 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.md
README.md
examples/Test_QuoteByProductCallable/
Industries_QuoteByProductCallable.cls
Industries_QuoteByProductCallableTest.cls
IndustriesCallableException.cls
TRANSCRIPT.md
examples/Test_VlocityOpenInterface2Conversion/
IndustriesCallableException.cls
MyCustomCallable.cls
MyCustomCallableTest.cls
MyCustomRemoteClass.cls
TRANSCRIPT.md
examples/Test_VlocityOpenInterfaceConversion/
IndustriesCallableException.cls
MyCustomCallable.cls
MyCustomCallableTest.cls
MyCustomVlocityOpenInterface2.cls
TRANSCRIPT.md

詳細

sf-industry-commoncore-callable-apex:Salesforce Industries Common Core 向け Callable Apex

Salesforce Industries Common Core の Callable Apex 実装を専門とするエージェントです。OmniStudio および Industries の拡張ポイントとスムーズに統合できる、安全で決定的かつ設定可能な Apex を作成します。

主な役割

  1. Callable の生成:安全なアクションディスパッチを備えた System.Callable クラスを構築する
  2. Callable のレビュー:既存の Callable 実装の正確性とリスクを監査する
  3. 検証と採点:120 点満点のルーブリックに照らして評価する
  4. Industries への適合性:OmniStudio/Industries の拡張ポイントとの互換性を確保する

ワークフロー(4 フェーズのパターン)

フェーズ 1:要件の収集

以下を確認します:

  • エントリーポイント(OmniScript、Integration Procedure、DataRaptor、その他の Industries フック)
  • アクション名(call に渡される文字列)
  • 入力/出力の契約(必須キー、型、レスポンス形式)
  • データアクセスの要件(オブジェクト/フィールド、CRUD/FLS ルール)
  • 副作用(DML、コールアウト、非同期処理の要件)

次に:

  1. 既存の Callable クラスを検索します:Glob: **/*Callable*.cls
  2. Industries 拡張で使用されている共通ユーティリティまたは基底クラスを特定します
  3. タスクリストを作成します

フェーズ 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 には空のマップを使用します。

実装ルール:

  1. call() は薄く保ち、プライベートメソッドまたはサービスクラスに処理を委譲します
  2. 入力の型は早い段階で検証し、適切な型に変換します(null セーフにします)
  3. CRUD/FLS と共有ルールを適用します(with sharing、Security.stripInaccessible())
  4. 引数にレコードのコレクションが含まれる場合は、一括処理に対応させます
  5. 適切な場合は、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 ガード
セキュリティ20with 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 のコア標準、テストパターン、ガードレールについては、以下を参照してください。


同梱の例

注意事項

  • 決定論的で、副作用を考慮した呼び出し可能なアクションを優先してください
  • アクションのコントラクトを安定させ、互換性を損なう変更には新しいアクションを導入してください
  • 同期呼び出し可能処理で時間のかかる作業は避け、必要に応じて非同期処理を使用してください

コメント