ラベル ワークフロー の投稿を表示しています。 すべての投稿を表示
ラベル ワークフロー の投稿を表示しています。 すべての投稿を表示

2025年7月31日木曜日

APEXアプリケーションからn8nのワークフローを呼び出す

n8nで定義したワークフローをOracle APEXのアプリケーションから呼び出してみます。また、ワークフローの処理に時間がかかる場合を想定して、n8nのワークフローよりOracle REST Data ServicesのREST APIを呼び出す手順を確認します。

n8nには色々なアプリケーションと連携するアクションが標準で含まれていますが、Oracle APEXやOracle Databaseと連携するアクションは見つかりませんでした。そのため、APEXアプリケーションからの呼び出しをWebhookで受け付け、HTTP RequestでORDSのREST APIを呼び出します。

以下のワークフローをn8nで作成しています。中間のCodeでは以下のJavaScriptのコードを実行し、受信したデータをそのまま出力に渡しています。

return $input.all();


APEXアプリからのn8nのWebhookの呼び出し、および、n8nのHTTP RequestでのORDS REST APIの呼び出しは、基本的にREST APIの呼び出しなので難しいことはありませんが、認証については少し工夫が必要です。本記事は、主に認証での工夫について説明しています。

以下より、実施した検証作業を紹介します。作業はApple SiliconのMacbook Proで実施しています。

Oracle APEXについては、こちらの記事「podmanを使ってOracle Database FreeとOracle REST Data Servicesをコンテナとして実行する」で紹介している手順で作成した環境を使用します。

n8nを動かすサーバーを準備します。n8n Community Editionをコンテナとして実行します。公式ドキュメントのDocker Installationに記載されている手順に沿って、作業を実施します。

最初にボリュームを作成します。作成したボリュームにすべてのデータを保存するため、ボリュームが維持されていれば、コンテナを作り直してもそれまでの作業は維持されるようです。

podman volume create n8n_data

% podman volume create n8n_data

n8n_data

% 


n8nを実行するコンテナをn8nという名前で作成します。

-e N8N_RUNNERS_ENABLED=true および -e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true のオプションについては、指定しないと警告が表示されたので追加しました。

podman run -it --rm --name n8n -e N8N_RUNNERS_ENABLED=true -e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n

% podman run -it --rm --name n8n -e N8N_RUNNERS_ENABLED=true -e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n

No encryption key found - Auto-generating and saving to: /home/node/.n8n/config

Initializing n8n process

n8n ready on ::, port 5678

Migrations in progress, please do NOT stop the process.

Starting migration InitialMigration1588102412422

Finished migration InitialMigration1588102412422


[中略]



n8n Task Broker ready on 127.0.0.1, port 5679

[license SDK] Skipping renewal on init because renewal is not due yet or cert is not initialized

Registered runner "JS Task Runner" (IFWQndGFfYn8ZyDN5Bma5) 

Version: 1.104.1


Editor is now accessible via:

http://localhost:5678


Press "o" to open in Browser.



オプション -p 5678:5678 を指定して、コンテナのポート5678をホストのポート5678にマップしています。ホストのブラウザからは、以下のURLでn8nにアクセスします。

http://localhost:5678/

最初にオーナー・アカウントの設定が求められます。以降は会社の規模や職種、利用者の役職や利用目的などの入力を求められます。


今回の作業はAPEXアプリケーションからの呼び出しができれば良かったので、Get paid features for free (forever)についてはskipしました。

アカウントの設定が完了すると、n8nのコンソールが開きます。

ワークフローの作成はスクラッチから始めます。


最初のノードとしてWebhookを追加します。


Webhookの基本的な設定を行います。

HTTP MethodはPOST、RespondはWhen Last Node Finishes、Response DataはAll Entriesとします。


AuthenticationをクリックするとBasic Auth、Header Auth、JWT Auth、Noneの4つの選択肢が表示されます。認証が簡単なのはBasic Authですが、今回はJWT Authを選びます。

Credential for JWT Authをクリックして、+ Create new credentialを実行します。


