2024年3月6日水曜日

ORDS Database APIを呼び出し別環境のAPEXアプリケーションのエクスポート/インポート/削除を行なう

Oracle REST Data ServicesはOracle REST Data Services APIとして、Oracle Databaseを操作するREST APIを提供しています。これらのREST APIは、ORDS開発者ガイドではORDS Database APIと呼ばれていて、利用するための準備は8 Enabling ORDS Database APIに記載されています。

ORDS Database APIには、APEXのワークスペースを操作するREST APIが含まれています。主要なAPIにアプリケーションの一覧/エクスポート/インポート/削除があり、これらのREST APIを呼び出すことにより、インスタンスの異なるワークスペースに含まれるアプリケーションを操作することができます。

異なるAutonomous DatabaseのAPEXワークスペースにあるアプリケーションの一覧、アプリケーションのインポート、エクスポートおよび削除を行なうAPEXアプリケーションを作成してみました。以下のように動作します。


ORDS Database APIについて、以下の記事を参考にしています。

ORDSのProduct ManagerのJeff Smithさんによる以下の記事
ORDS and our APEX REST APIs

Oracle APEXのArchitectのCarsten Czarskiさんによる以下の記事
Oracle APEX App Deployments made easy: Use the ORDS REST APIs

インスタンスURLワークスペース名により操作するAPEXのワークスペースを特定し、ユーザー認証にはOAuthのクライアント・クリデンシャル・フローのクライアントIDクライアントシークレットを使用します。

アプリケーションを作成するAPEXワークスペースと作成したアプリケーションから操作するAPEXワークスペースの両方に、Always FreeのAutonomous Databaseを使用して作業を進めます。ORDS Database APIはORDSに実装されている機能であるためネットワークとして接続できれば、オンプレミスの環境も操作の対象にできます。

これから作成するAPEXアプリケーションの操作対象となるインスタンスを準備します。操作対象となるAutonomous DatabaseのORDSに接続します。

https://ADBのホスト名/ords/

SQL Developer Webを開きます。


ADBの管理者ユーザーADMINでサインインします。パスワードはADBの作成時に設定しています。


RESTを開きます。


クライアントを開きます。


OAuthクライアントの作成を実行します。


クライアント定義のタブを開き、必要な情報を入力します。付与タイプCLIENT_CREDです。

名前説明サポート電子メールを入力します。これらの値は、ORDS Database APIを呼び出す際の認証情報としては使われません。


ロールのタブを開き、ORDS Database APIの実行に必要なロールSQL Developerを選択します。

作成をクリックします。


OAuthクライアントが作成されます。

クライアントIDクライアントシークレットをコピーし、安全に保存します。この値からORDS Database APIを呼び出す際に指定するAPEXのWeb資格証明を作成します。


操作対象とするAPEXワークスペースの準備は以上で完了です。

リモートのワークスペースを操作するAPEXアプリケーションを作成します。

最初にORDS Database APIを呼び出すパッケージUTL_APEX_REMOTEを作成します。ORDS Database APIの呼び出しをPL/SQLのプロシージャにラップすることにより、プロセス・タイプAPIの呼出しとして呼び出せるようにします。

