MOBILE-3659 core: Split calls with too many parameters

main
Dani Palou 2021-01-19 10:00:40 +01:00
parent 01e9e20014
commit d14540218a
2 changed files with 115 additions and 6 deletions

View File

@ -26,6 +26,7 @@ import {
CoreWSAjaxPreSets,
CoreWSExternalWarning,
CoreWSUploadFileResult,
CoreWSPreSetsSplitRequest,
} from '@services/ws';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreTextUtils } from '@services/utils/text';
@ -516,6 +517,7 @@ export class CoreSite {
cleanUnicode: this.cleanUnicode,
typeExpected: preSets.typeExpected,
responseExpected: preSets.responseExpected,
splitRequest: preSets.splitRequest,
};
if (wsPreSets.cleanUnicode && CoreTextUtils.instance.hasUnicodeData(data)) {
@ -2052,6 +2054,12 @@ export type CoreSiteWSPreSets = {
* Component id. Optionally included when 'component' is set.
*/
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;
};
/**

View File

@ -72,11 +72,16 @@ export class CoreWSProvider {
*
* @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 data Arguments to pass to the method.
* @param preSets Extra settings and information.
* @return Deferred promise resolved with the response data in success and rejected with the error if it fails.
*/
protected addToRetryQueue<T = unknown>(method: string, siteUrl: string, data: unknown, preSets: CoreWSPreSets): Promise<T> {
protected addToRetryQueue<T = unknown>(
method: string,
siteUrl: string,
data: Record<string, unknown>,
preSets: CoreWSPreSets,
): Promise<T> {
const call = {
method,
siteUrl,
@ -98,7 +103,7 @@ export class CoreWSProvider {
* @param preSets Extra settings and information.
* @return Promise resolved with the response data in success and rejected if it fails.
*/
call<T = unknown>(method: string, data: unknown, preSets: CoreWSPreSets): Promise<T> {
call<T = unknown>(method: string, data: Record<string, unknown>, preSets: CoreWSPreSets): Promise<T> {
if (!preSets) {
throw new CoreError(Translate.instance.instant('core.unexpectederror'));
} else if (!CoreApp.instance.isOnline()) {
@ -493,7 +498,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 siteUrl Complete site url to perform the call.
@ -501,7 +506,12 @@ export class CoreWSProvider {
* @param preSets Extra settings and information.
* @return Promise resolved with the response data in success and rejected with CoreWSError if it fails.
*/
performPost<T = unknown>(method: string, siteUrl: string, ajaxData: unknown, preSets: CoreWSPreSets): Promise<T> {
async performPost<T = unknown>(
method: string,
siteUrl: string,
ajaxData: Record<string, unknown>,
preSets: CoreWSPreSets,
): Promise<T> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const options: any = {};
@ -510,6 +520,71 @@ export class CoreWSProvider {
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<T>[] = [];
const splitParam = <unknown[]> ajaxData[preSets.splitRequest.param];
for (let i = 0; i < splitParam.length; i += preSets.splitRequest.maxLength) {
// Limit the array sent.
const limitedData = Object.assign({}, ajaxData);
limitedData[preSets.splitRequest.param] = splitParam.slice(i, i + preSets.splitRequest.maxLength);
promises.push(this.performSinglePost(method, siteUrl, limitedData, preSets, options));
}
const results = await Promise.all(promises);
// Combine the results.
const firstResult = results.shift();
if (preSets.splitRequest.combineCallback) {
return <T> results.reduce(preSets.splitRequest.combineCallback, firstResult);
}
return <T> results.reduce((previous: T, current: T) => this.combineObjectsArrays<T>(previous, current), firstResult);
}
/**
* Combine the arrays of two objects, adding them to the first object.
*
* @param object1 First object.
* @param object2 Second object.
* @return First object with items added.
*/
protected combineObjectsArrays<T>(object1: T, object2: T): T {
for (const name in object2) {
const value = object2[name];
if (Array.isArray(value)) {
(object1 as Record<string, unknown>)[name] = (object1[name] as typeof value).concat(value);
}
}
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<T>(
method: string,
siteUrl: string,
ajaxData: Record<string, unknown>,
preSets: CoreWSPreSets,
options: any, // eslint-disable-line @typescript-eslint/no-explicit-any
): Promise<T> {
// 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.
// POST variables take precedence over GET.
@ -1089,6 +1164,32 @@ export type CoreWSPreSets = {
* Defaults to false. Clean multibyte Unicode chars from data.
*/
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: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown;
};
/**
@ -1188,7 +1289,7 @@ type AngularHttpRequestOptions = Omit<HttpRequestOptions, 'data'|'params'> & {
type RetryCall = {
method: string;
siteUrl: string;
data: unknown;
data: Record<string, unknown>;
preSets: CoreWSPreSets;
deferred: PromiseDefer<unknown>;
};