
Google ドライブ: フォルダ検索
この工程は、Google ドライブ の指定フォルダ直下に、特定の名前のフォルダがあるかどうか調べます。
Basic Configs
- 工程名
- メモ
Configs for this Auto Step
- OAuth_V2
- C1: サービスアカウント設定 *
- ParentFolderId
- C2: 検索するフォルダの親フォルダの ID *#{EL}
- FolderName
- C3: 検索するフォルダの名称 *#{EL}
- FolderIdItem
- C4: 検索したフォルダの ID を保存するデータ項目
- WebViewUrlItem
- C5: 検索したフォルダの表示 URL を保存するデータ項目
Notes
- フォルダの ID は、URL に含まれています https://drive.google.com/drive/u/1/folders/(Folder ID)
- フォルダが見つからない場合、エラーになります
- 親フォルダを、事前に[C1: サービスアカウント設定]のサービスアカウントと共有しておく必要があります
- [C1: サービスアカウント設定]を設定するには:
- Google Cloud コンソールでサービスアカウントを準備します
- サービスアカウントキーを作成またはアップロードします
- Google Cloud コンソール上でサービスアカウントキーを作成すると、必要な情報を含む JSON ファイルをダウンロードできます
- サービスアカウントキーを作成またはアップロードします
- Questetra BPM Suite で OAuth2 JWT ベアラーフローの設定を作成し、C1 に設定します
- スコープ
https://www.googleapis.com/auth/drive.readonlyが必要です - 以降の項目は、下表のとおり設定してください
- スコープ
- Google Cloud コンソールでサービスアカウントを準備します
| Questetra BPM Suite の設定項目 | 対応する Google Cloud サービスアカウントキーの情報 | JSON ファイルの項目名 | 設定必須かどうか |
|---|---|---|---|
| クライアント ID | OAuth2 クライアント ID | client_id | 任意 |
| 秘密鍵 ID | キー ID | private_key_id | 必須 |
| 秘密鍵 | 秘密鍵 | private_key | 必須 |
| カスタム秘密情報 1 | メールアドレス | client_email | 必須 |

Capture

