2022年4月14日木曜日

Oracle REST Data ServicesのマニュアルにあるImage Galleryのサンプル・アプリを作成する

 Oracle REST Data Servicesのマニュアルの付録Dとして、イメージ・ギャラリの作成というセクションがあります。

Oracle APEXで作成したアプリケーションと、ORDSのREST APIの連携を実装しています。サーバー側のPL/SQLよりORDS REST APIを呼び出す例は多いのですが、このサンプルはブラウザのJavaScriptからORDSのREST APIを呼び出しています。

ただし、古い環境を元にしていて、書かれている通りに手順を追うことができません。Always FreeのAutonomous DatabaseのAPEX 21.2の環境上で、このサンプル・アプリケーションを作成してみました。

作成したアプリケーションは以下のようになります。


これより、実施した作業を紹介します。


ギャラリ・データベース表の作成


アップロードした画像を保存する表GALLERYを作成します。

SQLワークショップユーティリティクイックSQLを開きます。以下のモデルを使って、表GALLERYを作成します。

gallery
    title vc1000 /nn
    content_type vc1000 /nn
    image blob /nn

SQLの生成SQLスクリプトを保存レビューおよび実行を順次クリックします。

表GALLERYが作成されたら作業は完了です。アプリケーションの作成は行いません。


ギャラリRESTfulサービス・モジュールの作成


以下の3つのREST APIを作成します。

  1. アップロードされた画像を表GALLERYに保存する。
  2. 表GALLERYに保存されている画像の一覧を返す。
  3. 表GALLERYに保存されている画像を取り出す。
ORDSのREST APIを作成する方法はいくつかありますが、今回は、Oracle APEXが提供しているSQLワークショップRESTfulサービスを使います。


初回のアクセスの場合、ORDSに登録されていないスキーマとメッセージが表示されることがあります。このようなときは、ORDSにスキーマを登録を実行します。


ダイアログが開きます。

RESTfulアクセスの有効化ONになっていることを確認して、スキーマ属性の保存を実行します。


以上で、APEXのワークスペース・スキーマでREST APIを作成できるようになりました。

最初にモジュールを作成します。

左ペインよりモジュールを選択し、モジュールの作成を実行します。


モジュール名gallery.exampleベース・パスgallery/公開ONとして、モジュールの作成を実行します。


モジュールが作成されたら、続いてテンプレートの作成をクリックします。


URIテンプレートgallery/とします。

テンプレートの作成を実行します。


作成されたURIテンプレートに、ハンドラを作成します。

ハンドラの作成をクリックします。


メソッドとしてPOSTを選択します。ソースには以下を記述します。
declare
    image_id gallery.id%type;
begin
    insert into gallery (title,content_type,image) 
        values  (:title,:content_type,:body)
        returning id into image_id;
    :status_code := 201;
    :forward_location := './' || image_id;
end;
パラメータ行の追加をクリックします。アップロードされるファイル名をHTTPのヘッダーSlugとして受け付け、バインド変数titleに渡されるようにします。

追加した行の名前Slugバインド変数titleアクセス・メソッドINソース・タイプHTTP HEADERデータ型にはSTRINGを指定します。

以上を設定し、ハンドラの作成を実行します。


ハンドラのソースで使用しているstatus_codeおよびfoward_locationは、暗黙的パラメータとしてORDSが定義しているバインド変数です。status_codeはHTTPのレスポンス・コード、forward_locationはLocationヘッダーとして解釈されます。

テンプレートimages/に、メソッドGETのハンドラを作成します。

ソースは以下のSELECT文になります。
select id,title,content_type from gallery order by id desc
メソッドGETソース・タイプとしてCollection Queryを選択します。

以上を設定して、ハンドラの作成を実行します。


それぞれの画像を取り出すREST APIのテンプレートを作成します。

モジュールに戻り、テンプレートの作成をクリックします。テンプレートimages/を作成したときと、同じ手順になります。

URIテンプレートとしてimages/:idを設定し、テンプレートの作成を実行します。


