2024年3月28日木曜日

ページの再ポストを防止する仕組みについて

Oracle APEXのアプリケーションに含まれるページのプロパティに、ページの重複送信の許可があります。選択できる値は、はい - ページの再ポストを許可するまたは、いいえ - ページの再ポストを防止する、です。

この設定はページ・プロパティ詳細に含まれます。デフォルトは、はい - ページの再ポストを許可する、です。


ページの再ポストを防止する仕組みは、以下のように実装されています。

Oracle APEXの組込み置換文字列としてAPP_UNIQUE_PAGE_IDがあります。ドキュメントには以下のように説明されています。

APP_UNIQUE_PAGE_IDは、各ページ・ビューに固有なOracle順序から生成される整数です。この数値は、アプリケーションでの重複ページの送信を防止するために使用され、その他の用途にも使用できます。

APEXが生成する全てのページに、このAPP_UNIQUE_PAGE_IDが含まれていて、ページの送信時に同時に送信されます。非表示のパラメータp_page_submission_idの値がそれになります。


ちなみに非表示のパラメータp_flow_idアプリケーションID(APP_ID)p_flow_step_idページID(APP_PAGE_ID)p_instanceセッションID(APP_SESSION)が割り当たります。これらはOracle APEXの開発が始まった当初の名前がFlowsだったことに由来しています。(英語版WikipediaのOracle Application ExpressのBackgroundを参照のこと)。

ページの重複送信の許可いいえ - ページの再ポストを防止するになっていると、APP_UNIQUE_PAGE_IDを対象としたページの送信が1度だけしか行えないように制限されます。

簡単なAPEXアプリケーションを作成して、動作を確認してみます。

そのページのAPP_UNIQUE_PAGE_IDを表示、または、p_page_submission_idに設定する値を指定するページ・アイテムP1_APP_UNIQUE_PAGE_IDを作成します。ページを送信するボタンSUBMITp_page_submission_idにページ・アイテムP1_APP_UNIQUE_PAGE_IDの値を設定するボタンREPLACEを作成します。


ページ・アイテムP1_APP_UNIQUE_PAGE_IDは、識別タイプテキスト・フィールドとします。

ソースタイプアイテムを選択し、アイテムとしてAPP_UNIQUE_PAGE_IDを指定します。セッション・ステートストレージリクエストごと(メモリーのみ)を選択します。

ページがロードする際に、APP_UNIQUE_PAGE_IDの値がページ・アイテムに設定されます。


ボタンSUBMITは、アクションとして単にページの送信を行います。


ボタンREPLACEには動的アクションを作成し、ボタンをクリックしたときに以下のJavaScriptコードを実行するようにします。

ページ・アイテムP1_APP_UNIQUE_PAGE_IDに指定されている値を、IDがpPageSubmissionIdのINPUT要素(タイプはhidden)に設定しています。

$s("pPageSubmissionId", apex.items.P1_APP_UNIQUE_PAGE_ID.value);


ページ・プロパティのページの重複送信の許可いいえ - ページの再ポストを防止するに設定します。


アプリケーションを実行します。

ボタンSUBMITをクリックすると、APP_UNIQUE_PAGE_IDは毎回更新されます。


APP_UNIQUE_PAGE_IDの値をクリップボードにコピーします。

再度、ボタンSUBMITを複数回クリックします。


現在のAPP_UNIQUE_PAGE_IDの値を送信済みのAPP_UNIQUE_PAGE_IDに置き換え、ボタンREPLACEをクリックします。


ボタンSUBMITをクリックすると再ポストの防止が働き、「このページはすでに送信されているため、再送信できません。」とエラーが表示されます。


つまり、ページ送信時のパラメータp_page_submission_id(type=hiddenのINPUT要素 - idはpPageSubmissionId)として渡されるAPP_UNIQUE_PAGE_IDが以前に送信済みであれば、再ポストの防止が設定されているページでは、エラーが発生します。

ページの送信を行なうコードapex.page.submitの内容を確認すると、ページの再描画が行われる以前に、HTTPのPOSTの応答を受け取った時点でp_page_submission_idは更新されています。


現実的な状況としては、送信したページのレスポンスを受け取る前(p_page_submission_idが変わる前)に再度ページを送信した時に、エラーが発生すると言えます。