create or replace package utl_apex_remote
as
CO_DBAPI_VERSION constant varchar2(8) default 'stable';
CO_LIMIT constant number default 1000;
/**
* タイプがOAuth - Client CredentialsのWeb資格証明を作成する
* またはすでに同じタイプのWeb資格証明が作成済みであれば、Client IDとClient Secretを更新する。
*/
procedure create_or_update_web_credential(
p_credential_static_id in varchar2
,p_credential_name in varchar2
,p_client_id in varchar2
,p_client_secret in varchar2
);
/**
* インスタンスのURLとワークスペースを指定して、ワークスペースに作成されている
* アプリケーションの一覧を取得する。取得した一覧はAPEXコレクションに保存する。
*/
procedure load_apps_into_apex_collection(
p_collection_name in varchar2
,p_instance_url in varchar2
,p_workspace_name in varchar2
,p_credential_static_id in varchar2
,p_dbapi_version in varchar2 default CO_DBAPI_VERSION
,p_limit in number default CO_LIMIT
);
/**
* アップロードしたアプリケーションのエクスポートをリモートのAPEXインスタンスにインストールする。
*/
procedure import_application(
p_application_id in number
,p_file in varchar2
,p_instance_url in varchar2
,p_workspace_name in varchar2
,p_credential_static_id in varchar2
,p_dbapi_version in varchar2 default CO_DBAPI_VERSION
,p_refresh_collection in boolean default false
,p_collection_name in varchar2 default null
,p_limit in number default CO_LIMIT
);
/**
* リモートのAPEXインスタンスからアプリケーションをエクスポートする。
*/
procedure export_application(
p_application_id in number
,p_instance_url in varchar2
,p_workspace_name in varchar2
,p_credential_static_id in varchar2
,p_dbapi_version in varchar2 default CO_DBAPI_VERSION
);
/**
* アプリケーションを削除する。
*/
procedure delete_application(
p_application_id in number
,p_instance_url in varchar2
,p_workspace_name in varchar2
,p_credential_static_id in varchar2
,p_dbapi_version in varchar2 default CO_DBAPI_VERSION
,p_refresh_collection in boolean default false
,p_collection_name in varchar2 default null
,p_limit in number default CO_LIMIT
);
end;
/
create or replace package body utl_apex_remote
as
/*
* Web資格証明の作成と更新。
*/
procedure create_or_update_web_credential(
p_credential_static_id in varchar2
,p_credential_name in varchar2
,p_client_id in varchar2
,p_client_secret in varchar2
)
as
l_credential_type_code apex_workspace_credentials.credential_type_code%type;
e_invalid_credential_specified exception;
begin
/*
* Web資格証明の静的IDは必須項目だが、NULLでないことを確認してから作成または更新する。
*/
if p_credential_static_id is not null then
begin
select credential_type_code into l_credential_type_code from apex_workspace_credentials
where static_id = p_credential_static_id;
exception
when no_data_found then
/* Web資格証明を新規作成する。 */
apex_credential.create_credential(
p_credential_name => coalesce(p_credential_name, p_credential_static_id)
,p_credential_static_id => p_credential_static_id
,p_authentication_type => apex_credential.C_TYPE_OAUTH_CLIENT_CRED
/* 最低限のパラメータのみを設定している。 */
);
end;
if l_credential_type_code <> apex_credential.C_TYPE_OAUTH_CLIENT_CRED then
/* Web資格証明が存在するがタイプがOAuthでない場合は更新できないので、例外をあげる */
raise e_invalid_credential_specified;
end if;
/*
* Client IDとClient SecretによりWeb資格証明を更新する。
*/
if p_client_id is not null and p_client_secret is not null then
apex_credential.set_persistent_credentials(
p_credential_static_id => p_credential_static_id
,p_username => p_client_id
,p_password => p_client_secret
);
end if;
end if;
end create_or_update_web_credential;
/**
* アプリケーションの一覧をAPEXコレクションに保存する。
*/
procedure load_apps_into_apex_collection(
p_collection_name in varchar2
,p_instance_url in varchar2
,p_workspace_name in varchar2
,p_credential_static_id in varchar2
,p_dbapi_version in varchar2
,p_limit in number
)
as
l_request_url varchar2(4000);
l_token_url varchar2(4000);
l_response clob;
e_call_api_failed exception;
begin
apex_collection.create_or_truncate_collection(
p_collection_name => p_collection_name
);
l_request_url := p_instance_url || '_/db-api/' || p_dbapi_version || '/apex/workspaces/' || p_workspace_name || '/applications/?limit=' || p_limit;
l_token_url := p_instance_url || 'oauth/token';
/*
  * Get all applications in the specified workspace
  * https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/23.4/orrst/op-apex-workspaces-workspace_name-applications-get.html
*/
apex_web_service.clear_request_headers();
apex_web_service.set_request_headers('Content-Type', 'application/json', p_reset => false);
l_response := apex_web_service.make_rest_request(
p_url => l_request_url
,p_http_method => 'GET'
,p_credential_static_id => p_credential_static_id
,p_token_url => l_token_url
);
if apex_web_service.g_status_code <> 200 then
raise e_call_api_failed;
end if;
/*
* l_responseにはアプリケーション一覧のJSONドキュメントが含まれる。
*/
for c in (
select * from json_table(l_response, '$.items[*]'
columns(
application_id number path '$.application_id'
,application_name varchar2(255) path '$.application_name'
,application_alias varchar2(255) path '$.application_alias'
,application_group varchar2(255) path '$.application_group'
,application_owner varchar2(128) path '$.application_owner'
,page_count number path '$.page_count'
,workspace varchar2(255) path '$.workspace'
,availability_status varchar2(38) path '$.availability_status'
,last_updated_by varchar2(255) path '$.last_updated_by'
,last_updated_on date path '$.last_updated_on'
)
)
)
loop
apex_collection.add_member(
p_collection_name => p_collection_name
,p_n001 => c.application_id
,p_c001 => c.application_name
,p_c002 => c.application_alias
,p_c003 => c.application_group
,p_c004 => c.application_owner
,p_n002 => c.page_count
,p_c005 => c.workspace
,p_c006 => c.availability_status
,p_c007 => c.last_updated_by
,p_d001 => c.last_updated_on
);
end loop;
end load_apps_into_apex_collection;
/**
* アプリケーションをインポートする。
*/
procedure import_application(
p_application_id in number
,p_file in varchar2
,p_instance_url in varchar2
,p_workspace_name in varchar2
,p_credential_static_id in varchar2
,p_dbapi_version in varchar2
,p_refresh_collection in boolean
,p_collection_name in varchar2
,p_limit in number
)
as
l_request_url varchar2(4000);
l_token_url varchar2(4000);
l_response clob;
e_call_api_failed exception;
l_download apex_data_export.t_export;
begin
l_request_url := p_instance_url || '_/db-api/' || p_dbapi_version || '/apex/workspaces/' || p_workspace_name || '/applications/' || p_application_id;
l_token_url := p_instance_url || 'oauth/token';
for f in (
select blob_content from apex_application_temp_files where name = p_file
)
loop
/*
* Create or Update an APEX application, with a specific application id, in the specified workspace
* https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/23.4/orrst/op-apex-workspaces-workspace_name-applications-application_id-put.html
*/
apex_web_service.clear_request_headers();
apex_web_service.set_request_headers('Content-Type', 'application/sql', p_reset => false);
l_response := apex_web_service.make_rest_request(
p_url => l_request_url
,p_http_method => 'PUT'
,p_body_blob => f.blob_content
,p_credential_static_id => p_credential_static_id
,p_token_url => l_token_url
);
if apex_web_service.g_status_code not in (200,201) then
raise e_call_api_failed;
end if;
end loop;
/*
* アプリケーション削除後にAPEXコレクションをリフレッシュする。
*/
if p_refresh_collection then
load_apps_into_apex_collection(
p_collection_name => p_collection_name
,p_instance_url => p_instance_url
,p_workspace_name => p_workspace_name
,p_credential_static_id => p_credential_static_id
,p_dbapi_version => p_dbapi_version
,p_limit => p_limit
);
end if;
end;
procedure export_application(
p_application_id in number
,p_instance_url in varchar2
,p_workspace_name in varchar2
,p_credential_static_id in varchar2
,p_dbapi_version in varchar2
)
as
l_request_url varchar2(32767);
l_token_url varchar2(4000);
l_export clob;
e_call_api_failed exception;
l_download apex_data_export.t_export;
begin
l_request_url := p_instance_url || '_/db-api/' || p_dbapi_version || '/apex/applications/' || p_application_id;
l_request_url := l_request_url || '?export_format=SQL_SCRIPT';
l_token_url := p_instance_url || 'oauth/token';
/*
* Get or export a specific APEX application
* https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/23.4/orrst/op-apex-applications-application_id-get.html
*/
apex_web_service.clear_request_headers();
apex_web_service.set_request_headers('Content-Type', 'application/sql', p_reset => false);
l_export := apex_web_service.make_rest_request(
p_url => l_request_url
,p_http_method => 'GET'
,p_credential_static_id => p_credential_static_id
,p_token_url => l_token_url
);
if apex_web_service.g_status_code <> 200 then
raise e_call_api_failed;
end if;
l_download.file_name := 'f' ||p_application_id;
l_download.format := 'SQL';
l_download.mime_type := 'application/sql';
l_download.as_clob := true;
l_download.content_clob := l_export;
apex_data_export.download( p_export => l_download );
apex_application.stop_apex_engine;
end export_application;
/**
* アプリケーションを削除する。
*/
procedure delete_application(
p_application_id in number
,p_instance_url in varchar2
,p_workspace_name in varchar2
,p_credential_static_id in varchar2
,p_dbapi_version in varchar2
,p_refresh_collection in boolean
,p_collection_name in varchar2
,p_limit in number
)
as
l_request_url varchar2(4000);
l_token_url varchar2(4000);
l_response clob;
e_call_api_failed exception;
begin
l_request_url := p_instance_url || '_/db-api/' || p_dbapi_version || '/apex/workspaces/' || p_workspace_name || '/applications/' || p_application_id;
l_token_url := p_instance_url || 'oauth/token';
/*
* Delete the specified APEX application
* https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/23.4/orrst/op-apex-workspaces-workspace_name-applications-application_id-delete.html
*/
apex_web_service.clear_request_headers();
apex_web_service.set_request_headers('Content-Type', 'application/json', p_reset => false);
l_response := apex_web_service.make_rest_request(
p_url => l_request_url
,p_http_method => 'DELETE'
,p_credential_static_id => p_credential_static_id
,p_token_url => l_token_url
);
if apex_web_service.g_status_code <> 204 then
raise e_call_api_failed;
end if;
/*
* アプリケーション削除後にAPEXコレクションをリフレッシュする。
*/
if p_refresh_collection then
load_apps_into_apex_collection(
p_collection_name => p_collection_name
,p_instance_url => p_instance_url
,p_workspace_name => p_workspace_name
,p_credential_static_id => p_credential_static_id
,p_dbapi_version => p_dbapi_version
,p_limit => p_limit
);
end if;
htp.p('{ "status": "success" }');
end delete_application;
end;
/
クイックSQLの以下のモデルより、表DBAPEX_WORKSPACESを作成します。操作対象とする別環境のAPEXワークスペースを保持します。

