mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
All: Improves deletion fail-safe so it is based on percentage of notes deleted. And display warning on sidebar.
This commit is contained in:
parent
1836b9a0b0
commit
c9098b0489
@ -10,7 +10,7 @@ rsync -a "$ROOT_DIR/../ReactNativeClient/locales/" "$BUILD_DIR/locales/"
|
|||||||
mkdir -p "$BUILD_DIR/data"
|
mkdir -p "$BUILD_DIR/data"
|
||||||
|
|
||||||
if [[ $TEST_FILE != "" ]]; then
|
if [[ $TEST_FILE != "" ]]; then
|
||||||
(cd "$ROOT_DIR" && npm test tests-build/$TEST_FILE.js)
|
(cd "$ROOT_DIR" && NODE_ENV=testing npm test tests-build/$TEST_FILE.js)
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -21,27 +21,27 @@ function finish {
|
|||||||
trap finish EXIT
|
trap finish EXIT
|
||||||
|
|
||||||
cd "$ROOT_DIR"
|
cd "$ROOT_DIR"
|
||||||
npm test tests-build/ArrayUtils.js && \
|
NODE_ENV=testing npm test tests-build/ArrayUtils.js && \
|
||||||
npm test tests-build/encryption.js && \
|
NODE_ENV=testing npm test tests-build/encryption.js && \
|
||||||
npm test tests-build/EnexToMd.js && \
|
NODE_ENV=testing npm test tests-build/EnexToMd.js && \
|
||||||
npm test tests-build/HtmlToMd.js && \
|
NODE_ENV=testing npm test tests-build/HtmlToMd.js && \
|
||||||
npm test tests-build/markdownUtils.js && \
|
NODE_ENV=testing npm test tests-build/markdownUtils.js && \
|
||||||
npm test tests-build/models_BaseItem.js && \
|
NODE_ENV=testing npm test tests-build/models_BaseItem.js && \
|
||||||
npm test tests-build/models_Folder.js && \
|
NODE_ENV=testing npm test tests-build/models_Folder.js && \
|
||||||
npm test tests-build/models_ItemChange.js && \
|
NODE_ENV=testing npm test tests-build/models_ItemChange.js && \
|
||||||
npm test tests-build/models_Note.js && \
|
NODE_ENV=testing npm test tests-build/models_Note.js && \
|
||||||
npm test tests-build/models_Resource.js && \
|
NODE_ENV=testing npm test tests-build/models_Resource.js && \
|
||||||
npm test tests-build/models_Revision.js && \
|
NODE_ENV=testing npm test tests-build/models_Revision.js && \
|
||||||
npm test tests-build/models_Setting.js && \
|
NODE_ENV=testing npm test tests-build/models_Setting.js && \
|
||||||
npm test tests-build/models_Tag.js && \
|
NODE_ENV=testing npm test tests-build/models_Tag.js && \
|
||||||
npm test tests-build/pathUtils.js && \
|
NODE_ENV=testing npm test tests-build/pathUtils.js && \
|
||||||
npm test tests-build/services_InteropService.js && \
|
NODE_ENV=testing npm test tests-build/services_InteropService.js && \
|
||||||
npm test tests-build/services_KvStore.js && \
|
NODE_ENV=testing npm test tests-build/services_KvStore.js && \
|
||||||
npm test tests-build/services_ResourceService.js && \
|
NODE_ENV=testing npm test tests-build/services_ResourceService.js && \
|
||||||
npm test tests-build/services_rest_Api.js && \
|
NODE_ENV=testing npm test tests-build/services_rest_Api.js && \
|
||||||
npm test tests-build/services_SearchEngine.js && \
|
NODE_ENV=testing npm test tests-build/services_SearchEngine.js && \
|
||||||
npm test tests-build/services_Revision.js && \
|
NODE_ENV=testing npm test tests-build/services_Revision.js && \
|
||||||
npm test tests-build/StringUtils.js && \
|
NODE_ENV=testing npm test tests-build/StringUtils.js && \
|
||||||
npm test tests-build/TaskQueue.js && \
|
NODE_ENV=testing npm test tests-build/TaskQueue.js && \
|
||||||
npm test tests-build/synchronizer.js && \
|
NODE_ENV=testing npm test tests-build/synchronizer.js && \
|
||||||
npm test tests-build/urlUtils.js
|
NODE_ENV=testing npm test tests-build/urlUtils.js
|
@ -1481,19 +1481,27 @@ describe('Synchronizer', function() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should not wipe out user data when syncing with an empty target', asyncTest(async () => {
|
it('should not wipe out user data when syncing with an empty target', asyncTest(async () => {
|
||||||
await Note.save({ title: 'ma note' });
|
for (let i = 0; i < 10; i++) await Note.save({ title: 'note' });
|
||||||
await Note.save({ title: 'mon autre note' });
|
|
||||||
await Note.save({ title: 'ma troisième note' });
|
|
||||||
|
|
||||||
Setting.setValue('sync.wipeOutFailSafe', true);
|
Setting.setValue('sync.wipeOutFailSafe', true);
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
await fileApi().clearRoot(); // oops
|
await fileApi().clearRoot(); // oops
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
expect((await Note.all()).length).toBe(3); // but since the fail-safe if on, the notes have not been deleted
|
expect((await Note.all()).length).toBe(10); // but since the fail-safe if on, the notes have not been deleted
|
||||||
|
|
||||||
Setting.setValue('sync.wipeOutFailSafe', false); // Now switch it off
|
Setting.setValue('sync.wipeOutFailSafe', false); // Now switch it off
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
expect((await Note.all()).length).toBe(0); // Since the fail-safe was off, the data has been cleared
|
expect((await Note.all()).length).toBe(0); // Since the fail-safe was off, the data has been cleared
|
||||||
|
|
||||||
|
// Handle case where the sync target has been wiped out, then the user creates one note and sync.
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i++) await Note.save({ title: 'note' });
|
||||||
|
Setting.setValue('sync.wipeOutFailSafe', true);
|
||||||
|
await synchronizer().start();
|
||||||
|
await fileApi().clearRoot();
|
||||||
|
await Note.save({ title: 'ma note encore' });
|
||||||
|
await synchronizer().start();
|
||||||
|
expect((await Note.all()).length).toBe(11);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -5,6 +5,7 @@ const BaseItem = require('lib/models/BaseItem.js');
|
|||||||
const JoplinError = require('lib/JoplinError');
|
const JoplinError = require('lib/JoplinError');
|
||||||
const ArrayUtils = require('lib/ArrayUtils');
|
const ArrayUtils = require('lib/ArrayUtils');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
|
const { sprintf } = require('sprintf-js');
|
||||||
|
|
||||||
function requestCanBeRepeated(error) {
|
function requestCanBeRepeated(error) {
|
||||||
const errorCode = typeof error === 'object' && error.code ? error.code : null;
|
const errorCode = typeof error === 'object' && error.code ? error.code : null;
|
||||||
@ -262,13 +263,6 @@ async function basicDelta(path, getDirStatFn, options) {
|
|||||||
});
|
});
|
||||||
newContext.statIdsCache = newContext.statsCache.filter(item => BaseItem.isSystemPath(item.path)).map(item => BaseItem.pathToId(item.path));
|
newContext.statIdsCache = newContext.statsCache.filter(item => BaseItem.isSystemPath(item.path)).map(item => BaseItem.pathToId(item.path));
|
||||||
newContext.statIdsCache.sort(); // Items must be sorted to use binary search below
|
newContext.statIdsCache.sort(); // Items must be sorted to use binary search below
|
||||||
|
|
||||||
// At this point statIdsCache contains the list of all the item IDs on the sync target.
|
|
||||||
// If it's empty, it's most likely a configuration error or bug. For example, if the
|
|
||||||
// user moves their Nextcloud directory, or if a network drive gets disconnected and
|
|
||||||
// returns an empty dir instead of an error. In that case, we don't wipe out the user
|
|
||||||
// data, unless they have switched off the fail-safe.
|
|
||||||
if (options.wipeOutFailSafe && !newContext.statIdsCache.length) throw new JoplinError('Fail-safe: The delta operation was interrupted because the sync target appears to be empty. To override this behaviour disable the "Wipe out fail-safe" option in the settings.', 'failSafe');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = [];
|
let output = [];
|
||||||
@ -322,6 +316,15 @@ async function basicDelta(path, getDirStatFn, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const percentDeleted = itemIds.length ? deletedItems.length / itemIds.length : 0;
|
||||||
|
|
||||||
|
// If more than 90% of the notes are going to be deleted, it's most likely a
|
||||||
|
// configuration error or bug. For example, if the user moves their Nextcloud
|
||||||
|
// directory, or if a network drive gets disconnected and returns an empty dir
|
||||||
|
// instead of an error. In that case, we don't wipe out the user data, unless
|
||||||
|
// they have switched off the fail-safe.
|
||||||
|
if (options.wipeOutFailSafe && percentDeleted >= 0.90) throw new JoplinError(sprintf('Fail-safe: Sync was interrupted because %d%% of the data (%d items) is about to be deleted. To override this behaviour disable the fail-safe in the sync settings.', Math.round(percentDeleted * 100), deletedItems.length), 'failSafe');
|
||||||
|
|
||||||
output = output.concat(deletedItems);
|
output = output.concat(deletedItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,6 +362,10 @@ function shimInit() {
|
|||||||
bridge().openExternal(url);
|
bridge().openExternal(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
shim.isTestingEnv = () => {
|
||||||
|
return process.env.NODE_ENV === 'testing';
|
||||||
|
};
|
||||||
|
|
||||||
shim.waitForFrame = () => {};
|
shim.waitForFrame = () => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,4 +198,8 @@ shim.loadCssFromJs = name => {
|
|||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
shim.isTestingEnv = () => {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = { shim };
|
module.exports = { shim };
|
||||||
|
@ -715,7 +715,11 @@ class Synchronizer {
|
|||||||
// Or it's a temporary issue that will be resolved on next sync.
|
// Or it's a temporary issue that will be resolved on next sync.
|
||||||
this.logger().info(error.message);
|
this.logger().info(error.message);
|
||||||
|
|
||||||
if (error.code === 'failSafe') this.logLastRequests();
|
if (error.code === 'failSafe') {
|
||||||
|
// Get the message to display on UI, but not in testing to avoid poluting stdout
|
||||||
|
if (!shim.isTestingEnv()) this.progressReport_.errors.push(error.message);
|
||||||
|
this.logLastRequests();
|
||||||
|
}
|
||||||
} else if (error.code === 'unknownItemType') {
|
} else if (error.code === 'unknownItemType') {
|
||||||
this.progressReport_.errors.push(_('Unknown item type downloaded - please upgrade Joplin to the latest version'));
|
this.progressReport_.errors.push(_('Unknown item type downloaded - please upgrade Joplin to the latest version'));
|
||||||
this.logger().error(error);
|
this.logger().error(error);
|
||||||
|
Loading…
Reference in New Issue
Block a user