MOBILE-3977 filepool: Optimize files table
parent
808a242cbc
commit
7c834281ce
|
@ -271,6 +271,7 @@ testsConfig['rules']['padded-blocks'] = [
|
|||
switches: 'never',
|
||||
},
|
||||
];
|
||||
testsConfig['rules']['jest/expect-expect'] = 'off';
|
||||
testsConfig['plugins'].push('jest');
|
||||
testsConfig['extends'].push('plugin:jest/recommended');
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import { CorePromisedValue } from '@classes/promised-value';
|
|||
import { SQLiteDB, SQLiteDBRecordValues } from '@classes/sqlitedb';
|
||||
import { CoreDatabaseReducer, CoreDatabaseTable, CoreDatabaseConditions, GetDBRecordPrimaryKey } from './database-table';
|
||||
import { CoreEagerDatabaseTable } from './eager-database-table';
|
||||
import { CoreLazyDatabaseTable } from './lazy-database-table';
|
||||
|
||||
/**
|
||||
* Database table proxy used to route database interactions through different implementations.
|
||||
|
@ -164,6 +165,8 @@ export class CoreDatabaseTableProxy<
|
|||
switch (cachingStrategy) {
|
||||
case CoreDatabaseCachingStrategy.Eager:
|
||||
return new CoreEagerDatabaseTable(this.database, this.tableName, this.primaryKeyColumns);
|
||||
case CoreDatabaseCachingStrategy.Lazy:
|
||||
return new CoreLazyDatabaseTable(this.database, this.tableName, this.primaryKeyColumns);
|
||||
case CoreDatabaseCachingStrategy.None:
|
||||
return new CoreDatabaseTable(this.database, this.tableName, this.primaryKeyColumns);
|
||||
}
|
||||
|
@ -183,5 +186,6 @@ export interface CoreDatabaseConfiguration {
|
|||
*/
|
||||
export enum CoreDatabaseCachingStrategy {
|
||||
Eager = 'eager',
|
||||
Lazy = 'lazy',
|
||||
None = 'none',
|
||||
}
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import { SQLiteDBRecordValues } from '@classes/sqlitedb';
|
||||
import { CoreDatabaseTable, CoreDatabaseConditions, GetDBRecordPrimaryKey } from './database-table';
|
||||
|
||||
/**
|
||||
* Wrapper used to improve performance by caching records that are used often for faster read operations.
|
||||
*
|
||||
* This implementation works best for tables that have a lot of records and are read often; for tables with a few records use
|
||||
* CoreEagerDatabaseTable instead.
|
||||
*/
|
||||
export class CoreLazyDatabaseTable<
|
||||
DBRecord extends SQLiteDBRecordValues = SQLiteDBRecordValues,
|
||||
PrimaryKeyColumn extends keyof DBRecord = 'id',
|
||||
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
|
||||
> extends CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> {
|
||||
|
||||
protected records: Record<string, DBRecord | null> = {};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async find(conditions: Partial<DBRecord>): Promise<DBRecord> {
|
||||
let record: DBRecord | null =
|
||||
Object.values(this.records).find(record => record && this.recordMatches(record, conditions)) ?? null;
|
||||
|
||||
if (!record) {
|
||||
record = await super.find(conditions);
|
||||
|
||||
this.records[this.serializePrimaryKey(this.getPrimaryKeyFromRecord(record))] = record;
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async findByPrimaryKey(primaryKey: PrimaryKey): Promise<DBRecord> {
|
||||
const serializePrimaryKey = this.serializePrimaryKey(primaryKey);
|
||||
|
||||
if (!(serializePrimaryKey in this.records)) {
|
||||
try {
|
||||
const record = await super.findByPrimaryKey(primaryKey);
|
||||
|
||||
this.records[serializePrimaryKey] = record;
|
||||
|
||||
return record;
|
||||
} catch (error) {
|
||||
this.records[serializePrimaryKey] = null;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const record = this.records[serializePrimaryKey];
|
||||
|
||||
if (!record) {
|
||||
throw new CoreError('No records found.');
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async insert(record: DBRecord): Promise<void> {
|
||||
await super.insert(record);
|
||||
|
||||
this.records[this.serializePrimaryKey(this.getPrimaryKeyFromRecord(record))] = record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async update(updates: Partial<DBRecord>, conditions?: Partial<DBRecord>): Promise<void> {
|
||||
await super.update(updates, conditions);
|
||||
|
||||
for (const record of Object.values(this.records)) {
|
||||
if (!record || (conditions && !this.recordMatches(record, conditions))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object.assign(record, updates);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async updateWhere(updates: Partial<DBRecord>, conditions: CoreDatabaseConditions<DBRecord>): Promise<void> {
|
||||
await super.updateWhere(updates, conditions);
|
||||
|
||||
for (const record of Object.values(this.records)) {
|
||||
if (!record || !conditions.js(record)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object.assign(record, updates);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async delete(conditions?: Partial<DBRecord>): Promise<void> {
|
||||
await super.delete(conditions);
|
||||
|
||||
for (const [primaryKey, record] of Object.entries(this.records)) {
|
||||
if (!record || (conditions && !this.recordMatches(record, conditions))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.records[primaryKey] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async deleteByPrimaryKey(primaryKey: PrimaryKey): Promise<void> {
|
||||
await super.deleteByPrimaryKey(primaryKey);
|
||||
|
||||
this.records[this.serializePrimaryKey(primaryKey)] = null;
|
||||
}
|
||||
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { mock } from '@/testing/utils';
|
||||
import { CoreDatabaseTable } from '@classes/database/database-table';
|
||||
import {
|
||||
CoreDatabaseCachingStrategy,
|
||||
CoreDatabaseConfiguration,
|
||||
|
@ -30,7 +31,7 @@ function userMatches(user: User, conditions: Partial<User>) {
|
|||
return !Object.entries(conditions).some(([column, value]) => user[column] !== value);
|
||||
}
|
||||
|
||||
function prepareStubs(config: Partial<CoreDatabaseConfiguration> = {}): [User[], SQLiteDB, CoreDatabaseTableProxy<User>] {
|
||||
function prepareStubs(config: Partial<CoreDatabaseConfiguration> = {}): [User[], SQLiteDB, CoreDatabaseTable<User>] {
|
||||
const records: User[] = [];
|
||||
const database = mock<SQLiteDB>({
|
||||
getRecord: async <T>(_, conditions) => {
|
||||
|
@ -68,21 +69,7 @@ function prepareStubs(config: Partial<CoreDatabaseConfiguration> = {}): [User[],
|
|||
return [records, database, table];
|
||||
}
|
||||
|
||||
describe('CoreDatabaseTable with eager caching', () => {
|
||||
|
||||
let records: User[];
|
||||
let database: SQLiteDB;
|
||||
let table: CoreDatabaseTableProxy<User>;
|
||||
|
||||
beforeEach(() => [records, database, table] = prepareStubs({ cachingStrategy: CoreDatabaseCachingStrategy.Eager }));
|
||||
|
||||
it('reads all records on initialization', async () => {
|
||||
await table.initialize();
|
||||
|
||||
expect(database.getAllRecords).toHaveBeenCalledWith('users');
|
||||
});
|
||||
|
||||
it('finds items', async () => {
|
||||
async function testFindItems(records: User[], table: CoreDatabaseTable<User>) {
|
||||
const john = { id: 1, name: 'John', surname: 'Doe' };
|
||||
const amy = { id: 2, name: 'Amy', surname: 'Doe' };
|
||||
|
||||
|
@ -95,11 +82,9 @@ describe('CoreDatabaseTable with eager caching', () => {
|
|||
await expect(table.findByPrimaryKey({ id: 2 })).resolves.toEqual(amy);
|
||||
await expect(table.find({ surname: 'Doe', name: 'John' })).resolves.toEqual(john);
|
||||
await expect(table.find({ surname: 'Doe', name: 'Amy' })).resolves.toEqual(amy);
|
||||
}
|
||||
|
||||
expect(database.getRecord).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('inserts items', async () => {
|
||||
async function testInsertItems(records: User[], database: SQLiteDB, table: CoreDatabaseTable<User>) {
|
||||
// Arrange.
|
||||
const john = { id: 1, name: 'John', surname: 'Doe' };
|
||||
|
||||
|
@ -112,9 +97,9 @@ describe('CoreDatabaseTable with eager caching', () => {
|
|||
expect(database.insertRecord).toHaveBeenCalledWith('users', john);
|
||||
|
||||
await expect(table.findByPrimaryKey({ id: 1 })).resolves.toEqual(john);
|
||||
});
|
||||
}
|
||||
|
||||
it('deletes items', async () => {
|
||||
async function testDeleteItems(records: User[], database: SQLiteDB, table: CoreDatabaseTable<User>) {
|
||||
// Arrange.
|
||||
const john = { id: 1, name: 'John', surname: 'Doe' };
|
||||
const amy = { id: 2, name: 'Amy', surname: 'Doe' };
|
||||
|
@ -135,9 +120,9 @@ describe('CoreDatabaseTable with eager caching', () => {
|
|||
await expect(table.findByPrimaryKey({ id: 1 })).rejects.toThrow();
|
||||
await expect(table.findByPrimaryKey({ id: 2 })).rejects.toThrow();
|
||||
await expect(table.findByPrimaryKey({ id: 3 })).resolves.toEqual(jane);
|
||||
});
|
||||
}
|
||||
|
||||
it('deletes items by primary key', async () => {
|
||||
async function testDeleteItemsByPrimaryKey(records: User[], database: SQLiteDB, table: CoreDatabaseTable<User>) {
|
||||
// Arrange.
|
||||
const john = { id: 1, name: 'John', surname: 'Doe' };
|
||||
const amy = { id: 2, name: 'Amy', surname: 'Doe' };
|
||||
|
@ -155,15 +140,66 @@ describe('CoreDatabaseTable with eager caching', () => {
|
|||
|
||||
await expect(table.findByPrimaryKey({ id: 1 })).rejects.toThrow();
|
||||
await expect(table.findByPrimaryKey({ id: 2 })).resolves.toEqual(amy);
|
||||
}
|
||||
|
||||
describe('CoreDatabaseTable with eager caching', () => {
|
||||
|
||||
let records: User[];
|
||||
let database: SQLiteDB;
|
||||
let table: CoreDatabaseTable<User>;
|
||||
|
||||
beforeEach(() => [records, database, table] = prepareStubs({ cachingStrategy: CoreDatabaseCachingStrategy.Eager }));
|
||||
|
||||
it('reads all records on initialization', async () => {
|
||||
await table.initialize();
|
||||
|
||||
expect(database.getAllRecords).toHaveBeenCalledWith('users');
|
||||
});
|
||||
|
||||
it('finds items', async () => {
|
||||
await testFindItems(records, table);
|
||||
|
||||
expect(database.getRecord).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('inserts items', () => testInsertItems(records, database, table));
|
||||
it('deletes items', () => testDeleteItems(records, database, table));
|
||||
it('deletes items by primary key', () => testDeleteItemsByPrimaryKey(records, database, table));
|
||||
|
||||
});
|
||||
|
||||
describe('CoreDatabaseTable with lazy caching', () => {
|
||||
|
||||
let records: User[];
|
||||
let database: SQLiteDB;
|
||||
let table: CoreDatabaseTable<User>;
|
||||
|
||||
beforeEach(() => [records, database, table] = prepareStubs({ cachingStrategy: CoreDatabaseCachingStrategy.Lazy }));
|
||||
|
||||
it('reads no records on initialization', async () => {
|
||||
await table.initialize();
|
||||
|
||||
expect(database.getRecords).not.toHaveBeenCalled();
|
||||
expect(database.getAllRecords).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('finds items', async () => {
|
||||
await testFindItems(records, table);
|
||||
|
||||
expect(database.getRecord).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('inserts items', () => testInsertItems(records, database, table));
|
||||
it('deletes items', () => testDeleteItems(records, database, table));
|
||||
it('deletes items by primary key', () => testDeleteItemsByPrimaryKey(records, database, table));
|
||||
|
||||
});
|
||||
|
||||
describe('CoreDatabaseTable with no caching', () => {
|
||||
|
||||
let records: User[];
|
||||
let database: SQLiteDB;
|
||||
let table: CoreDatabaseTableProxy<User>;
|
||||
let table: CoreDatabaseTable<User>;
|
||||
|
||||
beforeEach(() => [records, database, table] = prepareStubs({ cachingStrategy: CoreDatabaseCachingStrategy.None }));
|
||||
|
||||
|
@ -175,78 +211,13 @@ describe('CoreDatabaseTable with no caching', () => {
|
|||
});
|
||||
|
||||
it('finds items', async () => {
|
||||
const john = { id: 1, name: 'John', surname: 'Doe' };
|
||||
const amy = { id: 2, name: 'Amy', surname: 'Doe' };
|
||||
|
||||
records.push(john);
|
||||
records.push(amy);
|
||||
|
||||
await table.initialize();
|
||||
|
||||
await expect(table.findByPrimaryKey({ id: 1 })).resolves.toEqual(john);
|
||||
await expect(table.findByPrimaryKey({ id: 2 })).resolves.toEqual(amy);
|
||||
await expect(table.find({ surname: 'Doe', name: 'John' })).resolves.toEqual(john);
|
||||
await expect(table.find({ surname: 'Doe', name: 'Amy' })).resolves.toEqual(amy);
|
||||
await testFindItems(records, table);
|
||||
|
||||
expect(database.getRecord).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
it('inserts items', async () => {
|
||||
// Arrange.
|
||||
const john = { id: 1, name: 'John', surname: 'Doe' };
|
||||
|
||||
await table.initialize();
|
||||
|
||||
// Act.
|
||||
await table.insert(john);
|
||||
|
||||
// Assert.
|
||||
expect(database.insertRecord).toHaveBeenCalledWith('users', john);
|
||||
|
||||
await expect(table.findByPrimaryKey({ id: 1 })).resolves.toEqual(john);
|
||||
});
|
||||
|
||||
it('deletes items', async () => {
|
||||
// Arrange.
|
||||
const john = { id: 1, name: 'John', surname: 'Doe' };
|
||||
const amy = { id: 2, name: 'Amy', surname: 'Doe' };
|
||||
const jane = { id: 3, name: 'Jane', surname: 'Smith' };
|
||||
|
||||
records.push(john);
|
||||
records.push(amy);
|
||||
records.push(jane);
|
||||
|
||||
await table.initialize();
|
||||
|
||||
// Act.
|
||||
await table.delete({ surname: 'Doe' });
|
||||
|
||||
// Assert.
|
||||
expect(database.deleteRecords).toHaveBeenCalledWith('users', { surname: 'Doe' });
|
||||
|
||||
await expect(table.findByPrimaryKey({ id: 1 })).rejects.toThrow();
|
||||
await expect(table.findByPrimaryKey({ id: 2 })).rejects.toThrow();
|
||||
await expect(table.findByPrimaryKey({ id: 3 })).resolves.toEqual(jane);
|
||||
});
|
||||
|
||||
it('deletes items by primary key', async () => {
|
||||
// Arrange.
|
||||
const john = { id: 1, name: 'John', surname: 'Doe' };
|
||||
const amy = { id: 2, name: 'Amy', surname: 'Doe' };
|
||||
|
||||
records.push(john);
|
||||
records.push(amy);
|
||||
|
||||
await table.initialize();
|
||||
|
||||
// Act.
|
||||
await table.deleteByPrimaryKey({ id: 1 });
|
||||
|
||||
// Assert.
|
||||
expect(database.deleteRecords).toHaveBeenCalledWith('users', { id: 1 });
|
||||
|
||||
await expect(table.findByPrimaryKey({ id: 1 })).rejects.toThrow();
|
||||
await expect(table.findByPrimaryKey({ id: 2 })).resolves.toEqual(amy);
|
||||
});
|
||||
it('inserts items', () => testInsertItems(records, database, table));
|
||||
it('deletes items', () => testDeleteItems(records, database, table));
|
||||
it('deletes items by primary key', () => testDeleteItemsByPrimaryKey(records, database, table));
|
||||
|
||||
});
|
||||
|
|
|
@ -48,6 +48,9 @@ import {
|
|||
} from '@services/database/filepool';
|
||||
import { CoreFileHelper } from './file-helper';
|
||||
import { CoreUrl } from '@singletons/url';
|
||||
import { CorePromisedValue } from '@classes/promised-value';
|
||||
import { CoreDatabaseTable } from '@classes/database/database-table';
|
||||
import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy';
|
||||
|
||||
/*
|
||||
* Factory for handling downloading files and retrieve downloaded files.
|
||||
|
@ -72,9 +75,13 @@ export class CoreFilepoolProvider {
|
|||
protected static readonly ERR_FS_OR_NETWORK_UNAVAILABLE = 'CoreFilepoolError:ERR_FS_OR_NETWORK_UNAVAILABLE';
|
||||
protected static readonly ERR_QUEUE_ON_PAUSE = 'CoreFilepoolError:ERR_QUEUE_ON_PAUSE';
|
||||
|
||||
protected static readonly FILE_UPDATE_UNKNOWN_WHERE_CLAUSE =
|
||||
protected static readonly FILE_IS_UNKNOWN_SQL =
|
||||
'isexternalfile = 1 OR ((revision IS NULL OR revision = 0) AND (timemodified IS NULL OR timemodified = 0))';
|
||||
|
||||
protected static readonly FILE_IS_UNKNOWN_JS =
|
||||
({ isexternalfile, revision, timemodified }: CoreFilepoolFileEntry): boolean =>
|
||||
isexternalfile === 1 || ((revision === null || revision === 0) && (timemodified === null || timemodified === 0));
|
||||
|
||||
protected logger: CoreLogger;
|
||||
protected queueState = CoreFilepoolProvider.QUEUE_PAUSED;
|
||||
protected urlAttributes: RegExp[] = [
|
||||
|
@ -94,6 +101,7 @@ export class CoreFilepoolProvider {
|
|||
// Variables for DB.
|
||||
protected appDB: Promise<SQLiteDB>;
|
||||
protected resolveAppDB!: (appDB: SQLiteDB) => void;
|
||||
protected filesTables: Record<string, CorePromisedValue<CoreDatabaseTable<CoreFilepoolFileEntry, 'fileId'>>> = {};
|
||||
|
||||
constructor() {
|
||||
this.appDB = new Promise(resolve => this.resolveAppDB = resolve);
|
||||
|
@ -114,6 +122,18 @@ export class CoreFilepoolProvider {
|
|||
NgZone.run(() => this.checkQueueProcessing());
|
||||
});
|
||||
});
|
||||
|
||||
CoreEvents.on(CoreEvents.SITE_DELETED, async ({ siteId }) => {
|
||||
if (!siteId || !(siteId in this.filesTables)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filesTable = await this.filesTables[siteId];
|
||||
|
||||
delete this.filesTables[siteId];
|
||||
|
||||
await filesTable.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,6 +149,33 @@ export class CoreFilepoolProvider {
|
|||
this.resolveAppDB(CoreApp.getDB());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get files table.
|
||||
*
|
||||
* @param siteId Site id.
|
||||
* @returns Files table.
|
||||
*/
|
||||
async getFilesTable(siteId?: string): Promise<CoreDatabaseTable<CoreFilepoolFileEntry, 'fileId'>> {
|
||||
siteId = siteId ?? CoreSites.getCurrentSiteId();
|
||||
|
||||
if (!(siteId in this.filesTables)) {
|
||||
const filesTable = this.filesTables[siteId] = new CorePromisedValue();
|
||||
const database = await CoreSites.getSiteDb(siteId);
|
||||
const table = new CoreDatabaseTableProxy<CoreFilepoolFileEntry, 'fileId'>(
|
||||
{ cachingStrategy: CoreDatabaseCachingStrategy.Lazy },
|
||||
database,
|
||||
FILES_TABLE_NAME,
|
||||
['fileId'],
|
||||
);
|
||||
|
||||
await table.initialize();
|
||||
|
||||
filesTable.resolve(table);
|
||||
}
|
||||
|
||||
return this.filesTables[siteId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Link a file with a component.
|
||||
*
|
||||
|
@ -215,9 +262,9 @@ export class CoreFilepoolProvider {
|
|||
...data,
|
||||
};
|
||||
|
||||
const db = await CoreSites.getSiteDb(siteId);
|
||||
const filesTable = await this.getFilesTable(siteId);
|
||||
|
||||
await db.insertRecord(FILES_TABLE_NAME, record);
|
||||
await filesTable.insert(record);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -558,13 +605,14 @@ export class CoreFilepoolProvider {
|
|||
*/
|
||||
async clearFilepool(siteId: string): Promise<void> {
|
||||
const db = await CoreSites.getSiteDb(siteId);
|
||||
const filesTable = await this.getFilesTable(siteId);
|
||||
|
||||
// Read the data first to be able to notify the deletions.
|
||||
const filesEntries = await db.getAllRecords<CoreFilepoolFileEntry>(FILES_TABLE_NAME);
|
||||
const filesEntries = await filesTable.all();
|
||||
const filesLinks = await db.getAllRecords<CoreFilepoolLinksRecord>(LINKS_TABLE_NAME);
|
||||
|
||||
await Promise.all([
|
||||
db.deleteRecords(FILES_TABLE_NAME),
|
||||
filesTable.delete(),
|
||||
db.deleteRecords(LINKS_TABLE_NAME),
|
||||
]);
|
||||
|
||||
|
@ -1119,13 +1167,14 @@ export class CoreFilepoolProvider {
|
|||
}
|
||||
|
||||
const db = await CoreSites.getSiteDb(siteId);
|
||||
const filesTable = await this.getFilesTable(siteId);
|
||||
const extension = CoreMimetypeUtils.getFileExtension(entry.path);
|
||||
if (!extension) {
|
||||
// Files does not have extension. Invalidate file (stale = true).
|
||||
// Minor problem: file will remain in the filesystem once downloaded again.
|
||||
this.logger.debug('Staled file with no extension ' + entry.fileId);
|
||||
|
||||
await db.updateRecords(FILES_TABLE_NAME, { stale: 1 }, { fileId: entry.fileId });
|
||||
await filesTable.update({ stale: 1 }, { fileId: entry.fileId });
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1135,7 +1184,7 @@ export class CoreFilepoolProvider {
|
|||
entry.fileId = CoreMimetypeUtils.removeExtension(fileId);
|
||||
entry.extension = extension;
|
||||
|
||||
await db.updateRecords(FILES_TABLE_NAME, entry, { fileId });
|
||||
await filesTable.update(entry, { fileId });
|
||||
if (entry.fileId == fileId) {
|
||||
// File ID hasn't changed, we're done.
|
||||
this.logger.debug('Removed extesion ' + extension + ' from file ' + entry.fileId);
|
||||
|
@ -1396,15 +1445,13 @@ export class CoreFilepoolProvider {
|
|||
*/
|
||||
async getFilesByComponent(siteId: string, component: string, componentId?: string | number): Promise<CoreFilepoolFileEntry[]> {
|
||||
const db = await CoreSites.getSiteDb(siteId);
|
||||
const filesTable = await this.getFilesTable(siteId);
|
||||
const items = await this.getComponentFiles(db, component, componentId);
|
||||
const files: CoreFilepoolFileEntry[] = [];
|
||||
|
||||
await Promise.all(items.map(async (item) => {
|
||||
try {
|
||||
const fileEntry = await db.getRecord<CoreFilepoolFileEntry>(
|
||||
FILES_TABLE_NAME,
|
||||
{ fileId: item.fileId },
|
||||
);
|
||||
const fileEntry = await filesTable.findByPrimaryKey({ fileId: item.fileId });
|
||||
|
||||
if (!fileEntry) {
|
||||
return;
|
||||
|
@ -2137,14 +2184,9 @@ export class CoreFilepoolProvider {
|
|||
* @return Resolved with file object from DB on success, rejected otherwise.
|
||||
*/
|
||||
protected async hasFileInPool(siteId: string, fileId: string): Promise<CoreFilepoolFileEntry> {
|
||||
const db = await CoreSites.getSiteDb(siteId);
|
||||
const entry = await db.getRecord<CoreFilepoolFileEntry>(FILES_TABLE_NAME, { fileId });
|
||||
const filesTable = await this.getFilesTable(siteId);
|
||||
|
||||
if (entry === undefined) {
|
||||
throw new CoreError('File not found in filepool.');
|
||||
}
|
||||
|
||||
return entry;
|
||||
return filesTable.findByPrimaryKey({ fileId });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2176,11 +2218,17 @@ export class CoreFilepoolProvider {
|
|||
* @return Resolved on success.
|
||||
*/
|
||||
async invalidateAllFiles(siteId: string, onlyUnknown: boolean = true): Promise<void> {
|
||||
const db = await CoreSites.getSiteDb(siteId);
|
||||
const filesTable = await this.getFilesTable(siteId);
|
||||
|
||||
const where = onlyUnknown ? CoreFilepoolProvider.FILE_UPDATE_UNKNOWN_WHERE_CLAUSE : undefined;
|
||||
|
||||
await db.updateRecordsWhere(FILES_TABLE_NAME, { stale: 1 }, where);
|
||||
onlyUnknown
|
||||
? await filesTable.updateWhere(
|
||||
{ stale: 1 },
|
||||
{
|
||||
sql: CoreFilepoolProvider.FILE_IS_UNKNOWN_SQL,
|
||||
js: CoreFilepoolProvider.FILE_IS_UNKNOWN_JS,
|
||||
},
|
||||
)
|
||||
: await filesTable.update({ stale: 1 });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2199,9 +2247,9 @@ export class CoreFilepoolProvider {
|
|||
const file = await this.fixPluginfileURL(siteId, fileUrl);
|
||||
const fileId = this.getFileIdByUrl(CoreFileHelper.getFileUrl(file));
|
||||
|
||||
const db = await CoreSites.getSiteDb(siteId);
|
||||
const filesTable = await this.getFilesTable(siteId);
|
||||
|
||||
await db.updateRecords(FILES_TABLE_NAME, { stale: 1 }, { fileId });
|
||||
await filesTable.update({ stale: 1 }, { fileId });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2221,7 +2269,7 @@ export class CoreFilepoolProvider {
|
|||
onlyUnknown: boolean = true,
|
||||
): Promise<void> {
|
||||
const db = await CoreSites.getSiteDb(siteId);
|
||||
|
||||
const filesTable = await this.getFilesTable(siteId);
|
||||
const items = await this.getComponentFiles(db, component, componentId);
|
||||
|
||||
if (!items.length) {
|
||||
|
@ -2236,10 +2284,19 @@ export class CoreFilepoolProvider {
|
|||
whereAndParams.sql = 'fileId ' + whereAndParams.sql;
|
||||
|
||||
if (onlyUnknown) {
|
||||
whereAndParams.sql += ' AND (' + CoreFilepoolProvider.FILE_UPDATE_UNKNOWN_WHERE_CLAUSE + ')';
|
||||
whereAndParams.sql += ' AND (' + CoreFilepoolProvider.FILE_IS_UNKNOWN_SQL + ')';
|
||||
}
|
||||
|
||||
await db.updateRecordsWhere(FILES_TABLE_NAME, { stale: 1 }, whereAndParams.sql, whereAndParams.params);
|
||||
await filesTable.updateWhere(
|
||||
{ stale: 1 },
|
||||
{
|
||||
sql: whereAndParams.sql,
|
||||
sqlParams: whereAndParams.params,
|
||||
js: record => fileIds.includes(record.fileId) && (
|
||||
!onlyUnknown || CoreFilepoolProvider.FILE_IS_UNKNOWN_JS(record)
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2657,6 +2714,8 @@ export class CoreFilepoolProvider {
|
|||
*/
|
||||
protected async removeFileById(siteId: string, fileId: string): Promise<void> {
|
||||
const db = await CoreSites.getSiteDb(siteId);
|
||||
const filesTable = await this.getFilesTable(siteId);
|
||||
|
||||
// Get the path to the file first since it relies on the file object stored in the pool.
|
||||
// Don't use getFilePath to prevent performing 2 DB requests.
|
||||
let path = this.getFilepoolFolderPath(siteId) + '/' + fileId;
|
||||
|
@ -2682,7 +2741,7 @@ export class CoreFilepoolProvider {
|
|||
const promises: Promise<unknown>[] = [];
|
||||
|
||||
// Remove entry from filepool store.
|
||||
promises.push(db.deleteRecords(FILES_TABLE_NAME, conditions));
|
||||
promises.push(filesTable.delete(conditions));
|
||||
|
||||
// Remove links.
|
||||
promises.push(db.deleteRecords(LINKS_TABLE_NAME, conditions));
|
||||
|
|
Loading…
Reference in New Issue