JWTの署名の生成に関わる情報を設定します。今回は、APEX側ではAPEX_JWT.ENCODEを呼び出してJWTを生成する予定です。そのため、署名アルゴリズム(Algorithm)として選択できるのはHS256のみになります。HS256はハッシュ・アルゴリズムなのでKey TypeはPassphraseになります。Secretにパスフレーズである文字列を入力します。この文字列を秘密キーとして、Oracle APEXのアプリケーションと共有します。

クリデンシャルの名前をAPEX Authに変更し、Saveします。


以上で、APEXアプリからの呼び出しを受け付けるWebhookが設定できました。

Webhook URLをコピーし、キャンバスに戻ります。


Webhookの後続のノードとしてCode(JavaScriptの実行)を追加します。


ModeはRun Once for All items、LanguageはJavaScript、JavaScriptに以下を記述します。

return $input.all();

以上の設定を行い、キャンバスに戻ります。


この時点で一旦ワークフローをSaveします。


Execute Workflowをクリックします。WebhookにWaiting for you to call the Test URLと表示されているように、Webhookの呼び出し待ちになります。


APEXでの作業に移り、このn8nのワークフローを呼び出すAPEXアプリケーションを作成します。

空のAPEXアプリケーションを作成します。名前はCall n8n Workflowとします。


アプリケーションが作成されます。機能はすべてホーム・ページに作成します。

ホーム・ページをページ・デザイナで開きます。


最初にボタンSUBMITを作成します。n8nのWebhookを呼び出し、ワークフローを開始するボタンです。

外観のホットをオン、テンプレート・オプションのWidthをStretchにします。動作のアクションはデフォルトのページの送信です。


送信するデータを入力するページ・アイテムP1_REQUESTを作成します。タイプはテキスト領域です。外観の高さを15に拡張しておきます。


同様にn8nのワークフローからのレスポンスを表示するページ・アイテムP1_RESPONSEを作成します。P1_REQUESTの右隣に配置するため、レイアウトの新規行の開始をオフにします。


ボタンSUBMITをクリックしたときに実行されるプロセスを作成します。

プロセスの名前はCall n8n Workflowとします。タイプはコードを実行、ソースのPL/SQLコードとして以下を記述します。


サーバー側の条件のボタン押下時にSUBMITを指定します。


コード中でWebhook URLをG_WEBHOOK_URL、シークレットをG_SECRETから参照しています。これらはアプリケーション定義の置換文字列として設定します。

APEXをpodmanのコンテナとして実行している場合は、Webhook URLのlocalhostの部分は、host.containers.internalに置き換える必要があります。

置換文字列G_WEBHOOK_URLとしてWebhookのURL、G_SECRETとしてAPEX Auth作成時に設定したSecretの文字列を設定します。


シークレットのような秘密の値を置換文字列として設定するのは、安全な方法ではありません。実際に運用する場合は、Oracle CloudであればVaultなどにシークレットとして保存するなど、安全を確保するためにもう一工夫が必要です。

以上で、APEXアプリケーションからn8nのワークフローを開始できるようになりました。

n8nのワークフローがWaiting for triggering eventの状態であることを確認します。


RequestにJSONを入力し、ボタンSubmitをクリックします。ワークフローは入力をエコーバックするだけなので、正しいJSONであれば内容はなんでも構いません。

ワークフローが返すレスポンスが表示されます。内容を見ると、送信したリクエストの他にHTTPヘッダーやJWTに含まれるiss、sub、audoの値なども参照できることが確認できます。


n8nのコンソールではWorkflow executed successfullyと表示され、ワークフローが終了します。


以上で、APEXアプリからn8nのワークフローを開始できるようになりました。

次にn8nのワークフローからのコールバックを実装します。

最初にn8nからコールバックされた内容を保持する表N8N_CALLBACKを作成します。



n8nから呼び出すOracle REST Data ServicesのREST APIを作成します。

モジュールとしてn8n、テンプレートとしてcallback、POSTハンドラとして以下のコードを記述しています。
begin
    insert into n8n_callback(response) values(:body_text);
    commit;
end;


作成したREST APIをOAuth2で保護します。

コードを実行するとclient_idとclient_secretが印刷されます。一度しか印刷されないので、コピーして保存します。この値をn8n側に設定します。