作成したテンプレートimages/:idに、画像を取り出すGETハンドラを作成します。

ソースとして、以下のSELECT文を記述します。バインド変数:idは、URIテンプレートから取得されます。
select content_type, image from gallery where id = :id
メソッドGETソース・タイプとしてMedia Resourceを選択し、ハンドラの作成を実行します。


以上で、今回作成するアプリケーションで使用するREST APIが完成しました。


ギャラリRESTfulサービスの保護



作成したREST APIを、APEXで認証されたセッションからのみ呼び出しができるように保護します。マニュアルでは、ファースト・パーティ認証として説明がされています。

今回はロールは新規作成せず、デフォルトで作成されているRESTful Servicesを使います。


左ペインにて権限を選択します。権限の作成をクリックします。


名前をモジュール名と同じgallery.exampleタイトルギャラリRESTfulサービスとします。ロールとしてRESTful Servicesモジュールとしてgallery.exampleを選択します。

以上で、権限の作成を実行します。


モジュールgallery.exampleのREST APIの保護が設定されました。

ユーザーとグループの管理を開き、gallery.exampleを呼び出すユーザーにRESTful Servicesのロールを割り当てます。


ロールを割り当てるユーザーの設定を開き、グループ割当てのセクションでRESTful Servicesを割り当てます。

変更の適用を実行します。


これで、グループ割当て(ロール)としてRESTful Servicesが割り当たったユーザーでサインインしたAPEXアプリケーションより、作成したギャラリRESTfulサービスを呼び出すことができるようになりました。


ギャラリ・アプリケーションの作成



ギャラリRESTfulサービスを使用するAPEXアプリケーションを作成します。

アプリケーション作成ウィザードを起動します。名前Image Galleryとし、それ以外は指定せず空のアプリケーションを作成します。

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


アプリケーションが作成されます。

Image Galleryのアプリケーションは、すべてホーム・ページに実装します。

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


最初にファイルをアップロードするダイアログを作成します。APEXが標準で提供している機能を使用せずファイルをアップロードするため、ファイルを選択するアイテムはHTMLを直接記述して生成します。また、ファイルのアップロード処理は、JavaScriptでコーディングします。

Bodyにリージョンを作成します。

識別タイトルファイル・アップロードタイプとして静的コンテンツを選択します。外観テンプレートとして、Inline Dialogを選択し、このリージョンがダイアログとして表示されるようにします。


テンプレート・オプションをクリックして開き、Auto Hieightチェックを入れます。OKをクリックして変更を保存します。


作成したダイアログのリージョンファイル・アップロードに、サブリージョンを作成します。

リージョンファイル・アップロード上でコンテキスト・メニューを表示させ、サブリージョンの作成を実行します。


識別タイトルファイルの選択タイプ静的コンテンツとします。ソースHTMLコードとして以下を記述し、ファイルを選択するHTML要素を記述します。

<label for="file">File</label>
<input type="file" name="file" id="file" class="text ui-widget-content ui-corner-all"/>

レイアウト親リージョンファイル・アップロードであることを確認し、位置をSub RegionsからRegion Bodyに変更します。

位置がSub Regionsだと、サブリージョンは必ず画面の底部に配置されます。Region Bodyに変更することにより、画面上の配置を自由に決めることができます。これは、APEX 21.2から利用できる新機能です。

外観テンプレートとしてBlank with Attributesを選び、リージョンのタイトルや枠の表示を省きます。


リージョンファイル・アップロードに、ファイルのアップロードを実行するボタンを作成します。

識別ボタン名B_UPLOADラベルアップロードとします。動作アクションとして、動的アクションで定義を選択します。


ボタンB_UPLOADに動的アクションを作成します。ボタンB_UPLOAD上でコンテキスト・メニューを開き、動的アクションの作成を実行します。

識別名前ファイルのアップロードとします。タイミングはデフォルトで、イベントクリック選択タイプボタンボタンB_UPLOADとなります。


デフォルトで作成されるTrueアクションを選択し、識別アクションJavaScriptコードの実行に変更します。

