mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
All: Handle case where resource blob is missing during sync
This commit is contained in:
parent
e355f4e49b
commit
86eee376bb
@ -683,12 +683,12 @@ describe('Synchronizer', function() {
|
|||||||
|
|
||||||
await switchClient(2);
|
await switchClient(2);
|
||||||
|
|
||||||
synchronizer().debugFlags_ = ['cancelDeltaLoop2'];
|
synchronizer().testingHooks_ = ['cancelDeltaLoop2'];
|
||||||
let context = await synchronizer().start();
|
let context = await synchronizer().start();
|
||||||
let notes = await Note.all();
|
let notes = await Note.all();
|
||||||
expect(notes.length).toBe(0);
|
expect(notes.length).toBe(0);
|
||||||
|
|
||||||
synchronizer().debugFlags_ = [];
|
synchronizer().testingHooks_ = [];
|
||||||
await synchronizer().start({ context: context });
|
await synchronizer().start({ context: context });
|
||||||
notes = await Note.all();
|
notes = await Note.all();
|
||||||
expect(notes.length).toBe(1);
|
expect(notes.length).toBe(1);
|
||||||
@ -702,9 +702,9 @@ describe('Synchronizer', function() {
|
|||||||
let disabledItems = await BaseItem.syncDisabledItems(syncTargetId());
|
let disabledItems = await BaseItem.syncDisabledItems(syncTargetId());
|
||||||
expect(disabledItems.length).toBe(0);
|
expect(disabledItems.length).toBe(0);
|
||||||
await Note.save({ id: noteId, title: "un mod", });
|
await Note.save({ id: noteId, title: "un mod", });
|
||||||
synchronizer().debugFlags_ = ['rejectedByTarget'];
|
synchronizer().testingHooks_ = ['rejectedByTarget'];
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
synchronizer().debugFlags_ = [];
|
synchronizer().testingHooks_ = [];
|
||||||
await synchronizer().start(); // Another sync to check that this item is now excluded from sync
|
await synchronizer().start(); // Another sync to check that this item is now excluded from sync
|
||||||
|
|
||||||
await switchClient(2);
|
await switchClient(2);
|
||||||
|
@ -121,7 +121,7 @@ class EncryptionConfigScreenComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const decryptedItemsInfo = this.props.encryptionEnabled ? <p style={theme.textStyle}>{shared.decryptedStatText(this)}</p> : null;
|
const decryptedItemsInfo = <p style={theme.textStyle}>{shared.decryptedStatText(this)}</p>;
|
||||||
const toggleButton = <button onClick={() => { onToggleButtonClick() }}>{this.props.encryptionEnabled ? _('Disable encryption') : _('Enable encryption')}</button>
|
const toggleButton = <button onClick={() => { onToggleButtonClick() }}>{this.props.encryptionEnabled ? _('Disable encryption') : _('Enable encryption')}</button>
|
||||||
|
|
||||||
let masterKeySection = null;
|
let masterKeySection = null;
|
||||||
|
@ -3,6 +3,15 @@
|
|||||||
// Make it possible to require("/lib/...") without specifying full path
|
// Make it possible to require("/lib/...") without specifying full path
|
||||||
require('app-module-path').addPath(__dirname);
|
require('app-module-path').addPath(__dirname);
|
||||||
|
|
||||||
|
// Disable React message in console "Download the React DevTools for a better development experience"
|
||||||
|
// https://stackoverflow.com/questions/42196819/disable-hide-download-the-react-devtools#42196820
|
||||||
|
__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
|
||||||
|
supportsFiber: true,
|
||||||
|
inject: function() {},
|
||||||
|
onCommitFiberRoot: function() {},
|
||||||
|
onCommitFiberUnmount: function() {},
|
||||||
|
};
|
||||||
|
|
||||||
const { app } = require('./app.js');
|
const { app } = require('./app.js');
|
||||||
const Folder = require('lib/models/Folder.js');
|
const Folder = require('lib/models/Folder.js');
|
||||||
const Resource = require('lib/models/Resource.js');
|
const Resource = require('lib/models/Resource.js');
|
||||||
|
@ -73,7 +73,7 @@ class MdToHtml {
|
|||||||
|
|
||||||
renderImage_(attrs, options) {
|
renderImage_(attrs, options) {
|
||||||
const loadResource = async (id) => {
|
const loadResource = async (id) => {
|
||||||
console.info('Loading resource: ' + id);
|
// console.info('Loading resource: ' + id);
|
||||||
|
|
||||||
// Initially set to to an empty object to make
|
// Initially set to to an empty object to make
|
||||||
// it clear that it is being loaded. Otherwise
|
// it clear that it is being loaded. Otherwise
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
const { isHidden } = require('lib/path-utils.js');
|
const { isHidden } = require('lib/path-utils.js');
|
||||||
const { Logger } = require('lib/logger.js');
|
const { Logger } = require('lib/logger.js');
|
||||||
|
const { shim } = require('lib/shim');
|
||||||
|
const JoplinError = require('lib/JoplinError');
|
||||||
|
|
||||||
class FileApi {
|
class FileApi {
|
||||||
|
|
||||||
@ -10,6 +12,10 @@ class FileApi {
|
|||||||
this.syncTargetId_ = null;
|
this.syncTargetId_ = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fsDriver() {
|
||||||
|
return shim.fsDriver();
|
||||||
|
}
|
||||||
|
|
||||||
driver() {
|
driver() {
|
||||||
return this.driver_;
|
return this.driver_;
|
||||||
}
|
}
|
||||||
@ -83,8 +89,13 @@ class FileApi {
|
|||||||
return this.driver_.get(this.fullPath_(path), options);
|
return this.driver_.get(this.fullPath_(path), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
put(path, content, options = null) {
|
async put(path, content, options = null) {
|
||||||
this.logger().debug('put ' + this.fullPath_(path));
|
this.logger().debug('put ' + this.fullPath_(path), options);
|
||||||
|
|
||||||
|
if (options && options.source === 'file') {
|
||||||
|
if (!await this.fsDriver().exists(options.path)) throw new JoplinError('File not found: ' + options.path, 'fileNotFound');
|
||||||
|
}
|
||||||
|
|
||||||
return this.driver_.put(this.fullPath_(path), content, options);
|
return this.driver_.put(this.fullPath_(path), content, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,14 +4,20 @@ const { GeolocationNode } = require('lib/geolocation-node.js');
|
|||||||
const { FileApiDriverLocal } = require('lib/file-api-driver-local.js');
|
const { FileApiDriverLocal } = require('lib/file-api-driver-local.js');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
const { setLocale, defaultLocale, closestSupportedLocale } = require('lib/locale.js');
|
const { setLocale, defaultLocale, closestSupportedLocale } = require('lib/locale.js');
|
||||||
|
const { FsDriverNode } = require('lib/fs-driver-node.js');
|
||||||
|
|
||||||
function shimInit() {
|
function shimInit() {
|
||||||
shim.fs = fs;
|
shim.fsDriver = () => { throw new Error('Not implemented') }
|
||||||
shim.FileApiDriverLocal = FileApiDriverLocal;
|
shim.FileApiDriverLocal = FileApiDriverLocal;
|
||||||
shim.Geolocation = GeolocationNode;
|
shim.Geolocation = GeolocationNode;
|
||||||
shim.FormData = require('form-data');
|
shim.FormData = require('form-data');
|
||||||
shim.sjclModule = require('lib/vendor/sjcl.js');
|
shim.sjclModule = require('lib/vendor/sjcl.js');
|
||||||
|
|
||||||
|
shim.fsDriver = () => {
|
||||||
|
if (!shim.fsDriver_) shim.fsDriver_ = new FsDriverNode();
|
||||||
|
return shim.fsDriver_;
|
||||||
|
}
|
||||||
|
|
||||||
shim.randomBytes = async (count) => {
|
shim.randomBytes = async (count) => {
|
||||||
const buffer = require('crypto').randomBytes(count);
|
const buffer = require('crypto').randomBytes(count);
|
||||||
return Array.from(buffer);
|
return Array.from(buffer);
|
||||||
|
@ -3,6 +3,7 @@ const { GeolocationReact } = require('lib/geolocation-react.js');
|
|||||||
const { PoorManIntervals } = require('lib/poor-man-intervals.js');
|
const { PoorManIntervals } = require('lib/poor-man-intervals.js');
|
||||||
const RNFetchBlob = require('react-native-fetch-blob').default;
|
const RNFetchBlob = require('react-native-fetch-blob').default;
|
||||||
const { generateSecureRandom } = require('react-native-securerandom');
|
const { generateSecureRandom } = require('react-native-securerandom');
|
||||||
|
const FsDriverRN = require('lib/fs-driver-rn.js').FsDriverRN;
|
||||||
|
|
||||||
function shimInit() {
|
function shimInit() {
|
||||||
shim.Geolocation = GeolocationReact;
|
shim.Geolocation = GeolocationReact;
|
||||||
@ -10,6 +11,11 @@ function shimInit() {
|
|||||||
shim.clearInterval = PoorManIntervals.clearInterval;
|
shim.clearInterval = PoorManIntervals.clearInterval;
|
||||||
shim.sjclModule = require('lib/vendor/sjcl-rn.js');
|
shim.sjclModule = require('lib/vendor/sjcl-rn.js');
|
||||||
|
|
||||||
|
shim.fsDriver = () => {
|
||||||
|
if (!shim.fsDriver_) shim.fsDriver_ = new FsDriverRN();
|
||||||
|
return shim.fsDriver_;
|
||||||
|
}
|
||||||
|
|
||||||
shim.randomBytes = async (count) => {
|
shim.randomBytes = async (count) => {
|
||||||
const randomBytes = await generateSecureRandom(count);
|
const randomBytes = await generateSecureRandom(count);
|
||||||
let temp = [];
|
let temp = [];
|
||||||
|
@ -107,7 +107,7 @@ shim.fetchWithRetry = async function(fetchFn, options = null) {
|
|||||||
shim.nativeFetch_ = typeof fetch !== 'undefined' ? fetch : null;
|
shim.nativeFetch_ = typeof fetch !== 'undefined' ? fetch : null;
|
||||||
shim.fetch = () => { throw new Error('Not implemented'); }
|
shim.fetch = () => { throw new Error('Not implemented'); }
|
||||||
shim.FormData = typeof FormData !== 'undefined' ? FormData : null;
|
shim.FormData = typeof FormData !== 'undefined' ? FormData : null;
|
||||||
shim.fs = null;
|
shim.fsDriver = () => { throw new Error('Not implemented') }
|
||||||
shim.FileApiDriverLocal = null;
|
shim.FileApiDriverLocal = null;
|
||||||
shim.readLocalFileBase64 = (path) => { throw new Error('Not implemented'); }
|
shim.readLocalFileBase64 = (path) => { throw new Error('Not implemented'); }
|
||||||
shim.uploadBlob = () => { throw new Error('Not implemented'); }
|
shim.uploadBlob = () => { throw new Error('Not implemented'); }
|
||||||
|
@ -10,7 +10,7 @@ const { time } = require('lib/time-utils.js');
|
|||||||
const { Logger } = require('lib/logger.js');
|
const { Logger } = require('lib/logger.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { shim } = require('lib/shim.js');
|
const { shim } = require('lib/shim.js');
|
||||||
const moment = require('moment');
|
const JoplinError = require('lib/JoplinError');
|
||||||
|
|
||||||
class Synchronizer {
|
class Synchronizer {
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ class Synchronizer {
|
|||||||
|
|
||||||
// Debug flags are used to test certain hard-to-test conditions
|
// Debug flags are used to test certain hard-to-test conditions
|
||||||
// such as cancelling in the middle of a loop.
|
// such as cancelling in the middle of a loop.
|
||||||
this.debugFlags_ = [];
|
this.testingHooks_ = [];
|
||||||
|
|
||||||
this.onProgress_ = function(s) {};
|
this.onProgress_ = function(s) {};
|
||||||
this.progressReport_ = {};
|
this.progressReport_ = {};
|
||||||
@ -279,7 +279,7 @@ class Synchronizer {
|
|||||||
const localResourceContentPath = result.path;
|
const localResourceContentPath = result.path;
|
||||||
await this.api().put(remoteContentPath, null, { path: localResourceContentPath, source: 'file' });
|
await this.api().put(remoteContentPath, null, { path: localResourceContentPath, source: 'file' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error && error.code === 'rejectedByTarget') {
|
if (error && ['rejectedByTarget', 'fileNotFound'].indexOf(error.code) >= 0) {
|
||||||
await handleCannotSyncItem(syncTargetId, local, error.message);
|
await handleCannotSyncItem(syncTargetId, local, error.message);
|
||||||
action = null;
|
action = null;
|
||||||
} else {
|
} else {
|
||||||
@ -303,11 +303,7 @@ class Synchronizer {
|
|||||||
|
|
||||||
let canSync = true;
|
let canSync = true;
|
||||||
try {
|
try {
|
||||||
if (this.debugFlags_.indexOf('rejectedByTarget') >= 0) {
|
if (this.testingHooks_.indexOf('rejectedByTarget') >= 0) throw new JoplinError('Testing rejectedByTarget', 'rejectedByTarget');
|
||||||
const error = new Error('Testing rejectedByTarget');
|
|
||||||
error.code = 'rejectedByTarget';
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
const content = await ItemClass.serializeForSync(local);
|
const content = await ItemClass.serializeForSync(local);
|
||||||
await this.api().put(path, content);
|
await this.api().put(path, content);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -449,7 +445,7 @@ class Synchronizer {
|
|||||||
this.logSyncOperation('fetchingTotal', null, null, 'Fetching delta items from sync target', remotes.length);
|
this.logSyncOperation('fetchingTotal', null, null, 'Fetching delta items from sync target', remotes.length);
|
||||||
|
|
||||||
for (let i = 0; i < remotes.length; i++) {
|
for (let i = 0; i < remotes.length; i++) {
|
||||||
if (this.cancelling() || this.debugFlags_.indexOf('cancelDeltaLoop2') >= 0) {
|
if (this.cancelling() || this.testingHooks_.indexOf('cancelDeltaLoop2') >= 0) {
|
||||||
hasCancelled = true;
|
hasCancelled = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user