2024年7月11日木曜日

Delta Sharingを使ってインスタンスが異なるAPEXワークスペース間でデータを共有する (2) - Providerの実装

Delta SharingのProvider側の実装を行います。主にLiveLabsのImplement Data Sharing with PL/SQLのLab 2、3、4の作業に相当します。

APEXアプリケーションのProviderのページに、以下の処理を組み込みます。
  1. Cloud Storage Linkの一覧、作成、削除
  2. Shareの一覧、作成、削除、パブリッシュ
  3. Share Tableの一覧、作成(Shareへの表の追加)、削除(Shareからの表の削除)
  4. Share Versionの一覧
  5. Share Recipientの一覧、作成、削除、更新
  6. Share Recipient Grantの一覧、作成、削除
以下より、実装を進めます。

Cloud Storage Linkを一覧する対話モード・レポートを作成します。Body以下に新規にリージョンを作成します。

識別名前Cloud Storage Linksタイプ対話モード・レポートを選択します。

ソースタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

select * from user_lineage_cloud_storage_links

外観テンプレートStandardを選択し、テンプレート・オプションを開いてRemove Body Paddingチェックを入れます。


Cloud Storage Linkの作成と削除を行なうフォームを作成します。

ページの作成を実行し、フォームを選択します。


ページ番号4名前Cloud Storage Linkページ・モードドロワーを選択します。ソース・タイプSQL問合せを選択し、SQL SELECT文を入力に以下を記述します。
select
    storage_link_id
    ,storage_link_name
    ,uri
    ,credential_name
from user_lineage_cloud_storage_links
ページ・モードドロワーを選択しているため、ナビゲーションはデフォルトでオフになります。

へ進みます。


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

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


フォームのページが作成されます。

ページ・アイテムP4_STORAGE_LINK_NAME識別タイプテキスト・フィールドへ変更します。


ページ・アイテムP4_CREDENTIAL_NAME識別タイプ選択リストに変更します。

LOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

select credential_name d, credential_name r from user_credentials

追加値の表示オフNULL値の表示オンNULL表示値として- Select Credential -を記述します。


プロセス・ビューを開き、Cloud Storage Link作成削除を行なうプロセスを作成します。

最初にすでに作成済みのプロセスプロセス・フォームCloud Storage Linkコメント・アウトします。


Cloud Storage Link作成するプロセスを作成します。

識別名前Createタイプコードを実行とします。ソースPL/SQLコードとして以下を記述します。
begin
    dbms_share.create_or_replace_cloud_storage_link(
        storage_link_name => :P4_STORAGE_LINK_NAME
        ,uri => :P4_URI
    );
    if :P4_CREDENTIAL_NAME is not null then
        dbms_share.set_storage_credential(
            storage_link_name => :P4_STORAGE_LINK_NAME
            ,credential_name => :P4_CREDENTIAL_NAME
        );
    end if;
end;
サーバー側の条件ボタン押下時CREATEを指定します。


Cloud Storage Link削除するプロセスを作成します。

識別名前Deleteタイプコードを実行とします。ソースPL/SQLコードとして以下を記述します。
begin
    dbms_share.drop_cloud_storage_link(
        storage_link_name => :P4_STORAGE_LINK_NAME
    );
end;
サーバー側の条件ボタン押下時DELETEを指定します。


最低限必要な機能がフォームに実装できました。もっと調整できる箇所もありますが(例えばボタンSAVEの扱いや、プロシージャ呼び出しへの引数の追加)、記事が長くなりすぎるため最低限の実装に留めます。

Providerのページを開き、対話モード・レポートCloud Storage Linksがフォームのページと連携するように、ボタンや設定を追加します。

対話モード・レポートCloud Storage Linksにボタンを作成します。

識別ボタン名CREATE_CLOUD_STORAGE_LINKラベルCreateとします。レイアウトスロット対話モード・レポートの検索バーの右を選択します。

動作アクションこのアプリケーションのページにリダイレクトを選択し、ターゲットとして先ほど作成したフォームのページ4を指定します。


対話モード・レポートCloud Storage Links属性タブを開き、リンク列としてカスタム・ターゲットへのリンクを選択します。ターゲットページ4です。


リンク・ビルダーを開き、ターゲットページ4アイテムの設定名前に主キー列に対応するページ・アイテムP4_STORAGE_IDと、に列\#STORAGE_LINK_ID#\を設定します。


フォームが閉じた時に対話モード・レポートCloud Storage Linksリフレッシュされるよう、対話モード・レポートに動的アクションを作成します。

