ファイルの作成、更新、削除、およびフォルダの作成、更新(名前の変更)、削除を実装します。
最初にMicrosoft Graph APIを呼び出して、上記の操作を行うプロシージャを実装したパッケージMS356_ONEDRIVEを作成します。
以下のコードをデータベースで実行します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
create or replace package ms365_onedrive | |
as | |
/* | |
* 個人用OneDriveを操作するMicrosoft Graph APIのエンドポイント | |
*/ | |
G_ENDPOINT constant varchar2(80) := 'https://graph.microsoft.com/v1.0/me/drive/items/'; | |
/** | |
* OneDriveにファイルを新規作成する。 | |
* | |
* @param p_file タイプがファイル参照のページ・アイテム値 | |
* @param p_folder_id ファイルをアップロードするフォルダ(DriveItem)のアイテムID | |
* @param p_credential_static_id Web資格証明の静的ID | |
*/ | |
procedure create_file( | |
p_file in varchar2 | |
,p_folder_id in varchar2 | |
,p_credential_static_id in varchar2 | |
); | |
/** | |
* ファイルの内容を置き換える。 | |
* | |
* @param p_file タイプがファイル参照のページ・アイテム値 | |
* @param p_item_id 置き換えるファイル(DriveItem)のアイテムID | |
* @param p_credential_static_id Web資格証明の静的ID | |
*/ | |
procedure update_file( | |
p_file in varchar2 | |
,p_item_id in varchar2 | |
,p_credential_static_id in varchar2 | |
); | |
/** | |
* DriveItem - フォルダ、ファイル - を削除する。 | |
* | |
* @param p_item_id 削除するDriveItemのアイテムID | |
* @param p_credential_static_id Web資格証明の静的ID | |
*/ | |
procedure delete_drive_item( | |
p_item_id in varchar2 | |
,p_credential_static_id in varchar2 | |
); | |
/** | |
* フォルダを作成する。 | |
* | |
* @param p_parent_folder_id フォルダを作成するフォルダのアイテムID | |
* @param p_folder_name 作成するフォルダの名前 | |
* @param p_credential_static_id Web資格証明の静的ID | |
*/ | |
procedure create_folder( | |
p_parent_folder_id in varchar2 | |
,p_folder_name in varchar2 | |
,p_credential_static_id in varchar2 | |
); | |
/** | |
* フォルダの名前を変更する。 | |
* | |
* @param p_folder_id 名前を変更するフォルダのアイテムID | |
* @param p_folder_name 変更するフォルダの名前 | |
* @param p_credential_static_id Web資格証明の静的ID | |
*/ | |
procedure rename_folder( | |
p_folder_id in varchar2 | |
,p_folder_name in varchar2 | |
,p_credential_static_id in varchar2 | |
); | |
end ms365_onedrive; | |
/ | |
create or replace package body ms365_onedrive | |
as | |
/** | |
* MS Graph APIの呼び出しでエラーが発生していないかどうか確認する。 | |
* エラーがあれば例外を発生させ、処理を中断する。 | |
*/ | |
procedure check_error( | |
p_status_code in number | |
,p_response in clob | |
) | |
as | |
l_response_json json_object_t; | |
l_error json_object_t; | |
begin | |
apex_debug.info('status code = %s', p_status_code); | |
if p_status_code >= 400 then | |
apex_debug.info(p_response); | |
l_response_json := json_object_t(p_response); | |
l_error := treat(l_response_json.get('error') as json_object_t); | |
raise_application_error(-20001,l_error.get_string('message')); | |
end if; | |
end check_error; | |
/** | |
* uploadUrlを取得して、ファイルをアップロードする。 | |
*/ | |
procedure upload_file( | |
p_file in varchar2 | |
,p_request_url in varchar2 | |
,p_conflict_behavior in varchar2 | |
,p_credential_static_id in varchar2 | |
) | |
as | |
/* アップロードされたファイル本体 */ | |
l_blob_content blob; | |
/* Graph API */ | |
l_response clob; | |
l_response_json json_object_t; | |
l_upload_url varchar2(32767); | |
l_request clob; | |
begin | |
/* APEXにアップロードされたファイルをBLOBとして取得する */ | |
select blob_content into l_blob_content | |
from apex_application_temp_files where name = p_file; | |
/* 最初にuploadUrlの取得を取得する。 */ | |
apex_debug.info('MS Graph API URL: %s', p_request_url); | |
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 => p_request_url | |
,p_http_method => 'POST' | |
,p_body => json_object( | |
'item' value json_object( | |
'@microsoft.graph.conflictBehavior' value p_conflict_behavior | |
) format json | |
) | |
,p_credential_static_id => p_credential_static_id | |
); | |
check_error(apex_web_service.g_status_code, l_response); | |
l_response_json := json_object_t(l_response); | |
l_upload_url := l_response_json.get_string('uploadUrl'); | |
apex_debug.info('uploadUrl = %s', l_upload_url); | |
/* 取得したuploadUrlを呼び出して、ファイルの内容をアップロードする。 */ | |
apex_web_service.clear_request_headers; | |
l_response := apex_web_service.make_rest_request( | |
p_url => l_upload_url | |
,p_http_method => 'PUT' | |
,p_body_blob => l_blob_content | |
,p_credential_static_id => p_credential_static_id | |
); | |
check_error(apex_web_service.g_status_code, l_response); | |
end upload_file; | |
procedure create_file( | |
p_file in varchar2 | |
,p_folder_id in varchar2 | |
,p_credential_static_id in varchar2 | |
) | |
as | |
l_filename apex_application_temp_files.filename%type; | |
l_request_url varchar2(32767); | |
begin | |
select filename into l_filename from apex_application_temp_files where name = p_file; | |
l_request_url := G_ENDPOINT || p_folder_id || ':/' | |
|| utl_url.escape(l_filename,true,'utf-8') || ':/createUploadSession'; | |
upload_file( | |
p_file => p_file | |
,p_request_url => l_request_url | |
,p_conflict_behavior => 'fail' | |
,p_credential_static_id => p_credential_static_id | |
); | |
end create_file; | |
procedure update_file( | |
p_file in varchar2 | |
,p_item_id in varchar2 | |
,p_credential_static_id in varchar2 | |
) | |
as | |
l_filename apex_application_temp_files.filename%type; | |
/* Graph API */ | |
l_request_url varchar2(32767); | |
l_response clob; | |
begin | |
/* 既存のDriveItemを宛先にして、ファイルをアップロードする。 */ | |
l_request_url := G_ENDPOINT || p_item_id || '/createUploadSession'; | |
upload_file( | |
p_file => p_file | |
,p_request_url => l_request_url | |
,p_conflict_behavior => 'replace' | |
,p_credential_static_id => p_credential_static_id | |
); | |
/* アップロードが完了したので、ファイル名を変更する。 */ | |
select filename into l_filename from apex_application_temp_files where name = p_file; | |
l_request_url := G_ENDPOINT || p_item_id; | |
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 => 'PATCH' | |
,p_body => json_object( | |
'name' value l_filename | |
) | |
,p_credential_static_id => p_credential_static_id | |
); | |
check_error(apex_web_service.g_status_code, l_response); | |
end update_file; | |
procedure delete_drive_item( | |
p_item_id in varchar2 | |
,p_credential_static_id in varchar2 | |
) | |
as | |
l_request_url varchar2(32767); | |
l_response clob; | |
begin | |
l_request_url := G_ENDPOINT || p_item_id; | |
apex_debug.info('MS Graph API URL: %s', l_request_url); | |
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 | |
); | |
check_error(apex_web_service.g_status_code, l_response); | |
end delete_drive_item; | |
procedure create_folder( | |
p_parent_folder_id in varchar2 | |
,p_folder_name in varchar2 | |
,p_credential_static_id in varchar2 | |
) | |
as | |
l_request_url varchar2(32767); | |
l_response clob; | |
l_response_json json_object_t; | |
l_upload_url varchar2(32767); | |
l_request clob; | |
begin | |
l_request_url := G_ENDPOINT || p_parent_folder_id || '/children'; | |
apex_debug.info('MS Graph API URL: %s', l_request_url); | |
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 => 'POST' | |
,p_body => json_object( | |
'name' value p_folder_name | |
,'folder' value '{}' format json | |
,'@microsoft.graph.conflictBehavior' value 'fail' | |
) | |
,p_credential_static_id => p_credential_static_id | |
); | |
check_error(apex_web_service.g_status_code, l_response); | |
end create_folder; | |
procedure rename_folder( | |
p_folder_id in varchar2 | |
,p_folder_name in varchar2 | |
,p_credential_static_id in varchar2 | |
) | |
as | |
l_request_url varchar2(32767); | |
l_response clob; | |
begin | |
l_request_url := G_ENDPOINT || p_folder_id; | |
apex_debug.info('MS Graph API URL: %s', l_request_url); | |
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 => 'PATCH' | |
,p_body => json_object( | |
'name' value p_folder_name | |
) | |
,p_credential_static_id => p_credential_static_id | |
); | |
check_error(apex_web_service.g_status_code, l_response); | |
end rename_folder; | |
end ms365_onedrive; | |
/ |
すべてのプロシージャは引数p_credential_static_idとして、Azure ADで認証されたWeb資格証明を必要としています。ここで指定するWeb資格証明を、置換文字列に定義します。
アプリケーション定義の置換を開き、置換文字列としてG_CREDENTIAL_NAME、置換値はMS_AZURE_ADとします。
変更の適用をクリックし、保存します。
これから、アプリケーションにドロワーのページを2つ作成します。ひとつはファイルの操作、もうひとつはフォルダの操作を行います。
最初に、ファイルを操作するページを作成します。
ページの作成を実行し、フォームを選択します。
データ・ソースはローカル・データベース、ソース・タイプとしてSQL問合せを選択し、SQL SELECT文を入力として以下を記述します。
select
'this_is_dummy_data_for_item_id' "ITEM_ID"
,'this_is_dummy_data_for_folder_id' "FOLDER_ID"
,'this_is_dummy_data_for_file' "FILE"
from dual
ページ作成ウィザードによって作成されるページに、ページ・アイテムとしてP4_ITEM_ID、P4_FOLDER_ID、P4_FILEが作成されます。また、ページ・タイプにフォームを選んでいるため、ボタンとしてCREATE、SAVE、DELETEおよびCANCELも作成されます。
ページ・タイプがフォームの場合、ナビゲーションは作成されません。
次へ進みます。
主キー列1としてITEM_ID(Varchar2)を選択します。ITEM_IDの指定がない場合は作成のページ、指定がある場合は変更または削除を行うページになります。
ページの作成をクリックします。
ページが作成されます。
作成されたページに含まれるリージョンFileの識別のタイプを、フォームから静的コンテンツに変更します。また、ヘッダーの前にあるプロセス初期化フォームFileを削除します。
ページ・アイテムP4_ITEM_IDとP4_FOLDER_IDを選択し、識別のタイプを非表示、セッション・ステートのストレージをリクエストごと(メモリーのみ)に変更します。
ページ・アイテムP4_FILEを選択し、識別のタイプをファイル参照...に変更します。設定のファイルをパージするタイミングはEnd of Requestとします。セッション・ステートのストレージはリクエストごと(メモリーのみ)に変更します。
検証の最大値に値が設定されている場合は、空白にします。
左ペインでプロセス・ビューを開きます。最初にプロセスプロセス・フォームFileを削除します。
プロセスを作成し、識別の名前をファイルの作成とします。タイプとしてAPIの呼出しを選択します。設定のパッケージとしてMS365_ONEDRIVE、プロシージャまたはファンクションとしてCREATE_FILEを選択します。
サーバー側の条件のボタン押下時としてCREATEを選択します。
パラメータとしてp_file、p_folder_id、p_credential_static_idが現れますが、p_file、p_folder_idはデフォルトでページ・アイテムP4_FILE、P4_FOLDER_IDが選択されます。
残りのp_credential_static_idの値は、タイプをアイテム、アイテムとしてG_CREDENTIAL_NAMEを指定します。
ファイルの更新を行うプロセスを作成します。プロシージャまたはファンクションはUPDATE_FILE、ボタン押下時にはSAVEを選択します。パラメータp_credential_static_idとしてアイテムG_CREDENTIAL_NAMEを設定します。
ファイルの削除を行うプロセスを作成します。プロシージャまたはファンクションはDELETE_DRIVE_ITEM、ボタン押下時にはDELETEを選択します。
ファイルを操作するページは以上で完成です。
続いてフォルダを操作するページを作成します。作成手順はファイルのドロワーと同じです。
ページ定義のページ番号は5、名前はFolderとします。ページ・モードはドロワーです。データ・ソースのSQL SELECT文を入力として以下を記述します。
select
'This_is_dummy_data_for_parent_folder_id' "PARENT_FOLDER_ID"
,'This_is_dummy_data_for_folder_id' "FOLDER_ID"
,'This_is_dummy_data_for_folder_name' "FOLDER_NAME"
from dual
主キー列1としてFOLDER_ID(Varchar2)を選択します。
作成されたページに含まれるリージョンFolderを選択し、タイプを静的コンテンツに変更します。また、ヘッダーの前のプロセス初期化フォームFolderを削除します。
作成されたページ・アイテムP5_FOLDER_ID、P5_PARENT_FOLDER_IDの識別のタイプを非表示にします。セッション・ステートのストレージはリクエストごと(メモリーのみ)とします。
フォルダの作成、更新(名前の変更)、削除を行うプロセスを作成します。
最初にプロセスプロセス・フォームFolderを削除します。
フォルダの作成を行うプロセスのプロシージャまたはファンクションは、CREATE_FOLDERです。ボタン押下時としてCREATEを選択します。
フォルダの更新をするプロセスのプロシージャまたはファンクションは、RENAME_FOLDERです。ボタン押下時としてSAVEを選択します。
フォルダの削除をするプロセスのプロシージャまたはファンクションは、DELETE_DRIVE_ITEMです。ボタン押下時としてDELETEを選択します。
パラメータp_item_idはデフォルトでアイテムが選択されません。アイテムとしてP5_FOLDER_IDを指定します。
フォルダを操作するページは以上で完成です。
ページ番号3のページFolderを開き、対話モード・レポートFolderよりファイルとフォルダの操作を呼び出せるようにします。
リージョンFolderにボタンを作成します。
識別のボタン名はCREATE、ラベルはCreateとします。レイアウトの位置として対話モード・レポートの検索バーの右を選びます。
動作のアクションとして、このアプリケーションのページにリダイレクトを選択します。
ターゲットのページはファイルの操作を実装したドロワーのページである4、アイテムの設定として名前P4_FOLDER_IDに値&P3_ITEM_ID.を設定します。
同様の手順にて、フォルダを作成するボタンを作成します。
識別のボタン名はCREATE_FOLDER、ラベルはCreate Folderとします。レイアウトの位置として対話モード・レポートの検索バーの右を選びます。
動作のアクションとして、このアプリケーションのページにリダイレクトを選択します。
ターゲットをクリックし、リンク・ビルダー・ターゲットを開きます。
ターゲットのページはフォルダの操作を実装したドロワーのページである5、アイテムの設定として名前P5_PARENT_FOLDER_IDに値&P3_ITEM_ID.を設定します。
すでに作成済みのファイルまたはフォルダを選択して、ドロワーを開く実装を追加します。
対話モード・レポートFolderのローカル後処理のSQL問合せに、以下の2つの列を追加します。ファイルの編集を行うL_EXIST_ITEMと、フォルダの編集を行うL_EXIST_FOLDERです。
,apex_page.get_url(
p_page => 4
,p_items => 'P4_FOLDER_ID,P4_ITEM_ID'
,p_values => :P3_ITEM_ID || ',' || ID
) L_EXIST_ITEM
,apex_page.get_url(
p_page => 5
,p_items => 'P5_FOLDER_ID,P5_FOLDER_NAME'
,p_values => ID || ',' || NAME
) L_EXIST_FOLDER
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
select ID, | |
CTAG, | |
ETAG, | |
SHA1HASH, | |
SHA256HASH, | |
QUICKXORHASH, | |
MIMETYPE, | |
NAME, | |
SIZE_, | |
WIDTH, | |
HEIGHT, | |
ISO, | |
FNUMBER, | |
CAMERAMAKE, | |
CAMERAMODEL, | |
FOCALLENGTH, | |
TAKENDATETIME, | |
EXPOSURENUMERATOR, | |
EXPOSUREDENOMINATOR, | |
WIDTH2, | |
FOURCC, | |
HEIGHT2, | |
BITRATE, | |
DURATION, | |
FRAMERATE, | |
AUDIOFORMAT, | |
AUDIOCHANNELS, | |
AUDIOBITSPERSAMPLE, | |
AUDIOSAMPLESPERSECOND, | |
ID2, | |
DISPLAYNAME, | |
SCOPE, | |
SHAREDDATETIME, | |
WEBURL, | |
ALTITUDE, | |
LATITUDE, | |
LONGITUDE, | |
ID3, | |
DISPLAYNAME2, | |
ID4, | |
DISPLAYNAME3, | |
COMMENTCOUNT, | |
CREATEDDATETIME, | |
LASTMODIFIEDDATETIME, | |
ID5, | |
DISPLAYNAME4, | |
ID6, | |
DISPLAYNAME5, | |
CREATEDDATETIME2, | |
ID7, | |
NAME2, | |
PATH, | |
DRIVEID, | |
DRIVETYPE, | |
LASTMODIFIEDDATETIME2, | |
DOWNLOADURL | |
,apex_page.get_url( | |
p_page => :APP_PAGE_ID | |
,p_items => 'P3_ITEM_ID' | |
,p_values => ID | |
) L_FOLDER_LINK | |
,apex_page.get_url( | |
p_page => 4 | |
,p_items => 'P4_FOLDER_ID,P4_ITEM_ID' | |
,p_values => :P3_ITEM_ID || ',' || ID | |
) L_EXIST_ITEM | |
,apex_page.get_url( | |
p_page => 5 | |
,p_items => 'P5_FOLDER_ID,P5_FOLDER_NAME' | |
,p_values => ID || ',' || NAME | |
) L_EXIST_FOLDER | |
from #APEX$SOURCE_DATA# |
追加された列L_EXIST_ITEM、L_EXIST_FOLDERを選択し、識別のタイプを非表示に変更します。
列IDを選択し、列の書式のHTML式として以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{if DOWNLOADURL/} | |
<a href="#L_EXIST_ITEM#">#ID#</a> | |
{else/} | |
<a href="#L_EXIST_FOLDER#">#ID#</a> | |
{endif/} |
左ペインで動的アクション・ビューを開きます。ダイアログのクローズ上でコンテキスト・メニューを開き、動的アクションの作成を実行します。
作成された動的アクションの識別の名前は、ファイルおよびフォルダの編集とします。タイミングのイベントはダイアログのクローズ、選択タイプはリージョン、リージョンとしてFolderを選択します。
TRUEアクションを選択します。識別の名前をFolderのリフレッシュ、アクションとしてリフレッシュを選択します。影響を受ける要素として、リフレッシュの対象であるリージョン、Folderを指定します。
以上でファイルとフォルダの操作を行うページの実装は完了です。
アプリケーションの実装は以上で完了です。
最後の記事は、いくつか作業中に気付いたことを紹介します。
続く