diff --git a/.eslintignore b/.eslintignore
index bfd1ebbe5e..5c059cc0d1 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -933,6 +933,9 @@ packages/lib/PoorManIntervals.js.map
packages/lib/SyncTargetJoplinServer.d.ts
packages/lib/SyncTargetJoplinServer.js
packages/lib/SyncTargetJoplinServer.js.map
+packages/lib/SyncTargetOneDrive.d.ts
+packages/lib/SyncTargetOneDrive.js
+packages/lib/SyncTargetOneDrive.js.map
packages/lib/Synchronizer.d.ts
packages/lib/Synchronizer.js
packages/lib/Synchronizer.js.map
@@ -996,6 +999,9 @@ packages/lib/models/utils/types.js.map
packages/lib/ntpDate.d.ts
packages/lib/ntpDate.js
packages/lib/ntpDate.js.map
+packages/lib/onedrive-api.d.ts
+packages/lib/onedrive-api.js
+packages/lib/onedrive-api.js.map
packages/lib/path-utils.d.ts
packages/lib/path-utils.js
packages/lib/path-utils.js.map
diff --git a/.gitignore b/.gitignore
index ce0868ae0e..cc8b8659ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -921,6 +921,9 @@ packages/lib/PoorManIntervals.js.map
packages/lib/SyncTargetJoplinServer.d.ts
packages/lib/SyncTargetJoplinServer.js
packages/lib/SyncTargetJoplinServer.js.map
+packages/lib/SyncTargetOneDrive.d.ts
+packages/lib/SyncTargetOneDrive.js
+packages/lib/SyncTargetOneDrive.js.map
packages/lib/Synchronizer.d.ts
packages/lib/Synchronizer.js
packages/lib/Synchronizer.js.map
@@ -984,6 +987,9 @@ packages/lib/models/utils/types.js.map
packages/lib/ntpDate.d.ts
packages/lib/ntpDate.js
packages/lib/ntpDate.js.map
+packages/lib/onedrive-api.d.ts
+packages/lib/onedrive-api.js
+packages/lib/onedrive-api.js.map
packages/lib/path-utils.d.ts
packages/lib/path-utils.js
packages/lib/path-utils.js.map
diff --git a/README.md b/README.md
index a1c2a9ad2c..cf5526f419 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ Linux |
| or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.4.11/joplin-v1.4.11.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.4.11/joplin-v1.4.11-32bit.apk)
+Android |
| or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.6.7/joplin-v1.6.7.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.6.7/joplin-v1.6.7-32bit.apk)
iOS |
| -
## Terminal application
diff --git a/packages/app-cli/package-lock.json b/packages/app-cli/package-lock.json
index 3f81aa8361..25aaca7173 100644
--- a/packages/app-cli/package-lock.json
+++ b/packages/app-cli/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "joplin",
- "version": "1.6.3",
+ "version": "1.6.4",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/packages/app-cli/package.json b/packages/app-cli/package.json
index b1bf314512..b54d5ba8a8 100644
--- a/packages/app-cli/package.json
+++ b/packages/app-cli/package.json
@@ -39,8 +39,8 @@
"node": ">=10.0.0"
},
"dependencies": {
- "@joplin/lib": "*",
- "@joplin/renderer": "*",
+ "@joplin/lib": "1.0.18",
+ "@joplin/renderer": "1.0.26",
"aws-sdk": "^2.588.0",
"chalk": "^4.1.0",
"clean-html": "^1.5.0",
diff --git a/packages/app-cli/tests/test-utils.ts b/packages/app-cli/tests/test-utils.ts
index 8c1aa9ceed..4bc16acc98 100644
--- a/packages/app-cli/tests/test-utils.ts
+++ b/packages/app-cli/tests/test-utils.ts
@@ -16,6 +16,8 @@ import KeychainServiceDriverDummy from '@joplin/lib/services/keychain/KeychainSe
import PluginRunner from '../app/services/plugins/PluginRunner';
import PluginService from '@joplin/lib/services/plugins/PluginService';
import FileApiDriverJoplinServer from '@joplin/lib/file-api-driver-joplinServer';
+import OneDriveApi from '@joplin/lib/onedrive-api';
+import SyncTargetOneDrive from '@joplin/lib/SyncTargetOneDrive';
const fs = require('fs-extra');
const { JoplinDatabase } = require('@joplin/lib/joplin-database.js');
@@ -40,7 +42,6 @@ const { shimInit } = require('@joplin/lib/shim-init-node.js');
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry.js');
const SyncTargetMemory = require('@joplin/lib/SyncTargetMemory.js');
const SyncTargetFilesystem = require('@joplin/lib/SyncTargetFilesystem.js');
-const SyncTargetOneDrive = require('@joplin/lib/SyncTargetOneDrive.js');
const SyncTargetNextcloud = require('@joplin/lib/SyncTargetNextcloud.js');
const SyncTargetDropbox = require('@joplin/lib/SyncTargetDropbox.js');
const SyncTargetAmazonS3 = require('@joplin/lib/SyncTargetAmazonS3.js');
@@ -52,7 +53,6 @@ const ResourceFetcher = require('@joplin/lib/services/ResourceFetcher.js');
const WebDavApi = require('@joplin/lib/WebDavApi');
const DropboxApi = require('@joplin/lib/DropboxApi');
const JoplinServerApi = require('@joplin/lib/JoplinServerApi2').default;
-const { OneDriveApi } = require('@joplin/lib/onedrive-api');
const { loadKeychainServiceAndSettings } = require('@joplin/lib/services/SettingUtils');
const md5 = require('md5');
const S3 = require('aws-sdk/clients/s3');
diff --git a/packages/app-mobile/android/app/build.gradle b/packages/app-mobile/android/app/build.gradle
index f362f0a960..524945b738 100644
--- a/packages/app-mobile/android/app/build.gradle
+++ b/packages/app-mobile/android/app/build.gradle
@@ -141,7 +141,7 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 2097620
+ versionCode 2097621
versionName "1.7.0"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
diff --git a/packages/app-mobile/ios/Joplin.xcodeproj/project.pbxproj b/packages/app-mobile/ios/Joplin.xcodeproj/project.pbxproj
index 170a8f1c23..2a708159fb 100644
--- a/packages/app-mobile/ios/Joplin.xcodeproj/project.pbxproj
+++ b/packages/app-mobile/ios/Joplin.xcodeproj/project.pbxproj
@@ -338,7 +338,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CURRENT_PROJECT_VERSION = 60;
+ CURRENT_PROJECT_VERSION = 61;
DEVELOPMENT_TEAM = A9BXAFS6CT;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Joplin/Info.plist;
@@ -365,7 +365,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CURRENT_PROJECT_VERSION = 60;
+ CURRENT_PROJECT_VERSION = 61;
DEVELOPMENT_TEAM = A9BXAFS6CT;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx
index 7b727c0521..e3be1f080b 100644
--- a/packages/app-mobile/root.tsx
+++ b/packages/app-mobile/root.tsx
@@ -25,6 +25,7 @@ import { loadKeychainServiceAndSettings } from '@joplin/lib/services/SettingUtil
import KeychainServiceDriverMobile from '@joplin/lib/services/keychain/KeychainServiceDriver.mobile';
import { setLocale, closestSupportedLocale, defaultLocale } from '@joplin/lib/locale';
import SyncTargetJoplinServer from '@joplin/lib/SyncTargetJoplinServer';
+import SyncTargetOneDrive from '@joplin/lib/SyncTargetOneDrive';
const { AppState, Keyboard, NativeModules, BackHandler, Animated, View, StatusBar } = require('react-native');
@@ -75,7 +76,6 @@ const WelcomeUtils = require('@joplin/lib/WelcomeUtils');
const { themeStyle } = require('./components/global-style.js');
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry.js');
-const SyncTargetOneDrive = require('@joplin/lib/SyncTargetOneDrive.js');
const SyncTargetFilesystem = require('@joplin/lib/SyncTargetFilesystem.js');
const SyncTargetNextcloud = require('@joplin/lib/SyncTargetNextcloud.js');
const SyncTargetWebDAV = require('@joplin/lib/SyncTargetWebDAV.js');
diff --git a/packages/lib/BaseApplication.ts b/packages/lib/BaseApplication.ts
index 7edaad7a78..e2b102c96a 100644
--- a/packages/lib/BaseApplication.ts
+++ b/packages/lib/BaseApplication.ts
@@ -7,6 +7,7 @@ import KeychainServiceDriver from './services/keychain/KeychainServiceDriver.nod
import { _, setLocale } from './locale';
import KvStore from './services/KvStore';
import SyncTargetJoplinServer from './SyncTargetJoplinServer';
+import SyncTargetOneDrive from './SyncTargetOneDrive';
const { createStore, applyMiddleware } = require('redux');
const { defaultState, stateUtils } = require('./reducer');
@@ -30,7 +31,6 @@ const EventEmitter = require('events');
const syswidecas = require('./vendor/syswide-cas');
const SyncTargetRegistry = require('./SyncTargetRegistry.js');
const SyncTargetFilesystem = require('./SyncTargetFilesystem.js');
-const SyncTargetOneDrive = require('./SyncTargetOneDrive.js');
const SyncTargetNextcloud = require('./SyncTargetNextcloud.js');
const SyncTargetWebDAV = require('./SyncTargetWebDAV.js');
const SyncTargetDropbox = require('./SyncTargetDropbox.js');
diff --git a/packages/lib/Logger.ts b/packages/lib/Logger.ts
index aeea77e3da..e2edb3a6d5 100644
--- a/packages/lib/Logger.ts
+++ b/packages/lib/Logger.ts
@@ -2,6 +2,9 @@ const moment = require('moment');
const time = require('./time').default;
const { FsDriverDummy } = require('./fs-driver-dummy.js');
const { sprintf } = require('sprintf-js');
+const Mutex = require('async-mutex').Mutex;
+
+const writeToFileMutex_ = new Mutex();
export enum TargetType {
Database = 'database',
@@ -205,12 +208,23 @@ class Logger {
const line = [timestamp];
if (targetPrefix) line.push(targetPrefix);
line.push(this.objectsToString(...object));
- try {
- // TODO: Should log async
- Logger.fsDriver().appendFileSync(target.path, `${line.join(': ')}\n`);
- } catch (error) {
+
+ // Write to file using a mutex so that log entries appear in the
+ // correct order (otherwise, since the async call is not awaited
+ // by caller, multiple log call in a row are not guaranteed to
+ // appear in the right order). We also can't use a sync call
+ // because that would slow down the main process, especially
+ // when many log operations are being done (eg. during sync in
+ // dev mode).
+ let release: Function = null;
+ writeToFileMutex_.acquire().then((r: Function) => {
+ release = r;
+ return Logger.fsDriver().appendFile(target.path, `${line.join(': ')}\n`, 'utf8');
+ }).catch((error: any) => {
console.error('Cannot write to log file:', error);
- }
+ }).finally(() => {
+ if (release) release();
+ });
} else if (target.type == 'database') {
const msg = [];
if (targetPrefix) msg.push(targetPrefix);
diff --git a/packages/lib/SyncTargetOneDrive.js b/packages/lib/SyncTargetOneDrive.ts
similarity index 87%
rename from packages/lib/SyncTargetOneDrive.js
rename to packages/lib/SyncTargetOneDrive.ts
index 4d23a1a637..45d8dec03e 100644
--- a/packages/lib/SyncTargetOneDrive.js
+++ b/packages/lib/SyncTargetOneDrive.ts
@@ -1,18 +1,20 @@
+import OneDriveApi from './onedrive-api';
+import { _ } from './locale';
+import Setting from './models/Setting';
+import Synchronizer from './Synchronizer';
+
const BaseSyncTarget = require('./BaseSyncTarget.js');
-const { _ } = require('./locale');
-const { OneDriveApi } = require('./onedrive-api.js');
-const Setting = require('./models/Setting').default;
const { parameters } = require('./parameters.js');
const { FileApi } = require('./file-api.js');
-const Synchronizer = require('./Synchronizer').default;
const { FileApiDriverOneDrive } = require('./file-api-driver-onedrive.js');
-class SyncTargetOneDrive extends BaseSyncTarget {
+export default class SyncTargetOneDrive extends BaseSyncTarget {
+
static id() {
return 3;
}
- constructor(db, options = null) {
+ constructor(db: any, options: any = null) {
super(db, options);
this.api_ = null;
}
@@ -58,9 +60,8 @@ class SyncTargetOneDrive extends BaseSyncTarget {
const isPublic = Setting.value('appType') != 'cli' && Setting.value('appType') != 'desktop';
this.api_ = new OneDriveApi(this.oneDriveParameters().id, this.oneDriveParameters().secret, isPublic);
- this.api_.setLogger(this.logger());
- this.api_.on('authRefreshed', a => {
+ this.api_.on('authRefreshed', (a: any) => {
this.logger().info('Saving updated OneDrive auth.');
Setting.setValue(`sync.${this.syncTargetId()}.auth`, a ? JSON.stringify(a) : null);
});
@@ -110,5 +111,3 @@ class SyncTargetOneDrive extends BaseSyncTarget {
}
}
-
-module.exports = SyncTargetOneDrive;
diff --git a/packages/lib/SyncTargetOneDriveDev.js b/packages/lib/SyncTargetOneDriveDev.js
deleted file mode 100644
index 48949925ff..0000000000
--- a/packages/lib/SyncTargetOneDriveDev.js
+++ /dev/null
@@ -1,27 +0,0 @@
-const SyncTargetOneDrive = require('./SyncTargetOneDrive.js');
-const { _ } = require('./locale');
-const { parameters } = require('./parameters.js');
-
-class SyncTargetOneDriveDev extends SyncTargetOneDrive {
- static id() {
- return 4;
- }
-
- static targetName() {
- return 'onedrive_dev';
- }
-
- static label() {
- return _('OneDrive Dev (For testing only)');
- }
-
- syncTargetId() {
- return SyncTargetOneDriveDev.id();
- }
-
- oneDriveParameters() {
- return parameters('dev').oneDrive;
- }
-}
-
-module.exports = SyncTargetOneDriveDev;
diff --git a/packages/lib/file-api-driver-onedrive.js b/packages/lib/file-api-driver-onedrive.js
index 8b0aa5ccc7..a9c07310e1 100644
--- a/packages/lib/file-api-driver-onedrive.js
+++ b/packages/lib/file-api-driver-onedrive.js
@@ -1,4 +1,5 @@
const moment = require('moment');
+const { basicDelta } = require('./file-api');
const { dirname, basename } = require('./path-utils');
const shim = require('./shim').default;
const Buffer = require('buffer').Buffer;
@@ -83,10 +84,12 @@ class FileApiDriverOneDrive {
context: null,
}, options);
- let query = this.itemFilter_();
+ let query = Object.assign({}, this.itemFilter_(), { '$top': 1000 });
let url = `${this.makePath_(path)}:/children`;
if (options.context) {
+ // If there's a context, it already includes all required query
+ // parameters, including $top
query = null;
url = options.context;
}
@@ -213,6 +216,24 @@ class FileApiDriverOneDrive {
}
async delta(path, options = null) {
+ const getDirStats = async path => {
+ let items = [];
+ let context = null;
+
+ while (true) {
+ const result = await this.list(path, { includeDirs: false, context: context });
+ items = items.concat(result.items);
+ context = result.context;
+ if (!result.hasMore) break;
+ }
+
+ return items;
+ };
+
+ return await basicDelta(path, getDirStats, options);
+ }
+
+ async delta_BROKEN(path, options = null) {
const output = {
hasMore: false,
context: {},
diff --git a/packages/lib/onedrive-api.js b/packages/lib/onedrive-api.ts
similarity index 84%
rename from packages/lib/onedrive-api.js
rename to packages/lib/onedrive-api.ts
index cf2fa07287..ee5b90a28c 100644
--- a/packages/lib/onedrive-api.js
+++ b/packages/lib/onedrive-api.ts
@@ -1,17 +1,28 @@
-const shim = require('./shim').default;
+import shim from './shim';
+import time from './time';
+import Logger from './Logger';
+import { _ } from './locale';
+
const { stringify } = require('query-string');
-const time = require('./time').default;
-const Logger = require('./Logger').default;
-const { _ } = require('./locale');
const urlUtils = require('./urlUtils.js');
const Buffer = require('buffer').Buffer;
-class OneDriveApi {
+const logger = Logger.create('OneDriveApi');
+
+export default class OneDriveApi {
+
+ private clientId_: string;
+ private clientSecret_: string;
+ private auth_: any = null;
+ private accountProperties_: any = null;
+ private isPublic_: boolean;
+ private listeners_: Record;
+
// `isPublic` is to tell OneDrive whether the application is a "public" one (Mobile and desktop
// apps are considered "public"), in which case the secret should not be sent to the API.
// In practice the React Native app is public, and the Node one is not because we
// use a local server for the OAuth dance.
- constructor(clientId, clientSecret, isPublic) {
+ constructor(clientId: string, clientSecret: string, isPublic: boolean) {
this.clientId_ = clientId;
this.clientSecret_ = clientSecret;
this.auth_ = null;
@@ -20,29 +31,20 @@ class OneDriveApi {
this.listeners_ = {
authRefreshed: [],
};
- this.logger_ = new Logger();
- }
-
- setLogger(l) {
- this.logger_ = l;
- }
-
- logger() {
- return this.logger_;
}
isPublic() {
return this.isPublic_;
}
- dispatch(eventName, param) {
+ dispatch(eventName: string, param: any) {
const ls = this.listeners_[eventName];
for (let i = 0; i < ls.length; i++) {
ls[i](param);
}
}
- on(eventName, callback) {
+ on(eventName: string, callback: Function) {
this.listeners_[eventName].push(callback);
}
@@ -54,11 +56,11 @@ class OneDriveApi {
return 'https://login.microsoftonline.com/common/oauth2/nativeclient';
}
- auth() {
+ auth(): any {
return this.auth_;
}
- setAuth(auth) {
+ setAuth(auth: any) {
this.auth_ = auth;
this.dispatch('authRefreshed', this.auth());
}
@@ -81,7 +83,7 @@ class OneDriveApi {
return `${r.parentReference.path}/${r.name}`;
}
- authCodeUrl(redirectUri) {
+ authCodeUrl(redirectUri: string) {
const query = {
client_id: this.clientId_,
scope: 'files.readwrite offline_access sites.readwrite.all',
@@ -91,8 +93,8 @@ class OneDriveApi {
return `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${stringify(query)}`;
}
- async execTokenRequest(code, redirectUri) {
- const body = {};
+ async execTokenRequest(code: string, redirectUri: string) {
+ const body: any = {};
body['client_id'] = this.clientId();
if (!this.isPublic()) body['client_secret'] = this.clientSecret();
body['code'] = code;
@@ -123,12 +125,12 @@ class OneDriveApi {
}
}
- oneDriveErrorResponseToError(errorResponse) {
+ oneDriveErrorResponseToError(errorResponse: any) {
if (!errorResponse) return new Error('Undefined error');
if (errorResponse.error) {
const e = errorResponse.error;
- const output = new Error(e.message);
+ const output: any = new Error(e.message);
if (e.code) output.code = e.code;
if (e.innerError) output.innerError = e.innerError;
return output;
@@ -137,7 +139,7 @@ class OneDriveApi {
}
}
- async uploadChunk(url, handle, buffer, options) {
+ async uploadChunk(url: string, handle: any, buffer: any, options: any) {
options = Object.assign({}, options);
if (!options.method) { options.method = 'POST'; }
@@ -159,7 +161,7 @@ class OneDriveApi {
return response;
}
- async uploadBigFile(url, options) {
+ async uploadBigFile(url: string, options: any) {
const response = await shim.fetch(url, {
method: 'POST',
headers: {
@@ -199,7 +201,7 @@ class OneDriveApi {
endByte = (i + 1) * chunkSize - 1;
contentLength = chunkSize;
}
- this.logger().debug(`Uploading File Fragment ${(startByte / 1048576).toFixed(2)} - ${(endByte / 1048576).toFixed(2)} from ${(byteSize / 1048576).toFixed(2)} Mbit ...`);
+ logger.debug(`Uploading File Fragment ${(startByte / 1048576).toFixed(2)} - ${(endByte / 1048576).toFixed(2)} from ${(byteSize / 1048576).toFixed(2)} Mbit ...`);
const headers = {
'Content-Length': contentLength,
'Content-Range': `bytes ${startByte}-${endByte}/${byteSize}`,
@@ -215,7 +217,7 @@ class OneDriveApi {
return { ok: true };
} catch (error) {
const type = (handle) ? 'Resource' : 'Note Content';
- this.logger().error(`Couldn't upload ${type} > 4 Mb. Got unhandled error:`, error ? error.code : '', error ? error.message : '', error);
+ logger.error(`Couldn't upload ${type} > 4 Mb. Got unhandled error:`, error ? error.code : '', error ? error.message : '', error);
throw error;
} finally {
if (handle) await shim.fsDriver().close(handle);
@@ -224,7 +226,7 @@ class OneDriveApi {
}
}
- async exec(method, path, query = null, data = null, options = null) {
+ async exec(method: string, path: string, query: any = null, data: any = null, options: any = null) {
if (!path) throw new Error('Path is required');
method = method.toUpperCase();
@@ -264,6 +266,12 @@ class OneDriveApi {
for (let i = 0; i < 5; i++) {
options.headers['Authorization'] = `bearer ${this.token()}`;
+ const handleRequestRepeat = async (error: any) => {
+ logger.info(`Got error below - retrying (${i})...`);
+ logger.info(error);
+ await time.sleep((i + 1) * 5);
+ };
+
let response = null;
try {
if (path.includes('/createUploadSession')) {
@@ -277,24 +285,31 @@ class OneDriveApi {
response = await shim.fetchBlob(url, options);
}
} catch (error) {
- this.logger().error('Got unhandled error:', error ? error.code : '', error ? error.message : '', error);
- throw error;
+ if (shim.fetchRequestCanBeRetried(error)) {
+ await handleRequestRepeat(error);
+ continue;
+ } else {
+ logger.error('Got unhandled error:', error ? error.code : '', error ? error.message : '', error);
+ throw error;
+ }
}
if (!response.ok) {
const errorResponseText = await response.text();
let errorResponse = null;
+
try {
errorResponse = JSON.parse(errorResponseText); // await response.json();
} catch (error) {
error.message = `OneDriveApi::exec: Cannot parse JSON error: ${errorResponseText} ${error.message}`;
- throw error;
+ await handleRequestRepeat(error);
+ continue;
}
const error = this.oneDriveErrorResponseToError(errorResponse);
if (error.code == 'InvalidAuthenticationToken' || error.code == 'unauthenticated') {
- this.logger().info('Token expired: refreshing...');
+ logger.info('Token expired: refreshing...');
await this.refreshAccessToken();
continue;
} else if (error && ((error.error && error.error.code == 'generalException') || error.code == 'generalException' || error.code == 'EAGAIN')) {
@@ -312,9 +327,7 @@ class OneDriveApi {
// type: 'system',
// errno: 'EAGAIN',
// code: 'EAGAIN' }
- this.logger().info(`Got error below - retrying (${i})...`);
- this.logger().info(error);
- await time.sleep((i + 1) * 3);
+ await handleRequestRepeat(error);
continue;
} else if (error && (error.code === 'resourceModified' || (error.error && error.error.code === 'resourceModified'))) {
// NOTE: not tested, very hard to reproduce and non-informative error message, but can be repeated
@@ -324,9 +337,7 @@ class OneDriveApi {
// Header: {"_headers":{"cache-control":["private"],"transfer-encoding":["chunked"],"content-type":["application/json"],"request-id":["d...ea47"],"client-request-id":["d99...ea47"],"x-ms-ags-diagnostic":["{\"ServerInfo\":{\"DataCenter\":\"North Europe\",\"Slice\":\"SliceA\",\"Ring\":\"2\",\"ScaleUnit\":\"000\",\"Host\":\"AGSFE_IN_13\",\"ADSiteName\":\"DUB\"}}"],"duration":["96.9464"],"date":[],"connection":["close"]}}
// Request: PATCH https://graph.microsoft.com/v1.0/drive/root:/Apps/JoplinDev/f56c5601fee94b8085524513bf3e352f.md null "{\"fileSystemInfo\":{\"lastModifiedDateTime\":\"....\"}}" {"headers":{"Content-Type":"application/json","Authorization":"bearer ...
- this.logger().info(`Got error below - retrying (${i})...`);
- this.logger().info(error);
- await time.sleep((i + 1) * 3);
+ await handleRequestRepeat(error);
continue;
} else if (error.code == 'itemNotFound' && method == 'DELETE') {
// Deleting a non-existing item is ok - noop
@@ -344,7 +355,7 @@ class OneDriveApi {
throw new Error(`Could not execute request after multiple attempts: ${method} ${url}`);
}
- setAccountProperties(accountProperties) {
+ setAccountProperties(accountProperties: any) {
this.accountProperties_ = accountProperties;
}
@@ -360,7 +371,7 @@ class OneDriveApi {
}
}
- async execJson(method, path, query, data) {
+ async execJson(method: string, path: string, query: any = null, data: any = null) {
const response = await this.exec(method, path, query, data);
const errorResponseText = await response.text();
try {
@@ -373,7 +384,7 @@ class OneDriveApi {
}
}
- async execText(method, path, query, data) {
+ async execText(method: string, path: string, query: any = null, data: any = null) {
const response = await this.exec(method, path, query, data);
const output = await response.text();
return output;
@@ -385,7 +396,7 @@ class OneDriveApi {
throw new Error(_('Cannot refresh token: authentication data is missing. Starting the synchronisation again may fix the problem.'));
}
- const body = {};
+ const body: any = {};
body['client_id'] = this.clientId();
if (!this.isPublic()) body['client_secret'] = this.clientSecret();
body['refresh_token'] = this.auth_.refresh_token;
@@ -410,5 +421,3 @@ class OneDriveApi {
this.setAuth(auth);
}
}
-
-module.exports = { OneDriveApi };
diff --git a/readme/changelog_cli.md b/readme/changelog_cli.md
index 6772856dc5..4e7945180e 100644
--- a/readme/changelog_cli.md
+++ b/readme/changelog_cli.md
@@ -1,5 +1,9 @@
# Joplin terminal app changelog
+## [cli-v1.6.4](https://github.com/laurent22/joplin/releases/tag/cli-v1.6.4) - 2021-01-21T10:01:15Z
+
+- Fixed: Fixed infinite sync issue with OneDrive (#4305)
+
## [cli-v1.6.3](https://github.com/laurent22/joplin/releases/tag/cli-v1.6.3) - 2021-01-11T11:52:11Z
- New: Add more log info when a revision cannot be deleted due to still-encrypted itel