MOBILE-3188 Web services: Record component that owns WS cache item

This commit adds optional component and componentId fields to the
preSets variable, which are recorded in the wscache table.

These are then used by (a) core site plugins where a module cmid
is available, and (b) the forum (as an example core implementation)
so that module site plugins and forum provide the new data.

(Note that this is not going to be very useful where we retrieve
data for multiple activities at once, which happens for activities
like Page. Still, it is optional.)
main
sam marshall 2019-10-11 16:03:28 +01:00 committed by Dani Palou
parent b67ea14abb
commit d0c93f6416
11 changed files with 97 additions and 23 deletions

View File

@ -354,7 +354,8 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
this.page = 0; this.page = 0;
} }
return this.forumProvider.getDiscussions(this.forum.id, this.selectedSortOrder.value, this.page).then((response) => { return this.forumProvider.getDiscussions(this.forum.id, this.forum.cmid,
this.selectedSortOrder.value, this.page).then((response) => {
let promise; let promise;
if (this.usesGroups) { if (this.usesGroups) {
promise = this.forumProvider.formatDiscussionsGroups(this.forum.cmid, response.discussions); promise = this.forumProvider.formatDiscussionsGroups(this.forum.cmid, response.discussions);

View File

@ -323,7 +323,7 @@ export class AddonModForumDiscussionPage implements OnDestroy {
let ratingInfo; let ratingInfo;
return syncPromise.then(() => { return syncPromise.then(() => {
return this.forumProvider.getDiscussionPosts(this.discussionId).then((response) => { return this.forumProvider.getDiscussionPosts(this.discussionId, this.cmId).then((response) => {
onlinePosts = response.posts; onlinePosts = response.posts;
ratingInfo = response.ratinginfo; ratingInfo = response.ratinginfo;
}).then(() => { }).then(() => {
@ -412,7 +412,7 @@ export class AddonModForumDiscussionPage implements OnDestroy {
// The discussion object was not passed as parameter and there is no starting post. Should not happen. // The discussion object was not passed as parameter and there is no starting post. Should not happen.
if (!this.discussion) { if (!this.discussion) {
promises.push(this.loadDiscussion(this.forumId, this.discussionId)); promises.push(this.loadDiscussion(this.forumId, this.cmId, this.discussionId));
} }
return Promise.all(promises); return Promise.all(promises);
@ -479,13 +479,14 @@ export class AddonModForumDiscussionPage implements OnDestroy {
* Convenience function to load discussion. * Convenience function to load discussion.
* *
* @param forumId Forum ID. * @param forumId Forum ID.
* @param cmId Forum cmid.
* @param discussionId Discussion ID. * @param discussionId Discussion ID.
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
protected loadDiscussion(forumId: number, discussionId: number): Promise<void> { protected loadDiscussion(forumId: number, cmId: number, discussionId: number): Promise<void> {
// Fetch the discussion if not passed as parameter. // Fetch the discussion if not passed as parameter.
if (!this.discussion && forumId) { if (!this.discussion && forumId) {
return this.forumHelper.getDiscussionById(forumId, discussionId).then((discussion) => { return this.forumHelper.getDiscussionById(forumId, cmId, discussionId).then((discussion) => {
this.discussion = discussion; this.discussion = discussion;
this.discussionId = this.discussion.discussion; this.discussionId = this.discussion.discussion;
}).catch(() => { }).catch(() => {

View File

@ -492,15 +492,18 @@ export class AddonModForumProvider {
* Get forum discussion posts. * Get forum discussion posts.
* *
* @param discussionId Discussion ID. * @param discussionId Discussion ID.
* @param cmId Forum cmid.
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @return Promise resolved with forum posts and rating info. * @return Promise resolved with forum posts and rating info.
*/ */
getDiscussionPosts(discussionId: number, siteId?: string): Promise<{posts: any[], ratinginfo?: CoreRatingInfo}> { getDiscussionPosts(discussionId: number, cmId: number, siteId?: string): Promise<{posts: any[], ratinginfo?: CoreRatingInfo}> {
const params = { const params = {
discussionid: discussionId discussionid: discussionId
}; };
const preSets = { const preSets = {
cacheKey: this.getDiscussionPostsCacheKey(discussionId) cacheKey: this.getDiscussionPostsCacheKey(discussionId),
component: AddonModForumProvider.COMPONENT,
componentId: cmId
}; };
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
@ -592,6 +595,7 @@ export class AddonModForumProvider {
* Get forum discussions. * Get forum discussions.
* *
* @param forumId Forum ID. * @param forumId Forum ID.
* @param cmId Forum cmid
* @param sortOrder Sort order. * @param sortOrder Sort order.
* @param page Page. * @param page Page.
* @param forceCache True to always get the value from cache. false otherwise. * @param forceCache True to always get the value from cache. false otherwise.
@ -601,7 +605,8 @@ export class AddonModForumProvider {
* discussion ID is discussion.discussion. * discussion ID is discussion.discussion.
* - canLoadMore: True if there may be more discussions to load. * - canLoadMore: True if there may be more discussions to load.
*/ */
getDiscussions(forumId: number, sortOrder?: number, page: number = 0, forceCache?: boolean, siteId?: string): Promise<any> { getDiscussions(forumId: number, cmId: number, sortOrder?: number, page: number = 0,
forceCache?: boolean, siteId?: string): Promise<any> {
sortOrder = sortOrder || AddonModForumProvider.SORTORDER_LASTPOST_DESC; sortOrder = sortOrder || AddonModForumProvider.SORTORDER_LASTPOST_DESC;
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
@ -626,7 +631,9 @@ export class AddonModForumProvider {
} }
} }
const preSets: CoreSiteWSPreSets = { const preSets: CoreSiteWSPreSets = {
cacheKey: this.getDiscussionsListCacheKey(forumId, sortOrder) cacheKey: this.getDiscussionsListCacheKey(forumId, sortOrder),
component: AddonModForumProvider.COMPONENT,
componentId: cmId
}; };
if (forceCache) { if (forceCache) {
preSets.omitExpires = true; preSets.omitExpires = true;
@ -673,6 +680,7 @@ export class AddonModForumProvider {
* If a page fails, the discussions until that page will be returned along with a flag indicating an error occurred. * If a page fails, the discussions until that page will be returned along with a flag indicating an error occurred.
* *
* @param forumId Forum ID. * @param forumId Forum ID.
* @param cmId Forum cmid.
* @param sortOrder Sort order. * @param sortOrder Sort order.
* @param forceCache True to always get the value from cache, false otherwise. * @param forceCache True to always get the value from cache, false otherwise.
* @param numPages Number of pages to get. If not defined, all pages. * @param numPages Number of pages to get. If not defined, all pages.
@ -682,8 +690,8 @@ export class AddonModForumProvider {
* - discussions: List of discussions. * - discussions: List of discussions.
* - error: True if an error occurred, false otherwise. * - error: True if an error occurred, false otherwise.
*/ */
getDiscussionsInPages(forumId: number, sortOrder?: number, forceCache?: boolean, numPages?: number, startPage?: number, getDiscussionsInPages(forumId: number, cmId: number, sortOrder?: number, forceCache?: boolean,
siteId?: string): Promise<any> { numPages?: number, startPage?: number, siteId?: string): Promise<any> {
if (typeof numPages == 'undefined') { if (typeof numPages == 'undefined') {
numPages = -1; numPages = -1;
} }
@ -700,7 +708,7 @@ export class AddonModForumProvider {
const getPage = (page: number): Promise<any> => { const getPage = (page: number): Promise<any> => {
// Get page discussions. // Get page discussions.
return this.getDiscussions(forumId, sortOrder, page, forceCache, siteId).then((response) => { return this.getDiscussions(forumId, cmId, sortOrder, page, forceCache, siteId).then((response) => {
result.discussions = result.discussions.concat(response.discussions); result.discussions = result.discussions.concat(response.discussions);
numPages--; numPages--;
@ -753,7 +761,7 @@ export class AddonModForumProvider {
this.getAvailableSortOrders().forEach((sortOrder) => { this.getAvailableSortOrders().forEach((sortOrder) => {
// We need to get the list of discussions to be able to invalidate their posts. // We need to get the list of discussions to be able to invalidate their posts.
promises.push(this.getDiscussionsInPages(forum.id, sortOrder.value, true).then((response) => { promises.push(this.getDiscussionsInPages(forum.id, forum.cmid, sortOrder.value, true).then((response) => {
// Now invalidate the WS calls. // Now invalidate the WS calls.
const promises = []; const promises = [];

View File

@ -270,15 +270,16 @@ export class AddonModForumHelperProvider {
* This function is inefficient because it needs to fetch all discussion pages in the worst case. * This function is inefficient because it needs to fetch all discussion pages in the worst case.
* *
* @param forumId Forum ID. * @param forumId Forum ID.
* @param cmId Forum cmid
* @param discussionId Discussion ID. * @param discussionId Discussion ID.
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @return Promise resolved with the discussion data. * @return Promise resolved with the discussion data.
*/ */
getDiscussionById(forumId: number, discussionId: number, siteId?: string): Promise<any> { getDiscussionById(forumId: number, cmId: number, discussionId: number, siteId?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
const findDiscussion = (page: number): Promise<any> => { const findDiscussion = (page: number): Promise<any> => {
return this.forumProvider.getDiscussions(forumId, undefined, page, false, siteId).then((response) => { return this.forumProvider.getDiscussions(forumId, cmId, undefined, page, false, siteId).then((response) => {
if (response.discussions && response.discussions.length > 0) { if (response.discussions && response.discussions.length > 0) {
// Note that discussion.id is the main post ID but discussion ID is discussion.discussion. // Note that discussion.id is the main post ID but discussion ID is discussion.discussion.
const discussion = response.discussions.find((discussion) => discussion.discussion == discussionId); const discussion = response.discussions.find((discussion) => discussion.discussion == discussionId);

View File

@ -114,7 +114,8 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
protected getPostsForPrefetch(forum: any, siteId?: string): Promise<any[]> { protected getPostsForPrefetch(forum: any, siteId?: string): Promise<any[]> {
const promises = this.forumProvider.getAvailableSortOrders().map((sortOrder) => { const promises = this.forumProvider.getAvailableSortOrders().map((sortOrder) => {
// Get discussions in first 2 pages. // Get discussions in first 2 pages.
return this.forumProvider.getDiscussionsInPages(forum.id, sortOrder.value, false, 2, 0, siteId).then((response) => { return this.forumProvider.getDiscussionsInPages(forum.id, forum.cmid,
sortOrder.value, false, 2, 0, siteId).then((response) => {
if (response.error) { if (response.error) {
return Promise.reject(null); return Promise.reject(null);
} }
@ -122,7 +123,7 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
const promises = []; const promises = [];
response.discussions.forEach((discussion) => { response.discussions.forEach((discussion) => {
promises.push(this.forumProvider.getDiscussionPosts(discussion.discussion, siteId)); promises.push(this.forumProvider.getDiscussionPosts(discussion.discussion, forum.cmid, siteId));
}); });
return Promise.all(promises); return Promise.all(promises);

View File

@ -127,6 +127,17 @@ export interface CoreSiteWSPreSets {
* Defaults to CoreSite.FREQUENCY_USUALLY. * Defaults to CoreSite.FREQUENCY_USUALLY.
*/ */
updateFrequency?: number; updateFrequency?: number;
/**
* Component name. Optionally included if this request is being made on behalf of a specific
* component (e.g. activity).
*/
component?: string;
/**
* Component id. Optionally included when 'component' is set.
*/
componentId?: number;
} }
/** /**
@ -197,7 +208,7 @@ export class CoreSite {
protected wsProvider: CoreWSProvider; protected wsProvider: CoreWSProvider;
// Variables for the database. // Variables for the database.
static WS_CACHE_TABLE = 'wscache'; static WS_CACHE_TABLE = 'ws_cache';
static CONFIG_TABLE = 'core_site_config'; static CONFIG_TABLE = 'core_site_config';
// Versions of Moodle releases. // Versions of Moodle releases.
@ -1128,6 +1139,13 @@ export class CoreSite {
entry.key = preSets.cacheKey; entry.key = preSets.cacheKey;
} }
if (preSets.component) {
entry.component = preSets.component;
if (preSets.componentId) {
entry.componentId = preSets.componentId;
}
}
return this.db.insertRecord(CoreSite.WS_CACHE_TABLE, entry); return this.db.insertRecord(CoreSite.WS_CACHE_TABLE, entry);
}); });
} }

View File

@ -9,4 +9,4 @@
</core-context-menu> </core-context-menu>
</core-navbar-buttons> </core-navbar-buttons>
<core-site-plugins-plugin-content *ngIf="component && method" [component]="component" [method]="method" [args]="args" [initResult]="initResult" [data]="jsData" [pageTitle]="pageTitle" (onContentLoaded)="contentLoaded($event)" (onLoadingContent)="contentLoading($event)"></core-site-plugins-plugin-content> <core-site-plugins-plugin-content *ngIf="component && method" [component]="component" [componentId]="componentId" [method]="method" [args]="args" [initResult]="initResult" [data]="jsData" [pageTitle]="pageTitle" (onContentLoaded)="contentLoaded($event)" (onLoadingContent)="contentLoading($event)"></core-site-plugins-plugin-content>

View File

@ -37,6 +37,7 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C
@ViewChild(CoreSitePluginsPluginContentComponent) content: CoreSitePluginsPluginContentComponent; @ViewChild(CoreSitePluginsPluginContentComponent) content: CoreSitePluginsPluginContentComponent;
component: string; component: string;
componentId: number;
method: string; method: string;
args: any; args: any;
initResult: any; initResult: any;
@ -77,6 +78,7 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C
if (handler) { if (handler) {
this.component = handler.plugin.component; this.component = handler.plugin.component;
this.componentId = this.module.id;
this.method = handler.handlerSchema.method; this.method = handler.handlerSchema.method;
this.args = { this.args = {
courseid: this.courseId, courseid: this.courseId,

View File

@ -32,6 +32,7 @@ export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
@ViewChild('compile') compileComponent: ElementRef; @ViewChild('compile') compileComponent: ElementRef;
@Input() component: string; @Input() component: string;
@Input() componentId?: number;
@Input() method: string; @Input() method: string;
@Input() args: any; @Input() args: any;
@Input() initResult: any; // Result of the init WS call of the handler. @Input() initResult: any; // Result of the init WS call of the handler.
@ -92,7 +93,13 @@ export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
this.forceCompile = false; this.forceCompile = false;
return this.sitePluginsProvider.getContent(this.component, this.method, this.args, this.preSets).then((result) => { const preSets = Object.assign({}, this.preSets);
preSets.component = this.component;
if (this.componentId) {
preSets.componentId = this.componentId;
}
return this.sitePluginsProvider.getContent(this.component, this.method, this.args, preSets).then((result) => {
this.content = result.templates.length ? result.templates[0].html : ''; // Load first template. this.content = result.templates.length ? result.templates[0].html : ''; // Load first template.
this.javascript = result.javascript; this.javascript = result.javascript;
this.otherData = result.otherdata; this.otherData = result.otherdata;

View File

@ -514,7 +514,14 @@ export class CoreSitePluginsProvider {
promises.push(this.callWS(method, params, {cacheKey: cacheKey})); promises.push(this.callWS(method, params, {cacheKey: cacheKey}));
} else { } else {
// It's a method to get content. // It's a method to get content.
promises.push(this.getContent(component, method, args).then((result) => { const preSets = {
component: component,
componentId: undefined
};
if (module) {
preSets.componentId = module.id;
}
promises.push(this.getContent(component, method, args, preSets).then((result) => {
const subPromises = []; const subPromises = [];
// Prefetch the files in the content. // Prefetch the files in the content.

View File

@ -360,7 +360,7 @@ export class CoreSitesProvider {
// Site schema for this provider. // Site schema for this provider.
protected siteSchema: CoreSiteSchema = { protected siteSchema: CoreSiteSchema = {
name: 'CoreSitesProvider', name: 'CoreSitesProvider',
version: 1, version: 2,
canBeCleared: [ CoreSite.WS_CACHE_TABLE ], canBeCleared: [ CoreSite.WS_CACHE_TABLE ],
tables: [ tables: [
{ {
@ -382,6 +382,14 @@ export class CoreSitesProvider {
{ {
name: 'expirationTime', name: 'expirationTime',
type: 'INTEGER' type: 'INTEGER'
},
{
name: 'component',
type: 'TEXT'
},
{
name: 'componentId',
type: 'INTEGER'
} }
] ]
}, },
@ -399,7 +407,27 @@ export class CoreSitesProvider {
} }
] ]
} }
] ],
async migrate (db: SQLiteDB, oldVersion: number, siteId: string): Promise<any> {
if (oldVersion && oldVersion < 2) {
const newTable = CoreSite.WS_CACHE_TABLE;
const oldTable = 'wscache';
try {
await db.tableExists(oldTable);
} catch (error) {
// Old table does not exist, ignore.
return;
}
// Cannot use insertRecordsFrom because there are extra fields, so manually code INSERT INTO.
await db.execute(
'INSERT INTO ' + newTable + ' ' +
'SELECT id, data, key, expirationTime, NULL as component, NULL as componentId ' +
'FROM ' + oldTable);
return await db.dropTable(oldTable);
}
}
}; };
constructor(logger: CoreLoggerProvider, constructor(logger: CoreLoggerProvider,