設定コードとして、以下を記述します。
var file = document.querySelector('input[type="file"]');
uploadFiles(gallery_url,file.files[0],function() {
    doFetch();
});
ファンクションuploadFilesおよびdoFetchは、この後にページ・プロパティJavaScriptのセクションに定義します。


ファイルのアップロードを開始したら、リージョンを閉じるようTrueアクションを追加します。JavaScriptコードの実行の下にTrueアクションを作成します。

作成したTrueアクションの識別アクションとして、リージョンを閉じるを選択します。影響を受ける要素選択タイプリージョンリージョンとしてファイル・アップロードを選択します。


ファイルをアップロードするボタンの作成は、以上で完了です。

ファイルのアップロードをせずダイアログを閉じるためのボタン、キャンセルを作成します。

Region Body上でコンテキスト・メニューを表示させ、ボタンの作成を実行します。

識別ボタン名B_CANCELラベルキャンセルとします。アップロード・ボタンの右隣に配置されるよう、レイアウト新規行の開始OFFにします。動作アクションとして、動的アクションで定義を選択します。


ボタンB_CANCELに動的アクションを作成します。

識別名前アップロードのキャンセルとします。タイミングデフォルトのまま変更しません。


デフォルトで作成されたTrueアクションを選択し、リージョンファイル・アップロードを閉じるアクションに変更します。ボタンB_UPLOADに作成したアクションリージョンを閉じると同じです。


以上で、ファイルのアップロードを実行するダイアログは完成です。

画像の一覧を表示するリージョンを作成します。

新規にBodyにリージョンを作成します。

識別タイトルImagesタイプとして静的コンテンツを選択します。外観テンプレートとしてBlank with Attributesを選択します。詳細静的IDとしてimagesを設定します。


APEXのカード・リージョンを使用した方が、簡単に画像の一覧を表示することができます。このアプリケーションの目的は、ORDSのREST APIを呼び出す例を提供することなので、あえてJavaScriptでコードを書いて画像の取得と描画をしています。

ページ・プロパティJavaScriptファンクションおよびグローバル変数の宣言として、以下を記述します。ORDS REST APIを呼び出して画像ファイルをアップロードするuploadFilesと、REST APIを呼び出して画像を画面に表示するdoFetchを実装しています。




ページがロードされたときに画像が表示されるよう、JavaScriptページ・ロード時に実行に、以下を記述します。

doFetch();


ファイルを選択するHTML要素や画像の表示は、APEXの機能を使わずに生成しています。APEXの外観テンプレート・オプションは適用できないため、CSSを記述します。

ページ・プロパティのCSSのインラインに、以下を記述します。




最後にファイル・アップロードのリージョンを開くボタンを作成します。

Body上でコンテキスト・メニューを開き、ボタンの作成を実行します。

Bodyの先頭に配置します。識別ボタン名B_OPEN_UPLOADラベルアップロードとします。動作アクションとして、動的アクションで定義を選択します。


ボタンB_OPEN_UPLOADに動的アクションを作成します。

ボタンB_OPEN_UPLOAD上でコンテスト・メニューを開き、動的アクションの作成を実行します。

識別名前ファイル・アップロードを開くとします。タイミングデフォルトから変更しません。


すでに作成されているTrueアクションを選択し、識別アクションリージョンを開くに変更します。影響を受ける要素選択タイプリージョンリージョンとしてファイル・アップロードを選択します。


ボタンB_OPEN_UPLOADをクリックすると、アップロードするファイルを選択するダイアログが表示されます。

以上でアプリケーションは完成です。アプリケーションを実行すると、記事の最初のGIF動画のような動作をします。

マニュアルにはこの他に、サード・パーティ・アプリケーションからのRESTfulサービスへのアクセスとして、OAuth2による保護について説明されています。こちらはAPEX以外のアプリケーションから呼び出すことが想定されています。今回の説明には含めません。OAuth2による保護については、以前に記事が参考になるかと思います。

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

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

2022年4月7日木曜日

