Skip to content

Commit 310a35c

Browse files
committed
user cache
1 parent 0a8a696 commit 310a35c

File tree

3 files changed

+204
-24
lines changed

3 files changed

+204
-24
lines changed

src/core/classes/stored-cache.ts

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// (C) Copyright 2015 Moodle Pty Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import { CoreSites, CoreSiteSchema } from '@services/sites';
16+
import { CoreText } from '@singletons/text';
17+
import { SQLiteDB } from './sqlitedb';
18+
import { CorePromiseUtils } from '@singletons/promise-utils';
19+
20+
/**
21+
* A cache to store values in database.
22+
*
23+
* The data is organized by "entries" that are identified by an ID. Each entry can have multiple values stored,
24+
* and each value has its own timemodified.
25+
*
26+
* Values expire after a certain time.
27+
*/
28+
export class CoreStoredCache {
29+
30+
constructor(protected tableName: string) {
31+
32+
}
33+
34+
/**
35+
* Clear the cache. Erasing all the entries.
36+
*
37+
* @param siteId ID of the site. If not defined, use current site.
38+
*/
39+
async clear(siteId?: string): Promise<void> {
40+
const db = await this.getDb(siteId);
41+
42+
await db.deleteRecords(this.tableName);
43+
}
44+
45+
/**
46+
* Get all the data stored in the cache for a certain id.
47+
*
48+
* @param id The ID to identify the entry.
49+
* @param siteId ID of the site. If not defined, use current site.
50+
* @returns The data from the cache. Undefined if not found.
51+
*/
52+
async getEntry<T>(id: number, siteId?: string): Promise<T> {
53+
const db = await this.getDb(siteId);
54+
55+
const record = await db.getRecord<CoreStoredCacheRecord>(this.tableName, { id });
56+
57+
return CoreText.parseJSON(record.data);
58+
}
59+
60+
/**
61+
* Invalidate all the cached data for a certain entry.
62+
*
63+
* @param id The ID to identify the entry.
64+
* @param siteId ID of the site. If not defined, use current site.
65+
*/
66+
async invalidate(id: number, siteId?: string): Promise<void> {
67+
const db = await this.getDb(siteId);
68+
69+
await db.updateRecords(this.tableName, { timemodified: 0 }, { id });
70+
}
71+
72+
/**
73+
* Update the status of a module in the "cache".
74+
*
75+
* @param id The ID to identify the entry.
76+
* @param value Value to set.
77+
* @param siteId ID of the site. If not defined, use current site.
78+
* @returns The set value.
79+
*/
80+
async setEntry<T>(
81+
id: number,
82+
value: T,
83+
siteId?: string,
84+
): Promise<void> {
85+
const db = await this.getDb(siteId);
86+
87+
let entry = await CorePromiseUtils.ignoreErrors(this.getEntry<T>(id, siteId), { id });
88+
89+
entry = {
90+
...entry,
91+
...value,
92+
};
93+
94+
const record: CoreStoredCacheRecord = {
95+
id,
96+
timemodified: Date.now(),
97+
data: JSON.stringify(entry),
98+
};
99+
100+
await db.insertRecord(this.tableName, record);
101+
}
102+
103+
/**
104+
* Delete an entry from the cache.
105+
*
106+
* @param id ID of the entry to delete.
107+
* @param siteId ID of the site. If not defined, use current site.
108+
*/
109+
async deleteEntry(id: number, siteId?: string): Promise<void> {
110+
const db = await this.getDb(siteId);
111+
112+
await db.deleteRecords(this.tableName, { id });
113+
}
114+
115+
/**
116+
* Get the database to use.
117+
*
118+
* @param siteId ID of the site. If not defined, use current site.
119+
* @returns Database.
120+
*/
121+
protected async getDb(siteId?: string): Promise<SQLiteDB> {
122+
const site = await CoreSites.getSite(siteId);
123+
124+
return site.getDb();
125+
}
126+
127+
}
128+
129+
/**
130+
* Helper function to get the schema to store cache in the database.
131+
*
132+
* @param schemaName Name of the schema.
133+
* @param tableName Name of the table.
134+
* @returns Schema.
135+
*/
136+
export function getStoredCacheDBSchema(schemaName: string, tableName: string): CoreSiteSchema {
137+
return {
138+
name: schemaName,
139+
version: 1,
140+
canBeCleared: [tableName],
141+
tables: [
142+
{
143+
name: tableName,
144+
columns: [
145+
{
146+
name: 'id',
147+
type: 'INTEGER',
148+
primaryKey: true,
149+
},
150+
{
151+
name: 'data',
152+
type: 'TEXT',
153+
},
154+
{
155+
name: 'timemodified',
156+
type: 'INTEGER',
157+
},
158+
],
159+
},
160+
],
161+
};
162+
}
163+
164+
/**
165+
* Stored cache entry.
166+
*/
167+
type CoreStoredCacheRecord = {
168+
id: number;
169+
data: string;
170+
timemodified: number;
171+
};