# prefix: dbapex
workspaces
workspace_name vc80 /nn
instance_url vc200 /nn
credential_name vc80
credential_static_id vc80 /nn

レビューおよび実行をクリックし処理を進め、表DBAPEX_WORKSPACESを作成します。

表が作成された時点で、その表を元にしたアプリケーションの作成を実行します。


アプリケーション作成ウィザードが起動します。

アプリケーションの名前Sample APEX DBAPIとします。ホーム・ページは不要なので削除します。結果として表DBAPEX_WORKSPACESを対象としたフォーム付き対話モード・レポートのページが作成されます。

以上の設定でアプリケーションの作成を実行します。


アプリケーションが作成されたら、とりあえず実行してみます。

作成ボタンをクリックします。


画面右に操作対象のAPEXワークスペースを登録するドロワーが開きます。

ワークスペース名(Workspace Name)、インスタンスのURL(Instance URL)、Web資格証明の名前(Credential Name)、Web資格証明の静的ID(Credential Static ID)を入力するページ・アイテムはありますが、OAuthのクライアントIDクライアントシークレットを入力するページ・アイテムは表DBAPEX_WORKSPACESの列として存在していないため、ありません。

OAuthのクライアントIDクライアントシークレットを入力するページ・アイテムを追加し、それらの値からWeb資格証明を作成するプロセスを作成します。