コピーを忘れてしまった場合は、スクリプトを再実行します。

APEXアプリケーションのホーム・ページに、表N8N_CALLBACKの内容を表示する対話モード・レポートを追加します。

識別の名前はn8n Callback、ソースの表名にN8N_CALLBACKを設定します。


以上でAPEXアプリケーションの準備は完了です。

n8nのワークフローにHTTP Requestのノードを追加します。


HTTP Requestに、以下の設定を行います。

MethodはPOST、URLはORDSのREST APIのURLを指定します。モジュール・パスが/n8n/、テンプレート名がcallbackとして作成しているため、以下の形式になります。

http://ホスト:ポート/ords/ワークスペース名/n8n/callback

今回の作業ではワークスペース名がapexdev、n8nはコンテナで動作しているため、以下を設定しています。

http://host.containers.internal:8181/ords/apexdev/n8n/callback

AuthenticationはGeneric Credential Type、Generic Auth TypeにOAuth2 APIを選択します。OAuth2 APIにAPEX OAuth2 Credを設定していますが、この設定については、この後に紹介します。

Send Bodyをオンにし、Body Content TypeにJSON、Specify BodyにUsing JSON、JSONに{{ $json.body }}を設定します。入力されたJSONドキュメントを、ORDS REST APIの呼び出しの本体としてそのまま返しています。


APEX OAuth2 Credとして、以下を設定しています。

Grant TypeはClient Credentialsです。Access Token URLはORDSが標準で提供しているトークンURLで、以下の形式になります。

http://ホスト:ポート/ords/ワークスペース名/oauth/token

今回の作業ではワークスペース名がapexdev、n8nはコンテナで動作しているため、以下を設定しています。

http://host.containers.internal:8181/ords/apexdev/oauth/token

Client IDおよびClient Secretは、APEX側でOAuth2による保護を行なうスクリプトを実行した時にclient_idおよびclient_secretとして印刷された値を設定します。

ScopeにはORDSのモジュールの保護に使用した権限名を設定します。今回はn8n.privを設定します。

ORDSのトークンURLは、client_idおよびclient_secretはAuthorizationヘッダーとして送信されることを期待しているため、AuthenticationにはHeaderを設定します。


以上で、n8nのワークフローの更新は完了です。

ワークフローを実行します。


改変したAPEXアプリケーションからワークフローを開始します。

Requestに以下を入力し、ボタンSubmitをクリックします。

{
    "name": "my first request"
}

ワークフローがすぐに終了するため、ページの再描画前にn8nのワークフローからのコールバックが完了するようです。

そのため、対話モード・レポートにRequestとして与えた文字列がすぐに表示されます。


今回の記事は以上になります。

簡単なアプリケーションですが、作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/call-n8n-workflow.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。

完

2024年2月16日金曜日

オラクルの公式ブログMulti Level Expense Approval using APEX Workflowのワークフローを理解する

Oracle CorporationでAPEXワークフローを開発しているAnanya Chatterjeeさんが、オラクルの公式ブログに以下の記事を寄稿しています。

Multi Level Expense Approval using APEX Workflow

Ananya ChatterjeeさんはOracle APEXの承認タスクの開発も行なっています。APEXワークフローがリリースされる前のAPEXで、承認タスクによる多段階承認の実装を紹介する記事も書かれています。

Implementing Multi-Approvals in Oracle APEX 22.1
https://blogs.oracle.com/apex/post/implementing-multi-approvals-in-oracle-apex-221

承認タスクを使った多段階認証の実装については、本ブログでも以下の記事で紹介しています。

APEX 22.1の承認コンポーネント(4) - 多段階承認の実装

Oracle APEX 23.2からAPEXワークフローが提供されたため、承認タスクによる多段階承認の記事に以下のコメントが追記されました。
Note: With the availability of Workflow feature in APEX 23.2 release, it is now possible to visualize and design multi-level approvals in a more declarative manner, which you can read about in another blog. This blog is still relevant if you are looking to use using Approval Components, standalone, without Workflow and is also relevant for pre 23.2 versions of APEX.

