2023-07-03
(C) Questetra, Inc. (MIT License)
3
2
This item generates a PDF file from HTML.
この工程は、HTML から PDF ファイルを生成します。
https://support.questetra.com/bpmn-icons/service-task-docraptor-pdf-generate/
https://support.questetra.com/ja/bpmn-icons/service-task-docraptor-pdf-generate/
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA4ZJREFUWEfF
l2lIVFEUx//vOaNOWZFlu1QmSQSBIzaSRQu2gNaDosSoCOuDhYVJORRRCQXNtNKHFKQMkiyCYFqw
oMiFnBlK/WCblS1amnumNpbje3Gnec9507zFnJj78d2z/M4595x7H4UAL0qt/7hjd0ZpHOx6AMsp
IBbALADj3fpdAD5yQA2Ax04dfavq2NofamwrAsQfuDeFpp1GCsgAEKrGKIB+DshnWY3p6ankr3I6
sgAJOZYMUDgNYLRKx95ifeCw32Zm8qX0JQESjJaLAHb9o2NvtTybidnty5ZPAEOO5RpFIc1Pzl1m
OA7FdjOz2dvmXwB+jlwxEyIAd83z/Bn5X7Y47PI8EwIAOe1BtPPdCA6c4GtRzGS8aOxC949fvmLp
G2Q10Xx3CAAGo+UcBWTxGrpgDc5sN+DJ6xbcsn2E45dTMTE0TcG8dSES502Gc5BF3v1XuFZR7yMJ
OG83MfvIhguADBmtg+3w7vP46AicS08ATVH40tmHBzWfcbPyg1RkqDiRAk0QLXJYVd+OzIJKb4j+
AR09gQwrF4DBaNlCAVd9hTg/cjzyMhKh9TDc8s2BR7VNuF5Rj7bv/S414pgA8KuhvRfF5fVwshzu
PmvwlYWtdhNTxANcooB0qRzPnjQGV/YuRbBGHB2R7+j5ifKXzaj91Ikjm/SCiV7HAFbmlkiWjQMu
203MDhdAgtFSjT/zXXJNCx+FoqxlIGdD7VqVW4Iex4CUeI3NxOh5gE6Pi0XSfnhYCO4dXq3K/yDL
YYP5IUi5JFaXzcSE8wCcGquHN8YiOS5SjSgOFj1F6fNmWVmbiaFUA6yJnYGjqUM1VqJ429SNbRfK
VAPIlmBmRBiKs1eA4JLUfmjpQfTUsSLj7d/7EabTIkQTBCfLoqGtF1vOl8oBiEogewh3JsVgR1KM
yxiZA2dv16L8eAq07q4gUIsP3RGchWqDoI+aiMq6FjmAoUNoMFpk25BYSVowHblpelS/78CegkoB
gOU4ZBfaYX/TqlQV0b6oDeUGkafW3GnjkLZkDnJvVKPseIprLljrWpFdaBuWcyLMAUODSGoUy1m1
nlwnbL9s7EJmgVXVfeFWEo9i8tH7MlIKKTUxCj8HBgUx65tWuZ73Tr/4MiK7/ryOFeB9X8dEKaAP
Ep46oE8yHiKgj9L/lInhPcsFiED+mPAQAf0182yngP2cKg2kke7/BmM1hzDqtBczAAAAAElFTkSu
QmCC
{
configs.put('conf_Auth', 'DocRaptor');
// ファイル型データ項目を作成・設定し、変換元ファイルを添付
const sourceFilesDef = engine.createDataDefinition('変換元ファイル', 1, 'q_SourceFiles', 'FILE');
configs.putObject('conf_SourceHtml', sourceFilesDef);
engine.setData(sourceFilesDef, sourceFiles);
// 生成されたファイルを保存する用のファイル型データ項目を作成し、設定
const filesDef = engine.createDataDefinition('ファイル保存先', 2, 'q_Files', 'FILE');
configs.putObject('conf_Files', filesDef);
// 保存時に他のファイルを削除するかどうかを設定
configs.putObject('conf_DeleteOtherFiles', deleteOtherFiles);
// 保存する際のファイル名を設定
configs.put('conf_FileName', fileName);
return filesDef;
};
/**
* 異常系のテスト
* @param func
* @param errorMsg
*/
const assertError = (func, errorMsg) => {
try {
func();
fail();
} catch (e) {
expect(e.toString()).toEqual(errorMsg);
}
};
/**
* 変換元ファイルが添付されていない
*/
test('No source file', () => {
prepareConfigs(null, false, '変換後.pdf');
assertError(main, 'No source file attached.');
});
/**
* 変換元ファイルが 2 つ以上添付されている
*/
test('More than one source files', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('ファイル1.html', 'text/html', ''));
sourceFiles.add(engine.createQfile('ファイル2.html', 'text/html', ''));
prepareConfigs(sourceFiles, false, '変換後.pdf');
assertError(main, 'More than one source files attached.');
});
/**
* 変換元ファイルの中身が空
*/
test('Source file is empty', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('変換元.html', 'text/html', ''));
prepareConfigs(sourceFiles, false, '変換後.pdf');
assertError(main, 'Source file is empty.');
});
/**
* 指定サイズの文字列を作成
* @param size
* @return text
*/
const createText = (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 text;
};
/**
* 変換元ファイルのサイズが大きすぎる
*/
test('Source file is too large', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('変換元.html', 'text/html', createText(MAX_FILE_SIZE + 1)));
prepareConfigs(sourceFiles, false, '変換後.pdf');
assertError(main, 'Source file is too large.');
});
/**
* 変換元ファイルが text/html でない
*/
test('Content-Type of the source file is not text/html', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('変換元.html', 'text/plain', 'aaa'));
prepareConfigs(sourceFiles, false, '変換後.pdf');
assertError(main, 'Content-Type of the source file is not text/html.');
});
/**
* ファイル名が空
*/
test('File name is blank', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('変換元.html', 'text/html', 'aaa'));
prepareConfigs(sourceFiles, false, '');
assertError(main, 'File name is blank.');
});
/**
* PDF ファイルを生成する API リクエストのテスト
* @param {Object} request
* @param request.url
* @param request.method
* @param request.contentType
* @param request.body
* @param html
*/
const assertGeneratePdfRequest = ({url, method, contentType, body}, html) => {
expect(url).toEqual('https://api.docraptor.com/docs');
expect(method).toEqual('POST');
expect(contentType).toEqual('application/json');
const bodyObj = JSON.parse(body);
expect(bodyObj.document_type).toEqual('pdf');
expect(bodyObj.document_content).toEqual(html);
expect(bodyObj.async).toEqual(true);
expect(bodyObj.name)
.toEqual(`Questetra-m${processInstance.getProcessModelInfoId()}-p${processInstance.getProcessInstanceId()}`);
expect(bodyObj.test).toEqual(undefined); // デバッグ実行でなければ、設定されない
};
/**
* PDF 生成の API リクエストでエラー
*/
test('Fail to generate PDF', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('変換元.html', 'text/html', 'aaa'));
prepareConfigs(sourceFiles, false, '変換後.pdf');
httpClient.setRequestHandler((request) => {
assertGeneratePdfRequest(request, 'aaa');
return httpClient.createHttpResponse(400, 'application/json', '{}');
});
assertError(main, 'Failed to post PDF generation request. status: 400');
});
/**
* PDF ファイルを生成する API リクエストのテスト
* @param {Object} request
* @param request.url
* @param request.method
* @param statusId
*/
const assertCheckStatusRequest = ({url, method}, statusId) => {
expect(url).toEqual(`https://docraptor.com/status/${statusId}`);
expect(method).toEqual('GET');
};
/**
* ステータス確認の API リクエストでエラー
*/
test('Fail to get PDF generation status', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('変換元.html', 'text/html', 'aaa'));
prepareConfigs(sourceFiles, false, '変換後.pdf');
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGeneratePdfRequest(request, 'aaa');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{"status_id": "status12345"}');
}
assertCheckStatusRequest(request, 'status12345');
return httpClient.createHttpResponse(400, 'application/json', '{}');
});
assertError(main, 'Failed to get PDF generation status. status: 400');
});
/**
* ステータス確認 API のレスポンスを作成
* @param status
* @param downloadUrl
* @return responseString
*/
const createStatusResponse = (status, downloadUrl) => {
const json = {status};
if (downloadUrl !== null) {
json.download_url = downloadUrl;
}
return JSON.stringify(json);
};
/**
* PDF 生成のステータスが failed
*/
test('PDF generation failed', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('変換元.html', 'text/html', 'aaa'));
const filesDef = prepareConfigs(sourceFiles, false, '変換後.pdf');
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGeneratePdfRequest(request, 'aaa');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{"status_id": "status12345"}');
}
assertCheckStatusRequest(request, 'status12345');
return httpClient.createHttpResponse(200, 'application/json', createStatusResponse('failed', null));
});
assertError(main, 'Failed to generate PDF.');
});
/**
* PDF ファイルをダウンロードする API リクエストのテスト
* @param {Object} request
* @param request.url
* @param request.method
* @param downloadUrl
*/
const assertDownloadRequest = ({url, method}, downloadUrl) => {
expect(url).toEqual(downloadUrl);
expect(method).toEqual('GET');
};
/**
* PDF ファイルをダウンロードする API リクエストでエラー
*/
test('Fail to download PDF file', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('変換元.html', 'text/html', 'aaa'));
prepareConfigs(sourceFiles, false, '変換後.pdf');
let reqCount = 0;
const downloadUrl = 'https://docraptor.com/download/12345';
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGeneratePdfRequest(request, 'aaa');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{"status_id": "status12345"}');
}
if (reqCount === 1) {
assertCheckStatusRequest(request, 'status12345');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', createStatusResponse('completed', downloadUrl));
}
assertDownloadRequest(request, downloadUrl);
return httpClient.createHttpResponse(400, 'application/json', '{}');
});
assertError(main, 'Failed to download PDF file. status: 400');
});
const SAMPLE_HTML = `
PDF 生成のテスト
これは PDF 生成のテストです。
`;
/**
* PDF ファイルのテスト
* @param file
* @param name
* @param body
*/
const assertPdfFile = (file, name, body) => {
expect(file.getName()).toEqual(name);
expect(file.getContentType()).toEqual('application/pdf');
expect(fileRepository.readFile(file, 'UTF-8')).toEqual(body);
};
/**
* 成功
* 変換元ファイルに charset の指定なし
* 他のファイルの保存なし, 他のファイルを削除しない
* main() でダウンロードまで完了
*/
test('Succeed - No charset', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('変換元.html', 'text/html', SAMPLE_HTML));
const filesDef = prepareConfigs(sourceFiles, false, '変換後.pdf');
let reqCount = 0;
const downloadUrl = 'https://docraptor.com/download/12345';
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGeneratePdfRequest(request, SAMPLE_HTML);
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{"status_id": "status12345"}');
}
if (reqCount === 1) {
assertCheckStatusRequest(request, 'status12345');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', createStatusResponse('completed', downloadUrl));
}
assertDownloadRequest(request, downloadUrl);
return httpClient.createHttpResponse(200, 'application/pdf', 'This is the content of PDF');
});
expect(main()).toEqual(undefined);
//ファイルデータ項目の値をチェック
const files = engine.findData(filesDef);
expect(files.size()).toEqual(1);
assertPdfFile(files.get(0), '変換後.pdf', 'This is the content of PDF');
});
/**
* 成功
* 変換元ファイルに charset=Shift_JIS を指定
* 他のファイルの保存あり, 他のファイルを削除しない
* main() でダウンロードまで完了
*/
test('Succeed - Shift_JIS, add file', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('変換元.html', 'text/html; charset=Shift_JIS', SAMPLE_HTML));
const filesDef = prepareConfigs(sourceFiles, false, '変換後');
const existingFiles = new java.util.ArrayList();
existingFiles.add(engine.createQfile('元々添付されているファイル', 'text/plain', 'aaa'));
engine.setData(filesDef, existingFiles);
let reqCount = 0;
const downloadUrl = 'https://docraptor.com/download/23456';
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGeneratePdfRequest(request, SAMPLE_HTML);
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{"status_id": "status23456"}');
}
if (reqCount === 1) {
assertCheckStatusRequest(request, 'status23456');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', createStatusResponse('completed', downloadUrl));
}
assertDownloadRequest(request, downloadUrl);
return httpClient.createHttpResponse(200, 'application/pdf', 'Content of PDF');
});
expect(main()).toEqual(undefined);
//ファイルデータ項目の値をチェック
const files = engine.findData(filesDef);
expect(files.size()).toEqual(2);
expect(files.get(0).getName()).toEqual('元々添付されているファイル');
assertPdfFile(files.get(1), '変換後', 'Content of PDF');
});
/**
* 成功
* 変換元ファイルに charset=UTF-8 を指定
* 他のファイルの保存あり, 他のファイルを削除する
* main() でダウンロードまで完了
*/
test('Succeed - UTF-8, delete other files', () => {
const sourceFiles = new java.util.ArrayList();
sourceFiles.add(engine.createQfile('変換元.html', 'text/html;charset=UTF-8', SAMPLE_HTML));
const filesDef = prepareConfigs(sourceFiles, true, '変換後_.pdf');
const existingFiles = new java.util.ArrayList();
existingFiles.add(engine.createQfile('元々添付されているファイル', 'text/plain', 'aaa'));
engine.setData(filesDef, existingFiles);
let reqCount = 0;
const downloadUrl = 'https://docraptor.com/download/67890';
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGeneratePdfRequest(request, SAMPLE_HTML);
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{"status_id": "status67890"}');
}
if (reqCount === 1) {
assertCheckStatusRequest(request, 'status67890');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', createStatusResponse('completed', downloadUrl));
}
assertDownloadRequest(request, downloadUrl);
return httpClient.createHttpResponse(200, 'application/pdf', 'Content of PDF');
});
expect(main()).toEqual(undefined);
//ファイルデータ項目の値をチェック
const files = engine.findData(filesDef);
expect(files.size()).toEqual(1);
assertPdfFile(files.get(0), '変換後_.pdf', 'Content of PDF');
});
/**
* 成功
* 変換元ファイルに charset の指定なし
* 変換元ファイルが最大サイズ
* 他のファイルの保存あり, 他のファイルを削除しない
* 1 回目の proceed() でダウンロードまで完了
*/
test('Succeed - Max source file size, add file, proceed once', () => {
const sourceFiles = new java.util.ArrayList();
const html = createText(MAX_FILE_SIZE);
sourceFiles.add(engine.createQfile('変換元.html', 'text/html', html));
const filesDef = prepareConfigs(sourceFiles, false, '変換後abc.pdf');
const existingFiles = new java.util.ArrayList();
existingFiles.add(engine.createQfile('元々添付されているファイル', 'text/plain', 'aaa'));
engine.setData(filesDef, existingFiles);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGeneratePdfRequest(request, html);
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{"status_id": "status12345"}');
}
assertCheckStatusRequest(request, 'status12345');
return httpClient.createHttpResponse(200, 'application/json', createStatusResponse('processing', null));
});
expect(main()).toEqual(false);
expect(engine.restoreTemporaryData()).toEqual('status12345');
reqCount = 0;
const downloadUrl = 'https://docraptor.com/download/12345';
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertCheckStatusRequest(request, 'status12345');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', createStatusResponse('completed', downloadUrl));
}
assertDownloadRequest(request, downloadUrl);
return httpClient.createHttpResponse(200, 'application/pdf', 'This is the content of PDF');
});
expect(proceed()).toEqual(undefined);
//ファイルデータ項目の値をチェック
const files = engine.findData(filesDef);
expect(files.size()).toEqual(2);
expect(files.get(0).getName()).toEqual('元々添付されているファイル');
assertPdfFile(files.get(1), '変換後abc.pdf', 'This is the content of PDF');
});
/**
* 設定の準備
* 文字型型データ項目で変換元 HTML を指定する場合
* @param sourceHtml
* @param deleteOtherFiles
* @param fileName
* @return filesDef
*/
const prepareConfigsWithString = (sourceHtml, deleteOtherFiles, fileName) => {
configs.put('conf_Auth', 'DocRaptor');
// 文字型データ項目を作成・設定し、変換元 HTML を保存
const sourceHtmlDef = engine.createDataDefinition('変換元 HTML', 1, 'q_SourceHtml', 'STRING_TEXTAREA');
configs.putObject('conf_SourceHtml', sourceHtmlDef);
engine.setData(sourceHtmlDef, sourceHtml);
// 生成されたファイルを保存する用のファイル型データ項目を作成し、設定
const filesDef = engine.createDataDefinition('ファイル保存先', 2, 'q_Files', 'FILE');
configs.putObject('conf_Files', filesDef);
// 保存時に他のファイルを削除するかどうかを設定
configs.putObject('conf_DeleteOtherFiles', deleteOtherFiles);
// 保存する際のファイル名を設定
configs.put('conf_FileName', fileName);
return filesDef;
};
/**
* 変換元 HTML が空
*/
test('Source HTML is empty.', () => {
prepareConfigsWithString(null, false, '変換後.pdf');
assertError(main, 'Source HTML is empty.');
});
/**
* 成功
* 変換元 HTML を文字型データ項目で指定
* 他のファイルの保存あり, 他のファイルを削除する
* 2 回目の proceed() でダウンロードまで完了
*/
test('Succeed - String source, delete other files, proceed twice', () => {
const filesDef = prepareConfigsWithString(SAMPLE_HTML, true, '変換後def.pdf');
const existingFiles = new java.util.ArrayList();
existingFiles.add(engine.createQfile('元々添付されているファイル', 'text/plain', 'aaa'));
engine.setData(filesDef, existingFiles);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertGeneratePdfRequest(request, SAMPLE_HTML);
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', '{"status_id": "status67890"}');
}
assertCheckStatusRequest(request, 'status67890');
return httpClient.createHttpResponse(200, 'application/json', createStatusResponse('processing', null));
});
expect(main()).toEqual(false);
expect(engine.restoreTemporaryData()).toEqual('status67890');
reqCount = 0;
const downloadUrl = 'https://docraptor.com/download/67890';
httpClient.setRequestHandler((request) => {
if (reqCount === 0) {
assertCheckStatusRequest(request, 'status67890');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', createStatusResponse('processing', null));
}
if (reqCount === 1) {
assertCheckStatusRequest(request, 'status67890');
reqCount++;
return httpClient.createHttpResponse(200, 'application/json', createStatusResponse('completed', downloadUrl));
}
assertDownloadRequest(request, downloadUrl);
return httpClient.createHttpResponse(200, 'application/pdf', 'This is the content of PDF');
});
expect(proceed()).toEqual(false);
expect(engine.restoreTemporaryData()).toEqual('status67890');
expect(proceed()).toEqual(undefined);
//ファイルデータ項目の値をチェック
const files = engine.findData(filesDef);
expect(files.size()).toEqual(1);
assertPdfFile(files.get(0), '変換後def.pdf', 'This is the content of PDF');
});
]]>