Skip to content

Commit

Permalink
Nodes only need to be loaded to be orphaned
Browse files Browse the repository at this point in the history
Fixes a problem with cascading deletions
  • Loading branch information
TylerZeroMaster committed May 20, 2024
1 parent a6b23f3 commit 5307162
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 18 deletions.
28 changes: 26 additions & 2 deletions server/src/model-manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,23 +318,47 @@ describe('processFilesystemChange()', () => {
loadSuccess(first(loadSuccess(first(loadSuccess(manager.bundle).books)).pages))

const sizeBefore = manager.bundle.allNodes.size

expect(manager.bundle.allPages.all.size).toBe(1)
expect(manager.bundle.allResources.all.size).toBe(0)
// Delete non-existent file
expect((await fireChange(FileChangeType.Deleted, 'media/newpic.png')).toArray()).toEqual([])
// Delete a file
const deletedModules = await fireChange(FileChangeType.Deleted, 'modules/m1234/index.cnxml')
expect(deletedModules.size).toBe(1)
expect(first(deletedModules)).toBeInstanceOf(PageNode)
// Page is still referenced by book: should still be in bundle
expect(manager.bundle.allPages.all.size).toBe(1)
// Delete a directory
expect((await fireChange(FileChangeType.Deleted, 'collections')).size).toBe(1)
// expect(sendDiagnosticsStub.callCount).toBe(0)
// Book deleted -> page orphaned -> page deleted
expect(manager.bundle.allPages.all.size).toBe(0)

// Delete everything (including the bundle)
// All nodes should still be in bundle, but they should no longer exist
expect((await fireChange(FileChangeType.Deleted, '')).size).toBe(sizeBefore)
expect((await fireChange(FileChangeType.Deleted, '')).size).toBe(sizeBefore - 1)
expect(manager.bundle.exists).toBe(false)
expect(manager.bundle.allNodes.every((n) => !n.exists))
})
it('cascades deletes correctly', async () => {
// Load the Bundle, Book, and Page
loadSuccess(first(loadSuccess(first(loadSuccess(manager.bundle).books)).pages))

const sizeBefore = manager.bundle.allNodes.size
expect(manager.bundle.allBooks.all.size).toBe(1)
expect(manager.bundle.allPages.all.size).toBe(1)
expect(manager.bundle.allResources.all.size).toBe(0)
expect(manager.bundle.allNodes.size).toBe(3) // bundle + book + page

// Delete everything (including the bundle)
expect((await fireChange(FileChangeType.Deleted, '')).size).toBe(sizeBefore)
expect(manager.bundle.allBooks.all.size).toBe(0)
expect(manager.bundle.allPages.all.size).toBe(0)
// Only the bundle should remain (bundles are immortal at the moment)
expect(manager.bundle.allNodes.size).toBe(1)
expect(first(manager.bundle.allNodes)).toBe(manager.bundle)
expect(manager.bundle.exists).toBe(false)
})
it('deletes Image/Page/Book', async () => {
// Load the Bundle, Book, and Page
const bundle = loadSuccess(manager.bundle)
Expand Down
38 changes: 22 additions & 16 deletions server/src/model-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,26 +176,26 @@ export class ModelManager {
}

public get orphanedBooks() {
const allBooks = this.bundle.allBooks.all.filter(loadedAndExists)
const loadedBooks = this.bundle.allBooks.all.filter((n) => n.isLoaded)
const referencedBooks = this.bundle.books
return allBooks.subtract(referencedBooks)
return !this.bundle.exists ? loadedBooks : loadedBooks.subtract(referencedBooks)
}

public get orphanedPages() {
const books = this.bundle.books.filter(loadedAndExists)
return this.bundle.allPages.all.filter(loadedAndExists).subtract(books.flatMap(b => b.pages))
return this.bundle.allPages.all.filter((n) => n.isLoaded).subtract(books.flatMap(b => b.pages))
}

public get orphanedResources() {
const books = this.bundle.books.filter(loadedAndExists)
const pages = books.flatMap(b => b.pages).filter(loadedAndExists)
return this.bundle.allResources.all.filter(loadedAndExists).subtract(pages.flatMap(p => p.resources))
return this.bundle.allResources.all.filter((n) => n.isLoaded).subtract(pages.flatMap(p => p.resources))
}

public get orphanedH5P() {
const books = this.bundle.books.filter(loadedAndExists)
const pages = books.flatMap(b => b.pages).filter(loadedAndExists)
return this.bundle.allH5P.all.filter(loadedAndExists).subtract(pages.flatMap(p => p.h5p))
return this.bundle.allH5P.all.filter((n) => n.isLoaded).subtract(pages.flatMap(p => p.h5p))
}

public get orphanedNodes() {
Expand Down Expand Up @@ -285,14 +285,15 @@ export class ModelManager {
ModelManager.debug('[FILESYSTEM_EVENT] Removing everything with this URI (including subdirectories if they exist)', uri)

const removedNodes = I.Set<Fileish>().withMutations(s => {
const orphanedNodes = this.orphanedNodes
const markRemoved = <T extends Fileish>(n: T) => {
ModelManager.debug(`[MODEL_MANAGER] Marking as removed: ${n.absPath}`)
this.errorHashesByPath.delete(n.absPath)
n.load(undefined)
s.add(n)
}
const filePathDir = `${uri}${PATH_SEP}`
// NOTE: order is important. Ideally try to delete things that could
// hold references first (books/pages)
const allFactories = [
bundle.allBooks, bundle.allPages, bundle.allH5P, bundle.allResources
]
Expand All @@ -309,7 +310,7 @@ export class ModelManager {
this.sendAllDiagnostics()

// Then remove nodes if they are orphaned, loaded, and not existing
orphanedNodes
this.orphanedNodes
.filter(({ isLoaded, exists }) => isLoaded && !exists)
.forEach(({ absPath }) => {
ModelManager.debug(`[MODEL_MANAGER] Dropping: ${absPath}`)
Expand Down Expand Up @@ -476,14 +477,17 @@ export class ModelManager {
.length > 0
},
getRange: this.rangeFinderFactory('src="', '"'),
getCompletionItems: (page, range) => this.orphanedResources.toArray().map(i => {
const insertText = path.relative(path.dirname(page.absPath), i.absPath)
const item = CompletionItem.create(insertText)
item.textEdit = TextEdit.replace(range, insertText)
item.kind = CompletionItemKind.File
item.detail = 'Orphaned Resource'
return item
})
getCompletionItems: (page, range) => this.orphanedResources
.filter((r) => r.exists)
.toArray()
.map(i => {
const insertText = path.relative(path.dirname(page.absPath), i.absPath)
const item = CompletionItem.create(insertText)
item.textEdit = TextEdit.replace(range, insertText)
item.kind = CompletionItemKind.File
item.detail = 'Orphaned Resource'
return item
})
}
return this.autocomplete(page, cursor, resourceAutocompleter)
}
Expand All @@ -498,7 +502,9 @@ export class ModelManager {
},
getRange: this.rangeFinderFactory('url="', '"'),
getCompletionItems: (_page, range) => {
return this.orphanedH5P.toArray().filter((h) => h.exists)
return this.orphanedH5P
.filter((h) => h.exists)
.toArray()
.map((h) => path.dirname(h.absPath))
.map((p) => path.basename(p))
.map((name) => {
Expand Down

0 comments on commit 5307162

Please sign in to comment.