APEX 23.2以降あれば、多段階承認はAPEXワークフローで実装することを推奨していると言えます。

以下より、ブログ記事「Multi Level Expense Approval using APEX Workflow」で作成しているワークフローで、多段階承認をどう実装しているのか説明しようと思います。ワークフローを組み込んだアプリケーションは、元記事の手順にそって作業を行うことで作成できます。英語ですが、手順が具体的でコードおよびスクリーンショットも揃っていることから、作業はそれほど難しくありません。

本記事では、ワークフローを以下のように若干簡素にしています。電子メールの送信のアクティビティを削除し、ワークフロー終了に接続しています。


以下より、多段階承認のワークフローを確認していきます。

最初に経費精算の承認ワークフローを開始します。

ユーザーSTEVEでサインインし、経費申請を行います。以下の画面で重要なパラメータはExpense Amount、つまり費用です。


費用はページ・アイテムP4_EXPENSE_AMOUNTに入力されます。


ボタンSUBMITを押すと、経費精算のワークフロー(定義はExpense Reimbursement Workflow)が開始します。


ワークフロー・パラメータAmountにページ・アイテムP4_EXPENSE_AMOUNTの値を割り当てています。ワークフロー・パラメータはワークフローの開始時に値が設定され、開始後は値を変更することはできません。経費精算の承認処理を行なっている間に、申請した費用が変更されたら困ります。経費申請の申請者や費用といった、ワークフローの処理中に変更できない値をワークフロー・パラメータとして設定します。


ワークフローExpense Reimbursement Workflowを確認します。

ワークフロー・パラメータとしてAmountが設定されています。このパラメータにページ・アイテムP4_EXPENSE_AMOUNTの値が設定されます。ワークフローの中でこのパラメータは静的IDのAMOUNTより参照します。

Oracle APEXでは、ページ・アイテムやアプリケーション・アイテムの値をバインド変数や置換文字列として参照できますが、その際はページ・アイテムやアプリケーション・アイテムの名前を指定します。ワークフローのワークフロー・パラメータおよびワークフロー・バージョン変数は名前の代わりに静的IDを指定して参照します。


ワークフローが開始すると、最初にアクティビティCompute Max Levelが実行されます。このアクティビティでは以下のPL/SQLコードが実行されます。
declare
    level_no number;
begin
    if :AMOUNT < 500 then
        level_no := -1;
    elsif :AMOUNT < 1000 then
        level_no := 1;
    elsif :AMOUNT < 5000 then
        level_no := 2;
    else
        level_no := 3;
    end if;
    :MAX_LEVEL := level_no;
end;
申請した費用が500より少なければMAX_LEVELは -1 、1000より少なければ 1、5000より少なければ 2、それ以上は 3 を設定しています。

表APPROVAL_LEVELSに、以下のシード・データが挿入されています。
insert into APPROVAL_LEVELS values (10, 'JANE', 'jane@expenseteam.com', 1);
insert into APPROVAL_LEVELS values (20, 'MATT', 'martin@expenseteam.com', 1);
insert into APPROVAL_LEVELS values (30, 'BO', 'bo@expenseteam.com', 2);
insert into APPROVAL_LEVELS values (40, 'CLARA', 'clara@expenseteam.com', 2);
insert into APPROVAL_LEVELS values (50, 'SUSIE', 'susie@expenseteam.com', 3);
このワークフローでは、費用が500より少なければ自動承認、1000より少なければ承認レベル(表APPROVAL_LEVELSの列LEVEL_CODEの値)が1であるJANEかMATTが承認が必要、1000以上5000未満ではさらに承認レベルが2であるBOかCLARAの承認が必要、それ以上はさらに承認レベルが3であるSUSIEの承認が必要、という承認フローを実装しています。


MAX_LEVELはワークフロー・バージョン変数として作成されています。


続く切替えApproval Needed?では、ワークフロー・バージョン変数MAX_LEVELが-1であれば自動承認としてワークフローを終了させ、それ以外(1か2か3)であれば、ヒューマン・タスクを生成するアクティビティCreate Approved Requestへ進みます。


