fix(backup): synchronize calls to localForage (#3932)

This commit is contained in:
Erez Rokah 2020-06-21 12:42:30 +03:00 committed by GitHub
parent 330fadd1d7
commit 86562ad47a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 30 deletions

View File

@ -114,7 +114,9 @@ describe('Backend', () => {
}); });
describe('getLocalDraftBackup', () => { describe('getLocalDraftBackup', () => {
const { localForage } = require('netlify-cms-lib-util'); const { localForage, asyncLock } = require('netlify-cms-lib-util');
asyncLock.mockImplementation(() => ({ acquire: jest.fn(), release: jest.fn() }));
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();

View File

@ -34,6 +34,8 @@ import {
getPathDepth, getPathDepth,
Config as ImplementationConfig, Config as ImplementationConfig,
blobToFileObj, blobToFileObj,
asyncLock,
AsyncLock,
} from 'netlify-cms-lib-util'; } from 'netlify-cms-lib-util';
import { basename, join, extname, dirname } from 'path'; import { basename, join, extname, dirname } from 'path';
import { status } from './constants/publishModes'; import { status } from './constants/publishModes';
@ -178,6 +180,7 @@ export class Backend {
authStore: AuthStore | null; authStore: AuthStore | null;
config: Config; config: Config;
user?: User | null; user?: User | null;
backupSync: AsyncLock;
constructor( constructor(
implementation: Implementation, implementation: Implementation,
@ -196,6 +199,7 @@ export class Backend {
if (this.implementation === null) { if (this.implementation === null) {
throw new Error('Cannot instantiate a Backend with no implementation'); throw new Error('Cannot instantiate a Backend with no implementation');
} }
this.backupSync = asyncLock();
} }
async status() { async status() {
@ -535,8 +539,11 @@ export class Backend {
} }
async persistLocalDraftBackup(entry: EntryMap, collection: Collection) { async persistLocalDraftBackup(entry: EntryMap, collection: Collection) {
try {
await this.backupSync.acquire();
const key = getEntryBackupKey(collection.get('name'), entry.get('slug')); const key = getEntryBackupKey(collection.get('name'), entry.get('slug'));
const raw = this.entryToRaw(collection, entry); const raw = this.entryToRaw(collection, entry);
if (!raw.trim()) { if (!raw.trim()) {
return; return;
} }
@ -560,14 +567,28 @@ export class Backend {
path: entry.get('path'), path: entry.get('path'),
mediaFiles, mediaFiles,
}); });
return localForage.setItem(getEntryBackupKey(), raw); const result = await localForage.setItem(getEntryBackupKey(), raw);
return result;
} catch (e) {
console.warn('persistLocalDraftBackup', e);
} finally {
this.backupSync.release();
}
} }
async deleteLocalDraftBackup(collection: Collection, slug: string) { async deleteLocalDraftBackup(collection: Collection, slug: string) {
try {
await this.backupSync.acquire();
await localForage.removeItem(getEntryBackupKey(collection.get('name'), slug)); await localForage.removeItem(getEntryBackupKey(collection.get('name'), slug));
// delete new entry backup if not deleted // delete new entry backup if not deleted
slug && (await localForage.removeItem(getEntryBackupKey(collection.get('name')))); slug && (await localForage.removeItem(getEntryBackupKey(collection.get('name'))));
return this.deleteAnonymousBackup(); const result = await this.deleteAnonymousBackup();
return result;
} catch (e) {
console.warn('deleteLocalDraftBackup', e);
} finally {
this.backupSync.release();
}
} }
// Unnamed backup for use in the global error boundary, should always be // Unnamed backup for use in the global error boundary, should always be