src/core/features/user/services/database/user.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import { CoreSiteSchema } from '@services/sites';
1616
import { CoreUserBasicData } from '../user';
17+
import { getStoredCacheDBSchema } from '@classes/stored-cache';
1718

1819
/**
1920
* Database variables for CoreUser service.
@@ -33,18 +34,21 @@ export const CORE_USER_CACHE_SITE_SCHEMA: CoreSiteSchema = {
3334
primaryKey: true,
3435
},
3536
{
36-
name: 'fullname',
37+
name: 'data',
3738
type: 'TEXT',
3839
},
3940
{
40-
name: 'profileimageurl',
41-
type: 'TEXT',
41+
name: 'timemodified',
42+
type: 'INTEGER',
4243
},
4344
],
4445
},
4546
],
4647
};
4748

49+
export const USERS_CACHE_TABLE_NAME = 'users_cache';
50+
export const CORE_USER_CACHE_SITE_SCHEMA = getStoredCacheDBSchema('CoreUser', USERS_CACHE_TABLE_NAME);
51+
4852
/**
4953
* Database variables for CoreUserOffline service.
5054
*/

src/core/features/user/services/user.ts

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ import { makeSingleton, Translate } from '@singletons';
2525
import { CoreEvents, CoreEventSiteData, CoreEventUserDeletedData, CoreEventUserSuspendedData } from '@singletons/events';
2626
import { CoreStatusWithWarningsWSResponse, CoreWSExternalWarning } from '@services/ws';
2727
import { CoreError } from '@classes/errors/error';
28-
import { USERS_TABLE_NAME, CoreUserDBRecord } from './database/user';
28+
import { USERS_CACHE_TABLE_NAME, CoreUserDBRecord } from './database/user';
2929
import { CoreUrl } from '@singletons/url';
3030
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
3131
import { CoreCacheUpdateFrequency, CoreConstants } from '@/core/constants';
3232
import { CorePromiseUtils } from '@singletons/promise-utils';
3333
import { CoreTextFormat } from '@singletons/text';
3434
import { CORE_USER_PROFILE_REFRESHED, CORE_USER_PROFILE_PICTURE_UPDATED, CORE_USER_PARTICIPANTS_LIST_LIMIT } from '../constants';
35+
import { CoreStoredCache } from '@classes/stored-cache';
3536