ページ・デザイナでページ番号のドロワーのページを開きます。


クライアントIDを入力するページ・アイテムP2_CLIENT_IDを作成します。

識別タイプテキスト・フィールドラベルClient IDとします。セッション・ステートストレージとしてリクエストごと(メモリーのみ)を選択します。


クライアントシークレットを入力するページ・アイテムP2_CLIENT_SECRETを作成します。

識別タイプパスワードラベルClient Secretとします。セッション・ステートストレージとしてリクエストごと(メモリーのみ)を選択します。

パスワードの設定[Enter]を押すと送信オフにします。


左ペインでプロセス・ビューを開きます。

新規にプロセスを作成し、プロセスの先頭に配置します。識別名前Web資格証明の作成または更新とし、タイプAPIの呼出しとします。

設定タイプPL/SQLパッケージを選択し、パッケージは作成済みのUTL_APEX_REMOTEを選択します。プロシージャまたはファンクションとしてCREATE_OR_UPDATE_WEB_CREDENTIALを選択します。

パラメータ名に一致するようにページ・アイテム名を決めているため、デフォルトで適切なページ・アイテムがパラメータに紐づきます。

サーバー側の条件タイプリクエストは値に含まれるを選択し、としてCREATE SAVEを設定します。ボタン作成および変更の確定をクリックしたときに実行されます。削除に関する処理は実装していないため、登録しているAPEXワークスペースを削除しても、紐づいているWeb資格証明は削除されずAPEXのワークスペースに残ります。