簡単なアプリケーションですがエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/prevent-page-repost.zip

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

2024年3月22日金曜日

CohereのConnectorとしてOracle REST Data ServicesのREST APIを呼び出す

Cohereが最近ベータ版として提供を開始したConnectorsという機能があります。

Introduction to Connectors
https://docs.cohere.com/docs/connectors

Coralへリクエストを送信する際に、connectorsに自分で作成したコネクタを含めることにより、LLMに外部の知識を追加することができるようになります。

Oracle REST Data ServicesにREST APIを作成し、自作コネクタとして定義してCohereのPlaygroundから呼び出してみました。以下、作業の手順を記載します。

とりあえず、Playgroundから呼び出すだけ呼び出してみました。


作成したConnectorはDashboardから確認できます。なぜか最初のConnectorはAPIからでないと作成できませんでした。1つConnectorが作成されていると、New connectorのボタンが表示されるようになります。


CohereのConnectorとして登録するORDSのREST APIを作成します。

モジュールはcohere、テンプレートはsearchとします。CohereからはPOSTメソッドが呼び出されます。

ハンドラのコードとして以下を記述します。特に何かを検索せず、リクエストにqueryとして渡される文字列をそのままtextとして返します。



認証による保護はかけません。Connectorはservice_authoauth2(認可コード・グラント)、user_access_token(パス・スルー)の3種類の認証手順を使ってConnector(今回はOracle REST Data ServicesのREST API)を呼び出すことができますが、どれもORDSのREST APIの保護と(今までの調査の範囲では)互換がなさそうです。ORDSのREST APIをCohereのConnectorとして呼び出す際のAPIの保護に関しては課題です。


以下より、CohereのConnector APIを呼び出して、Oracle REST Data ServicesのREST APIをConnectorとして追加するAPEXアプリケーションを作成します。

CohereのAPIキーがWeb資格証明COHERE_API_KEYとして登録しておきます。

認証タイプとしてHTTPヘッダーを選択します。資格証明名Authorization、 資格証明シークレットBearerで始めて空白で区切り、CohereのAPIキーを続けた文字列を設定します。


空のアプリケーションを作成します。名前Cohere Connectorsとします。


アプリケーションが作成されたら、共有コンポーネントRESTデータ・ソースを開きます。Connectorの操作はRESTデータ・ソースとして実装します。


RESTデータ・ソース作成を開始します。


RESTデータ・ソースの作成最初からを選択し、へ進みます。続く画面で、RESTデータ・ソースの一般的な設定を行います。

RESTデータ・ソース・タイプとして簡易HTTPを選択します。名前Cohere Connectors APIURLエンドポイントとして以下を設定します。

https://api.cohere.ai/v1/connectors

へ進みます。


リモート・サーバーベースURLサービスURLパスなどが自動的に設定されます。

変更は不要なので、そのままへ進みます。


ページ区切りタイプページ区切りなしです。

へ進みます。


認証が必要ですオンにし、資格証明としてCOHERE_API_KEYを選択します。

検出をクリックします。


標準のConnectorであるweb-searchとcohere-public-docsが検出されます。

RESTデータ・ソースの作成をクリックします。


RESTデータ・ソースCohere Connector APIが作成されます。

このRESTデータ・ソースを開き、Connectorの作成、更新、削除を行なう操作を追加します。


操作の追加をクリックします。


HTTPメソッドとしてPOSTデータベース操作として行の挿入を選択します。リクエスト本文テンプレートとして以下を記述します。
{  
  "name":"#NAME#",  
  "description":"#DESCRIPTION#",  
  "url":"#URL!RAW#",  
  "service_auth": {  
    "type": "#TYPE#",
    "token": "#TOKEN!RAW#"
  }
}
以上を設定し、一旦、作成をクリックします。


作成された操作POSTを再度開き、操作パラメータ本文との同期をクリックします。


Content-Typeヘッダーを操作パラメータとして追加します。

パラメータの追加をクリックします。


タイプとしてHTTPヘッダーを選択します。名前Content-Type目的入力デフォルト値application/jsonです。静的オンにし値の変更を禁止します。

パラメータの追加をクリックします。


