From 5269a4b7fa66830c9bbe5027009c5077aadb75fa Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sun, 8 Nov 2020 16:46:48 +0000 Subject: [PATCH] Chore: Convert ResourceService to TypeScript --- .eslintignore | 6 ++ .gitignore | 6 ++ .ignore | 6 ++ packages/app-cli/app/app.js | 2 +- packages/app-cli/tests/models_ItemChange.js | 2 +- ...Service.js => services_ResourceService.ts} | 67 +++++++++--------- .../app-cli/tests/services_SearchFilter.js | 2 +- .../app-cli/tests/services_SearchFuzzy.js | 2 +- packages/app-cli/tests/services_rest_Api.ts | 2 +- packages/app-cli/tests/test-utils.js | 2 +- packages/app-desktop/app.ts | 2 +- packages/app-mobile/root.js | 2 +- packages/lib/models/Resource.js | 2 +- packages/lib/services/DecryptionWorker.js | 2 +- packages/lib/services/ResourceFetcher.js | 2 +- ...{ResourceService.js => ResourceService.ts} | 70 +++++++++---------- 16 files changed, 98 insertions(+), 79 deletions(-) rename packages/app-cli/tests/{services_ResourceService.js => services_ResourceService.ts} (80%) rename packages/lib/services/{ResourceService.js => ResourceService.ts} (78%) diff --git a/.eslintignore b/.eslintignore index 941c1fde5c..b9aeb5bab7 100644 --- a/.eslintignore +++ b/.eslintignore @@ -184,6 +184,9 @@ packages/app-cli/tests/services_InteropService.js.map packages/app-cli/tests/services_PluginService.d.ts packages/app-cli/tests/services_PluginService.js packages/app-cli/tests/services_PluginService.js.map +packages/app-cli/tests/services_ResourceService.d.ts +packages/app-cli/tests/services_ResourceService.js +packages/app-cli/tests/services_ResourceService.js.map packages/app-cli/tests/services_keychainService.d.ts packages/app-cli/tests/services_keychainService.js packages/app-cli/tests/services_keychainService.js.map @@ -898,6 +901,9 @@ packages/lib/services/ResourceEditWatcher/index.js.map packages/lib/services/ResourceEditWatcher/reducer.d.ts packages/lib/services/ResourceEditWatcher/reducer.js packages/lib/services/ResourceEditWatcher/reducer.js.map +packages/lib/services/ResourceService.d.ts +packages/lib/services/ResourceService.js +packages/lib/services/ResourceService.js.map packages/lib/services/SettingUtils.d.ts packages/lib/services/SettingUtils.js packages/lib/services/SettingUtils.js.map diff --git a/.gitignore b/.gitignore index d481802daa..6e832ab25c 100644 --- a/.gitignore +++ b/.gitignore @@ -176,6 +176,9 @@ packages/app-cli/tests/services_InteropService.js.map packages/app-cli/tests/services_PluginService.d.ts packages/app-cli/tests/services_PluginService.js packages/app-cli/tests/services_PluginService.js.map +packages/app-cli/tests/services_ResourceService.d.ts +packages/app-cli/tests/services_ResourceService.js +packages/app-cli/tests/services_ResourceService.js.map packages/app-cli/tests/services_keychainService.d.ts packages/app-cli/tests/services_keychainService.js packages/app-cli/tests/services_keychainService.js.map @@ -890,6 +893,9 @@ packages/lib/services/ResourceEditWatcher/index.js.map packages/lib/services/ResourceEditWatcher/reducer.d.ts packages/lib/services/ResourceEditWatcher/reducer.js packages/lib/services/ResourceEditWatcher/reducer.js.map +packages/lib/services/ResourceService.d.ts +packages/lib/services/ResourceService.js +packages/lib/services/ResourceService.js.map packages/lib/services/SettingUtils.d.ts packages/lib/services/SettingUtils.js packages/lib/services/SettingUtils.js.map diff --git a/.ignore b/.ignore index 54156dcc67..12474285cf 100644 --- a/.ignore +++ b/.ignore @@ -131,6 +131,9 @@ packages/app-cli/tests/services_InteropService.js.map packages/app-cli/tests/services_PluginService.d.ts packages/app-cli/tests/services_PluginService.js packages/app-cli/tests/services_PluginService.js.map +packages/app-cli/tests/services_ResourceService.d.ts +packages/app-cli/tests/services_ResourceService.js +packages/app-cli/tests/services_ResourceService.js.map packages/app-cli/tests/services_keychainService.d.ts packages/app-cli/tests/services_keychainService.js packages/app-cli/tests/services_keychainService.js.map @@ -845,6 +848,9 @@ packages/lib/services/ResourceEditWatcher/index.js.map packages/lib/services/ResourceEditWatcher/reducer.d.ts packages/lib/services/ResourceEditWatcher/reducer.js packages/lib/services/ResourceEditWatcher/reducer.js.map +packages/lib/services/ResourceService.d.ts +packages/lib/services/ResourceService.js +packages/lib/services/ResourceService.js.map packages/lib/services/SettingUtils.d.ts packages/lib/services/SettingUtils.js packages/lib/services/SettingUtils.js.map diff --git a/packages/app-cli/app/app.js b/packages/app-cli/app/app.js index 946572d369..f8bf7397fd 100644 --- a/packages/app-cli/app/app.js +++ b/packages/app-cli/app/app.js @@ -1,6 +1,6 @@ const BaseApplication = require('@joplin/lib/BaseApplication').default; const { FoldersScreenUtils } = require('@joplin/lib/folders-screen-utils.js'); -const ResourceService = require('@joplin/lib/services/ResourceService'); +const ResourceService = require('@joplin/lib/services/ResourceService').default; const BaseModel = require('@joplin/lib/BaseModel').default; const Folder = require('@joplin/lib/models/Folder.js'); const BaseItem = require('@joplin/lib/models/BaseItem.js'); diff --git a/packages/app-cli/tests/models_ItemChange.js b/packages/app-cli/tests/models_ItemChange.js index 58cc590f30..0f4023b0c3 100644 --- a/packages/app-cli/tests/models_ItemChange.js +++ b/packages/app-cli/tests/models_ItemChange.js @@ -4,7 +4,7 @@ const time = require('@joplin/lib/time').default; const { asyncTest, fileContentEqual, revisionService, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js'); const SearchEngine = require('@joplin/lib/services/searchengine/SearchEngine'); -const ResourceService = require('@joplin/lib/services/ResourceService'); +const ResourceService = require('@joplin/lib/services/ResourceService').default; const ItemChangeUtils = require('@joplin/lib/services/ItemChangeUtils'); const Note = require('@joplin/lib/models/Note'); const Setting = require('@joplin/lib/models/Setting').default; diff --git a/packages/app-cli/tests/services_ResourceService.js b/packages/app-cli/tests/services_ResourceService.ts similarity index 80% rename from packages/app-cli/tests/services_ResourceService.js rename to packages/app-cli/tests/services_ResourceService.ts index 7379498792..1ae3606bad 100644 --- a/packages/app-cli/tests/services_ResourceService.js +++ b/packages/app-cli/tests/services_ResourceService.ts @@ -1,38 +1,14 @@ -/* eslint-disable no-unused-vars */ +import time from '@joplin/lib/time'; +import NoteResource from '@joplin/lib/models/NoteResource'; +import ResourceService from '@joplin/lib/services/ResourceService'; +import shim from '@joplin/lib/shim'; - -const time = require('@joplin/lib/time').default; -const { asyncTest, resourceService, decryptionWorker, encryptionService, loadEncryptionMasterKey, allSyncTargetItemsEncrypted, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js'); -const InteropService = require('@joplin/lib/services/interop/InteropService').default; +const { asyncTest, resourceService, decryptionWorker, encryptionService, loadEncryptionMasterKey, allSyncTargetItemsEncrypted, setupDatabaseAndSynchronizer, db, synchronizer, switchClient } = require('./test-utils.js'); const Folder = require('@joplin/lib/models/Folder.js'); const Note = require('@joplin/lib/models/Note.js'); -const Tag = require('@joplin/lib/models/Tag.js'); -const NoteTag = require('@joplin/lib/models/NoteTag.js'); const Resource = require('@joplin/lib/models/Resource.js'); -const ItemChange = require('@joplin/lib/models/ItemChange.js'); -const NoteResource = require('@joplin/lib/models/NoteResource').default; -const ResourceService = require('@joplin/lib/services/ResourceService.js'); -const fs = require('fs-extra'); -const ArrayUtils = require('@joplin/lib/ArrayUtils'); -const ObjectUtils = require('@joplin/lib/ObjectUtils'); -const shim = require('@joplin/lib/shim').default; const SearchEngine = require('@joplin/lib/services/searchengine/SearchEngine'); -process.on('unhandledRejection', (reason, p) => { - console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); -}); - -function exportDir() { - return `${__dirname}/export`; -} - -function fieldsEqual(model1, model2, fieldNames) { - for (let i = 0; i < fieldNames.length; i++) { - const f = fieldNames[i]; - expect(model1[f]).toBe(model2[f], `For key ${f}`); - } -} - describe('services_ResourceService', function() { beforeEach(async (done) => { @@ -99,7 +75,7 @@ describe('services_ResourceService', function() { it('should not delete a resource that has never been associated with any note, because it probably means the resource came via sync, and associated note has not arrived yet', asyncTest(async () => { const service = new ResourceService(); - const resource = await shim.createResourceFromPath(`${__dirname}/../tests/support/photo.jpg`); + await shim.createResourceFromPath(`${__dirname}/../tests/support/photo.jpg`); await service.indexNoteResources(); await service.deleteOrphanResources(0); @@ -130,9 +106,8 @@ describe('services_ResourceService', function() { const service = new ResourceService(); const folder1 = await Folder.save({ title: 'folder1' }); - let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id }); - note1 = await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`); - const resource1 = (await Resource.all())[0]; + const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id }); + await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`); await service.indexNoteResources(); @@ -212,4 +187,30 @@ describe('services_ResourceService', function() { expect(!!nr.is_associated).toBe(true); // And it should have fixed the situation by re-indexing the note content })); + // it('should auto-delete resource even if the associated note was deleted immediately', asyncTest(async () => { + // // Previoulsy, when a resource was be attached to a note, then the + // // note was immediately deleted, the ResourceService would not have + // // time to quick in an index the resource/note relation. It means + // // that when doing the orphan resource deletion job, those + // // resources would permanently stay behing. + // // https://github.com/laurent22/joplin/issues/932 + + // const service = new ResourceService(); + + // let note = await Note.save({}); + // note = await shim.attachFileToNote(note, `${__dirname}/../tests/support/photo.jpg`); + // const resource = (await Resource.all())[0]; + + // const noteIds = await NoteResource.associatedNoteIds(resource.id); + + // expect(noteIds[0]).toBe(note.id); + + // await Note.save({ id: note.id, body: '' }); + + // await resourceService().indexNoteResources(); + // await service.deleteOrphanResources(0); + + // expect((await Resource.all()).length).toBe(0); + // })); + }); diff --git a/packages/app-cli/tests/services_SearchFilter.js b/packages/app-cli/tests/services_SearchFilter.js index cd3b79ca20..7e0fbfb91a 100644 --- a/packages/app-cli/tests/services_SearchFilter.js +++ b/packages/app-cli/tests/services_SearchFilter.js @@ -12,7 +12,7 @@ const ItemChange = require('@joplin/lib/models/ItemChange'); const Setting = require('@joplin/lib/models/Setting').default; const Resource = require('@joplin/lib/models/Resource.js'); const shim = require('@joplin/lib/shim').default; -const ResourceService = require('@joplin/lib/services/ResourceService.js'); +const ResourceService = require('@joplin/lib/services/ResourceService').default; process.on('unhandledRejection', (reason, p) => { diff --git a/packages/app-cli/tests/services_SearchFuzzy.js b/packages/app-cli/tests/services_SearchFuzzy.js index e3880afde2..14d3fd37d7 100644 --- a/packages/app-cli/tests/services_SearchFuzzy.js +++ b/packages/app-cli/tests/services_SearchFuzzy.js @@ -12,7 +12,7 @@ // const Setting = require('@joplin/lib/models/Setting'); // const Resource = require('@joplin/lib/models/Resource.js'); // const shim = require('@joplin/lib/shim').default; -// const ResourceService = require('@joplin/lib/services/ResourceService.js'); +// const ResourceService = require('@joplin/lib/services/ResourceService').default; // process.on('unhandledRejection', (reason, p) => { // console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); diff --git a/packages/app-cli/tests/services_rest_Api.ts b/packages/app-cli/tests/services_rest_Api.ts index 8edb91921a..c78f274172 100644 --- a/packages/app-cli/tests/services_rest_Api.ts +++ b/packages/app-cli/tests/services_rest_Api.ts @@ -8,7 +8,7 @@ const Resource = require('@joplin/lib/models/Resource'); const Note = require('@joplin/lib/models/Note'); const Tag = require('@joplin/lib/models/Tag'); const NoteTag = require('@joplin/lib/models/NoteTag'); -const ResourceService = require('@joplin/lib/services/ResourceService'); +const ResourceService = require('@joplin/lib/services/ResourceService').default; async function msleep(ms:number) { return new Promise((resolve) => { diff --git a/packages/app-cli/tests/test-utils.js b/packages/app-cli/tests/test-utils.js index 39db0139d7..c5c41a400a 100644 --- a/packages/app-cli/tests/test-utils.js +++ b/packages/app-cli/tests/test-utils.js @@ -38,7 +38,7 @@ const SyncTargetDropbox = require('@joplin/lib/SyncTargetDropbox.js'); const SyncTargetAmazonS3 = require('@joplin/lib/SyncTargetAmazonS3.js'); const EncryptionService = require('@joplin/lib/services/EncryptionService.js'); const DecryptionWorker = require('@joplin/lib/services/DecryptionWorker.js'); -const ResourceService = require('@joplin/lib/services/ResourceService.js'); +const ResourceService = require('@joplin/lib/services/ResourceService').default; const RevisionService = require('@joplin/lib/services/RevisionService.js'); const ResourceFetcher = require('@joplin/lib/services/ResourceFetcher.js'); const KvStore = require('@joplin/lib/services/KvStore').default; diff --git a/packages/app-desktop/app.ts b/packages/app-desktop/app.ts index 83978c5001..979dc42f13 100644 --- a/packages/app-desktop/app.ts +++ b/packages/app-desktop/app.ts @@ -27,7 +27,7 @@ const Tag = require('@joplin/lib/models/Tag.js'); const { reg } = require('@joplin/lib/registry.js'); const packageInfo = require('./packageInfo.js'); const DecryptionWorker = require('@joplin/lib/services/DecryptionWorker'); -const ResourceService = require('@joplin/lib/services/ResourceService'); +const ResourceService = require('@joplin/lib/services/ResourceService').default; const ClipperServer = require('@joplin/lib/ClipperServer'); const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher'); const { webFrame } = require('electron'); diff --git a/packages/app-mobile/root.js b/packages/app-mobile/root.js index 3895cfb837..0f7d6c05e4 100644 --- a/packages/app-mobile/root.js +++ b/packages/app-mobile/root.js @@ -31,7 +31,7 @@ const MasterKey = require('@joplin/lib/models/MasterKey.js'); const Revision = require('@joplin/lib/models/Revision.js'); const BaseModel = require('@joplin/lib/BaseModel').default; const BaseService = require('@joplin/lib/services/BaseService').default; -const ResourceService = require('@joplin/lib/services/ResourceService'); +const ResourceService = require('@joplin/lib/services/ResourceService').default; const RevisionService = require('@joplin/lib/services/RevisionService'); const KvStore = require('@joplin/lib/services/KvStore').default; const { JoplinDatabase } = require('@joplin/lib/joplin-database.js'); diff --git a/packages/lib/models/Resource.js b/packages/lib/models/Resource.js index 78f6849d02..cf59f72f61 100644 --- a/packages/lib/models/Resource.js +++ b/packages/lib/models/Resource.js @@ -1,7 +1,7 @@ const BaseModel = require('../BaseModel').default; const BaseItem = require('./BaseItem.js'); const ItemChange = require('./ItemChange.js'); -const NoteResource = require('./NoteResource.js'); +const NoteResource = require('./NoteResource').default; const ResourceLocalState = require('./ResourceLocalState.js'); const Setting = require('./Setting').default; const pathUtils = require('../path-utils'); diff --git a/packages/lib/services/DecryptionWorker.js b/packages/lib/services/DecryptionWorker.js index 948738620e..5c8b14c544 100644 --- a/packages/lib/services/DecryptionWorker.js +++ b/packages/lib/services/DecryptionWorker.js @@ -2,7 +2,7 @@ const BaseItem = require('../models/BaseItem'); const BaseModel = require('../BaseModel').default; const MasterKey = require('../models/MasterKey'); const Resource = require('../models/Resource'); -const ResourceService = require('./ResourceService'); +const ResourceService = require('./ResourceService').default; const Logger = require('../Logger').default; const EventEmitter = require('events'); const shim = require('../shim').default; diff --git a/packages/lib/services/ResourceFetcher.js b/packages/lib/services/ResourceFetcher.js index 319d312d13..afa05800d0 100644 --- a/packages/lib/services/ResourceFetcher.js +++ b/packages/lib/services/ResourceFetcher.js @@ -1,7 +1,7 @@ const Resource = require('../models/Resource'); const Setting = require('../models/Setting').default; const BaseService = require('./BaseService').default; -const ResourceService = require('./ResourceService'); +const ResourceService = require('./ResourceService').default; const { Dirnames } = require('./synchronizer/utils/types'); const Logger = require('../Logger').default; const EventEmitter = require('events'); diff --git a/packages/lib/services/ResourceService.js b/packages/lib/services/ResourceService.ts similarity index 78% rename from packages/lib/services/ResourceService.js rename to packages/lib/services/ResourceService.ts index a953756eb8..3e91d73e42 100644 --- a/packages/lib/services/ResourceService.js +++ b/packages/lib/services/ResourceService.ts @@ -1,31 +1,24 @@ +import NoteResource from '../models/NoteResource'; +import BaseModel from '../BaseModel'; +import BaseService from './BaseService'; +import Setting from '../models/Setting'; +import shim from '../shim'; const ItemChange = require('../models/ItemChange'); -const NoteResource = require('../models/NoteResource').default; const Note = require('../models/Note'); const Resource = require('../models/Resource'); -const BaseModel = require('../BaseModel').default; -const BaseService = require('./BaseService').default; const SearchEngine = require('./searchengine/SearchEngine'); -const Setting = require('../models/Setting').default; -const shim = require('../shim').default; const ItemChangeUtils = require('./ItemChangeUtils'); const { sprintf } = require('sprintf-js'); -class ResourceService extends BaseService { - constructor() { - super(); +export default class ResourceService extends BaseService { - this.maintenanceCalls_ = []; - this.maintenanceTimer1_ = null; - this.maintenanceTimer2_ = null; - } + private static isRunningInBackground_:boolean = false; - static instance() { - if (this.instance_) return this.instance_; - this.instance_ = new ResourceService(); - return this.instance_; - } + private maintenanceCalls_:boolean[] = []; + private maintenanceTimer1_:any = null + private maintenanceTimer2_:any = null - async indexNoteResources() { + public async indexNoteResources() { this.logger().info('ResourceService::indexNoteResources: Start'); await ItemChange.waitForAllSaved(); @@ -46,10 +39,10 @@ class ResourceService extends BaseService { if (!changes.length) break; - const noteIds = changes.map(a => a.item_id); + const noteIds = changes.map((a:any) => a.item_id); const notes = await Note.modelSelectAll(`SELECT id, title, body, encryption_applied FROM notes WHERE id IN ("${noteIds.join('","')}")`); - const noteById = noteId => { + const noteById = (noteId:string) => { for (let i = 0; i < notes.length; i++) { if (notes[i].id === noteId) return notes[i]; } @@ -77,7 +70,7 @@ class ResourceService extends BaseService { break; } - await this.setAssociatedResources_(note); + await this.setAssociatedResources(note.id, note.body); } else { this.logger().warn(`ResourceService::indexNoteResources: A change was recorded for a note that has been deleted: ${change.item_id}`); } @@ -102,12 +95,12 @@ class ResourceService extends BaseService { this.logger().info('ResourceService::indexNoteResources: Completed'); } - async setAssociatedResources_(note) { - const resourceIds = await Note.linkedResourceIds(note.body); - await NoteResource.setAssociatedResources(note.id, resourceIds); + public async setAssociatedResources(noteId:string, noteBody:string) { + const resourceIds = await Note.linkedResourceIds(noteBody); + await NoteResource.setAssociatedResources(noteId, resourceIds); } - async deleteOrphanResources(expiryDelay = null) { + public async deleteOrphanResources(expiryDelay:number = null) { if (expiryDelay === null) expiryDelay = Setting.value('revisionService.ttlDays') * 24 * 60 * 60 * 1000; const resourceIds = await NoteResource.orphanResources(expiryDelay); this.logger().info('ResourceService::deleteOrphanResources:', resourceIds); @@ -118,7 +111,7 @@ class ResourceService extends BaseService { const note = await Note.load(results[0].id); if (note) { this.logger().info(sprintf('ResourceService::deleteOrphanResources: Skipping deletion of resource %s because it is still referenced in note %s. Re-indexing note content to fix the issue.', resourceId, note.id)); - await this.setAssociatedResources_(note); + await this.setAssociatedResources(note.id, note.body); } } else { await Resource.delete(resourceId); @@ -126,7 +119,7 @@ class ResourceService extends BaseService { } } - static async autoSetFileSize(resourceId, filePath, waitTillExists = true) { + private static async autoSetFileSize(resourceId:string, filePath:string, waitTillExists:boolean = true) { const itDoes = await shim.fsDriver().waitTillExists(filePath, waitTillExists ? 10000 : 0); if (!itDoes) { // this.logger().warn('Trying to set file size on non-existent resource:', resourceId, filePath); @@ -136,7 +129,7 @@ class ResourceService extends BaseService { await Resource.setFileSizeOnly(resourceId, fileStat.size); } - static async autoSetFileSizes() { + public static async autoSetFileSizes() { const resources = await Resource.needFileSizeSet(); for (const r of resources) { @@ -144,7 +137,7 @@ class ResourceService extends BaseService { } } - async maintenance() { + public async maintenance() { this.maintenanceCalls_.push(true); try { await this.indexNoteResources(); @@ -154,7 +147,7 @@ class ResourceService extends BaseService { } } - static runInBackground() { + public static runInBackground() { if (this.isRunningInBackground_) return; this.isRunningInBackground_ = true; @@ -169,13 +162,13 @@ class ResourceService extends BaseService { }, 1000 * 60 * 60 * 4); } - async cancelTimers() { + public async cancelTimers() { if (this.maintenanceTimer1_) { - shim.clearTimeout(this.maintenanceTimer1); + shim.clearTimeout(this.maintenanceTimer1_); this.maintenanceTimer1_ = null; } if (this.maintenanceTimer2_) { - shim.clearInterval(this.maintenanceTimer2); + shim.clearInterval(this.maintenanceTimer2_); this.maintenanceTimer2_ = null; } @@ -188,6 +181,13 @@ class ResourceService extends BaseService { }, 100); }); } -} -module.exports = ResourceService; + private static instance_:ResourceService = null; + + public static instance() { + if (this.instance_) return this.instance_; + this.instance_ = new ResourceService(); + return this.instance_; + } + +}