識別名前onClose Dialog Cloud Storage Linkとします。タイミングイベントダイアログのクローズ選択タイプリージョンリージョンとしてCloud Storage Linksを選択します。


TRUEアクションリフレッシュ影響を受ける要素選択タイプリージョンリージョンCloud Storage Linksになります。


以上でCloud Storage Linkに関する実装は完了です。

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

対話モード・レポートCloud Storage Links上にあるボタンCreateをクリックします。


Storage Link Name
DATA_SHARE_STORAGE_LINKとします、Uriにデータ共有用に作成したオブジェクト・ストレージ上のバケットへのURLを入力し、Credential NameSHARE_BUCKET_CREDENTIALを選択します。

作成をクリックします。


対話モード・レポート上に作成したCloud Storage Link、DATA_SHARE_STORAGE_LINKが表示されます。


続いてShareについて実装します。

作成する対話モード・レポートSharesソースSQL問合せは以下になります。外観の設定は同じです。

select * from user_shares


Shareを操作するために作成するフォームのページは、ページ番号5名前Shareデータ・ソースSQLには以下を記述します。
select
    share_id
    ,share_name
    ,share_type
    ,'' storage_link_name
from user_shares
主キー列1にはSHARE_IDを選択します。


フォームのページShareが作成されます。

ページ・アイテムP5_SHARE_TYPE識別タイプ選択リストに変更します。LOVタイプ静的値を選択し、静的値としてVERSIONEDLIVEのふたつ(表示値戻り値は同じ)を設定します。追加値の表示オフNULL値の表示オンNULL表示値- Select Type -と記述します。


静的値の設定です。表示値戻り値ともにVERSIONEDLIVEを設定します。設定順で表示するには、ソート実行時にソートオフにします。


ページ・アイテムP5_STORAGE_LINK_NAMEタイプ選択リストに変更します。LOVタイプSQL問合せSQL問合せとして以下を記述します。

select storage_link_name d, storage_link_name r from user_lineage_cloud_storage_links

追加値の表示オフNULL値の表示オンNULL表示値- Select Storage Link -と記述します。


Share作成削除Publishを行なうプロセスを作成します。デフォルトで作成されているプロセスプロセス・フォームShareコメント・アウトします。

プロセスCreateでは、以下のコードを実行します。ボタンCREATEの押下で実行します。
begin
    dbms_share.create_share(
        share_name => :P5_SHARE_NAME
        ,share_type => :P5_SHARE_TYPE
        ,storage_link_name => :P5_STORAGE_LINK_NAME
    );
end;

プロセスDeleteでは、以下のコードを実行します。ボタンDELETEの押下で実行します。
begin
    dbms_share.drop_share(
        share_name => :P5_SHARE_NAME
    );
end;

プロセスPublishでは、以下のコードを実行します。ボタンSAVEの押下で実行します。
begin
    dbms_share.publish_share(
        share_name => :P5_SHARE_NAME
    );
end;

レンダリング・ビューに戻り、ボタンSAVEのラベルをPublishに変更します。


以上でフォームについては完成です。

Providerのページに戻り対話モード・レポートSharesに、ボタンCREATE_SHAREを作成します。ターゲットページになります。


属性リンク列カスタム・ターゲットへのリンクとし、ターゲットはページ、主キーのアイテムの名前としてP5_SHARE_IDとなる列に\#SHARE_ID#\を指定します。


ダイアログがクローズしたときに対話モード・レポートSharesリフレッシュする動的アクションを作成します。識別名前onClose Dialog Shareとします。


以上でSharesについて出来上がりました。動作を確認してみます。

アプリケーションを実行し、対話モード・レポートSharesにあるボタンCreateをクリックします。


Share Namedemo_shareShare TypeVERSIONEDStorage Link NameDATA_SHARE_STORAGE_LINKを選択し、作成をクリックします。


ShareとしてDEMO_SHAREが作成されます。


Publishは表をShareに追加した後に実行します。

次にShare表を追加する、または、Shareから表を削除する機能を実装します。

作成する対話モード・レポートShare TablesソースSQL問合せは以下になります。

select * from user_share_tables


Shareに表を追加したり削除したりするフォームのページは、ページ番号名前Share Tableデータ・ソースSQLには以下を記述します。
select
    share_table_id
    ,share_name
    ,table_name
    ,share_table_name
from user_share_tables
主キー列1にはSHARE_TABLE_IDを選択します。


フォームのページShare Tableが作成されます。

ページ・アイテムP6_SHARE_NAME識別タイプ選択リストに変更します。LOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