See also
Script (click to open)
- 次のスクリプトが記述されている XML ファイルをダウンロードできます
- google-drive-folder-search.xml (C) Questetra, Inc. (MIT License)
- Professional のワークフロー基盤では、ファイル内容を改変しオリジナルのアドオン自動工程として活用できます
function main() {
//// == 工程コンフィグ・ワークフローデータの参照 / Config & Data Retrieving ==
const oauthV2 = configs.getObject("OAuth_V2");
let parentFolderId = configs.get("ParentFolderId");
if (parentFolderId === "") {
throw new Error("Parent Folder ID is blank.");
}
const folderName = configs.get("FolderName");
if (folderName === "" || folderName === null) {
throw new Error("Folder Name is blank.");
}
const idDataDef = configs.getObject("FolderIdItem");
const urlDataDef = configs.getObject("WebViewUrlItem");
// If neither C4 nor C5 are set, throw error
if (idDataDef === null && urlDataDef === null) {
throw new Error("Neither of Data Items to save result are set.");
}
//// == 演算 / Calculating ==
const driveId = getDriveId(oauthV2, parentFolderId);
const folders = searchFolder(oauthV2, driveId, parentFolderId, folderName);
const folderNum = folders.length;
if (folderNum === 0) {
throw new Error(`Could not find Folder:${folderName} with Parent Folder ID:${parentFolderId}`);
}
const folderIdList = [];
const folderUrlList = [];
for (let i = 0; i < folderNum; i++) {
folderIdList.push(folders[i].id);
folderUrlList.push(folders[i].webViewLink);
}
//// == ワークフローデータへの代入 / Data Updating ==
setFolderData(idDataDef, folderIdList);
setFolderData(urlDataDef, folderUrlList);
}
const URL_TOKEN_REQUEST = 'https://oauth2.googleapis.com/token';
const SCOPE = 'https://www.googleapis.com/auth/drive.readonly';
/**
* @param auth HTTP 認証設定
* @returns {any} アクセストークンを含むオブジェクト
*/
const getAccessToken = (auth) => {
const privateKeyId = auth.getPrivateKeyId();
const privateKey = auth.getPrivateKey();
const serviceAccount = auth.getCustomSecret1();
const scope = auth.getScope();
if (!scope.split(' ').includes(SCOPE)) {
throw new Error(`Scope ${SCOPE} must be included in the scope.`);
}
if (privateKeyId === '') {
throw new Error('Private Key ID is required.');
}
if (privateKey === '') {
throw new Error('Private Key is required.');
}
if (serviceAccount === '') {
throw new Error('Service Account must be set to Custom Secret 1.');
}
const header = {
"alg": "RS256",
"typ": "at+jwt",
"kid": privateKeyId
};
const now = Math.floor(Date.now() / 1000);
const payload = {
"iss": serviceAccount,
"aud": URL_TOKEN_REQUEST,
"sub": '',
"iat": now,
"exp": now + 3600,
/**
* https://developers.google.com/identity/protocols/oauth2/service-account#jwt-auth
* "without OAuth" の話だが、OAuth でも 1 hour になるようだ。
* 1 hour より長ければエラー。短ければ、1 hour のトークンが返ってくる。
*/
scope
};
const keyB = rsa.readKeyFromPkcs8(privateKey);
const assertion = jwt.build(header, payload, keyB);
const response = httpClient.begin()
.formParam("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
.formParam('assertion', assertion)
.post(URL_TOKEN_REQUEST);
const responseText = response.getResponseAsString();
if (response.getStatusCode() !== 200) {
engine.log(responseText);
throw new Error(`Failed to get Access token. status: ${response.getStatusCode()}`);
}
const result = JSON.parse(response.getResponseAsString());
if (result.access_token === undefined) {
engine.log(responseText);
throw new Error(`Failed to get Access token. access token not found.`);
}
return result;
};
/**
* 親フォルダのドライブ ID を取得する
* @param {AuthSettingWrapper} oauthV2 OAuth2 認証設定(サービスアカウント設定)
* @param {String} parentFolderId 親フォルダの ID
* @return {String} driveId ドライブ ID (共有ドライブになければ null)
*/
function getDriveId(oauthV2, parentFolderId) {
if (parentFolderId === "root") {
return null;
}
const url = `https://www.googleapis.com/drive/v3/files/${parentFolderId}`;
let request = httpClient.begin();
request = request.oauth2JwtBearer(oauthV2, () => getAccessToken(oauthV2));
const response = request.queryParam("fields", "driveId")
.queryParam("supportsAllDrives", "true")
.get(url);
const status = response.getStatusCode();
const responseTxt = response.getResponseAsString();
if (status >= 300) {
const errorMsg = `Failed to get parent folder with parent folder ID:${parentFolderId}. status:${status}`;
engine.log(responseTxt);
throw new Error(errorMsg);
}
const driveId = JSON.parse(responseTxt).driveId;
if (driveId === undefined) {
return null;
}
return driveId;
}
/**
* Google ドライブのフォルダを検索
* @param {AuthSettingWrapper} oauthV2 OAuth2 認証設定(サービスアカウント設定)
* @param {String} driveId 親フォルダのドライブ ID (共有ドライブになければ null)
* @param {String} parentFolderId 親フォルダの ID
* @param {String} folderName 検索するフォルダの名前
* @return {Array} folders 検索結果一覧
* @return {String} folders[].id フォルダの ID
* @return {String} folders[].webViewLink フォルダの表示 URL
*/
function searchFolder(oauthV2, driveId, parentFolderId, folderName) {
const folderNameRep = folderName.replace(/['\\]/g, "\\$&"); // ' と \ をエスケープ
const q = `mimeType = 'application/vnd.google-apps.folder' and trashed = false and name = '${folderNameRep}' and '${parentFolderId}' in parents`;
const url = "https://www.googleapis.com/drive/v3/files";
let request = httpClient.begin();
request = request.oauth2JwtBearer(oauthV2, () => getAccessToken(oauthV2));
request = request.queryParam("q", q)
.queryParam("pageSize", "1000")
.queryParam("fields", "files(id,webViewLink)");
if (driveId !== null) { // 親フォルダが共有ドライブにある場合
request = request
.queryParam("includeItemsFromAllDrives", "true")
.queryParam("supportsAllDrives", "true")
.queryParam("corpora", "drive")
.queryParam("driveId", driveId);
}
const response = request.get(url);
const status = response.getStatusCode();
const responseTxt = response.getResponseAsString();
if (status >= 300) {
const errorMsg = `Failed to search. status:${status}`;
engine.log(responseTxt);
throw new Error(errorMsg);
}
const folders = JSON.parse(responseTxt).files;
return folders;
}
/**
* フォルダの情報をデータ項目にセットする
* @param {ProcessDataDefinitionView} dataDef 保存先データ項目の ProcessDataDefinitionView
* @param {Array<String>} folderInfoList 保存するフォルダ情報の配列
*/
function setFolderData(dataDef, folderInfoList) {
if (dataDef !== null) {
//Multiple Judge
if (dataDef.matchDataType("STRING_TEXTFIELD") && folderInfoList.length > 1) {
throw new Error("Multiple folders are found. Can't set data to single-line string Data Item.");
}
const folderInfoStr = folderInfoList.join("\n");
engine.setData(dataDef, folderInfoStr);
}
}