アクティビティCreate Approval Requestでは、承認タスクReimbursement Requestを作成します。承認タスクの結果はワークフロー・バージョン変数TASK_OUTCOMEに設定され、後続の切替えApproved?により参照されます。承認タスクの結果はAPPROVEDまたはREJECTEDです。


承認タスクReimbursement RequestのパラメータにLevel Noがあります。Level Noには、ワークフロー・バージョン変数CURRENT_LEVELが割り当てられています。


ワークフロー・バージョン変数CURRENT_LEVELには、値のタイプに静的値、静的値として1が設定されています。ワークフロー・バージョン変数はアクティビティによって変更できるため、この値は初期値またはデフォルト値になります。


承認タスクReimbursement Requestのタスク定義では、潜在的所有者にSQL問合せが設定されています。
select mgr_name from approval_levels where level_code = :LEVEL_NO
パラメータLEVEL_NOにはアクティビティCreate Approval Requestが呼び出された時点でのワークフロー・バージョン変数CURRENT_LEVELの値が割り当てられて、承認タスクが作成されます。つまりCreate Approval Requestの初回呼び出し時はCURRENT_LEVELは初期値の1なので、LEVEL_NOは1になります。

結果として費用が500以上の経費申請(自動承認の対象にならない経費申請)は、最初に承認レベルが1のJANEかMATTのどちらかの承認を必要とします。


JANEかMATTが承認タスクを承認すると、切替えApproved?を経由して切替えAll Approvals Completed?へ進みます。

反対に承認タスクを却下すると、切替えRejected At First Level?に進みます。


切替えAll Approvals Completed?では、ワークフロー・バージョン変数CURRENT_LEVELと比較する切替えのタイプが設定されています。タイプCheck Workflow Variableでは、実際の条件は切替えから伸びている接続に設定されます。


切替えAll Approvals Completed?とアクティビティIncrement Levelの接続Noに条件として&MAX_LEVEL.より小さいが設定されています。申請した費用が4000のときはMAX_LEVELが2なのでCURRENT_LEVELの1より小さいため、こちらの接続が選択されます。

それ以外、つまりMAX_LEVELとCURRENT_LEVELが同じ値であれば、経費申請が承認されてワークフローは終了します。


アクティビティIncrement Levelでは以下のPL/SQLコードが実行されます。ワークフロー・バージョン変数CURRENT_LEVELに1が加算されます。
begin
:CURRENT_LEVEL := :CURRENT_LEVEL+1;
end;
その後、アクティビティCreate Approval Requestが呼び出されます。アクティビティCreate Approval Requestにて作成される承認タスクの潜在的所有者はパラメータLEVEL_NO、つまりワークフロー・バージョン変数CURRENT_LEVELで決まります。CURRENT_LEVELが2の場合、承認レベルが2のBOかCLARAの承認が必要になります。


JANEかMATTが承認タスクを却下した場合は、切替えRejected at First Level?に進みます。この切替えは条件として、CURRENT_LEVELが1であれば経費申請を却下してワークフローを終了します。

JANEかMATTが経費申請を承認し、次の承認レベルのBOからCLARAにより経費申請が却下されるとCURRENT_LEVELは2になるため、アクティビティReset Levelに進みます。


アクティビティReset Levelでは以下のPL/SQLコードを実行し、CURRENT_LEVELを1に戻しています。
begin
:CURRENT_LEVEL := 1;
end;
つまり経費申請の承認は、承認レベルが1のJANEとMATTの承認から取り直しになります。


以上が、ブログ記事Multi Level Expense Approval using APEX Workflowで実装しているワークフローの説明になります。

今回の説明に使用したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/expense-reimbursement.zip

ワークスペースへのユーザー作成を避けるため、以下のPL/SQLファンクションによるカスタム認証をカレント・スキームにしています。
function demo_authentication (
    p_username in varchar2,
    p_password in varchar2 )
    return boolean
is
begin
    if upper(p_username) in (
        'STEVE','JANE','BO','SUSIE','CLARA'
    ) then
        return true;
    end if;
    return false;
end;

Oracle APEXのアプリケーション作成の参考になれば幸いです。

完