はじめに
Salesforce CLIはコマンドラインからSalesforce開発に必要な操作を実行できるツールです。
プラグインをインストールすることで機能を拡張することもできます。
プラグイン作成用のツールが用意されているので、意外と簡単にプラグインを自作することができます。
イベントモニタリングのログを出力するプラグインを例にプラグインの作成方法について説明します。
事前準備
プラグインを作成するのに下記のツールが必要です。
- Node.js(LTS)
- Yarn package manager
- Salesforce cli(最新)
- plugin-dev
インストールしているSalesforce CLIのバージョンが古い場合は下記のコマンドでアップデートしてください。
$ sfdx update
plugin-devはSalesforce CLIプラグインを作成するためのツールです。このツール自体がプラグインです。
下記のコマンドでインストールできます。
$ sf plugins install @salesforce/plugin-dev
プラグインの作成
作成の流れ
- プロジェクトフォルダを作成する
- コマンドを作成する
- 必要に応じてコマンドにフラグを追加する
- 実装する
- コンパイルする
- Salesforce CLIにリンクする
1. プロジェクトフォルダの作成
plugin-devツールを使用してプロジェクトフォルダを作成します。
下記のコマンドを実行するといくつか質問が表示されるので、その質問に答えることでプロジェクトフォルダが作成されます。
最初の質問はSalesforceの内部開発者かどうか問われるのでn
と答えます。そのほかの質問でツールの名称、説明、作成者、必要なコードカバレッジ率などを入力します。
$ sf dev generate plugin
つぎに依存パッケージをインストールします。
$ yarn install
ここまでの作業でプロジェクトフォルダの準備は完了です。
コマンドの実行方法
プロジェクトフォルダにはhello world
というサンプルコマンドが作成されています。
サンプルコマンドの構成は下記になります。
- src/commands/hello/world.ts: メインの TypeScript ファイル。
- messages/hello.world.md: コマンドのヘルプとエラーを構成するメッセージを含むファイル。
- test/commands/hello/world.nut.ts: 非単体テスト。
- test/commands/hello/world.test.ts: 単体テスト。
こちらのツールを使用してコマンドの実行方法を説明します。
開発中のツールを実行する場合、下記のようにbin/dev
を実行します。
$ bin/dev hello world
Hello World at Mon Apr 10 2023.
通常のsfコマンドのように実行するためには、コンパイルを行なって、Salesforce CLIにリンクする必要があります。
$ yarn compile
$ sf plugins link .
$ sf hello world
Hello World at Mon Apr 10 2023.
Salesforce CLIにリンクされているsfプラグインを確認するにはpluginsコマンドを実行します。
$ sf plugins
dev 0.7.0
my-plugin 0.0.1 (link) /Users/tool/sf-plugins/my-plugin
Salesforce CLIのプラグインのリンクを解除する場合はplugins unlink
コマンドを実行します。
$ sf plugins unlink プラグイン名
2. コマンドの作成
EventMonitoringLogを出力するコマンドを作成していきます。
コマンド名のガイドラインは公式開発者ガイドに記載があります。
$ sf elf export --connected-org org-alias --date-range Last_n_Days:2 --output-dir ./output
下記のコマンドを実行してコマンド本体のTypeScriptファイルを作成します。
$ # sf dev generate command --name トピック名:コマンド名
$ sf dev generate command --name elf:export
このコマンドによって下記のファイルが作成されます。
- src/commands/elf/export.ts
- messages/elf.export.md
- test/commands/elf/export.nut.ts
- test/commands/elf/export.test.ts
3. フラグの追加
作成したコマンドに下記のフラグを追加していきます。
- --connected-org: Salesforce組織を指定する
- --date-range: ログを取得する日を文字列で指定する
- --output-dir: 出力するフォルダを指定する
フラグはdev generate flag
コマンドで追加できます。
このコマンドを実行することでsrc/commands/elf/export.ts
が上書きされます。
フラグ種別にrequiredOrg
を選択することで簡単にSalesforceに接続させることができます。
$ sf dev generate flag
sf dev generate flag
でフラグを作成すると自動的にtsファイルに下記のような内容が追加されます。
public static readonly flags = {
name: Flags.string({
summary: messages.getMessage('flags.name.summary'),
char: 'n',
required: false,
}),
'connected-org': Flags.requiredOrg({
summary: messages.getMessage('flags.connected-org.summary'),
char: 'o',
required: true,
}),
'date-range': Flags.string({
summary: messages.getMessage('flags.date-range.summary'),
char: 'r',
default: 'Last_n_Days:2'
}),
'output-dir': Flags.directory({
summary: messages.getMessage('flags.output-dir.summary'),
char: 'd',
required: true
}),
};
4. 処理の実装
Salesforce組織に接続する
Flags.requiredOrg
のフラグを作成している場合、下記のように簡単にSalesforce組織に接続できます。
const { flags } = await this.parse(ElfExport);
const conn = flags['connected-org'].getConnection();
このgetConnection
の戻り値はjsforceのconnectionを継承しているクラスのインスタンスなので、jsforceと同じメソッドが使用できます。
const dateRange = flags['date-range'];
const query = `SELECT Id, EventType, LogDate FROM EventLogFile WHERE LogDate = ${dateRange}`;
const result = await conn.query<{ Id: string, EventType: string, LogDate: string }>(query);
const record = result.records[0];
const contentStream = conn.sobject('EventLogFile').record(record.Id).blob('LogFile');
(サンプル)イベントモニタリング出力スクリプト
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
import * as fs from 'fs'
Messages.importMessagesDirectory(__dirname);
const messages = Messages.load('elf-downloader', 'elf.export', [
'summary',
'description',
'examples',
'flags.name.summary',
'flags.connected-org.summary',
'flags.date-range.summary',
'flags.output-dir.summary',
]);
export type ElfExportResult = {
length: number;
};
export default class ElfExport extends SfCommand<ElfExportResult> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
public static readonly flags = {
name: Flags.string({
summary: messages.getMessage('flags.name.summary'),
char: 'n',
required: false,
}),
'connected-org': Flags.requiredOrg({
summary: messages.getMessage('flags.connected-org.summary'),
char: 'o',
required: true,
}),
'date-range': Flags.string({
summary: messages.getMessage('flags.date-range.summary'),
char: 'r',
default: 'Last_n_Days:2'
}),
'output-dir': Flags.directory({
summary: messages.getMessage('flags.output-dir.summary'),
char: 'd',
required: true
}),
};
public async run(): Promise<ElfExportResult> {
const { flags } = await this.parse(ElfExport);
const dateRange = flags['date-range'];
const query = `SELECT Id, EventType, LogDate FROM EventLogFile WHERE LogDate = ${dateRange}`;
const conn = flags['connected-org'].getConnection();
const result = await conn.query<{ Id: string, EventType: string, LogDate: string }>(query);
const dir = flags['output-dir'];
if (result.records.length > 0) {
for (const record of result.records) {
const type = record.EventType;
const date = record.LogDate.substring(0, 10);
const fileName = `${date}-${type}.csv`;
this.spinner.start(`Downloading: ${fileName} to ${dir}`);
const contentStream = conn.sobject('EventLogFile').record(record.Id).blob('LogFile');
const chunks = [];
contentStream.on('data', (chunk)=>{
chunks.push(chunk);
});
contentStream.on("end", ()=>{
const body = Buffer.concat(chunks);
fs.mkdirSync(dir, {recursive: true});
fs.writeFileSync(`${dir}/${fileName}`, body);
});
this.spinner.stop();
}
}
return {length: result.records.length};
}
}
5. コンパイル
下記のコマンドでコンパイルを実施します。
$ yarn compile
6. Salesforce CLIにリンク
下記のコマンドでSalesforce CLIにリンクできます。
$ sf plugins link .
リンクを行ったら下記のコマンドで実行できます。
$ sf elf export --connected-org org-alias --date-range Last_n_Days:2 --output-dir output