2022-11-21 (C) Questetra, Inc. (MIT License) 2 This item uploads files to the specified folder on Box. この工程は、Box の指定フォルダにファイルをアップロードします。 https://support.questetra.com/bpmn-icons/box-upload/ https://support.questetra.com/ja/bpmn-icons/box-upload/ iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAD/UlEQVRYR82XbWibVRTHf/fJ2jXJ 2qSd65x11aC0YgJWZUwEWZ0D/SJaHYKKmgykbL70Bf2k4IYwZOKWOhFRMM9QZxHUjSEo+7AOlA1E 7WSRThzdqq1tWWxjm7Zrk1y5N+SlydM2XdvN50sebs5z7u+e8z/n3iu4xo9Y1Pz+3x6F5BagAYQb oX4BSTfIUaAbIboIeY8W63dhAH+vGyZaQLYihLsox1LBiCA4OjA9CmzOZ34Af9gP8kDRE+dPo0GM AObtR+YimBvAfzaIEC1FrXghIymDmL42KzNrgMBZE8RzC/ld1P8SE9MbyP+mEGA5V16Ykg5MX2vu 8GwAlXNBaFErW6yxFE25msgCaLXHeq9YcMWCaGE6PenqyAEI70bwRrF+lmQn2YPp3a18ZAECYVWv rrTj0I4bqHLaeOTgn0uay/JjFQXTV5kFUB1OyK9zjYeC9ThKBeW7epYfQHfPlBZSEbBQ/nBHCmDN zpUCkLoiUgCBcBegenzmUQDO1QaHT0dpqC3T4yd7YrzyxZB+33V/Jds3VbBmtUH/SJzmQwN07ryR 7r4p2jtTNh88uwHPdaU8uP+iVRRPEvI2pgEuADflA6wrtxV8+GPvJBcuzfDY3RXYjBzgfxNU2A0M AW2fD1Jht/FmUzWx6STuFyyiqDYw03tnGkDmz6QioADUZO98F8HtMHh521o9lpQwNSMxvx/l9PkJ XtpWxSaPnZFYArfDRmQ8oeFcDhvvHv+Hts5Bax2FvGJeAFUFW97q5Yc/JrWDVx9ay74n1uv3Iz+P 0fRetkIiB+uxlxj80jfFvbfatc2Jnhhb91mGPwWUA1CQgoH9dawrX0VN+zmGxxLa/qnNLj5rrtHv ShtPf9ifWdnfB+p02X7z6zhNd5Xr8U9PRXnmo6zN7DDIM4R8DXOK8NRrHu65xY7K+QNvX2SDaxXH Wmqpu75U+1FhVsL78qcxDjfX8ORml05BeZmN6YTUDUalQenh/RMjC4jQogwb6x189eJGKp02YpeT 2AxBWYmgLzLDwGhcw8UTaJG57AaT0xIlJGXTcTyi09HcWMml8QTVLecKAWRuGVo0IvXF1tuc7N1e rUspnpSE+y+z4+N+/hqJ88nzNdxX58BRajAYjbPn6DB7H1/P70PTPNzRpyf8tr2WjVUleF8/bwGQ 24jU33mt2Fq2yzYaJeTVx7v/0WaktmMxoaohsyEt23pnO4oiHTcXbsfK6JoeSNKUK3oky54D0tNd vUMp8hAhnz8/rVfpWF648vkjkEmHPqiYSxBmFCn8V3YxyUDoq1krAnWcLrZCokjU1Sy4tKtZfsJS l9NGhGgA6QZxR8pEngExipTdYHTNt+LiNbBCTSDf7X8FbX8wYecpbwAAAABJRU5ErkJggg== { configs.put('conf_OAuth2', 'Box'); const fileDef = engine.createDataDefinition('ファイル', 1, 'q_files', 'FILE'); configs.putObject('uploadedFile', fileDef); engine.setData(fileDef, files); configs.put('uploadedFolderId', folderId); const idDef = engine.createDataDefinition('ファイルID', 2, 'q_ids', 'STRING_TEXTAREA'); configs.putObject('fileId', idDef); engine.setData(idDef, '事前文字列'); const urlDef = engine.createDataDefinition('ファイルURL', 3, 'q_urls', 'STRING_TEXTAREA'); configs.putObject('fileUrl', urlDef); engine.setData(urlDef, '事前文字列'); return { fileDef, idDef, urlDef }; }; /** * 指定サイズのテキストファイルを作成 * @param name * @param size * @return qfile */ const createQfile = (name, size) => { let text = ''; if (size >= 4000) { text = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'.repeat(100); // 40 * 100 = 4000 } while (text.length < size) { if (text.length !== 0 && text.length * 2 <= size) { text += text; } else if (text.length + 1000 <= size) { text += 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'.repeat(25); // 40 * 25 = 1000 } else { text += 'a'; } } return engine.createQfile(name, 'text/plain; charset=US-ASCII', text); }; test('File is null', () => { const { idDef, urlDef } = prepareConfigs(configs, null, ''); execute(); expect(engine.findData(idDef)).toEqual(''); expect(engine.findData(urlDef)).toEqual(''); }); test('File Count > requestingLimit', () => { const files = new java.util.ArrayList(); while (files.size() <= httpClient.getRequestingLimit()) { files.add(createQfile('テスト', 0)); } prepareConfigs(configs, files, ''); expect(execute).toThrow('Number of requests is over the limit'); }); test('File Name contains /', () => { const files = new java.util.ArrayList(); files.add(createQfile('テスト', 0)); files.add(createQfile('テスト/テスト', 0)); prepareConfigs(configs, files, ''); expect(execute).toThrow('Invalid File Name'); }); test('File Name contains \\', () => { const files = new java.util.ArrayList(); files.add(createQfile('テスト', 0)); files.add(createQfile('テスト\\テスト', 0)); prepareConfigs(configs, files, ''); expect(execute).toThrow('Invalid File Name'); }); test('File Name starts with space', () => { const files = new java.util.ArrayList(); files.add(createQfile('テスト', 0)); files.add(createQfile(' test', 0)); prepareConfigs(configs, files, ''); expect(execute).toThrow('Invalid File Name'); }); test('File Name ends with space', () => { const files = new java.util.ArrayList(); files.add(createQfile('テスト', 0)); files.add(createQfile('test ', 0)); prepareConfigs(configs, files, ''); expect(execute).toThrow('Invalid File Name'); }); test('File Name is .', () => { const files = new java.util.ArrayList(); files.add(createQfile('テスト', 0)); files.add(createQfile('.', 0)); prepareConfigs(configs, files, ''); expect(execute).toThrow('Invalid File Name'); }); test('File Name is ..', () => { const files = new java.util.ArrayList(); files.add(createQfile('テスト', 0)); files.add(createQfile('..', 0)); prepareConfigs(configs, files, ''); expect(execute).toThrow('Invalid File Name'); }); test('File Names are duplicated', () => { const files = new java.util.ArrayList(); files.add(createQfile('...', 0)); files.add(createQfile('...', 0)); prepareConfigs(configs, files, ''); expect(execute).toThrow('Two or more files to upload have the same name.'); }); test('Fail to multipart upload', () => { const files = new java.util.ArrayList(); files.add(createQfile('fail.txt', 8)); prepareConfigs(configs, files, ''); httpClient.setRequestHandler(({url, method, contentType}) => { expect(url).toEqual('https://upload.box.com/api/2.0/files/content?fields=id'); expect(method).toEqual('POST'); expect(contentType).startsWith('multipart/form-data'); return httpClient.createHttpResponse(409, 'application/json', JSON.stringify({})); }); expect(execute).toThrow('failed to upload\nstatus: 409\nfile: fail.txt'); }); test('Failed to create upload session', () => { const FILE_SIZE = 52428800 + 4; const files = new java.util.ArrayList(); files.add(createQfile('large.txt', FILE_SIZE)); prepareConfigs(configs, files, ''); httpClient.setRequestHandler(({url, method, contentType}) => { expect(url).toEqual('https://upload.box.com/api/2.0/files/upload_sessions'); expect(method).toEqual('POST'); return httpClient.createHttpResponse(403, 'application/json', JSON.stringify({})); }); expect(execute).toThrow('failed to create upload session \nstatus: 403\nfile: large.txt'); }); test('Failed to partial upload', () => { const FILE_SIZE = 52428800 + 4; const PART_SIZE = FILE_SIZE / 2; const files = new java.util.ArrayList(); files.add(createQfile('large.txt', FILE_SIZE)); prepareConfigs(configs, files, ''); httpClient.setRequestHandler(({url, method, contentType}) => { engine.log(url); switch (url) { case 'https://upload.box.com/api/2.0/files/upload_sessions': { expect(method).toEqual('POST'); const responseObj = { "id": "S0", "type": "upload_session", "part_size": PART_SIZE }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); } case 'https://upload.box.com/api/2.0/files/upload_sessions/S0': { expect(method).toEqual('PUT'); return httpClient.createHttpResponse(412, 'application/json', '{}'); } default: fail('Request Error'); } }); expect(execute).toThrow('failed to upload\nstatus: 412\nfile: large.txt'); }); test('Failed to commit upload session', () => { const FILE_SIZE = 52428800 + 4; const PART_SIZE = FILE_SIZE / 2; const files = new java.util.ArrayList(); files.add(createQfile('大きい.txt', FILE_SIZE)); prepareConfigs(configs, files, ''); httpClient.setRequestHandler(({url, method, contentType}) => { engine.log(url); switch (url) { case 'https://upload.box.com/api/2.0/files/upload_sessions': { expect(method).toEqual('POST'); const responseObj = { "id": "S0", "type": "upload_session", "part_size": PART_SIZE }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); } case 'https://upload.box.com/api/2.0/files/upload_sessions/S0': { expect(method).toEqual('PUT'); const responseObj = { "part": { "size": PART_SIZE } }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); } case 'https://upload.box.com/api/2.0/files/upload_sessions/S0/commit?fields=id': { expect(method).toEqual('POST'); return httpClient.createHttpResponse(416, 'application/json', '{}'); } default: fail('Request Error'); } }); expect(execute).toThrow('failed to commit upload session \nstatus: 416\nfile: 大きい.txt'); }); test('File Count > 1 and FileId is TextField', () => { const files = new java.util.ArrayList(); files.add(createQfile('1.txt', 0)); files.add(createQfile('2.txt', 0)); prepareConfigs(configs, files, ''); // TEXTFIELD に上書き const idDef = engine.createDataDefinition('ファイルID', 2, 'q_ids', 'STRING_TEXTFIELD'); configs.putObject('fileId', idDef); expect(execute).toThrow('Multiple files are set though the Data Item to save the output is Single-line String.'); }); test('File Count > 1 and FileUrl is TextField', () => { const files = new java.util.ArrayList(); files.add(createQfile('1.txt', 0)); files.add(createQfile('2.txt', 0)); prepareConfigs(configs, files, ''); // TEXTFIELD に上書き const urlDef = engine.createDataDefinition('ファイルURL', 3, 'q_urls', 'STRING_TEXTFIELD'); configs.putObject('fileUrl', urlDef); expect(execute).toThrow('Multiple files are set though the Data Item to save the output is Single-line String.'); }); test('File Count = 1 and FileId FileUrl is TextField', () => { const files = new java.util.ArrayList(); files.add(createQfile('1.txt', 10)); prepareConfigs(configs, files, '12345'); // TEXTFIELD に上書き const idDef = engine.createDataDefinition('ファイルID', 2, 'q_ids', 'STRING_TEXTFIELD'); configs.putObject('fileId', idDef); const urlDef = engine.createDataDefinition('ファイルURL', 3, 'q_urls', 'STRING_TEXTFIELD'); configs.putObject('fileUrl', urlDef); httpClient.setRequestHandler(({url, method, contentType, body}) => { expect(url).toEqual('https://upload.box.com/api/2.0/files/content?fields=id'); expect(method).toEqual('POST'); expect(contentType).startsWith('multipart/form-data'); expect(body.size()).toEqual(2); const attributes = JSON.parse(body.get('attributes').get(0)); expect(attributes.parent.id).toEqual('12345'); expect(attributes.name).toEqual('1.txt'); const file = body.get('file').get(0); expect(file.getName()).toEqual('1.txt'); expect(file.getContentType()).startsWith('text/plain'); expect(file.getLength()).toEqual(10); const responseObj = { "entries": [ { "id": 1234, "etag": 1, "type": "file" } ], "total_count": 1 }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); }); execute(); expect(engine.findData(idDef)).toEqual('1234'); expect(engine.findData(urlDef)).toEqual('https://app.box.com/file/1234'); }); test('Failed to Upload too Large File', () => { const maxRequests = httpClient.getRequestingLimit() - 2; const FILE_SIZE = 52428800 + 1; const PART_SIZE = Math.ceil(FILE_SIZE / maxRequests) - 1; // HTTP request わずかに足りない PART_SIZE const files = new java.util.ArrayList(); files.add(createQfile('large.txt', FILE_SIZE)); prepareConfigs(configs, files, ''); httpClient.setRequestHandler(({url, method, contentType}) => { expect(url).toEqual('https://upload.box.com/api/2.0/files/upload_sessions'); expect(method).toEqual('POST'); const responseObj = { "id": `S99999`, "type": "upload_session", "part_size": PART_SIZE }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); }); expect(execute).toThrow('Number of requests is over the limit'); }); test('Failed to Upload Large File and Small File', () => { const maxRequests = httpClient.getRequestingLimit() - 2; const FILE_SIZE = 52428800 + 1; const PART_SIZE = Math.ceil(FILE_SIZE / maxRequests); // HTTP request ぎりぎりの PART_SIZE const files = new java.util.ArrayList(); files.add(createQfile('large.txt', FILE_SIZE)); files.add(createQfile('smal.txt', 0)); // サイズの小さなファイルがあるため、request 数が足りない prepareConfigs(configs, files, ''); httpClient.setRequestHandler(({url, method, contentType}) => { expect(url).toEqual('https://upload.box.com/api/2.0/files/upload_sessions'); expect(method).toEqual('POST'); const responseObj = { "id": `S99999`, "type": "upload_session", "part_size": PART_SIZE }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); }); expect(execute).toThrow('Number of requests is over the limit'); }); test('Succeed to Upload Large File', () => { const maxRequests = httpClient.getRequestingLimit() - 2; const FILE_SIZE = 52428800 + 1; const PART_SIZE = Math.ceil(FILE_SIZE / maxRequests); // HTTP request 使い切る PART_SIZE const files = new java.util.ArrayList(); files.add(createQfile('large.txt', FILE_SIZE)); const { idDef, urlDef } = prepareConfigs(configs, files, ''); httpClient.setRequestHandler(({url, method, contentType, body}) => { engine.log(url); switch (url) { case 'https://upload.box.com/api/2.0/files/upload_sessions': { expect(method).toEqual('POST'); expect(contentType).toEqual('application/json; charset=UTF-8'); const bodyObj = JSON.parse(body); expect(bodyObj.file_name).toEqual('large.txt'); expect(bodyObj.file_size).toEqual(FILE_SIZE); expect(bodyObj.folder_id).toEqual('0'); const responseObj = { "id": `S99999`, "type": "upload_session", "part_size": PART_SIZE }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); } case 'https://upload.box.com/api/2.0/files/upload_sessions/S99999': { expect(method).toEqual('PUT'); const responseObj = { "part": { "size": PART_SIZE } }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); } case 'https://upload.box.com/api/2.0/files/upload_sessions/S99999/commit?fields=id': { expect(method).toEqual('POST'); const responseObj = { "entries": [ { "id": 98765, "etag": 1, "type": "file" } ], "total_count": 1 }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); } default: fail('Request Error'); } }); execute(); expect(engine.findData(idDef)).toEqual('98765'); expect(engine.findData(urlDef)).toEqual('https://app.box.com/file/98765'); }); test('Succeed to Upload multiple size files', () => { const FILE_SIZE = 52428800 + 4; const PART_SIZE = FILE_SIZE / 2; const files = new java.util.ArrayList(); files.add(createQfile('...', 8)); files.add(createQfile('large.txt', FILE_SIZE)); files.add(createQfile('大きい.txt', FILE_SIZE)); files.add(createQfile('test テスト', 16)); const { idDef, urlDef } = prepareConfigs(configs, files, '12345'); let fileCount = 0; const fileIds = [10, 20, 30, 40]; let sessionCount = 0; httpClient.setRequestHandler(({url, method, contentType, body}) => { engine.log(url); switch (url) { case 'https://upload.box.com/api/2.0/files/content?fields=id': { expect(method).toEqual('POST'); expect(contentType).startsWith('multipart/form-data'); expect(body.size()).toEqual(2); const attributes = JSON.parse(body.get('attributes').get(0)); expect(attributes.parent.id).toEqual('12345'); const file = body.get('file').get(0); expect(file.getContentType()).startsWith('text/plain'); const responseObj = { "entries": [ { "id": fileIds[fileCount++], "etag": 1, "type": "file" } ], "total_count": 1 }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); } case 'https://upload.box.com/api/2.0/files/upload_sessions': { expect(method).toEqual('POST'); expect(contentType).toEqual('application/json; charset=UTF-8'); const bodyObj = JSON.parse(body); expect(bodyObj.file_size).toEqual(FILE_SIZE); expect(bodyObj.folder_id).toEqual('12345'); const responseObj = { "id": `S${sessionCount++}`, "type": "upload_session", "part_size": PART_SIZE }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); } case 'https://upload.box.com/api/2.0/files/upload_sessions/S0': case 'https://upload.box.com/api/2.0/files/upload_sessions/S1': { expect(method).toEqual('PUT'); const responseObj = { "part": { "size": PART_SIZE } }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); } case 'https://upload.box.com/api/2.0/files/upload_sessions/S0/commit?fields=id': case 'https://upload.box.com/api/2.0/files/upload_sessions/S1/commit?fields=id': { expect(method).toEqual('POST'); const responseObj = { "entries": [ { "id": fileIds[fileCount++], "etag": 1, "type": "file" } ], "total_count": 1 }; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(responseObj)); } default: fail('Request Error'); } }); execute(); expect(engine.findData(idDef)).toEqual('10\n20\n30\n40'); expect(engine.findData(urlDef)).toEqual('https://app.box.com/file/10\nhttps://app.box.com/file/20\nhttps://app.box.com/file/30\nhttps://app.box.com/file/40'); }); ]]>