以上でConnectorの作成を行なう操作は追加できました。

変更の適用をクリックします。


先ほど同じ手順で操作で、Connectorの更新を行なう操作を追加します。

URLパターン/:idを設定します。HTTPメソッドPATCHデータベース操作行の更新です。リクエスト本文テンプレートとして以下を記述します。
{  
  "service_auth": {  
    "type": "#TYPE#",
    "token": "#TOKEN!RAW#"
  }
}
一旦、操作PATCHを作成し、再度操作を開いた後に、本文の同期とContent-Typeヘッダーの追加を行います。


URLパターンidも、操作パラメータとして追加します。


Connectorの削除を行なう操作を追加します。

URLパターン/:idHTTPメソッドとしてDELETEデータベース操作として行の削除を選択します。

操作DELETEではリクエスト本文パラメータContent-Typeヘッダー不要です。操作パラメータURLパターンidを追加します。


以上でCohereのConnectorを操作するRESTデータ・ソースは完成です。

Connectorの一覧と操作を行なうページを、フォーム付き対話モード・レポートのページとして作成します。

ページの作成を開始し、対話モード・レポートを選択します。

レポートの名前Connectorsフォーム・ページを含めるオンにし、フォーム・ページ名Connector Detailとします。

データ・ソースとしてRESTデータ・ソースを選択し、RESTデータ・ソースとしてCohere Connector APIを選択します。

へ進みます。


主キー列1としてID (Varchar2)を選択します。

ページの作成をクリックします。


対話モード・レポートとフォームのページが作成されます。

対話モード・レポートのページはそのままで利用できますが、フォームはレポートの列と作成、更新、削除の操作が受け付けるパラメータが異なるため、調整が必要です。


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

ページ・アイテムとしてP3_IDP3_URLP3_NAMEP3_DESCRIPTIONP3_TYPEP3_TOKENを作成します。P3_IDタイプ非表示で作成済みですが、他のページ・アイテムは作成されていないものもあります。P3_DESCRIPTIONタイプテキスト領域、それ以外はテキスト・フィールドとして作成します。


ページ・アイテムP3_URLP3_NAMEP3_DESCRIPTIONは作成時にのみ設定可能で、更新はできません。そのため、読取り専用タイプアイテムはNULLではないを選択し、アイテムP3_IDを設定します。


Connectorの認証パラメータであるservice_authtypeは常にbearerとするため、デフォルトタイプ静的を選択し、静的値bearerを設定します。


ページ・アイテムの準備は以上で完了です。

プロセス・ビューを開きます。

デフォルトで作成されているプロセスプロセス・フォームConnector Detailでは処理が適切に行われないため、ビルド・オプションコメント・アウトします。


Connectorを作成するプロセスを、名前Create Connectorとして作成します。タイプAPIの呼出しです。

設定タイプRESTソースRESTソースとしてCohere Connector API操作POSTを選択します。

サーバー側の条件ボタン押下時CREATEを設定します。

パラメータは、ページ番号の接頭辞を除きページ・アイテムの名前と一致しているため、適切なページ・アイテムが割り当てられます。


Connectorの更新を行なうプロセスは名前Update Connectorとして作成します。

操作PATCH - /:idサーバー側の条件ボタン押下時SAVEを設定します。


Connectorの削除を行なうプロセスは名前Delete Connectorとして作成します。

操作DELETE - /:idサーバー側の条件ボタン押下時DELETEを設定します。


以上でCohereのConnectorを操作するアプリケーションは完成です。

先ほど作成したORDS REST APIをConnectorとして作成します。

名前My First Connectorとしています。URLはORDS REST APIの完全なURLを設定します。REST API自体には保護はかけられていないため、Tokenには適当な文字列を入力します。


ConnectorとしてMy First Connectorが登録されます。


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

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

2024年3月21日木曜日

データベース単体のコンテナへのOracle APEXのインストールを自動化する

Oracle Container RegistryにあるOracle Database 23ai Freeのコンテナ・イメージより作成したコンテナへ、Oracle APEXのインストールおよび構成をするスクリプトを記述してみました。

以下のスクリプトになります。


このスクリプトを使った、Oracle APEX環境の構築手順を以下に紹介します。

