3 2 2024-04-02 (C) Questetra, Inc. (MIT License) This item downloads the specified files on Google Drive. You can download multiple files at once. When you download multiple files, you should write one File ID per line. この工程は、Google ドライブ内の指定ファイルをダウンロードします。一度に複数のダウンロードが可能です。複数ダウンロードする場合、データ項目では 1 行につき 1 つずつファイル ID を書くようにしてください。 https://support.questetra.com/bpmn-icons/service-task-google-drive-file-download/ https://support.questetra.com/ja/bpmn-icons/service-task-google-drive-file-download/ iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADLUlEQVRYR2NkYGBgZBhAALJ81AFE hUBmYabNj19/dglzM7ERirGerpkshNTA5ImOgvTClGu/fjEq5AfeYdeX+YrT0cv2S3y69kjgVGv7 IjdiHEGUAzKK0nKf8X9peyz5ntv5jty/7qTzTPgML51n+O/rbyaH6f3TjxByBFEOSMxO/XZB/wnH d6k/DGpnRb82WT/nwhcKF59w/5++VfnmzP45WhQ7IL0wdfZzga8Rt4xfc4MM43zGwkBMKICi4vBt idoZfbMm43ME3hAAJbx/DL9nHXR+qAEzBOQA2eeCBEMBpD5psv6P+VNnc5HtgOrKuF2XOf5YwnyP 7oiN4XfAoYILEJMgcYYAyPdPBT5uR7ccFg2gUAgX+/I3yvEFHyUJEqcDQNnulOZTDVDCQwegaAAB g4syPwhlS1CCXL1Xdi+ubInVAbBsh833yNEAcgAb2/8HM9IvwdMIttDAlyAxHIAt4WEzFJQdJT9w rwDJ2am9DMUXFfiyJYYD0LMdNstBUQDyPSyFg8qJebkXOQglyEO3xFfP7J+diqwOxQGk+F7qI08V LI+DosxW9UUzoQQJypYcbCxuyCUkigPwJTzkuDe7Ln0DvZQD6c30vqtOqIRET5BwBxCT8ECOsN8r f4OJgTUNvZyHhR6pCRLuAOTyHldcwhIeejzC1IPSDyhBBuvu42MXwV4NoGdLsAOISXj4fA9zACwU JgavwJktQQ4DZUtYgoQ6AHehAzMc5HvhV9zM/xkl/uFL7SA5xv8vmAg1XD7//nsLlI7ADiAm/i23 K/y48ywQnNXY9S0ZePlx1zGfP37D60aZN33fONheVoJyETwN4MsBIN+/vxADrngIWU4odDjeH/vP 93kpvK0AdwCszXfc8wFKgQKyXOi1MuPdp5ZclFoOchz/3ZZvnKxvPGC5CK0cwGx8gEo8UNBTw3KI 75fNQ85FGEUxLDvCajtqWQ5KF8ofSzEKMAwHwBIkyAGgoH8iUsSFL8ERinOYPCjoYQkPZ12AKFBS rj0X+Cb77f9UbmpYjp7wCDoAlCC//xbZQazviFGHnPAIOoAYA6mlhqh+AbUsw2bOgDsAAHEO2FDM 6/Q2AAAAAElFTkSuQmCC { // 認証設定を作成し、指定 const jwtAuth = httpClient.createAuthSettingOAuth2JwtBearer('JWT', scope, '', '', privateKeyId, privateKey, serviceAccount, ''); configs.putObject('conf_Auth', jwtAuth); // ファイル ID が保存されているデータ項目を作成し、指定 const fileIdsDef = engine.createDataDefinition('ファイル ID', 1, 'q_fileIds', 'STRING_TEXTAREA'); configs.putObject('conf_FileIds', fileIdsDef); engine.setData(fileIdsDef, fileIds); // ファイルを保存するデータ項目を作成し、指定 const fileDef = engine.createDataDefinition('ファイル', 2, 'q_files', 'FILE'); configs.putObject('conf_FileData', fileDef); engine.setData(fileDef, files); return fileDef; }; /** * 異常系のテスト * @param errorMsg */ const assertError = (errorMsg) => { let failed = false; try { main(); } catch (e) { failed = true; expect(e.message).toEqual(errorMsg); } if (!failed) { fail('No error was thrown.'); } }; /** * スコープが空 */ test('Scope is empty', () => { const scope = ''; const privateKeyId = 'key-12345'; const serviceAccount = 'service@questetra.com'; const fileIds = '1234567890'; prepareConfigs(scope, privateKeyId, PRIVATE_KEY, serviceAccount, fileIds, []); assertError(`Scope ${SCOPE} must be included in the scope.`); }); const ANOTHER_SCOPE = 'https://www.googleapis.com/auth/cloud-platform'; /** * スコープに必須スコープを含まない */ test('Required scope is missing', () => { const privateKeyId = 'key-12345'; const serviceAccount = 'service@questetra.com'; const fileIds = '1234567890'; prepareConfigs(ANOTHER_SCOPE, privateKeyId, PRIVATE_KEY, serviceAccount, fileIds, []); assertError(`Scope ${SCOPE} must be included in the scope.`); }); /** * 秘密鍵 ID が空 */ test('Private Key ID is empty', () => { const privateKeyId = ''; const serviceAccount = 'service@questetra.com'; const fileIds = '1234567890'; prepareConfigs(SCOPE, privateKeyId, PRIVATE_KEY, serviceAccount, fileIds, []); assertError('Private Key ID is required.'); }); /** * カスタム秘密情報1 に設定されているサービスアカウントが空 */ test('Service Account is empty', () => { const privateKeyId = 'key-12345'; const serviceAccount = ''; const fileIds = '1234567890'; prepareConfigs(SCOPE, privateKeyId, PRIVATE_KEY, serviceAccount, fileIds, []); assertError('Service Account must be set to Custom Secret 1.'); }); /** * ファイル ID が空 */ test('No File IDs - null', () => { const privateKeyId = 'key-12345'; const serviceAccount = 'service@questetra.com'; prepareConfigs(SCOPE, privateKeyId, PRIVATE_KEY, serviceAccount, null, []); assertError('No File IDs.'); }); /** * ファイル ID が空行のみ */ test('No File IDs - blank line', () => { const privateKeyId = 'key-12345'; const serviceAccount = 'service@questetra.com'; const fileIds = '\n\n'; prepareConfigs(SCOPE, privateKeyId, PRIVATE_KEY, serviceAccount, fileIds, []); assertError('No File IDs.'); }); /** * ファイル ID の数が多すぎる */ test('Too many File IDs', () => { const privateKeyId = 'key-12345'; const serviceAccount = 'service@questetra.com'; let fileIds = ''; let httpReqCount = 0; while (true) { fileIds += '1234567890\n'; httpReqCount += REQUEST_NUM_PER_FILE; if (httpReqCount > httpClient.getRequestingLimit()) { break; } } prepareConfigs(SCOPE, privateKeyId, PRIVATE_KEY, serviceAccount, fileIds, []); assertError('Number of File IDs exceeds the limit.'); }); /** * トークン取得リクエストのテスト * @param {Object} request * @param request.url * @param request.method * @param request.contentType * @param request.body * @param {String} scope * @param {String} serviceAccount */ const assertTokenRequest = ({ url, method, contentType, body }, scope, serviceAccount) => { expect(url).toEqual(URL_TOKEN_REQUEST); expect(method).toEqual('POST'); expect(contentType).startsWith('application/x-www-form-urlencoded'); const query = `grant_type=${encodeURIComponent('urn:ietf:params:oauth:grant-type:jwt-bearer')}&assertion=`; expect(body).startsWith(query); const assertion = decodeURIComponent(body.substring(query.length)); const publicKey = rsa.readKeyFromX509(CERTIFICATE); expect(jwt.verify(assertion, publicKey)).toEqual(true); const payloadJson = base64.decodeFromUrlSafeString(assertion.split('.')[1]); const payload = JSON.parse(payloadJson); expect(payload.iss).toEqual(serviceAccount); expect(payload.aud).toEqual(URL_TOKEN_REQUEST); expect(payload.scope).toEqual(scope); }; /** * メタデータ取得の API リクエストのテスト * @param {Object} request * @param request.url * @param request.method * @param request.contentType * @param request.headers * @param request.body * @param {String} token * @param {String} fileId */ const assertGetMetadataRequest = ({url, method, headers, body}, token, fileId) => { expect(url).toEqual(`https://www.googleapis.com/drive/v3/files/${encodeURIComponent(fileId)}?supportsAllDrives=true`); expect(method).toEqual('GET'); expect(headers.Authorization).toEqual(`Bearer ${token}`); }; /** * メタデータ取得の API リクエストでエラー */ test('Fail to get metadata', () => { const privateKeyId = 'key-12345'; const serviceAccount = 'service@questetra.com'; const fileId = '123456789'; const fileDef = prepareConfigs(SCOPE, privateKeyId, PRIVATE_KEY, serviceAccount, fileId, []); const token = 'token'; let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount++ === 0) { assertTokenRequest(request, SCOPE, serviceAccount); return httpClient.createHttpResponse(200, 'application/json', JSON.stringify({ 'access_token': token })); } assertGetMetadataRequest(request, token, fileId); return httpClient.createHttpResponse(400, 'application/json', '{}'); }); assertError('Failed to get metadata of file. status: 400'); }); /** * メタデータ取得の API レスポンスを作成 * @param {String} name * @param {String} mimeType * @returns {HttpResponse} */ const createMetadataResponse = (name, mimeType) => { const json = { name, mimeType }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(json)); } /** * ファイルダウンロードの API リクエストのテスト * @param {Object} request * @param request.url * @param request.method * @param request.headers * @param request.body * @param {String} token * @param {String} fileId */ const assertGetFileRequest = ({url, method, headers, body}, token, fileId) => { expect(url).toEqual(`https://www.googleapis.com/drive/v3/files/${encodeURIComponent(fileId)}?supportsAllDrives=true&alt=media`); expect(method).toEqual('GET'); expect(headers.Authorization).toEqual(`Bearer ${token}`); }; /** * ファイルダウンロードの API リクエストでエラー */ test('Fail to download file', () => { const privateKeyId = 'key-12345'; const serviceAccount = 'service@questetra.com'; const fileId = '123456789'; const fileDef = prepareConfigs(SCOPE, privateKeyId, PRIVATE_KEY, serviceAccount, fileId, []); const token = 'token'; let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount % 2 === 0) { assertTokenRequest(request, SCOPE, serviceAccount); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify({ 'access_token': token })); } if (reqCount === 1) { assertGetMetadataRequest(request, token, fileId); reqCount++; return createMetadataResponse('ファイル.html', 'text/html'); } assertGetFileRequest(request, token, fileId); return httpClient.createHttpResponse(400, 'application/json', '{}'); }); assertError('Failed to download file. status: 400'); }); /** * ファイルデータの準備 * @param {String} id * @param {String} name * @param {String} contentType * @param {String} body * @returns {Object} */ const prepareFileData = (id, name, contentType, body) => { return { id, name, contentType, body }; }; /** * ファイルのテスト * @param file * @param expectedFile.name * @param expectedFile.contentType * @param expectedFile.body */ const assertFile = (file, {name, contentType, body}) => { expect(file.getName()).toEqual(name); expect(file.getContentType()).toEqual(contentType); let text = ''; fileRepository.readFile(file, 'UTF-8', line => text += line + '\n'); expect(text).toEqual(`${body}\n`); }; /** * 成功 - ファイル ID をデータ項目で指定し、事前にファイルが添付されていない場合 */ test('Succeed - file IDs set by data item, no pre-attached files', () => { const privateKeyId = 'key-12345'; const serviceAccount = 'service@questetra.com'; const filesToDownload = [ prepareFileData('id-1', 'file1.txt', 'text/plain', 'Hello, World!'), prepareFileData('id-2', 'file2.txt', 'text/plain', 'こんにちは、世界!'), prepareFileData('id-3', 'PDFファイル.pdf', 'application/pdf', 'PDF data'), ]; const fileIdsStr = filesToDownload.map(file => file.id).join('\n') + '\n'; const fileDef = prepareConfigs(SCOPE, privateKeyId, PRIVATE_KEY, serviceAccount, fileIdsStr, []); const token = 'token1'; let reqCount = 0; httpClient.setRequestHandler((request) => { const fileData = filesToDownload[Math.floor(reqCount / REQUEST_NUM_PER_FILE)]; if (reqCount % REQUEST_NUM_PER_FILE === 0 || reqCount % REQUEST_NUM_PER_FILE === 2) { assertTokenRequest(request, SCOPE, serviceAccount); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify({ 'access_token': token })); } if (reqCount % REQUEST_NUM_PER_FILE === 1) { // メタデータ取得のリクエスト assertGetMetadataRequest(request, token, fileData.id); reqCount++; return createMetadataResponse(fileData.name, fileData.contentType); } // ファイルダウンロードのリクエスト assertGetFileRequest(request, token, fileData.id); reqCount++; return httpClient.createHttpResponse(200, fileData.contentType, fileData.body); }); expect(main()).toEqual(undefined); expect(reqCount).toEqual(filesToDownload.length * REQUEST_NUM_PER_FILE); // ファイルのテスト const files = engine.findData(fileDef); expect(files.size()).toEqual(filesToDownload.length); filesToDownload.forEach((expectedFile, i) => assertFile(files[i], expectedFile)); }); /** * 成功 - ファイル ID を固定値で指定し、事前にファイルが添付されている場合 */ test('Succeed - file ID set as fixed value, with pre-attached files', () => { const privateKeyId = 'key-67890'; const serviceAccount = 'service2@questetra.com'; const filesToDownload = [ prepareFileData('id-1', 'file1.txt', 'text/plain', 'Hello, World!') ]; const fileIdsStr = filesToDownload.map(file => file.id).join('\n'); const preattachedFiles = new java.util.ArrayList(); preattachedFiles.add(engine.createQfile('test.html', 'text/html', '')); preattachedFiles.add(engine.createQfile('test.xml', 'text/xml', 'さようなら')); const fileDef = prepareConfigs(SCOPE, privateKeyId, PRIVATE_KEY, serviceAccount, 'dummyFileId', preattachedFiles); // ファイル ID を固定値で指定 configs.put('conf_FileIds', fileIdsStr); const token = 'token1'; let reqCount = 0; httpClient.setRequestHandler((request) => { const fileData = filesToDownload[Math.floor(reqCount / REQUEST_NUM_PER_FILE)]; if (reqCount % REQUEST_NUM_PER_FILE === 0 || reqCount % REQUEST_NUM_PER_FILE === 2) { assertTokenRequest(request, SCOPE, serviceAccount); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify({ 'access_token': token })); } if (reqCount % REQUEST_NUM_PER_FILE === 1) { // メタデータ取得のリクエスト assertGetMetadataRequest(request, token, fileData.id); reqCount++; return createMetadataResponse(fileData.name, fileData.contentType); } // ファイルダウンロードのリクエスト assertGetFileRequest(request, token, fileData.id); reqCount++; return httpClient.createHttpResponse(200, fileData.contentType, fileData.body); }); expect(main()).toEqual(undefined); expect(reqCount).toEqual(filesToDownload.length * REQUEST_NUM_PER_FILE); // ファイルのテスト const files = engine.findData(fileDef); expect(files.size()).toEqual(filesToDownload.length + 2); // 事前にファイルが 2 つ添付されている filesToDownload.forEach((expectedFile, i) => assertFile(files[i + 2], expectedFile)); }); ]]>