diff --git a/packages/server/src/models/ChangeModel.test.ts b/packages/server/src/models/ChangeModel.test.ts index e26e66994..1aae4199a 100644 --- a/packages/server/src/models/ChangeModel.test.ts +++ b/packages/server/src/models/ChangeModel.test.ts @@ -1,4 +1,4 @@ -import { createUserAndSession, beforeAllDb, afterAllTests, beforeEachDb, models, expectThrow, createFolder, createItemTree3, expectNotThrow, createNote, updateNote } from '../utils/testing/testUtils'; +import { createUserAndSession, beforeAllDb, afterAllTests, beforeEachDb, models, expectThrow, createFolder, createItemTree3, expectNotThrow, createNote, updateNote, deleteNote } from '../utils/testing/testUtils'; import { ChangeType } from '../services/database/types'; import { Day, msleep } from '../utils/time'; import { ChangePagination } from './ChangeModel'; @@ -271,4 +271,58 @@ describe('ChangeModel', () => { jest.useRealTimers(); }); + test('should return whole item when doing a delta call', async () => { + const { user, session } = await createUserAndSession(1, true); + + await createItemTree3(user.id, '', '', [ + { + id: '000000000000000000000000000000F1', + title: 'Folder 1', + children: [ + { + id: '00000000000000000000000000000001', + title: 'Note 1', + }, + { + id: '00000000000000000000000000000002', + title: 'Note 2', + }, + ], + }, + ]); + + let cursor = ''; + + { + const result = await models().change().delta(user.id); + cursor = result.cursor; + const titles = result.items.map(it => it.jopItem.title).sort(); + expect(titles).toEqual(['Folder 1', 'Note 1', 'Note 2']); + } + + await msleep(1); + + await updateNote(session.id, { + id: '00000000000000000000000000000001', + title: 'new title', + }); + + { + const result = await models().change().delta(user.id, { cursor }); + cursor = result.cursor; + expect(result.items.length).toBe(1); + expect(result.items[0].jopItem.title).toBe('new title'); + } + + await msleep(1); + + await deleteNote(user.id, '00000000000000000000000000000002'); + + { + const result = await models().change().delta(user.id, { cursor }); + expect(result.items.length).toBe(1); + expect(result.items[0].jopItem).toBe(null); + } + }); + }); diff --git a/packages/server/src/models/ChangeModel.ts b/packages/server/src/models/ChangeModel.ts index 7830361ab..d83a7d87d 100644 --- a/packages/server/src/models/ChangeModel.ts +++ b/packages/server/src/models/ChangeModel.ts @@ -14,6 +14,7 @@ export const defaultChangeTtl = 180 * Day; export interface DeltaChange extends Change { jop_updated_time?: number; + jopItem: any; } export type PaginatedDeltaChanges = PaginatedResults; @@ -300,18 +301,33 @@ export default class ChangeModel extends BaseModel { false, ); - const items: Item[] = await this.db('items').select('id', 'jop_updated_time').whereIn('items.id', changes.map(c => c.item_id)); + let items: Item[] = await this.db('items').select('id', 'jop_updated_time').whereIn('items.id', changes.map(c => c.item_id)); let processedChanges = this.compressChanges(changes); processedChanges = await this.removeDeletedItems(processedChanges, items); - const finalChanges: DeltaChange[] = processedChanges.map(c => { - const item = items.find(item => item.id === c.item_id); - if (!item) return c; - return { - ...c, + items = await this.models().item().loadWithContentMulti(processedChanges.map(c => c.item_id), { + fields: [ + 'content', + 'id', + 'jop_encryption_applied', + 'jop_id', + 'jop_parent_id', + 'jop_share_id', + 'jop_type', + 'jop_updated_time', + ], + }); + + const finalChanges = processedChanges.map(change => { + const item = items.find(item => item.id === change.item_id); + if (!item) return { ...change, jopItem: null }; + const deltaChange: DeltaChange = { + ...change, jop_updated_time: item.jop_updated_time, + jopItem: item.jop_type ? this.models().item().itemToJoplinItem(item) : null, }; + return deltaChange; }); return { diff --git a/packages/server/src/utils/testing/testUtils.ts b/packages/server/src/utils/testing/testUtils.ts index 2711d763a..c50360182 100644 --- a/packages/server/src/utils/testing/testUtils.ts +++ b/packages/server/src/utils/testing/testUtils.ts @@ -376,6 +376,11 @@ export async function updateNote(sessionId: string, note: NoteEntity): Promise { + const item = await models().item().loadByJopId(userId, noteJopId, { fields: ['id'] }); + await models().item().delete(item.id); +} + export async function updateFolder(sessionId: string, folder: FolderEntity): Promise { return updateItem(sessionId, `root:/${folder.id}.md:`, makeFolderSerializedBody(folder)); }