3 2 2025-12-15 (C) Questetra, Inc. (MIT License) This item copies the specified file on Google Drive. You can optionally specify the destination folder and the new file name, and store the ID or web view URL of the created file. この工程は、Google ドライブ上の指定ファイルをコピーします。保存先のフォルダや新しいファイル名を任意で設定でき、作成さ れたファイルの ID や表示用 URL を保存できます。 https://support.questetra.com/bpmn-icons/googledrive-filecopy/ https://support.questetra.com/ja/bpmn-icons/googledrive-filecopy/ iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA71JREFUWEfF V11oFFcU/s7M7K6TRs1vK1VUSGmbCK2SB6VFgpLWlYC2omIDLaUlCBGfimwWgw9Fu9mW0ocSoUhE aKmlgUgUyShiEUWSh4AW7L9CS5PGZhuTJmTi7swcmUn3d+ZOdtGy87Ts3HvOd77zfefeIZT5oVLy h2MP3wTMViJqZqABQM1/+6cIuMvMo4B8RYuGzhUbd0kArT1TK2VWj5CEg2DUFRWYkGALX5ikf3Kl q2bGb48vgJ0x/T2WEC86cWEmQoIsRIai6mkRCCGAcHy+F0ydRVW81CLik1qk4pDXMk8A4bj+NRhv LRW3pPeEs1pEbXeTVPDPE63clc3NRB4DTs8JfX6V7d2soKVRgizlk5cyGBdvmbj8velLDDHez9VE JoqtdoXU3/wE19Io452tCtQgUP2Uu3u/TliInE1CT/pgICQM1p9LuyMTZUdMP06Eo37wYweC2LhO wsQ0o245QZHzVxsm8O2IgS+vG74sMOPEpajabS/KAAjH9Um/6vdvUdD+qoKQAiykgFmdUb/CzcLE DCN+PoWfxi0xCEJCi6j1GQDOhCNrQLRjdQ2h+40g1tdnE/4zy6gIkdOO3Mdi4OodE59eTPmbhKU9 9sR0IoZj870gsec7tivY1axAkbIxDQuY/JexaiWBCoiYnmd8fsnAzV98BMl8UotWHHK27uzRhxnY 7AX55XUSjrQFULvcTbdNs92G2kr3u1u/W4h+I1YjASNDXeqWRQZ69EnAe85Hdwew9QXZVeXsAqP3 soENayS0bZJR4EpHJ2eupTA4KmQhoXWpi00N9+j2qhyCF7l4/SUZHdsUVC7Lr9Du87UfTXx8IYW1 dYRje4JYXe1m4d7fjI8GkxibYi9yLa1LlYUAbHEd3xdE0xoXLhQqPdchuZlsnZwfNXDqqqct8wC4 WlBK0FLA5gDMtsBLhJ+9HcSLz7qrH3/AmFtgBJR8ym0uQwG4tGJawNBtE/3D+Szki9DDhrubZbzb EsCyQBaz3wASmX7sAePDgST+SBToINeGokF0Yn8Qm9ZLTlXMwF/TjKdXuEewKHnSAPpHDHx1w0MD uYPIcYLHKH7leRmHdyioqiCHdltU9u9inx/+tNDd73E4FY5iO6DoMPqgLYBtTTLuzzBWVZHL7yIw cw8Zfd8Z0G6754DnYSQ6jm2fd74WQMMzhMpQcdXb7br+s4nYoMd5IDqOnZFcxIWkWPpF64QXkvSG sl7JsiDKeCn9X5go9VqeBlHWD5M0iLJ+mhUquSwfp49ru6X2PwLQFL0wM02BagAAAABJRU5ErkJg gg== { const auth = httpClient.createAuthSettingOAuth2JwtBearer('JWT', scope, '', '', privateKeyId, privateKey, serviceAccount, ''); configs.putObject('conf_Auth', auth); return auth; }; const prepareSourceIdConfig = (value) => { const sourceDef = engine.createDataDefinition('コピー元ファイルID', 1, 'q_sourceFileId', 'STRING_TEXTFIELD'); configs.putObject('conf_SourceFileId', sourceDef); if (value !== undefined) { engine.setData(sourceDef, value); } return sourceDef; }; const prepareDestinationConfig = (value) => { const destinationDef = engine.createDataDefinition('保存先フォルダID', 2, 'q_destinationFolderId', 'STRING_TEXTFIELD'); configs.putObject('conf_DestinationFolderId', destinationDef); if (value !== undefined) { engine.setData(destinationDef, value); } return destinationDef; }; const prepareResultDataDefs = () => { const idDef = engine.createDataDefinition('コピー後ファイルID', 3, 'q_newFileId', 'STRING_TEXTFIELD'); configs.putObject('conf_NewFileIdItem', idDef); engine.setData(idDef, '既存値'); const urlDef = engine.createDataDefinition('コピー後ファイルURL', 4, 'q_newFileUrl', 'STRING_TEXTFIELD'); configs.putObject('conf_NewFileUrlItem', urlDef); engine.setData(urlDef, '既存値'); return { idDef, urlDef }; }; 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); }; const assertCopyRequest = ({ url, method, headers, contentType, body }, token, sourceFileId, destinationId, newFileName) => { const fields = encodeURIComponent('id,name,parents,webViewLink'); expect(url).toEqual(`https://www.googleapis.com/drive/v3/files/${encodeURIComponent(sourceFileId)}/copy?fields=${fields}&supportsAllDrives=true`); expect(method).toEqual('POST'); expect(headers.Authorization).toEqual(`Bearer ${token}`); expect(contentType).toEqual('application/json; charset=UTF-8'); const bodyObj = JSON.parse(body); if (destinationId !== null) { expect(bodyObj.parents).toEqual([destinationId]); } else { expect(bodyObj.parents ?? null).toEqual(null); } if (newFileName !== '') { expect(bodyObj.name).toEqual(newFileName); } else { expect(bodyObj.name ?? null).toEqual(null); } }; const assertError = (func, errorMsg) => { let thrown = false; try { func(); } catch (e) { thrown = true; if (e instanceof Error) { expect(e.message).toEqual(errorMsg); } else { expect(e.toString()).toEqual(errorMsg); } } if (!thrown) { fail('No error was thrown.'); } }; test('Scope is missing', () => { createAuthSetting(''); configs.put('conf_SourceFileId', 'src123'); assertError(() => main(), `Scope ${SCOPE} must be included in the scope.`); }); test('Private Key ID is empty', () => { createAuthSetting(SCOPE, ''); configs.put('conf_SourceFileId', 'src123'); assertError(() => main(), 'Private Key ID is required.'); }); test('Private Key is empty', () => { createAuthSetting(SCOPE, DEFAULT_PRIVATE_KEY_ID, ''); configs.put('conf_SourceFileId', 'src123'); assertError(() => main(), 'Private Key is required.'); }); test('Service Account is empty', () => { createAuthSetting(SCOPE, DEFAULT_PRIVATE_KEY_ID, PRIVATE_KEY, ''); configs.put('conf_SourceFileId', 'src123'); assertError(() => main(), 'Service Account must be set to Custom Secret 1.'); }); test('Source File ID is blank', () => { createAuthSetting(); configs.put('conf_SourceFileId', ' '); assertError(() => main(), "Source File ID isn't set."); }); test('Failed to copy', () => { const serviceAccount = DEFAULT_SERVICE_ACCOUNT; createAuthSetting(); prepareSourceIdConfig('src123'); configs.put('conf_NewFileName', 'Copy'); 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 })); } assertCopyRequest(request, token, 'src123', null, 'Copy'); return httpClient.createHttpResponse(403, 'application/json', '{"error":"forbidden"}'); }); assertError(() => main(), 'Failed to copy. Source ID: src123, status: 403'); }); test('Success without destination (no webViewLink)', () => { const serviceAccount = DEFAULT_SERVICE_ACCOUNT; createAuthSetting(); prepareSourceIdConfig('src456'); configs.put('conf_NewFileName', ''); const { idDef, urlDef } = prepareResultDataDefs(); 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 })); } assertCopyRequest(request, token, 'src456', null, ''); return httpClient.createHttpResponse(200, 'application/json', '{"id":"new456"}'); }); main(); expect(engine.findData(idDef)).toEqual('new456'); expect(engine.findData(urlDef)).toEqual('https://drive.google.com/file/d/new456/view'); }); test('Success with destination and new name', () => { const serviceAccount = DEFAULT_SERVICE_ACCOUNT; createAuthSetting(); prepareSourceIdConfig('src789'); prepareDestinationConfig('folder987'); configs.put('conf_NewFileName', 'CopyFile.docx'); const { idDef, urlDef } = prepareResultDataDefs(); 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 })); } assertCopyRequest(request, token, 'src789', 'folder987', 'CopyFile.docx'); return httpClient.createHttpResponse( 200, 'application/json', '{"id":"new789","webViewLink":"https://drive.google.com/file/d/new789/view","name":"CopyFile.docx"}' ); }); main(); expect(engine.findData(idDef)).toEqual('new789'); expect(engine.findData(urlDef)).toEqual('https://drive.google.com/file/d/new789/view'); }); ]]>