MOBILE-3571 core: Split calls with too many parameters

main
Dani Palou 2020-10-16 12:18:42 +02:00
parent 8d95636690
commit 5452ce6cff
4 changed files with 106 additions and 5 deletions

View File

@ -20,7 +20,9 @@ import { CoreDbProvider } from '@providers/db';
import { CoreEventsProvider } from '@providers/events'; import { CoreEventsProvider } from '@providers/events';
import { CoreFileProvider } from '@providers/file'; import { CoreFileProvider } from '@providers/file';
import { CoreLoggerProvider } from '@providers/logger'; import { CoreLoggerProvider } from '@providers/logger';
import { CoreWSProvider, CoreWSPreSets, CoreWSFileUploadOptions, CoreWSAjaxPreSets } from '@providers/ws'; import {
CoreWSProvider, CoreWSPreSets, CoreWSFileUploadOptions, CoreWSAjaxPreSets, CoreWSPreSetsSplitRequest
} from '@providers/ws';
import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreTimeUtilsProvider } from '@providers/utils/time';
@ -138,6 +140,12 @@ export interface CoreSiteWSPreSets {
* Component id. Optionally included when 'component' is set. * Component id. Optionally included when 'component' is set.
*/ */
componentId?: number; componentId?: number;
/**
* Whether to split a request if it has too many parameters. Sending too many parameters to the site
* can cause the request to fail (see PHP's max_input_vars).
*/
splitRequest?: CoreWSPreSetsSplitRequest;
} }
/** /**
@ -650,7 +658,8 @@ export class CoreSite {
siteUrl: this.siteUrl, siteUrl: this.siteUrl,
cleanUnicode: this.cleanUnicode, cleanUnicode: this.cleanUnicode,
typeExpected: preSets.typeExpected, typeExpected: preSets.typeExpected,
responseExpected: preSets.responseExpected responseExpected: preSets.responseExpected,
splitRequest: preSets.splitRequest,
}; };
if (wsPreSets.cleanUnicode && this.textUtils.hasUnicodeData(data)) { if (wsPreSets.cleanUnicode && this.textUtils.hasUnicodeData(data)) {

View File

@ -471,7 +471,11 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
preSets: CoreSiteWSPreSets = { preSets: CoreSiteWSPreSets = {
cacheKey: this.getCourseUpdatesCacheKey(courseId), cacheKey: this.getCourseUpdatesCacheKey(courseId),
emergencyCache: false, // If downloaded data has changed and offline, just fail. See MOBILE-2085. emergencyCache: false, // If downloaded data has changed and offline, just fail. See MOBILE-2085.
uniqueCacheKey: true uniqueCacheKey: true,
splitRequest: {
param: 'tocheck',
maxLength: 10,
},
}; };
return site.read('core_course_check_updates', params, preSets).then((response) => { return site.read('core_course_check_updates', params, preSets).then((response) => {

View File

@ -272,7 +272,11 @@ export class CoreFilterProvider {
}, },
preSets = { preSets = {
cacheKey: this.getAvailableInContextsCacheKey(contextsToSend), cacheKey: this.getAvailableInContextsCacheKey(contextsToSend),
updateFrequency: CoreSite.FREQUENCY_RARELY updateFrequency: CoreSite.FREQUENCY_RARELY,
splitRequest: {
param: 'contexts',
maxLength: 300,
},
}; };
return site.read('core_filters_get_available_in_context', data, preSets) return site.read('core_filters_get_available_in_context', data, preSets)

View File

@ -57,8 +57,34 @@ export interface CoreWSPreSets {
* Defaults to false. Clean multibyte Unicode chars from data. * Defaults to false. Clean multibyte Unicode chars from data.
*/ */
cleanUnicode?: boolean; cleanUnicode?: boolean;
/**
* Whether to split a request if it has too many parameters. Sending too many parameters to the site
* can cause the request to fail (see PHP's max_input_vars).
*/
splitRequest?: CoreWSPreSetsSplitRequest;
} }
/**
* Options to split a request.
*/
export type CoreWSPreSetsSplitRequest = {
/**
* Name of the parameter used to split the request if too big. Must be an array parameter.
*/
param: string;
/**
* Max number of entries sent per request.
*/
maxLength: number;
/**
* Callback to combine the results. If not supplied, arrays in the result will be concatenated.
*/
combineCallback?: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any;
};
/** /**
* PreSets accepted by AJAX WS calls. * PreSets accepted by AJAX WS calls.
*/ */
@ -622,7 +648,7 @@ export class CoreWSProvider {
} }
/** /**
* Perform the post call and save the promise while waiting to be resolved. * Perform the post call. It can be split into several requests.
* *
* @param method The WebService method to be called. * @param method The WebService method to be called.
* @param siteUrl Complete site url to perform the call. * @param siteUrl Complete site url to perform the call.
@ -639,6 +665,64 @@ export class CoreWSProvider {
options['responseType'] = 'text'; options['responseType'] = 'text';
} }
if (!preSets.splitRequest || !ajaxData[preSets.splitRequest.param]) {
return this.performSinglePost(method, siteUrl, ajaxData, preSets, options);
}
// Split the request into several requests if needed.
const promises: Promise<any>[] = [];
for (let i = 0; i < ajaxData[preSets.splitRequest.param].length; i += preSets.splitRequest.maxLength) {
// Limit the array sent.
const limitedData = Object.assign({}, ajaxData);
limitedData[preSets.splitRequest.param] =
ajaxData[preSets.splitRequest.param].slice(i, i + preSets.splitRequest.maxLength);
promises.push(this.performSinglePost(method, siteUrl, limitedData, preSets, options));
}
return Promise.all(promises).then((results) => {
// Combine the results.
const firstResult = results.shift();
if (preSets.splitRequest.combineCallback) {
return results.reduce(preSets.splitRequest.combineCallback, firstResult);
}
return results.reduce(this.combineObjectsArrays, firstResult);
});
}
/**
* Combine the arrays of two objects.
*
* @param object1 First object.
* @param object2 Second object.
* @return Combined object.
*/
protected combineObjectsArrays(object1: any, object2: any): any {
for (const name in object2) {
if (Array.isArray(object2[name])) {
object1[name] = object1[name].concat(object2[name]);
}
}
return object1;
}
/**
* Perform a single post request.
*
* @param method The WebService method to be called.
* @param siteUrl Complete site url to perform the call.
* @param ajaxData Arguments to pass to the method.
* @param preSets Extra settings and information.
* @param options Request options.
* @return Promise resolved with the response data in success and rejected with CoreWSError if it fails.
*/
protected performSinglePost(method: string, siteUrl: string, ajaxData: any, preSets: CoreWSPreSets, options: any)
: Promise<any> {
// We add the method name to the URL purely to help with debugging. // We add the method name to the URL purely to help with debugging.
// This duplicates what is in the ajaxData, but that does no harm. // This duplicates what is in the ajaxData, but that does no harm.
// POST variables take precedence over GET. // POST variables take precedence over GET.