(core) add explicit doc and inventory creation step

Summary:
Currently, if a document is created by importing a file, inventory
creation is a little haphazard - it works, but triggers a
"surprise" message.  This diff makes initialization of inventory
explicit, so that surprise messages shouldn't happen during
document creation.

Test Plan: manual

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D2696
pull/3/head
Paul Fitzpatrick 3 years ago
parent 24e76b4abc
commit d5b00f5169

@ -300,6 +300,7 @@ export class ActiveDoc extends EventEmitter {
@ActiveDoc.keepDocOpen
public async createDoc(docSession: OptDocSession): Promise<ActiveDoc> {
this.logDebug(docSession, "createDoc");
await this._docManager.storageManager.prepareToCreateDoc(this.docName);
await this.docStorage.createFile();
await this._rawPyCall('load_empty');
const timezone = docSession.browserSettings ? docSession.browserSettings.timezone : DEFAULT_TIMEZONE;

@ -110,6 +110,17 @@ export class DocSnapshotInventory implements IInventory {
constructor(private _doc: ExternalStorage, private _meta: ExternalStorage,
private _getFilename: (key: string) => Promise<string>) {}
/**
* Start keeping inventory for a new document.
*/
public async create(key: string) {
await this._mutex.runExclusive(key, async() => {
const fname = await this._getFilename(key);
await this._saveToFile(fname, []);
this._needFlush.add(key);
});
}
/**
* Add a new snapshot of a document to the existing inventory. A prevSnapshotId may
* be supplied as a cross-check. It will be matched against the most recent
@ -126,7 +137,7 @@ export class DocSnapshotInventory implements IInventory {
await this._mutex.runExclusive(key, async() => {
const snapshots = await this._getSnapshots(key, prevSnapshotId);
// Could be already added if reconstruction happened.
if (snapshots[0].snapshotId === snapshot.snapshotId) { return; }
if (snapshots[0] && snapshots[0].snapshotId === snapshot.snapshotId) { return; }
this._normalizeMetadata(snapshot);
snapshots.unshift(snapshot);
const fname = await this._getFilename(key);

@ -87,6 +87,10 @@ export class DocStorageManager implements IDocStorageManager {
*/
public async prepareLocalDoc(docName: string, docSession: OptDocSession): Promise<boolean> { return false; }
public async prepareToCreateDoc(docName: string): Promise<void> {
// nothing to do
}
/**
* Returns a promise for the list of docNames to show in the doc list. For the file-based
* storage, this will include all .grist files under the docsRoot.
@ -212,7 +216,7 @@ export class DocStorageManager implements IDocStorageManager {
// nothing to do
}
public addToStorage(id: string): void {
public async addToStorage(id: string): Promise<void> {
// nothing to do
}

@ -184,7 +184,8 @@ export class HostedStorageManager implements IDocStorageManager {
*/
public async addToStorage(docId: string) {
if (this._disableS3) { return; }
await this._ext.upload(docId, this.getPath(docId));
this._uploads.addOperation(docId);
await this._uploads.expediteOperationAndWait(docId);
}
public getPath(docName: string): string {
@ -234,6 +235,13 @@ export class HostedStorageManager implements IDocStorageManager {
}
}
public async prepareToCreateDoc(docName: string): Promise<void> {
if (this._inventory) {
await this._inventory.create(docName);
this._onInventoryChange(docName);
}
}
// Gets a copy of the document, eg. for downloading. Returns full file path.
// Copy won't change if edits are made to the document. It is caller's responsibility
// to delete the result.
@ -656,10 +664,7 @@ export class HostedStorageManager implements IDocStorageManager {
metadata
}
await this._inventory.add(docId, snapshot, prevSnapshotId);
const scheduled = this._pruner.requestPrune(docId);
if (!scheduled) {
await this._inventory.flush(docId);
}
await this._onInventoryChange(docId);
} finally {
// Clean up backup.
// NOTE: fse.remove succeeds also when the file does not exist.
@ -667,6 +672,14 @@ export class HostedStorageManager implements IDocStorageManager {
}
}
// Make sure inventory change is followed up on.
private async _onInventoryChange(docId: string) {
const scheduled = this._pruner.requestPrune(docId);
if (!scheduled) {
await this._inventory.flush(docId);
}
}
// Extract actionHash, actionNum, and timezone from a document backup.
private async _getDocMetadata(fname: string): Promise<{[key: string]: string}> {
const result: Record<string, string> = {};

@ -12,6 +12,7 @@ export interface IDocStorageManager {
// In the current implementation, it is called in the context of an
// AsyncCreate[docName].
prepareLocalDoc(docName: string, docSession: OptDocSession): Promise<boolean>;
prepareToCreateDoc(docName: string): Promise<void>;
listDocs(): Promise<DocEntry[]>;
deleteDoc(docName: string, deletePermanently?: boolean): Promise<void>;
@ -24,7 +25,7 @@ export interface IDocStorageManager {
// If reason is set to 'edit' the user-facing timestamp on the document should be updated.
markAsChanged(docName: string, reason?: 'edit'): void;
testReopenStorage(): void; // restart storage during tests
addToStorage(docName: string): void; // add a new local document to storage
addToStorage(docName: string): Promise<void>; // add a new local document to storage
prepareToCloseStorage(): void; // speed up sync with remote store
getCopy(docName: string): Promise<string>; // get an immutable copy of a document

Loading…
Cancel
Save