こちらの記事と同様の手順で準備した、クラウド環境のインスタンス上に環境を構成します。RHEL 9と互換性のあるOSであれば、同じ手順になるでしょう。

作成したユーザーoracleで作業を行います。

sudo su - oracle

[opc@apex ~]$ sudo su - oracle

Last login: Thu Mar 21 06:51:58 GMT 2024 on pts/1

[oracle@apex ~]$ 


構成スクリプトを配置するディレクトリsetupと起動スクリプトを配置するディレクトリstartupを作成します。

mkdir setup startup
ls -l

[oracle@apex ~]$ mkdir setup startup

[oracle@apex ~]$ ls -l

total 0

drwxr-xr-x. 2 oracle oinstall 6 Mar 21 06:56 setup

drwxr-xr-x. 2 oracle oinstall 6 Mar 21 06:56 startup

[oracle@apex ~]$ 


ディクトリsetup以下に、記事の先頭に記載したスクリプト01_install_apex.shを配置します。

[oracle@apex ~]$ ls -l setup

total 4

-rwxr-xr-x. 1 oracle oinstall 2849 Mar 21 06:58 01_install_apex.sh

[oracle@apex ~]$ 


ディレクトリstartup以下に、Oracle REST Data Servicesを起動するスクリプト01_ords.shを配置します。


[oracle@apex ~]$ ls -l startup

total 4

-rwxr-xr-x. 1 oracle oinstall 99 Mar 21 07:03 01_ords.sh

[oracle@apex ~]$ 


作成されるデータベースのSYSのパスワードとなる文字列を、podman secretのoracle_pwdに保存します。<パスワード>の部分を実際のパスワードに置き換えて実行します。

echo -n <パスワード> | podman secret create oracle_pwd -

[oracle@apex ~]$ echo -n ******* | podman secret create oracle_pwd -

e710844f1de63ecede82909f3

[oracle@apex ~]$ 


コンテナapex-dbを作成します。以下のコマンドを実行します。
podman run -d --name apex-db --secret=oracle_pwd \
-p 1521:1521 -p 8181:8181 -p 8443:8443 \
--privileged --userns=keep-id:uid=54321,gid=54321 \
-v /home/oracle/setup:/opt/oracle/scripts/setup -v /home/oracle/startup:/opt/oracle/scripts/startup \
container-registry.oracle.com/database/free:latest
コンテナ・イメージがダウンロード済みでなければ、最初にコンテナ・イメージのダウンロードが行われます。その後、コンテナapex-dbが作成されます。

[oracle@apex ~]$ podman run -d --name apex-db --secret=oracle_pwd \

-p 1521:1521 -p 8181:8181 -p 8443:8443 \

--privileged --userns=keep-id:uid=54321,gid=54321 \

-v /home/oracle/setup:/opt/oracle/scripts/setup -v /home/oracle/startup:/opt/oracle/scripts/startup \

container-registry.oracle.com/database/free:latest

Trying to pull container-registry.oracle.com/database/free:latest...

Getting image source signatures

Copying blob b7d28faa08b4 done  

Copying blob 43c899d88edc done  

Copying blob 089fdfcd47b7 done  

Copying blob f8d07bb55995 done  

Copying blob 47aa6f1886a1 done  

Copying blob c31c8c658c1e done  

Copying blob 1d0d5c628f6f done  

Copying blob db82a695dad3 done  

Copying blob 25a185515793 done  

Copying config 39cabc8e6d done  

Writing manifest to image destination

9212ba6e9e62d9dd683a54c60c7748ec7ddfc96874351208d7915171a9f4d0e5

[oracle@apex ~]$ 


コンテナapex-dbが作成されデータベースが利用可能かどうか、確認します。

podman logs apex-db

出力されたログにDATABASE IS READY TO USE!のメッセージが含まれていれば、データベースは利用可能です。

[oracle@apex ~]$ podman logs apex-db

Starting Oracle Net Listener.

Oracle Net Listener started.

Starting Oracle Database instance FREE.

Oracle Database instance FREE started.


The Oracle base remains unchanged with value /opt/oracle


SQL*Plus: Release 23.0.0.0.0 - Production on Thu Mar 21 07:14:21 2024

Version 23.3.0.23.09