select share_name d, share_name r from user_shares

追加値の表示オフNULL値の表示オンNULL表示値- Select Share -と記述します。


ページ・アイテムP6_TABLE_NAME識別タイプポップアップLOVに変更します。LOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

select table_name from user_tables

追加値の表示オフNULL値の表示オンNULL表示値- Select Table -と記述します。


ページ・アイテムP6_SHARE_TABLE_NAME識別タイプテキスト・フィールドに変更します。このページ・アイテムの値はDBMS_SHARE.ADD_TO_SHAREの引数share_table_nameに与えられる値ですが、デフォルトをP6_TABLE_NAMEとするために検証必須の値オフにします。あわせて、外観テンプレートOptional - Floatingに変更します。


Shareへの表の追加および削除を行なうプロセスを作成します。デフォルトで作成されているプロセスプロセス・フォームShare Tableコメント・アウトします。

プロセスCreateでは、以下のコードを実行します。ボタンCREATEの押下で実行します。
begin
    dbms_share.add_to_share(
        share_name => :P6_SHARE_NAME
        ,table_name => :P6_TABLE_NAME
        ,share_table_name => coalesce(:P6_SHARE_TABLE_NAME,:P6_TABLE_NAME)
    );
end;

プロセスDeleteでは、以下のコードを実行します。ボタンDELETEの押下で実行します。
begin
    dbms_share.remove_from_share(
        share_name => :P6_SHARE_NAME
        ,share_table_name => :P6_SHARE_TABLE_NAME
    );
end;

以上でフォームは完成です。

Providerのページに戻り対話モード・レポートShare Tablesに、ボタンCERATE_SHARE_TABLEを作成します。ターゲットページになります。


属性リンク列カスタム・ターゲットへのリンクとし、ターゲットページ6、主キーのアイテムの名前としてP6_SHARE_TABLE_IDとなる列に\#SHARE_TABLE_ID#\を指定します。


ダイアログがクローズしたときに対話モード・レポートShare Tablesリフレッシュする動的アクションを作成します。識別名前onClose Dialog Share Tableとします。


以上でShare Tablesについて出来上がりました。動作を確認してみます。

アプリケーションを実行し、対話モード・レポートShare TablesにあるボタンCreateをクリックします。


Share NameDEMO_SHARETables NameCUSTSALESを選択し、作成をクリックします。


ShareのDEMO_SHAREに表CUSTSALESが、Share Table NameCUSTSALESとして追加されます。


この後、ShareのDEMO_SHAREをPublishすることにより表CUSTSALESが共有できるようになります。

その作業の前に、Publishされた状況を一覧する対話モード・レポートを作成します。

作成する対話モード・レポートShare VersionsソースSQL問合せは以下になります。

select * from user_share_versions


今回はShare Versionの削除といった処理を組み込まず、単にShare Versionの表示のみを行います。

最新の状態のShare Versionsを表示するため、対話モード・レポートをリフレッシュするボタンを作成します。

ボタン名REFRESH_SHARE_VERSIONSラベルRefreshとします。レイアウトスロット対話モード・レポートの検索バーの右を選択します。動作アクション動的アクションで定義です。


ボタンREFRESH_SHARE_VERSIONSに動的アクションを作成します。識別名前onClick Refresh Share Versionsタイミングイベントは、ボタンのデフォルトのクリックです。


TRUEアクションにはリフレッシュ影響を受ける要素選択タイプリージョンを選択し、リージョンとしてShare Versionsを選びます。


これからアプリケーションを実行し、ShareをPublishします。

Always FreeのAutonomous Databaseで作業を行っている場合は、あらかじめ以下のコマンドを実行します。
BEGIN
    DBMS_SHARE.UPDATE_DEFAULT_SHARE_PROPERTY('job_type', 'DBMS_CLOUD');
END;
LiveLabsでは、この設定を行なう理由について説明されていませんが、おそらく表をオブジェクト・ストレージにエクスポートする際にDBMS_CLOUD.EXPORT_DATAを使うように設定していると思われます。デフォルトはODI - Oracle Data Integratorを使用します。


対話モード・レポートSharesDEMO_SHAREのフォームを開きます。


開いたドロワーにあるボタンPublishをクリックします。


ドロワーが閉じます。

ボタンPublishを押してドロワーが閉じたときに自動的に対話モード・レポートShare Versionsがリフレッシュされないため、ボタンRefreshをクリックします。


対話モード・レポートShare VersionsDEMO_SHAREの行が表示されます。StatusEXPORTINGで、時間が経つとCURRENTに変わります。