以上でAPEXワークスペースを登録するページは完成です。

アプリケーションよりWeb資格証明を作成できるように、アプリケーション定義セキュリティ詳細に含まれるランタイムAPIの使用状況ワークスペース・リポジトリを変更チェックします。


アプリケーションを実行し、操作対象のワークスペースを登録します。

作成をクリックし、先ほどOAuthのクライアントIDクライアントシークレットを作成したインスタンスとワークスペースを登録します。

Autonomous Databaseの場合、インスタンスURLは以下の形式で設定します。管理者ユーザーADMINの権限で実行するためインスタンスURLの末尾はadmin/になります。

https://ホスト名/ords/admin/

Oracle APEX REST Endpointsの一覧は/apex/から始まっています。最終的に呼び出すREST APIのエンドポイントは、インスタンスURLに_/db-api/stableとOracle APEX REST Endpointsを繋げたURLになります。

https://ホスト名/ords/admin/_/db-api/stable/apex/...

Credential Nameの指定がない場合は、静的IDWeb資格証明の名前にしています。今回の例ではDBAPI_CRED_APEXDEVWeb資格証明静的IDとしています。


操作対象のAPEXワークスペースが登録されます。


Web資格証明を開くと、指定した静的IDWeb資格証明が作成されていることも確認できます。


続けて別環境のワークスペースに含まれるアプリケーションの一覧とエクスポート/インポート/削除を行なうページを作成します。

取得したアプリケーションの一覧はAPEXコレクションに保存します。使用するAPEXコレクションの名前を変えられるように、アプリケーション定義置換置換文字列としてG_WORKSPACE_APPLICATIONSを作成します。置換値WORKSPACEAPPSとします。この値が実際のコレクション名になります。


新規にページを作成します。


空白ページを選択します。


ページ番号名前Applicationsとします。その他はデフォルトから変更せず、ページ・モード標準ナビゲーションは両方ともオンとします。

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


空白ページが作成されます。

ORDS Database APIの引数となる値を保持するページ・アイテムを作成します。ページ・アイテムのタイプ非表示です。セッション・ステートストレージセッションごと(永続)を選択し、ページを送信した後も値が維持されるようにします。

作成するページ・アイテムP3_WORKSPACE_NAMEP3_INSTANCE_URLP3_CREDENTIAL_STATIC_IDの3つです。


ORDS Database APIのGet all applications in the specified workspaceのAPIを発行し、取得したアプリケーションでAPEXコレクションを初期化するプロセスを作成します。

新規にプロセスを作成しレンダリング前ヘッダーの前に配置します。

