1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-11-24 08:12:24 +02:00

Chore: Convert ResourceService to TypeScript

This commit is contained in:
Laurent Cozic 2020-11-08 16:46:48 +00:00
parent 88a2f9c85c
commit 5269a4b7fa
16 changed files with 98 additions and 79 deletions

View File

@ -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

6
.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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');

View File

@ -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;

View File

@ -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);
// }));
});

View File

@ -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) => {

View File

@ -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);

View File

@ -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) => {

View File

@ -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;

View File

@ -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');

View File

@ -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');

View File

@ -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');

View File

@ -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;

View File

@ -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');

View File

@ -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_;
}
}