少し時間をおいてRefreshを再度クリックすると、StatusCURRENTに変わっていることが確認できます。


この状態で、オブジェクト・ストレージのバケットにparquetファイルがエクスポートされていることが確認できます。


データに関する設定は以上で完了です。

これから、Consumer側で使用する受信者、つまりShare Recipient一覧作成削除を実装します。

作成する対話モード・レポートShare RecipientsソースSQL問合せは以下になります。

select * from user_share_recipients


Share Recipientの作成や削除を行なうフォームのページは、ページ番号名前Share Recipientデータ・ソースSQLには以下を記述します。
select
    recipient_id,
    recipient_name,
    '' email,
    '' recipient_property,
    '' new_value
from user_share_recipients
主キー列1にはRECIPIENT_IDを選択します。


フォームのページShare Recipientが作成されます。

ページ・アイテムP7_RECIPIENT_NAME識別タイプテキスト・フィールドに変更します。


この他のページ・アイテムも、タイプテキスト・フィールドでない場合、テキスト・フィールドに変更します。

Share Recipientの作成、削除、更新を行なうプロセスを作成します。デフォルトで作成されているプロセスプロセス・フォームShare Recipientコメント・アウトします。

プロセスCreateでは、以下のコードを実行します。ボタンCREATEの押下で実行します。
begin
    dbms_share.create_or_replace_share_recipient(
        recipient_name => :P7_RECIPIENT_NAME
        ,email => :P7_EMAIL
    );
end;

プロセスDeleteでは、以下のコードを実行します。ボタンDELETEの押下で実行します。
begin
    dbms_share.drop_recipient(
        recipient_name => :P7_RECIPIENT_NAME
    );
end;

プロセスSaveでは、以下のコードを実行します。ボタンSAVEの押下で実行します。
begin
    dbms_share.update_recipient_property(
        recipient_name => :P7_RECIPIENT_NAME
        ,recipient_property => :P7_RECIPIENT_PROPERTY
        ,new_value => :P7_NEW_VALUE
    );
end;

以上でフォームは完成です。

Providerのページに戻り対話モード・レポートShare Recipientsに、ボタンCREATE_SHARE_RECIPIENTを作成します。ターゲットページになります。


属性リンク列カスタム・ターゲットへのリンクとし、ターゲットページ7、主キーのアイテムの名前としてP7_RECIPIENT_IDとなる列に\#RECIPIENT_ID#\を指定します。


ダイアログがクローズしたときに対話モード・レポートShare Recipientsリフレッシュする動的アクションを作成します。識別名前onClose Dialog Share Recipientとします。


以上でShare Recipientsについて出来上がりました。動作を確認してみます。

アプリケーションを実行し、対話モード・レポートShare RecipientsにあるボタンCreateをクリックします。


Recipient Nametraining_userEmailtraining_user@oracle.comを入力し、作成をクリックします。


Share RecipientsとしてTRAINING_USERが追加されます。


Share RecipientのTRANING_USERのプロパティを更新します。編集アイコンクリックし、ドロワーを開きます。

Recipient PropertyTOKEN_LIFETIMEを入力し、New Valueとして90 00:00:00を入力します。

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


ドロワーが閉じます。

Share RecipientがShareにアクセスする権限を与える機能を実装します。

作成する対話モード・レポートShare Recipient GrantsソースのSQL問合せは以下になります。

select * from user_share_recipient_grants


Share RecipientがShareにアクセスする権限を与えるフォームは、ページ番号名前Share Recipient Grantデータ・ソースSQLには以下を記述します。
select
    recipient_id,
    share_id,
    recipient_name,
    share_name
from user_share_recipient_grants
主キー列1としてRECIPIENT_ID主キー列2としてSHARE_IDを選択します。


フォームのページShare Recipient Grantが作成されます。

ページ・アイテムP8_RECIPIENT_NAME識別タイプ選択リストに変更します。LOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

select recipient_name d, recipient_name r from user_share_recipients

追加値の表示オフNULL値の表示オンNULL表示値- Select Share Recipient -と記述します。


ページ・アイテムP8_SHARE_NAME識別タイプ選択リストに変更します。LOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

select share_name d, share_name r from user_shares

追加値の表示オフNULL値の表示オンNULL表示値- Select Share -と記述します。


Share RecipientにShareへアクセスする権限を与えるプロセスを作成します。デフォルトで作成されているプロセスプロセス・フォームShare Recipient Grantコメント・アウトします。