Copyright (c) 1982, 2023, Oracle.  All rights reserved.



Connected to:

Oracle Database 23c Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.3.0.23.09


SQL> 

User altered.


SQL> 

User altered.


SQL> 

Session altered.


SQL> 

User altered.


SQL> Disconnected from Oracle Database 23c Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.3.0.23.09

The Oracle base remains unchanged with value /opt/oracle

#########################

DATABASE IS READY TO USE!

#########################


Executing user defined scripts

/opt/oracle/runUserScripts.sh: running /opt/oracle/scripts/startup/01_ords.sh


DONE: Executing user defined scripts


The following output is now a tail of the alert.log:

Dumping current patch information

===========================================================

No patches have been applied

===========================================================

2024-03-21T07:14:20.798481+00:00

FREEPDB1(3):Opening pdb with Resource Manager plan: DEFAULT_PLAN

Completed: Pluggable database FREEPDB1 opened read write 

Completed: ALTER DATABASE OPEN

2024-03-21T07:14:23.272674+00:00

FREEPDB1(3):TABLE AUDSYS.AUD$UNIFIED: ADDED INTERVAL PARTITION SYS_P342 (3552) VALUES LESS THAN (TIMESTAMP' 2024-03-22 00:00:00')

[oracle@apex ~]$ 


Oracle Database 23c Freeのコンテナ・イメージの説明には、/opt/oracle/scripts/seetup以下に配置されている拡張子.shまたは.sqlのファイルは、データベース構成完了後に一度だけ実行されると記載されていますが、なぜか実行されないようです。

そのため、手動でAPEXの構成スクリプトを実行します。環境にもよると思いますが、20分程度の時間がかかります。

podman exec -it apex-db /opt/oracle/scripts/setup/01_install_apex.sh

[oracle@apex ~]$ podman exec -it apex-db /opt/oracle/scripts/setup/01_install_apex.sh

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

100  173M  100  173M    0     0   118M      0  0:00:01  0:00:01 --:--:--  118M

Oracle Linux 8 BaseOS Latest (x86_64)                                                         208 kB/s | 3.6 kB     00:00    

Oracle Linux 8 BaseOS Latest (x86_64)                                                          49 MB/s |  71 MB     00:01    

Oracle Linux 8 Application Stream (x86_64)                                                    299 kB/s | 3.9 kB     00:00    

Oracle Linux 8 Application Stream (x86_64)                                                     78 MB/s |  55 MB     00:00    

Oracle Linux 8 Development Packages (x86_64)                                                  241 kB/s | 3.3 kB     00:00    

Oracle Linux 8 Development Packages (x86_64)                                                   51 MB/s | 126 MB     00:02    

Last metadata expiration check: 0:00:11 ago on Thu Mar 21 07:20:55 2024.

Dependencies resolved.

==============================================================================================================================

 Package                   Architecture              Version                            Repository                       Size

==============================================================================================================================

Installing:

 jdk-17                    x86_64                    2000:17.0.10-11                    @commandline                    174 M


Transaction Summary

==============================================================================================================================

Install  1 Package


Total size: 174 M

Installed size: 303 M

Downloading Packages:


[中略]


Configuration:

  /etc/ords/config/


The global setting named: standalone.static.context.path was set to: /i


ORDS: Release 23.4 Production on Thu Mar 21 07:31:50 2024


Copyright (c) 2010, 2024, Oracle.


Configuration:

  /etc/ords/config/


The global setting named: standalone.static.path was set to: /opt/oracle/apex/23.2.0/images

[oracle@apex ~]$


コンテナapex-dbを再起動します。/opt/oracle/scripts/startup以下の01_ords.shは、コンテナ・イメージの説明通り、起動時に実行されます。

podman restart apex-db

[oracle@apex ~]$ podman restart apex-db

WARN[0010] StopSignal SIGTERM failed to stop container apex-db in 10 seconds, resorting to SIGKILL 

apex-db

[oracle@apex ~]$ 


以上でOracle APEXにアクセスできるようになりました。

HTTPで構成しているため、ホスト名oci-apexにコンテナが稼働しているホストのIPアドレスが登録されていると、以下のURLでブラウザよりアクセスできます。



APEXにもアクセスできます。