オラクルのマニュアルを検索するアプリを作る - 検索履歴の取得

 サンプルというには少々簡単すぎる気もしたので、先日作成したマニュアルを検索するアプリに、検索履歴を保存するコードを追加してみました。

最初に検索履歴を保存する表MDSH_HISTORYを作成します。クイックSQLの以下のモデルを使います。

# prefix: mdsh
history
    site_url vc400
    term     vc200
    search_date date /default sysdate

ホーム・ページにAjaxコールバックとしてプロセスRECORDを作成します。

識別名前RECORDPL/SQLコードとして以下のINSERT文を記述します。
insert into mdsh_history
(
    site_url
    , term
)
values
(
    apex_application.g_x01
    , apex_application.g_x02
);
sys.htp.p('{}');

ページ・プロパティJavaScriptファンクションおよびグローバル変数の宣言に記述しているコードに、apex.server.processを使ったAjaxコールバックRECORDの呼び出しを追加します。

// サイトとして設定されているドキュメントを検索する。
var elem = document.getElementById("searchDoc");
elem.addEventListener('keypress', do_search);
// Enterを押したときの列のサイトを検索する。
function do_search(e) {
  	if (e.keyCode === 13) {
        let ie = e.srcElement;
        let idx = ie.getAttribute("data-term-index");
        let term = ie.value; 
        let site_url = "site:" + document.querySelector("#site_url-" + idx).value + " ";
        let searchUrl = "https://www.google.com/search?q=" + escape(site_url) + term;
        console.log(searchUrl);
        // 検索機歴を保存する -- 追加する行。
        const result = apex.server.process(
            "RECORD",
            {
                x01: site_url,
                x02: term
            }
        );
        window.open(searchUrl, "_blank");
	} 
	return false;
}

以上で完成です。

機能を追加したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/my-document-search-wh.sql

Ajaxコールバックとapex.server.processの使い方のサンプルとして作ってみました。x01x02の使い方などは、サンプルがないと分かりにくいかと思います。

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

2022年4月6日水曜日

オラクルのマニュアルを検索するアプリを作る

 APEXでアプリを作っていると、オラクル・データベースのSQLリファレンスやPL/SQLリファレンス、APEXのリファレンスをよく検索するのですが、そのまま検索すると古いバージョンのマニュアルがヒットします。

siteを指定すれば限定されるのですが指定が面倒なので、siteに指定したいURLをデータベースに登録して、そのサイトに限定して検索を呼び出すアプリを作ってみました。

https://apex.oracle.com/pls/apex/japancommunity/r/search/


以下、作成手順を紹介します。

最初に検索対象とするサイトを保持する表MDSH_SITESを作成します。

SQLワークショップユーティリティクイックSQLを開き、以下のモデルを記述します。

# prefix: mdsh
sites
    site_name vc100 /nn /unique
    site_url  vc400 /nn /unique
    display_order num

SQLの生成、続けてSQLスクリプトを保存、最後にレビューおよび実行を行います。


SQLのレビュー画面が開きます。実行をクリックし、表MDSH_SITESを作成します。確認画面が開いたら、即時実行をクリックします。


表が作成されたのちに、アプリケーションの作成を実行します。確認画面が表示されたら、重ねてアプリケーションの実行をクリックします。


アプリケーション作成ウィザードが開きます。アプリケーションの名前検索とします。表MDSH_SITESへのサイトの登録を管理作業とするため、Sitesのページの編集をクリックします。


詳細をクリックします。管理ページとして設定チェックを入れ、変更の保存をクリックします。


アプリケーションの作成をクリックします。


処理はすべてホーム・ページに実装します。

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


Bodyにリージョンを作成します。

識別名前検索タイプとしてクラシック・レポートを選択します。ソースタイプSQL問合せを選択し、SQL問合せに以下を記述します。

APEXのPL/SQL APIのパッケージAPEX_ITEMを使って、レポートに検索する単語を入力するテキスト・フィールドを作成しています。


