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;
}
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;
if (this.usesGroups) {
promise = this.forumProvider.formatDiscussionsGroups(this.forum.cmid, response.discussions);

View File

@ -323,7 +323,7 @@ export class AddonModForumDiscussionPage implements OnDestroy {
let ratingInfo;
return syncPromise.then(() => {
return this.forumProvider.getDiscussionPosts(this.discussionId).then((response) => {
return this.forumProvider.getDiscussionPosts(this.discussionId, this.cmId).then((response) => {
onlinePosts = response.posts;
ratingInfo = response.ratinginfo;
}).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.
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);
@ -479,13 +479,14 @@ export class AddonModForumDiscussionPage implements OnDestroy {
* Convenience function to load discussion.
*
* @param forumId Forum ID.
* @param cmId Forum cmid.
* @param discussionId Discussion ID.
* @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.
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.discussionId = this.discussion.discussion;
}).catch(() => {

View File

@ -492,15 +492,18 @@ export class AddonModForumProvider {
* Get forum discussion posts.
*
* @param discussionId Discussion ID.
* @param cmId Forum cmid.
* @param siteId Site ID. If not defined, current site.
* @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 = {
discussionid: discussionId
};
const preSets = {
cacheKey: this.getDiscussionPostsCacheKey(discussionId)
cacheKey: this.getDiscussionPostsCacheKey(discussionId),
component: AddonModForumProvider.COMPONENT,
componentId: cmId
};
return this.sitesProvider.getSite(siteId).then((site) => {
@ -592,6 +595,7 @@ export class AddonModForumProvider {
* Get forum discussions.
*
* @param forumId Forum ID.
* @param cmId Forum cmid
* @param sortOrder Sort order.
* @param page Page.
* @param forceCache True to always get the value from cache. false otherwise.
@ -601,7 +605,8 @@ export class AddonModForumProvider {
* discussion ID is discussion.discussion.
* - 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;
return this.sitesProvider.getSite(siteId).then((site) => {
@ -626,7 +631,9 @@ export class AddonModForumProvider {
}
}
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getDiscussionsListCacheKey(forumId, sortOrder)
cacheKey: this.getDiscussionsListCacheKey(forumId, sortOrder),
component: AddonModForumProvider.COMPONENT,
componentId: cmId
};
if (forceCache) {
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.
*
* @param forumId Forum ID.
* @param cmId Forum cmid.
* @param sortOrder Sort order.
* @param forceCache True to always get the value from cache, false otherwise.
* @param numPages Number of pages to get. If not defined, all pages.
@ -682,8 +690,8 @@ export class AddonModForumProvider {
* - discussions: List of discussions.
* - error: True if an error occurred, false otherwise.
*/
getDiscussionsInPages(forumId: number, sortOrder?: number, forceCache?: boolean, numPages?: number, startPage?: number,
siteId?: string): Promise<any> {
getDiscussionsInPages(forumId: number, cmId: number, sortOrder?: number, forceCache?: boolean,
numPages?: number, startPage?: number, siteId?: string): Promise<any> {
if (typeof numPages == 'undefined') {
numPages = -1;
}
@ -700,7 +708,7 @@ export class AddonModForumProvider {
const getPage = (page: number): Promise<any> => {
// 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);
numPages--;
@ -753,7 +761,7 @@ export class AddonModForumProvider {
this.getAvailableSortOrders().forEach((sortOrder) => {
// 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.
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.
*
* @param forumId Forum ID.
* @param cmId Forum cmid
* @param discussionId Discussion ID.
* @param siteId Site ID. If not defined, current site.
* @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();
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) {
// Note that discussion.id is the main post ID but discussion ID is discussion.discussion.
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[]> {
const promises = this.forumProvider.getAvailableSortOrders().map((sortOrder) => {
// 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) {
return Promise.reject(null);
}
@ -122,7 +123,7 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
const promises = [];
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);

View File

@ -127,6 +127,17 @@ export interface CoreSiteWSPreSets {
* Defaults to CoreSite.FREQUENCY_USUALLY.
*/
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;
// Variables for the database.
static WS_CACHE_TABLE = 'wscache';
static WS_CACHE_TABLE = 'ws_cache';
static CONFIG_TABLE = 'core_site_config';
// Versions of Moodle releases.
@ -1128,6 +1139,13 @@ export class CoreSite {
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);
});
}

View File

@ -9,4 +9,4 @@
</core-context-menu>
</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;
component: string;
componentId: number;
method: string;
args: any;
initResult: any;
@ -77,6 +78,7 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C
if (handler) {
this.component = handler.plugin.component;
this.componentId = this.module.id;
this.method = handler.handlerSchema.method;
this.args = {
courseid: this.courseId,

View File

@ -32,6 +32,7 @@ export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
@ViewChild('compile') compileComponent: ElementRef;
@Input() component: string;
@Input() componentId?: number;
@Input() method: string;
@Input() args: any;
@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;
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.javascript = result.javascript;
this.otherData = result.otherdata;

View File

@ -514,7 +514,14 @@ export class CoreSitePluginsProvider {
promises.push(this.callWS(method, params, {cacheKey: cacheKey}));
} else {
// 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 = [];
// Prefetch the files in the content.

View File

@ -360,7 +360,7 @@ export class CoreSitesProvider {
// Site schema for this provider.
protected siteSchema: CoreSiteSchema = {
name: 'CoreSitesProvider',
version: 1,
version: 2,
canBeCleared: [ CoreSite.WS_CACHE_TABLE ],
tables: [
{
@ -382,6 +382,14 @@ export class CoreSitesProvider {
{
name: 'expirationTime',
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,