3637
declare module '@singletons/events' {
3738

@@ -61,6 +62,7 @@ export class CoreUserProvider {
6162
static readonly PARTICIPANTS_LIST_LIMIT = CORE_USER_PARTICIPANTS_LIST_LIMIT;
6263

6364
protected logger: CoreLogger;
65+
protected userCache = new CoreStoredCache(USERS_CACHE_TABLE_NAME);
6466

6567
constructor() {
6668
this.logger = CoreLogger.getInstance('CoreUserProvider');
@@ -160,7 +162,7 @@ export class CoreUserProvider {
160162

161163
await Promise.all([
162164
this.invalidateUserCache(userId, site.getId()),
163-
site.getDb().deleteRecords(USERS_TABLE_NAME, { id: userId }),
165+
this.userCache.deleteEntry(userId, site.getId()),
164166
]);
165167
}
166168

@@ -303,9 +305,7 @@ export class CoreUserProvider {
303305
* @returns Promise resolve when the user is retrieved.
304306
*/
305307
protected async getUserFromLocalDb(userId: number, siteId?: string): Promise<CoreUserDBRecord> {
306-
const site = await CoreSites.getSite(siteId);
307-
308-
return site.getDb().getRecord(USERS_TABLE_NAME, { id: userId });
308+
return await this.userCache.getEntry(userId, siteId);
309309
}
310310

311311
/**
@@ -382,7 +382,8 @@ export class CoreUserProvider {
382382
if ('country' in user && user.country) {
383383
user.country = CoreCountries.getCountryName(user.country);
384384
}
385-
this.storeUser(user.id, user.fullname, user.profileimageurl);
385+
386+
this.storeUser(user);
386387

387388
return user;
388389
}
@@ -706,25 +707,26 @@ export class CoreUserProvider {
706707
/**
707708
* Store user basic information in local DB to be retrieved if the WS call fails.
708709
*
709-
* @param userId User ID.
710-
* @param fullname User full name.
711-
* @param avatar User avatar URL.
710+
* @param user User to store.
712711
* @param siteId ID of the site. If not defined, use current site.
713712
*/
714-
protected async storeUser(userId: number, fullname: string, avatar?: string, siteId?: string): Promise<void> {
715-
if (!userId) {
713+
protected async storeUser(user: CoreUserBasicData, siteId?: string): Promise<void> {
714+
if (!user.id) {
716715
return;
717716
}
718717

719-
const site = await CoreSites.getSite(siteId);
720-
718+
// Filter and map data to store.
721719
const userRecord: CoreUserDBRecord = {
722-
id: userId,
723-
fullname: fullname,
724-
profileimageurl: avatar,
720+
id: user.id,
721+
fullname: user.fullname,
722+
profileimageurl: user.profileimageurl,
723+
firstname: user.firstname,
724+
lastname: user.lastname,
725+
initials: user.initials,
725726
};
726727

727-
await site.getDb().insertRecord(USERS_TABLE_NAME, userRecord);
728+
729+
await this.userCache.setEntry(user.id, userRecord, siteId);
728730
}
729731

730732
/**
@@ -733,9 +735,8 @@ export class CoreUserProvider {
733735
* @param users Users to store.
734736
* @param siteId ID of the site. If not defined, use current site.
735737
*/
736-
async storeUsers(users: CoreUserBasicData[], siteId?: string): Promise<void> {
737-
await Promise.all(users.map((user) =>
738-
this.storeUser(Number(user.id), user.fullname, user.profileimageurl, siteId)));
738+
async storeUsers(users: CoreUserDBRecord[], siteId?: string): Promise<void> {
739+
await Promise.all(users.map((user) => this.storeUser(user, siteId)));
739740
}
740741

741742
/**
@@ -853,7 +854,11 @@ export type CoreUserProfilePictureUpdatedData = {
853854
export type CoreUserBasicData = {
854855
id: number; // ID of the user.
855856
fullname: string; // The fullname of the user.
856-
profileimageurl?: string; // User image profile URL - big version.
857+
profileimageurl: string; // User image profile URL - big version.
858+
firstname?: string; // The first name(s) of the user.
859+
lastname?: string; // The family name of the user.
860+
lastaccess?: number;
861+
initials?: string; // Initials.
857862
};
858863

859864
/**

0 commit comments

Comments
 (0)