[feature] Ignore encrypted changes

This commit is contained in:
Sergey Konovalov
2018-07-02 17:32:31 +03:00
parent 1f0f46e693
commit c2b0e1acf1
3 changed files with 213 additions and 197 deletions

View File

@ -830,7 +830,7 @@ function handleDeadLetter(data) {
* @param callback
* @param baseUrl
*/
function* sendStatusDocument(docId, bChangeBase, userAction, callback, baseUrl, opt_userData) {
function* sendStatusDocument(docId, bChangeBase, userAction, callback, baseUrl, opt_userData, opt_forceClose) {
if (!callback) {
var getRes = yield* getCallback(docId);
if (getRes) {
@ -848,7 +848,7 @@ function* sendStatusDocument(docId, bChangeBase, userAction, callback, baseUrl,
var participants = yield* getOriginalParticipantsId(docId);
if (0 === participants.length) {
var puckerIndex = yield* getChangesIndex(docId);
if (!(puckerIndex > 0)) {
if (!(puckerIndex > 0) || opt_forceClose) {
status = c_oAscServerStatus.Closed;
}
}
@ -1000,10 +1000,10 @@ function* cleanDocumentOnExit(docId, deleteChanges) {
yield storage.deletePath(cfgForgottenFiles + '/' + docId);
}
}
function* cleanDocumentOnExitNoChanges(docId, opt_userId) {
function* cleanDocumentOnExitNoChanges(docId, opt_userId, opt_forceClose) {
var userAction = opt_userId ? new commonDefines.OutputAction(commonDefines.c_oAscUserAction.Out, opt_userId) : null;
// Отправляем, что все ушли и нет изменений (чтобы выставить статус на сервере об окончании редактирования)
yield* sendStatusDocument(docId, c_oAscChangeBase.No, userAction);
yield* sendStatusDocument(docId, c_oAscChangeBase.No, userAction, undefined, undefined, undefined, opt_forceClose);
//если пользователь зашел в документ, соединение порвалось, на сервере удалилась вся информация,
//при восстановлении соединения userIndex сохранится и он совпадет с userIndex следующего пользователя
yield* cleanDocumentOnExit(docId, false);

View File

@ -328,35 +328,35 @@ function* commandOpen(conn, cmd, outputData, opt_upsertRes, opt_bIsRestore) {
task.statusInfo = constants.NO_ERROR;
let updateIfRes = yield taskResult.updateIf(task, updateMask);
if (updateIfRes.affectedRows > 0) {
let forgottenId = cfgForgottenFiles + '/' + cmd.getDocId();
let forgotten = yield storage.listObjects(forgottenId);
//replace url with forgotten file because it absorbed all lost changes
if (forgotten.length > 0) {
logger.debug("commandOpen from forgotten: docId = %s", cmd.getDocId());
cmd.setUrl(undefined);
cmd.setForgotten(forgottenId);
if (updateIfRes.affectedRows > 0) {
let forgottenId = cfgForgottenFiles + '/' + cmd.getDocId();
let forgotten = yield storage.listObjects(forgottenId);
//replace url with forgotten file because it absorbed all lost changes
if (forgotten.length > 0) {
logger.debug("commandOpen from forgotten: docId = %s", cmd.getDocId());
cmd.setUrl(undefined);
cmd.setForgotten(forgottenId);
}
//add task
cmd.setOutputFormat(constants.AVS_OFFICESTUDIO_FILE_CANVAS);
cmd.setEmbeddedFonts(false);
var dataQueue = new commonDefines.TaskQueueData();
dataQueue.setCmd(cmd);
dataQueue.setToFile('Editor.bin');
var priority = constants.QUEUE_PRIORITY_HIGH;
var formatIn = formatChecker.getFormatFromString(cmd.getFormat());
//decrease pdf, djvu, xps convert priority becase long open time
if (constants.AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_PDF === formatIn ||
constants.AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_DJVU === formatIn ||
constants.AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_XPS === formatIn) {
priority = constants.QUEUE_PRIORITY_LOW;
}
yield* docsCoServer.addTask(dataQueue, priority);
} else {
yield* commandOpenFillOutput(conn, cmd, outputData, opt_bIsRestore);
}
//add task
cmd.setOutputFormat(constants.AVS_OFFICESTUDIO_FILE_CANVAS);
cmd.setEmbeddedFonts(false);
var dataQueue = new commonDefines.TaskQueueData();
dataQueue.setCmd(cmd);
dataQueue.setToFile('Editor.bin');
var priority = constants.QUEUE_PRIORITY_HIGH;
var formatIn = formatChecker.getFormatFromString(cmd.getFormat());
//decrease pdf, djvu, xps convert priority becase long open time
if (constants.AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_PDF === formatIn ||
constants.AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_DJVU === formatIn ||
constants.AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_XPS === formatIn) {
priority = constants.QUEUE_PRIORITY_LOW;
}
yield* docsCoServer.addTask(dataQueue, priority);
} else {
yield* commandOpenFillOutput(conn, cmd, outputData, opt_bIsRestore);
}
}
}
function* commandOpenFillOutput(conn, cmd, outputData, opt_bIsRestore) {
let needAddTask = false;
let selectRes = yield taskResult.select(cmd.getDocId());
@ -640,191 +640,197 @@ function checkAuthorizationLength(authorization, data){
function* commandSfcCallback(cmd, isSfcm, isEncrypted) {
var docId = cmd.getDocId();
logger.debug('Start commandSfcCallback: docId = %s', docId);
var saveKey = cmd.getSaveKey();
var statusInfo = cmd.getStatusInfo();
var isError = constants.NO_ERROR != statusInfo;
var isErrorCorrupted = constants.CONVERT_CORRUPTED == statusInfo;
var savePathDoc = saveKey + '/' + cmd.getOutputPath();
var savePathChanges = saveKey + '/changes.zip';
var savePathHistory = saveKey + '/changesHistory.json';
var getRes = yield* docsCoServer.getCallback(docId);
var forceSave = cmd.getForceSave();
var forceSaveType = forceSave ? forceSave.getType() : commonDefines.c_oAscForceSaveTypes.Command;
var isSfcmSuccess = false;
let storeForgotten = false;
let replyStr;
var statusOk;
var statusErr;
if (isSfcm) {
statusOk = docsCoServer.c_oAscServerStatus.MustSaveForce;
statusErr = docsCoServer.c_oAscServerStatus.CorruptedForce;
} else {
statusOk = docsCoServer.c_oAscServerStatus.MustSave;
statusErr = docsCoServer.c_oAscServerStatus.Corrupted;
}
let updateMask = new taskResult.TaskResultData();
updateMask.key = docId;
if (!isEncrypted) {
updateMask.status = taskResult.FileStatus.SaveVersion;
updateMask.statusInfo = cmd.getData();
} else {
updateMask.status = taskResult.FileStatus.Ok;
}
let updateIfTask = new taskResult.TaskResultData();
updateIfTask.status = taskResult.FileStatus.UpdateVersion;
updateIfTask.statusInfo = Math.floor(Date.now() / 60000);//minutes
let updateIfRes;
if (getRes) {
logger.debug('Callback commandSfcCallback: docId = %s callback = %s', docId, getRes.server.href);
var outputSfc = new commonDefines.OutputSfcData();
outputSfc.setKey(docId);
outputSfc.setEncrypted(isEncrypted);
var users = [];
let isOpenFromForgotten = false;
//setUserId - set from changes in convert
//setUserActionId - used in case of save without changes(forgotten files)
let userLastChangeId = cmd.getUserId() || cmd.getUserActionId();
if (userLastChangeId) {
users.push(userLastChangeId);
}
outputSfc.setUsers(users);
if (!isSfcm) {
var actions = [];
//use UserId case UserActionId miss in gc convertion
var userActionId = cmd.getUserActionId() || cmd.getUserId();
if (userActionId) {
actions.push(new commonDefines.OutputAction(commonDefines.c_oAscUserAction.Out, userActionId));
}
outputSfc.setActions(actions);
}
outputSfc.setUserData(cmd.getUserData());
if (!isError || isErrorCorrupted) {
try {
let forgottenId = cfgForgottenFiles + '/' + docId;
let forgotten = yield storage.listObjects(forgottenId);
let isSendHistory = 0 === forgotten.length;
if (!isSendHistory) {
//check indicator file to determine if opening was from the forgotten file
var forgottenMarkPath = docId + '/' + cfgForgottenFilesName + '.txt';
var forgottenMark = yield storage.listObjects(forgottenMarkPath);
isOpenFromForgotten = 0 !== forgottenMark.length;
isSendHistory = !isOpenFromForgotten;
logger.debug('commandSfcCallback forgotten no empty: docId = %s isSendHistory = %s', docId, isSendHistory);
}
if (isSendHistory && !isEncrypted) {
//don't send history info because changes isn't from file in storage
var data = yield storage.getObject(savePathHistory);
outputSfc.setChangeHistory(JSON.parse(data.toString('utf-8')));
let changeUrl = yield storage.getSignedUrl(getRes.baseUrl, savePathChanges,
commonDefines.c_oAscUrlTypes.Temporary);
outputSfc.setChangeUrl(changeUrl);
} else {
//for backward compatibility. remove this when Community is ready
outputSfc.setChangeHistory({});
}
let url = yield storage.getSignedUrl(getRes.baseUrl, savePathDoc, commonDefines.c_oAscUrlTypes.Temporary);
outputSfc.setUrl(url);
} catch (e) {
logger.error('Error commandSfcCallback: docId = %s\r\n%s', docId, e.stack);
}
if (outputSfc.getUrl() && outputSfc.getUsers().length > 0) {
outputSfc.setStatus(statusOk);
} else {
isError = true;
}
}
if (isError) {
outputSfc.setStatus(statusErr);
}
var uri = getRes.server.href;
if (constants.EDITOR_CHANGES !== statusInfo || isSfcm) {
var saveKey = cmd.getSaveKey();
var isError = constants.NO_ERROR != statusInfo;
var isErrorCorrupted = constants.CONVERT_CORRUPTED == statusInfo;
var savePathDoc = saveKey + '/' + cmd.getOutputPath();
var savePathChanges = saveKey + '/changes.zip';
var savePathHistory = saveKey + '/changesHistory.json';
var getRes = yield* docsCoServer.getCallback(docId);
var forceSave = cmd.getForceSave();
var forceSaveType = forceSave ? forceSave.getType() : commonDefines.c_oAscForceSaveTypes.Command;
var isSfcmSuccess = false;
let storeForgotten = false;
var statusOk;
var statusErr;
if (isSfcm) {
var selectRes = yield taskResult.select(docId);
var row = selectRes.length > 0 ? selectRes[0] : null;
//send only if FileStatus.Ok to prevent forcesave after final save
if (row && row.status == taskResult.FileStatus.Ok) {
if (forceSave) {
outputSfc.setForceSaveType(forceSaveType);
outputSfc.setLastSave(new Date(forceSave.getTime()).toISOString());
statusOk = docsCoServer.c_oAscServerStatus.MustSaveForce;
statusErr = docsCoServer.c_oAscServerStatus.CorruptedForce;
} else {
statusOk = docsCoServer.c_oAscServerStatus.MustSave;
statusErr = docsCoServer.c_oAscServerStatus.Corrupted;
}
let updateMask = new taskResult.TaskResultData();
updateMask.key = docId;
if (!isEncrypted) {
updateMask.status = taskResult.FileStatus.SaveVersion;
updateMask.statusInfo = cmd.getData();
} else {
updateMask.status = taskResult.FileStatus.Ok;
}
let updateIfTask = new taskResult.TaskResultData();
updateIfTask.status = taskResult.FileStatus.UpdateVersion;
updateIfTask.statusInfo = Math.floor(Date.now() / 60000);//minutes
let updateIfRes;
if (getRes) {
logger.debug('Callback commandSfcCallback: docId = %s callback = %s', docId, getRes.server.href);
var outputSfc = new commonDefines.OutputSfcData();
outputSfc.setKey(docId);
outputSfc.setEncrypted(isEncrypted);
var users = [];
let isOpenFromForgotten = false;
//setUserId - set from changes in convert
//setUserActionId - used in case of save without changes(forgotten files)
let userLastChangeId = cmd.getUserId() || cmd.getUserActionId();
if (userLastChangeId) {
users.push(userLastChangeId);
}
outputSfc.setUsers(users);
if (!isSfcm) {
var actions = [];
//use UserId case UserActionId miss in gc convertion
var userActionId = cmd.getUserActionId() || cmd.getUserId();
if (userActionId) {
actions.push(new commonDefines.OutputAction(commonDefines.c_oAscUserAction.Out, userActionId));
}
outputSfc.setActions(actions);
}
outputSfc.setUserData(cmd.getUserData());
if (!isError || isErrorCorrupted) {
try {
replyStr = yield* docsCoServer.sendServerRequest(docId, uri, outputSfc, checkAuthorizationLength);
let replyData = docsCoServer.parseReplyData(docId, replyStr);
isSfcmSuccess = replyData && commonDefines.c_oAscServerCommandErrors.NoError == replyData.error;
} catch (err) {
logger.error('sendServerRequest error: docId = %s;url = %s;data = %j\r\n%s', docId, uri, outputSfc, err.stack);
let forgottenId = cfgForgottenFiles + '/' + docId;
let forgotten = yield storage.listObjects(forgottenId);
let isSendHistory = 0 === forgotten.length;
if (!isSendHistory) {
//check indicator file to determine if opening was from the forgotten file
var forgottenMarkPath = docId + '/' + cfgForgottenFilesName + '.txt';
var forgottenMark = yield storage.listObjects(forgottenMarkPath);
isOpenFromForgotten = 0 !== forgottenMark.length;
isSendHistory = !isOpenFromForgotten;
logger.debug('commandSfcCallback forgotten no empty: docId = %s isSendHistory = %s', docId, isSendHistory);
}
if (isSendHistory && !isEncrypted) {
//don't send history info because changes isn't from file in storage
var data = yield storage.getObject(savePathHistory);
outputSfc.setChangeHistory(JSON.parse(data.toString('utf-8')));
let changeUrl = yield storage.getSignedUrl(getRes.baseUrl, savePathChanges,
commonDefines.c_oAscUrlTypes.Temporary);
outputSfc.setChangeUrl(changeUrl);
} else {
//for backward compatibility. remove this when Community is ready
outputSfc.setChangeHistory({});
}
let url = yield storage.getSignedUrl(getRes.baseUrl, savePathDoc, commonDefines.c_oAscUrlTypes.Temporary);
outputSfc.setUrl(url);
} catch (e) {
logger.error('Error commandSfcCallback: docId = %s\r\n%s', docId, e.stack);
}
if (outputSfc.getUrl() && outputSfc.getUsers().length > 0) {
outputSfc.setStatus(statusOk);
} else {
isError = true;
}
}
} else {
//if anybody in document stop save
let editorsCount = yield docsCoServer.getEditorsCountPromise(docId);
logger.debug('commandSfcCallback presence: docId = %s count = %d', docId, editorsCount);
if (0 === editorsCount || (isEncrypted && 1 === editorsCount)) {
let lastSave = yield* docsCoServer.getLastSave(docId);
let notModified = yield* docsCoServer.getLastForceSave(docId, lastSave);
var lastSaveDate = lastSave ? new Date(lastSave.time) : new Date();
outputSfc.setLastSave(lastSaveDate.toISOString());
outputSfc.setNotModified(notModified);
updateIfRes = yield taskResult.updateIf(updateIfTask, updateMask);
if (updateIfRes.affectedRows > 0) {
updateMask.status = updateIfTask.status;
updateMask.statusInfo = updateIfTask.statusInfo;
if (isError) {
outputSfc.setStatus(statusErr);
}
var uri = getRes.server.href;
if (isSfcm) {
var selectRes = yield taskResult.select(docId);
var row = selectRes.length > 0 ? selectRes[0] : null;
//send only if FileStatus.Ok to prevent forcesave after final save
if (row && row.status == taskResult.FileStatus.Ok) {
if (forceSave) {
outputSfc.setForceSaveType(forceSaveType);
outputSfc.setLastSave(new Date(forceSave.getTime()).toISOString());
}
try {
replyStr = yield* docsCoServer.sendServerRequest(docId, uri, outputSfc, checkAuthorizationLength);
let replyData = docsCoServer.parseReplyData(docId, replyStr);
isSfcmSuccess = replyData && commonDefines.c_oAscServerCommandErrors.NoError == replyData.error;
} catch (err) {
logger.error('sendServerRequest error: docId = %s;url = %s;data = %j\r\n%s', docId, uri, outputSfc, err.stack);
}
var requestRes = false;
var replyData = docsCoServer.parseReplyData(docId, replyStr);
if (replyData && commonDefines.c_oAscServerCommandErrors.NoError == replyData.error) {
//в случае comunity server придет запрос в CommandService проверяем результат
var multi = redisClient.multi([
['get', redisKeySaved + docId],
['del', redisKeySaved + docId]
]);
var execRes = yield utils.promiseRedis(multi, multi.exec);
var savedVal = execRes[0];
requestRes = (null == savedVal || '1' === savedVal);
}
if (requestRes) {
updateIfTask = undefined;
yield docsCoServer.cleanDocumentOnExitPromise(docId, true);
if (isOpenFromForgotten) {
//remove forgotten file in cache
yield cleanupCache(docId);
}
} else {
//if anybody in document stop save
let editorsCount = yield docsCoServer.getEditorsCountPromise(docId);
logger.debug('commandSfcCallback presence: docId = %s count = %d', docId, editorsCount);
if (0 === editorsCount || (isEncrypted && 1 === editorsCount)) {
let lastSave = yield* docsCoServer.getLastSave(docId);
let notModified = yield* docsCoServer.getLastForceSave(docId, lastSave);
var lastSaveDate = lastSave ? new Date(lastSave.time) : new Date();
outputSfc.setLastSave(lastSaveDate.toISOString());
outputSfc.setNotModified(notModified);
updateIfRes = yield taskResult.updateIf(updateIfTask, updateMask);
if (updateIfRes.affectedRows > 0) {
updateMask.status = updateIfTask.status;
updateMask.statusInfo = updateIfTask.statusInfo;
try {
replyStr = yield* docsCoServer.sendServerRequest(docId, uri, outputSfc, checkAuthorizationLength);
} catch (err) {
logger.error('sendServerRequest error: docId = %s;url = %s;data = %j\r\n%s', docId, uri, outputSfc, err.stack);
}
var requestRes = false;
var replyData = docsCoServer.parseReplyData(docId, replyStr);
if (replyData && commonDefines.c_oAscServerCommandErrors.NoError == replyData.error) {
//в случае comunity server придет запрос в CommandService проверяем результат
var multi = redisClient.multi([
['get', redisKeySaved + docId],
['del', redisKeySaved + docId]
]);
var execRes = yield utils.promiseRedis(multi, multi.exec);
var savedVal = execRes[0];
requestRes = (null == savedVal || '1' === savedVal);
}
if (requestRes) {
updateIfTask = undefined;
yield docsCoServer.cleanDocumentOnExitPromise(docId, true);
if (isOpenFromForgotten) {
//remove forgotten file in cache
yield cleanupCache(docId);
}
} else {
storeForgotten = true;
}
} else {
storeForgotten = true;
updateIfTask = undefined;
}
} else {
updateIfTask = undefined;
}
}
} else {
logger.warn('Empty Callback commandSfcCallback: docId = %s', docId);
storeForgotten = true;
}
if (undefined !== updateIfTask && !isSfcm) {
logger.debug('commandSfcCallback restore FileStatus.Ok status: docId = %s', docId);
updateIfTask.status = taskResult.FileStatus.Ok;
updateIfTask.statusInfo = constants.NO_ERROR;
updateIfRes = yield taskResult.updateIf(updateIfTask, updateMask);
if (!(updateIfRes.affectedRows > 0)) {
logger.debug('commandSfcCallback restore FileStatus.Ok status failed: docId = %s', docId);
}
}
if (storeForgotten && (!isError || isErrorCorrupted)) {
try {
logger.debug("storeForgotten: docId = %s", docId);
let forgottenName = cfgForgottenFilesName + pathModule.extname(cmd.getOutputPath());
yield storage.copyObject(savePathDoc, cfgForgottenFiles + '/' + docId + '/' + forgottenName);
} catch (err) {
logger.error('Error storeForgotten: docId = %s\r\n%s', docId, err.stack);
}
}
if (forceSave) {
yield* docsCoServer.setForceSave(docId, forceSave, cmd, isSfcmSuccess && !isError);
}
} else {
logger.warn('Empty Callback commandSfcCallback: docId = %s', docId);
storeForgotten = true;
}
if (undefined !== updateIfTask && !isSfcm) {
logger.debug('commandSfcCallback restore FileStatus.Ok status: docId = %s', docId);
updateIfTask.status = taskResult.FileStatus.Ok;
updateIfTask.statusInfo = constants.NO_ERROR;
updateIfRes = yield taskResult.updateIf(updateIfTask, updateMask);
if (!(updateIfRes.affectedRows > 0)) {
logger.debug('commandSfcCallback restore FileStatus.Ok status failed: docId = %s', docId);
}
}
if (storeForgotten && (!isError || isErrorCorrupted)) {
try {
logger.debug("storeForgotten: docId = %s", docId);
let forgottenName = cfgForgottenFilesName + pathModule.extname(cmd.getOutputPath());
yield storage.copyObject(savePathDoc, cfgForgottenFiles + '/' + docId + '/' + forgottenName);
} catch (err) {
logger.error('Error storeForgotten: docId = %s\r\n%s', docId, err.stack);
}
}
if (forceSave) {
yield* docsCoServer.setForceSave(docId, forceSave, cmd, isSfcmSuccess && !isError);
logger.debug('commandSfcCallback cleanDocumentOnExitNoChangesPromise: docId = %s', docId);
yield docsCoServer.cleanDocumentOnExitNoChangesPromise(docId, undefined, true);
}
if ((docsCoServer.getIsShutdown() && !isSfcm) || cmd.getRedisKey()) {
let keyRedis = cmd.getRedisKey() ? cmd.getRedisKey() : redisKeyShutdown;
yield utils.promiseRedis(redisClient, redisClient.srem, keyRedis, docId);

View File

@ -307,6 +307,7 @@ function* downloadFileFromStorage(id, strPath, dir) {
}
}
function* processDownloadFromStorage(dataConvert, cmd, task, tempDirs) {
let res = constants.NO_ERROR;
let needConcatFiles = false;
if (task.getFromOrigin() || task.getFromSettings()) {
dataConvert.fileFrom = path.join(tempDirs.source, 'origin.' + cmd.getFormat());
@ -326,8 +327,9 @@ function* processDownloadFromStorage(dataConvert, cmd, task, tempDirs) {
yield* concatFiles(tempDirs.source);
}
if (task.getFromChanges()) {
yield* processChanges(tempDirs, cmd);
res = yield* processChanges(tempDirs, cmd);
}
return res;
}
function* concatFiles(source) {
@ -356,6 +358,7 @@ function* concatFiles(source) {
}
function* processChanges(tempDirs, cmd) {
let res = constants.NO_ERROR;
let changesDir = path.join(tempDirs.source, 'changes');
fs.mkdirSync(changesDir);
let indexFile = 0;
@ -378,6 +381,12 @@ function* processChanges(tempDirs, cmd) {
let changes = yield baseConnector.getChangesPromise(cmd.getDocId(), curIndexStart, curIndexEnd, forceSaveTime);
for (let i = 0; i < changes.length; ++i) {
let change = changes[i];
if (change.change_data.startsWith('ENCRYPTED;')) {
logger.warn('processChanges encrypted changes (id=%s)', cmd.getDocId());
//todo sql request instead?
res = constants.EDITOR_CHANGES;
break;
}
if (null === changesAuthor || changesAuthor !== change.user_id_original) {
if (null !== changesAuthor) {
yield* streamEnd(streamObj, ']');
@ -406,6 +415,7 @@ function* processChanges(tempDirs, cmd) {
}
cmd.setUserId(changesAuthor);
fs.writeFileSync(path.join(tempDirs.result, 'changesHistory.json'), JSON.stringify(changesHistory), 'utf8');
return res;
}
function* streamCreate(docId, changesDir, indexFile, opt_options) {
@ -576,7 +586,7 @@ function* ExecuteTask(task) {
clientStatsD.timing('conv.downloadFileFromStorage', new Date() - curDate);
curDate = new Date();
}
yield* processDownloadFromStorage(dataConvert, cmd, task, tempDirs);
error = yield* processDownloadFromStorage(dataConvert, cmd, task, tempDirs);
} else if (cmd.getForgotten()) {
yield* downloadFileFromStorage(cmd.getDocId(), cmd.getForgotten(), tempDirs.source);
logger.debug('downloadFileFromStorage complete(id=%s)', dataConvert.key);