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 NameにSHARE_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のタイプに静的値を選択し、静的値としてVERSIONED、LIVEのふたつ(表示値と戻り値は同じ)を設定します。追加値の表示はオフ、NULL値の表示はオン、NULL表示値は- Select Type -と記述します。


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


ページ・アイテム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を作成します。ターゲットはページ5になります。


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


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


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

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


Share Nameにdemo_share、Share TypeにVERSIONED、Storage Link NameにDATA_SHARE_STORAGE_LINKを選択し、作成をクリックします。


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


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

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

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

select * from user_share_tables


Shareに表を追加したり削除したりするフォームのページは、ページ番号が6、名前は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になります。


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


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


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

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


Share NameにDEMO_SHARE、Tables NameにCUSTSALESを選択し、作成をクリックします。


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


この後、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を使用します。


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


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


ドロワーが閉じます。

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


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


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


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


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

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

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

select * from user_share_recipients


Share Recipientの作成や削除を行なうフォームのページは、ページ番号が7、名前は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になります。


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


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


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

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


Recipient Nameにtraining_user、Emailにtraining_user@oracle.comを入力し、作成をクリックします。


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


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

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

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


ドロワーが閉じます。

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

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

select * from user_share_recipient_grants


Share RecipientがShareにアクセスする権限を与えるフォームは、ページ番号が8、名前は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になります。


属性のリンク列をカスタム・ターゲットへのリンクとし、ターゲットはページ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 NameにTRAINING_USER、Share NameにDEMO_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の設定を行います。

続く