識別名前APEXコレクションの初期化とします。タイプAPIの呼出しです。設定タイプPL/SQLパッケージを選び、パッケージとしてUTL_APEX_REMOTEプロシージャまたはファンクションとしてLOAD_APPS_INTO_APEX_COLLECTIONを選択します。


パラメータp_collection_nameを選択し、タイプ静的値静的値として&G_WORKSPACE_APPLICATIONS.を指定します。それ以外のパラメータはデフォルトで適切なページ・アイテムまたはAPIデフォルトが割り当たります。


アプリケーションの一覧を表示する対話モード・レポートを作成します。

識別タイトルApplicationsとします。タイプ対話モード・レポートです。ソースタイプSQL問合せとし、SQL問合せとして以下を記述します。列EXPDELには、アプリケーションのエクスポート削除を呼び出すボタンを後で作成します。

select
n001 as application_id
,c001 as application_name
,c002 as application_alias
,c003 as application_group
,c004 as application_owner
,n002 as page_count
,c005 as workspace
,c006 as availability_status
,c007 as last_updated_by
,d001 as last_updated_on
,'EXPORT' as EXP
,'DELETE' as DEL
from apex_collections
where collection_name = :G_WORKSPACE_APPLICATIONS
JavaScriptよりレポートのリフレッシュを実行するため、詳細静的IDとしてAPPLICATIONSを設定します。

変更を一旦保存します。


ページ・デザイナでページ番号1の対話モード・レポートのページを開きます。

WORKSPACE_NAMEをクリックすることにより、先ほど作成したページ番号を開いて別環境にあるワークスペースに含まれるアプリケーションの一覧を表示するようにします。

識別タイプリンクに変更します。リンクターゲットを開きます。


ターゲットページです。ページ番号3にあるページ・アイテムP3_WORKSPACE_NAMEP3_INSTANCE_URLP3_CREDENTIAL_STATIC_IDに、それぞれ列の値\#WORKSPACE_NAME#\\#INSTANCE_URL#\\#CREDENTIAL_STATIC_ID#\を設定します。列INSTANCE_URLには(コロン)が含まれます。:(コロン)はAPEXの内部で値を分割する文字として認識されるため、その影響を受けないように値を\(バックスラッシュ)で囲みます。

OKをクリックします。


変更を保存し、アプリケーションを実行して動作を確認します。

ワークスペース名クリックします。


ページ番号3が開き、そのワークスペースに作成されているアプリケーションが一覧されます。


アプリケーションをインポートする機能を追加します。

ページ・アイテムとしてP1_APPLICATION_IDを作成し、インポートするアプリケーションに割り当てるアプリケーションIDを指定します。ORDS Database APIによるインポートではアプリケーションIDは必須であり、空いているIDを自動で割り当てる機能はないようです。空いているIDを見つけるようにすると親切ですが今回はその機能は実装を見送り、アプリケーションIDは明示的に指定するようにします。同じアプリケーションIDのアプリケーションがある場合は上書きされるため注意が必要です。

識別タイプ数値フィールドラベルApplication IDとします。設定数字の位置合せ開始仮想キーボード数値を選択します。

検証必須の値オンセッション・ステートストレージリクエストごと(メモリーのみ)を選択します。


ページ・アイテムとしてP3_FILEを作成し、アプリケーションとしてインポートするファイルを選択します。

識別タイプファイルのアップロードラベルFileとします。ストレージタイプ表APEX_APPLICATION_TEMP_FILESを選択し、ファイルをパージするタイミングリクエストの終わりとします。

検証必須の値オンセッション・ステートストレージリクエストごと(メモリーのみ)を選択します。


アプリケーションのインポートを実行するボタンIMPORTを作成します。

識別ラベルImportとします。外観ホットオンにし、テンプレート・オプションWidthStretchを設定します。

動作アクションはデフォルトのページの送信のまま、変更しません。


プロセス・ビューを開き、アプリケーションをインポートするプロセスを作成します。