プロセスCreateでは、以下のコードを実行します。ボタンCREATEの押下で実行します。
begin
    dbms_share.grant_to_recipient(
        share_name => :P8_SHARE_NAME
        ,recipient_name => :P8_RECIPIENT_NAME
    );
end;

プロセスDeleteでは、以下のコードを実行します。ボタンDELETEの押下で実行します。
begin
    dbms_share.revoke_from_recipient(
        share_name => :P8_SHARE_NAME
        ,recipient_name => :P8_RECIPIENT_NAME
    );
end;

以上でフォームは完成です。

Providerのページに戻り対話モード・レポートShare Recipient Grantsに、ボタンCREATE_SHARE_RECIPIENT_GRANTを作成します。ターゲットページになります。


属性リンク列カスタム・ターゲットへのリンクとし、ターゲットページ8、主キーのアイテムの名前としてP8_RECIPIENT_IDとなる列に\#RECIPIENT_ID#\、また、名前としてP8_SHARE_IDとなる列に\#SHARE_ID#\の2行を指定します。


ダイアログがクローズしたときに対話モード・レポートShare Recipient Grantsリフレッシュする動的アクションを作成します。識別名前onClose Dialog Share Recipient Grantとします。


以上でShare Recipient Grantsについて出来上がりました。動作を確認してみます。

アプリケーションを実行し、対話モード・レポートShare Recipient GrantsにあるボタンCreateをクリックします。


Recipient NameTRAINING_USERShare NameDEMO_SHAREを選択し、作成をクリックします。


Recipient NameのTRAINING_USERにShare NameのDEMO_SHAREにアクセスする権限が割り当てられました。


以上で、Share RecipientのTRAINING_USERがDEMO_SHAREへアクセスする権限が割り当てられました。

Delta SharingのConsumerが使用するプロファイルを取得します。

最初にプロファイルが記載されたJSONファイルをダウンロードするリンクを生成します。SQLコマンドで以下を実行します。
BEGIN
    DBMS_OUTPUT.PUT_LINE(DBMS_SHARE.GET_ACTIVATION_LINK('TRAINING_USER'));
END;
一度だけ利用できる、プロファイルのダウンロード・リンクが出力されます。


リンクをブラウザにコピペして開きます。本来であれば、Consumerとなる人にこのリンクを安全な方法で送信します。

Delta Sharingのプロファイルをファイルとして取得するページが表示されます。Get Profile Informationをクリックします。


なぜか、リンクがすでに使用済みか期限切れか不正かのどれかの理由で、プロファイルのダウンロードを拒否されます


仕方がないので、SQLclでワークスペース・スキーマwksp_dwapexに直接接続し、以下のSQLコマンドを実行しました。
set serveroutput on
set lines 180 pages 100 trims on trimo on
declare
  v_share_profile sys.json_object_t;
  v_share_profile_text varchar2(4000);
begin
  dbms_share.populate_share_profile(
    recipient_name => 'TRAINING_USER',
    share_profile => v_share_profile
  );
  v_share_profile_text := v_share_profile.to_string;
  dbms_output.put_line (json_query(v_share_profile_text, '$' pretty));
end;
/
Delta SharingのConsumerが必要とするプロファイルが、JSONドキュメントとして印刷されます。

% sql -cloudconfig Wallet_DWAPEX.zip wksp_dwapex@dwapex_low



SQLcl: 水 7月 10 19:03:45 2024のリリース23.4 Production


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


パスワード (**********?) **************

接続先:

Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production

Version 19.24.0.1.0


SQL> set serveroutput on

SQL> set lines 180 pages 100 trims on trimo on

SQL> declare

       v_share_profile sys.json_object_t;

       v_share_profile_text varchar2(4000);

     begin

       dbms_share.populate_share_profile(

         recipient_name => 'TRAINING_USER',

         share_profile => v_share_profile

       );

       v_share_profile_text := v_share_profile.to_string;

       dbms_output.put_line (json_query(v_share_profile_text, '$' pretty));

     end;

     /

{

  "shareCredentialsVersion" : 1,

  "endpoint" : "https://ge***********f-dwapex.adb.ca-toronto-1.oraclecloudapps.com/ords/dwapex/_delta_sharing/",

  "bearerToken" : "**********************",

  "tokenEndpoint" : "https://ge***********f-dwapex.adb.ca-toronto-1.oraclecloudapps.com/ords/dwapex/oauth/token",

  "clientID" : "**********************",

  "clientSecret" : "**********************"

}



PL/SQLプロシージャが正常に完了しました。




このプロファイルを使って、Consumerの設定を行います。

続く