ファンクションAPEX_ITEM.HIDDENおよびTEXTはHTMLを生成します。レポートに表示される列の値は、デフォルトではエスケープされ、文字列として表示されます。そうではなくHTMLとして解釈されるようにします。列SITE_NAMEもHTMLを記述しています。

列をすべて選択し、セキュリティ特殊文字をエスケープOFFにします。


JavaScriptのコードよりクラシック・レポートのリージョンを指定するために、詳細静的IDとしてsearchDocを設定します。


検索する単語を入力してEnterを入力したときに、別画面に検索結果を表示させるJavaScriptのコードを記述します。

ホーム・ページJavaScriptファンクションおよびグローバル変数の宣言として、以下のJavaScriptのコードを記述します。




列SITE_URLはデータとしては必要ですが(レポート列として非表示にできない)、画面には表示させたくありません。

CSSインラインに以下を記述し、列SITE_URLを非表示にします。



検索に関する動作の実行は、以上で完了です。

これ以降は、ブレッドクラムの削除や管理ページの構成など、見栄えの調整になります。これらの作業については説明を割愛します。

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

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

2022年4月4日月曜日

CSVファイルをオブジェクト・ストレージを介して表にインポートするアプリ

 処理時間が短いDBMS_CLOUD.COPY_DATAを使って、CSVファイルをデータベースにインポートするAPEXアプリケーションを作成してみます。

こちらの記事(とこちらの記事)と同じ環境で作業を行います。

作成されるアプリケーションは以下のような動作をします。アップロードしているCSVファイルは1000万行、圧縮したサイズは41MB、非圧縮で834MBのサイズです。ファイルのアップロードから表へのインポートが完了するまで、処理時間は大体50秒です。


以下よりアプリケーションの作成手順を紹介します。

アプリケーション作成ウィザードを実行し、空のアプリケーションを作成します。

名前はCSVファイルのロードとし、アプリケーションの作成を実行します。


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

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


Bodyの位置に、新規にリージョンを作成します。

識別タイトルBRICKS_IMPタイプとして対話モード・レポートを選択します。ソース表名BRICKS_IMPを指定します。


対話モード・レポートのリージョンに、オブジェクト・ストレージ上のアップロード先となる、リージョンネームスペースおよびバケットを指定するページ・アイテムP1_REGIONP1_NAMESPACEおよびP1_BUCKETを作成します。また、CSVファイルを選択するページ・アイテムP1_FILEも作成します。

ページ・アイテムを作成し、識別名前P1_REGIONとします。タイプテキスト・フィールドラベルリージョンです。


同様にページ・アイテムP1_NAMESPACEを作成します。ラベルネームスペースとします。


ページ・アイテムP1_BUCKETを作成します。ラベルバケットとします。


アップロードするCSVファイルを選択するページ・アイテムP1_FILEを作成します。

識別名前P1_FILEタイプとしてファイル参照...を選択します。ラベルアップロードするCSVファイル(圧縮)とします。

設定ドロップゾーンのタイトルファイルを選択する記憶域タイプはデフォルトのTable APEX_APPLICATION_TEMP_FILESのままとします。APEXが提供している表を使うので、ファイルを保存するための表を用意する手間が省けます。最大ファイル・サイズ100000とします。(100MBが上限となる)


ファイルのアップロードを実行するボタンを作成します。

識別名前B_UPLOADラベルアップロードとします。動作アクションはデフォルトのページの送信のままとします。


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

左ペインでプロセス・ビューを表示させ、プロセスの作成を実行します。

識別名前CSVファイルのロードタイプとしてコードの実行を選択します。ソースPL/SQLコードには以下を記述します。

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


以上でアプリケーションは完成です。アプリケーションを実行すると、最初のGIF動画のような動作になります。

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

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

追記

オブジェクト・ストレージを操作するOCI PL/SQL SDKを使うと、以下のようなコードになります。

処理時間にそれほど違いはないようです。APIキーの準備などが必要ですが、PL/SQL SDKも充実してきています。PL/SQL SDKのマニュアルはこちらになります。