識別名前アプリケーションのインポートタイプAPIの呼出しとします。設定タイプとしてPL/SQLパッケージを選択し、パッケージUTL_APEX_REMOTEプロシージャまたはファンクションとしてIMPORT_APPLICATIONを選びます。

成功メッセージアプリケーションはインポートされました。とします。サーバー側の条件ボタン押下時IMPORTを設定します。


以上でアプリケーションのインポート処理が実装できました。

続いてアプリケーションのエクスポートを実装します。

アプリケーションをエクスポートするプロセスを、Ajaxコールバックとして作成します。

識別名前EXPORTタイプコードの実行を選びます。Ajaxコールバックとして作成したプロセスの場合、タイプAPIの呼出しを選ぶことはできません(不具合か仕様かは不明です)。そのためソースPL/SQLコードとして、プロシージャUTL_APEX_REMOTE.EXPORT_APPLICATIONの呼び出しを記述します。
utl_apex_remote.export_application(
    p_application_id        => :P3_APPLICATION_ID
    ,p_instance_url         => :P3_INSTANCE_URL
    ,p_workspace_name       => :P3_WORKSPACE_NAME
    ,p_credential_static_id => :P3_CREDENTIAL_STATIC_ID
);

対話モード・レポートの列EXPより、このAjaxコールバックのプロセスEXPORTを呼び出します。

EXPを選択し、識別タイプリンクに変更します。次のスクリーンショットにあるようにターゲットを設定したのち、リンク・テキストに以下を設定します。リンクをボタンとして表示します。

<button type="button" class="t-Button">Export</button>


ターゲットページアイテムの設定名前P3_APPLICATION_ID#APPLICATION_ID#を設定します。クリックしたボタンEXPORTがある行のアプリケーションIDがページ・アイテムP3_APPLICATION_IDに設定されます。AjaxコールバックEXPORTP3_APPLICATION_IDの値より、エクスポートするアプリケーションを決定します。

詳細リクエストAPPLICATION_PROCESS=EXPORTを設定することにより、AjaxコールバックEXPORTが呼び出されます。


アプリケーションを削除するプロセスを、Ajaxコールバックとして作成します。

識別名前DELETEタイプコードの実行ソースPL/SQLコードとして以下を記述します。
utl_apex_remote.delete_application(
    p_application_id        => apex_application.g_x01
    ,p_instance_url         => :P3_INSTANCE_URL
    ,p_workspace_name       => :P3_WORKSPACE_NAME
    ,p_credential_static_id => :P3_CREDENTIAL_STATIC_ID
    ,p_refresh_collection   => true
    ,p_collection_name      => :G_WORKSPACE_APPLICATIONS
);

JavaScriptによるAPEXアクションとしてdelete-applicationを作成し、そのアクションの引数idアプリケーションIDを渡してAjaxコールバックのプロセスDELETEを呼び出すようにします。

ページ・プロパティJavaScriptページ・ロード時に実行に以下を記述します。

apex.actions.add([
{
name: "delete-application",
action: function( event, element, args ) {
apex.message.confirm(
"Are you sure?",
(okPressed) => {
if (okPressed) {
let result = apex.server.process(
"DELETE",
{
x01: args.id
}
);
result.done(
(data) => {
apex.message.showPageSuccess(
'application deleted successfully.',
);
apex.event.trigger(
"#APPLICATIONS",
"apexrefresh"
);
}
).fail(
(jqXHR, textStatus, errorThrown) => {
apex.message.alert('Failed to delete application.');
}
);
}
}
)
}
}
]);


対話モード・レポートの列DELを選択し、列の書式のHTML式として以下を記述します。カスタム属性data-actionを指定し、APEXアクションdelete-applicationを呼び出しています。

<button type="button" class="t-Button t-Button--danger" data-action="#action$delete-application?id=#APPLICATION_ID#">Delete</button>


以上でアプリケーションは完成です。これで記事の先頭にあるGIF動画のように、別環境のワークスペースにあるアプリケーションの一覧、アプリケーションのインポート/エクスポート/削除ができるようになりました。

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

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