Move document improvements (#927)

* Show all collections in UI

* Introduce command pattern

* Actually remove from previous collection

* Stash

* Fixes: Promises resolved outside of response lifecycle

* 💚

* 💚

* documentMover tests

* Transaction

* Perf. More in transactions
This commit is contained in:
Tom Moor
2019-04-08 21:25:13 -07:00
committed by GitHub
parent 16066c0b24
commit 763f57a3dc
16 changed files with 313 additions and 146 deletions

View File

@@ -156,8 +156,12 @@ Collection.prototype.addDocumentToStructure = async function(
) {
if (!this.documentStructure) return;
let unlock;
// documentStructure can only be updated by one request at a time
const unlock = await asyncLock(`collection-${this.id}`);
if (options.save !== false) {
unlock = await asyncLock(`collection-${this.id}`);
}
// If moving existing document with children, use existing structure
const documentJson = {
@@ -195,8 +199,11 @@ Collection.prototype.addDocumentToStructure = async function(
// Sequelize doesn't seem to set the value with splice on JSONB field
this.documentStructure = this.documentStructure;
await this.save(options);
unlock();
if (options.save !== false) {
await this.save(options);
if (unlock) unlock();
}
return this;
};
@@ -234,34 +241,21 @@ Collection.prototype.updateDocument = async function(
return this;
};
/**
* moveDocument is combination of removing the document from the structure
* and placing it back the the new location with the existing children.
*/
Collection.prototype.moveDocument = async function(document, index) {
if (!this.documentStructure) return;
const documentJson = await this.removeDocumentInStructure(document);
await this.addDocumentToStructure(document, index, { documentJson });
};
Collection.prototype.deleteDocument = async function(document) {
await this.removeDocumentInStructure(document, { save: true });
await this.removeDocumentInStructure(document);
await document.deleteWithChildren();
};
Collection.prototype.removeDocumentInStructure = async function(
document,
options?: { save?: boolean }
options
) {
if (!this.documentStructure) return;
let returnValue;
let unlock;
if (options && options.save) {
// documentStructure can only be updated by one request at the time
unlock = await asyncLock(`collection-${this.id}`);
}
// documentStructure can only be updated by one request at the time
unlock = await asyncLock(`collection-${this.id}`);
const removeFromChildren = async (children, id) => {
children = await Promise.all(
@@ -287,10 +281,8 @@ Collection.prototype.removeDocumentInStructure = async function(
document.id
);
if (options && options.save) {
await this.save(options);
if (unlock) await unlock();
}
await this.save(options);
if (unlock) await unlock();
return returnValue;
};

View File

@@ -145,37 +145,7 @@ describe('#updateDocument', () => {
});
});
describe('#moveDocument', () => {
test('should move a document without children', async () => {
const { collection, document } = await seed();
expect(collection.documentStructure[1].id).toBe(document.id);
await collection.moveDocument(document, 0);
expect(collection.documentStructure[0].id).toBe(document.id);
});
test('should move a document with children', async () => {
const { collection, document } = await seed();
const newDocument = await Document.create({
parentDocumentId: document.id,
collectionId: collection.id,
teamId: collection.teamId,
userId: collection.creatorId,
lastModifiedById: collection.creatorId,
createdById: collection.creatorId,
title: 'Child document',
text: 'content',
});
await collection.addDocumentToStructure(newDocument);
await collection.moveDocument(document, 0);
expect(collection.documentStructure[0].children[0].id).toBe(newDocument.id);
});
});
describe('#removeDocument', () => {
const destroyMock = jest.fn();
test('should save if removing', async () => {
const { collection, document } = await seed();
jest.spyOn(collection, 'save');
@@ -260,18 +230,4 @@ describe('#removeDocument', () => {
});
expect(collectionDocuments.count).toBe(2);
});
describe('options: deleteDocument = false', () => {
test('should remove documents from the structure but not destroy them from the DB', async () => {
const { collection, document } = await seed();
jest.spyOn(collection, 'save');
const removedNode = await collection.removeDocumentInStructure(document);
expect(collection.documentStructure.length).toBe(1);
expect(destroyMock).not.toBeCalled();
expect(collection.save).not.toBeCalled();
expect(removedNode.id).toBe(document.id);
expect(removedNode.children).toEqual([]);
});
});
});

View File

@@ -362,7 +362,7 @@ Document.prototype.publish = async function() {
Document.prototype.archive = async function(userId) {
// archive any children and remove from the document structure
const collection = await this.getCollection();
await collection.removeDocumentInStructure(this, { save: true });
await collection.removeDocumentInStructure(this);
this.collection = collection;
await this.archiveWithChildren(userId);