MOBILE-3565 core: Migrate some core classes
parent
1e979b57bb
commit
811bb39781
|
@ -0,0 +1,352 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreEvents, CoreEventsProvider } from '@services/events';
|
||||||
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreLogger } from '@singletons/logger';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Superclass to help creating delegates
|
||||||
|
*/
|
||||||
|
export class CoreDelegate {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger instance.
|
||||||
|
*/
|
||||||
|
protected logger: CoreLogger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of registered handlers.
|
||||||
|
*/
|
||||||
|
protected handlers: { [s: string]: CoreDelegateHandler } = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of registered handlers enabled for the current site.
|
||||||
|
*/
|
||||||
|
protected enabledHandlers: { [s: string]: CoreDelegateHandler } = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default handler
|
||||||
|
*/
|
||||||
|
protected defaultHandler: CoreDelegateHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time when last updateHandler functions started.
|
||||||
|
*/
|
||||||
|
protected lastUpdateHandlersStart: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature prefix to check is feature is enabled or disabled in site.
|
||||||
|
* This check is only made if not false. Override on the subclass or override isFeatureDisabled function.
|
||||||
|
*/
|
||||||
|
protected featurePrefix: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the property to be used to index the handlers. By default, the handler's name will be used.
|
||||||
|
* If your delegate uses a Moodle component name to identify the handlers, please override this property.
|
||||||
|
* E.g. CoreCourseModuleDelegate uses 'modName' to index the handlers.
|
||||||
|
*/
|
||||||
|
protected handlerNameProperty = 'name';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of promises to update a handler, to prevent doing the same operation twice.
|
||||||
|
*/
|
||||||
|
protected updatePromises: {[siteId: string]: {[name: string]: Promise<any>}} = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether handlers have been initialized.
|
||||||
|
*/
|
||||||
|
protected handlersInitialized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise to wait for handlers to be initialized.
|
||||||
|
*/
|
||||||
|
protected handlersInitPromise: Promise<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to resolve the handlers init promise.
|
||||||
|
*/
|
||||||
|
protected handlersInitResolve: (value?: any) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of the Delegate.
|
||||||
|
*
|
||||||
|
* @param delegateName Delegate name used for logging purposes.
|
||||||
|
* @param listenSiteEvents Whether to update the handler when a site event occurs (login, site updated, ...).
|
||||||
|
*/
|
||||||
|
constructor(delegateName: string, listenSiteEvents?: boolean) {
|
||||||
|
this.logger = CoreLogger.getInstance(delegateName);
|
||||||
|
|
||||||
|
this.handlersInitPromise = new Promise((resolve): void => {
|
||||||
|
this.handlersInitResolve = resolve;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (listenSiteEvents) {
|
||||||
|
// Update handlers on this cases.
|
||||||
|
CoreEvents.instance.on(CoreEventsProvider.LOGIN, this.updateHandlers.bind(this));
|
||||||
|
CoreEvents.instance.on(CoreEventsProvider.SITE_UPDATED, this.updateHandlers.bind(this));
|
||||||
|
CoreEvents.instance.on(CoreEventsProvider.SITE_PLUGINS_LOADED, this.updateHandlers.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a certain function in a enabled handler.
|
||||||
|
* If the handler isn't found or function isn't defined, call the same function in the default handler.
|
||||||
|
*
|
||||||
|
* @param handlerName The handler name.
|
||||||
|
* @param fnName Name of the function to execute.
|
||||||
|
* @param params Parameters to pass to the function.
|
||||||
|
* @return Function returned value or default value.
|
||||||
|
*/
|
||||||
|
protected executeFunctionOnEnabled(handlerName: string, fnName: string, params?: any[]): any {
|
||||||
|
return this.execute(this.enabledHandlers[handlerName], fnName, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a certain function in a handler.
|
||||||
|
* If the handler isn't found or function isn't defined, call the same function in the default handler.
|
||||||
|
*
|
||||||
|
* @param handlerName The handler name.
|
||||||
|
* @param fnName Name of the function to execute.
|
||||||
|
* @param params Parameters to pass to the function.
|
||||||
|
* @return Function returned value or default value.
|
||||||
|
*/
|
||||||
|
protected executeFunction(handlerName: string, fnName: string, params?: any[]): any {
|
||||||
|
return this.execute(this.handlers[handlerName], fnName, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a certain function in a handler.
|
||||||
|
* If the handler isn't found or function isn't defined, call the same function in the default handler.
|
||||||
|
*
|
||||||
|
* @param handler The handler.
|
||||||
|
* @param fnName Name of the function to execute.
|
||||||
|
* @param params Parameters to pass to the function.
|
||||||
|
* @return Function returned value or default value.
|
||||||
|
*/
|
||||||
|
private execute(handler: any, fnName: string, params?: any[]): any {
|
||||||
|
if (handler && handler[fnName]) {
|
||||||
|
return handler[fnName].apply(handler, params);
|
||||||
|
} else if (this.defaultHandler && this.defaultHandler[fnName]) {
|
||||||
|
return this.defaultHandler[fnName].apply(this.defaultHandler, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a handler.
|
||||||
|
*
|
||||||
|
* @param handlerName The handler name.
|
||||||
|
* @param enabled Only enabled, or any.
|
||||||
|
* @return Handler.
|
||||||
|
*/
|
||||||
|
protected getHandler(handlerName: string, enabled: boolean = false): CoreDelegateHandler {
|
||||||
|
return enabled ? this.enabledHandlers[handlerName] : this.handlers[handlerName];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the handler full name for a given name. This is useful when the handlerNameProperty is different than "name".
|
||||||
|
* E.g. blocks are indexed by blockName. If you call this function passing the blockName it will return the name.
|
||||||
|
*
|
||||||
|
* @param name Name used to indentify the handler.
|
||||||
|
* @return Full name of corresponding handler.
|
||||||
|
*/
|
||||||
|
getHandlerName(name: string): string {
|
||||||
|
const handler = this.getHandler(name, true);
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if function exists on a handler.
|
||||||
|
*
|
||||||
|
* @param handlerName The handler name.
|
||||||
|
* @param fnName Name of the function to execute.
|
||||||
|
* @param onlyEnabled If check only enabled handlers or all.
|
||||||
|
* @return Function returned value or default value.
|
||||||
|
*/
|
||||||
|
protected hasFunction(handlerName: string, fnName: string, onlyEnabled: boolean = true): any {
|
||||||
|
const handler = onlyEnabled ? this.enabledHandlers[handlerName] : this.handlers[handlerName];
|
||||||
|
|
||||||
|
return handler && handler[fnName];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a handler name has a registered handler (not necessarily enabled).
|
||||||
|
*
|
||||||
|
* @param name The handler name.
|
||||||
|
* @param enabled Only enabled, or any.
|
||||||
|
* @return If the handler is registered or not.
|
||||||
|
*/
|
||||||
|
hasHandler(name: string, enabled: boolean = false): boolean {
|
||||||
|
return enabled ? typeof this.enabledHandlers[name] !== 'undefined' : typeof this.handlers[name] !== 'undefined';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a time belongs to the last update handlers call.
|
||||||
|
* This is to handle the cases where updateHandlers don't finish in the same order as they're called.
|
||||||
|
*
|
||||||
|
* @param time Time to check.
|
||||||
|
* @return Whether it's the last call.
|
||||||
|
*/
|
||||||
|
isLastUpdateCall(time: number): boolean {
|
||||||
|
if (!this.lastUpdateHandlersStart) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return time == this.lastUpdateHandlersStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a handler.
|
||||||
|
*
|
||||||
|
* @param handler The handler delegate object to register.
|
||||||
|
* @return True when registered, false if already registered.
|
||||||
|
*/
|
||||||
|
registerHandler(handler: CoreDelegateHandler): boolean {
|
||||||
|
const key = handler[this.handlerNameProperty] || handler.name;
|
||||||
|
|
||||||
|
if (typeof this.handlers[key] !== 'undefined') {
|
||||||
|
this.logger.log(`Handler '${handler[this.handlerNameProperty]}' already registered`);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log(`Registered handler '${handler[this.handlerNameProperty]}'`);
|
||||||
|
this.handlers[key] = handler;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the handler for the current site.
|
||||||
|
*
|
||||||
|
* @param handler The handler to check.
|
||||||
|
* @param time Time this update process started.
|
||||||
|
* @return Resolved when done.
|
||||||
|
*/
|
||||||
|
protected updateHandler(handler: CoreDelegateHandler, time: number): Promise<void> {
|
||||||
|
const siteId = CoreSites.instance.getCurrentSiteId();
|
||||||
|
const currentSite = CoreSites.instance.getCurrentSite();
|
||||||
|
let promise;
|
||||||
|
|
||||||
|
if (this.updatePromises[siteId] && this.updatePromises[siteId][handler.name]) {
|
||||||
|
// There's already an update ongoing for this handler, return the promise.
|
||||||
|
return this.updatePromises[siteId][handler.name];
|
||||||
|
} else if (!this.updatePromises[siteId]) {
|
||||||
|
this.updatePromises[siteId] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CoreSites.instance.isLoggedIn()) {
|
||||||
|
promise = Promise.reject(null);
|
||||||
|
} else if (this.isFeatureDisabled(handler, currentSite)) {
|
||||||
|
promise = Promise.resolve(false);
|
||||||
|
} else {
|
||||||
|
promise = Promise.resolve(handler.isEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the handler is enabled.
|
||||||
|
this.updatePromises[siteId][handler.name] = promise.catch(() => {
|
||||||
|
return false;
|
||||||
|
}).then((enabled: boolean) => {
|
||||||
|
// Check that site hasn't changed since the check started.
|
||||||
|
if (CoreSites.instance.getCurrentSiteId() === siteId) {
|
||||||
|
const key = handler[this.handlerNameProperty] || handler.name;
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
this.enabledHandlers[key] = handler;
|
||||||
|
} else {
|
||||||
|
delete this.enabledHandlers[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
// Update finished, delete the promise.
|
||||||
|
delete this.updatePromises[siteId][handler.name];
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.updatePromises[siteId][handler.name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if feature is enabled or disabled in the site, depending on the feature prefix and the handler name.
|
||||||
|
*
|
||||||
|
* @param handler Handler to check.
|
||||||
|
* @param site Site to check.
|
||||||
|
* @return Whether is enabled or disabled in site.
|
||||||
|
*/
|
||||||
|
protected isFeatureDisabled(handler: CoreDelegateHandler, site: CoreSite): boolean {
|
||||||
|
return typeof this.featurePrefix != 'undefined' && site.isFeatureDisabled(this.featurePrefix + handler.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the handlers for the current site.
|
||||||
|
*
|
||||||
|
* @return Resolved when done.
|
||||||
|
*/
|
||||||
|
protected updateHandlers(): Promise<void> {
|
||||||
|
const promises = [],
|
||||||
|
now = Date.now();
|
||||||
|
|
||||||
|
this.logger.debug('Updating handlers for current site.');
|
||||||
|
|
||||||
|
this.lastUpdateHandlersStart = now;
|
||||||
|
|
||||||
|
// Loop over all the handlers.
|
||||||
|
for (const name in this.handlers) {
|
||||||
|
promises.push(this.updateHandler(this.handlers[name], now));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(promises).then(() => {
|
||||||
|
return true;
|
||||||
|
}, () => {
|
||||||
|
// Never reject.
|
||||||
|
return true;
|
||||||
|
}).then(() => {
|
||||||
|
|
||||||
|
// Verify that this call is the last one that was started.
|
||||||
|
if (this.isLastUpdateCall(now)) {
|
||||||
|
this.handlersInitialized = true;
|
||||||
|
this.handlersInitResolve();
|
||||||
|
|
||||||
|
this.updateData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update handlers Data.
|
||||||
|
* Override this function to update handlers data.
|
||||||
|
*/
|
||||||
|
updateData(): any {
|
||||||
|
// To be overridden.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CoreDelegateHandler {
|
||||||
|
/**
|
||||||
|
* Name of the handler, or name and sub context (AddonMessages, AddonMessages:blockContact, ...).
|
||||||
|
* This name will be used to check if the feature is disabled.
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
* @return Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean>;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base Error class.
|
||||||
|
*
|
||||||
|
* The native Error class cannot be extended in Typescript without restoring the prototype chain, extend this
|
||||||
|
* class instead.
|
||||||
|
*
|
||||||
|
* @see https://stackoverflow.com/questions/41102060/typescript-extending-error-class
|
||||||
|
*/
|
||||||
|
export class CoreError extends Error {
|
||||||
|
|
||||||
|
constructor(message?: string) {
|
||||||
|
super(message);
|
||||||
|
|
||||||
|
// Fix prototype chain: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
|
||||||
|
this.name = new.target.name;
|
||||||
|
Object.setPrototypeOf(this, new.target.prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interceptor for Http calls. Adds the header 'Content-Type'='application/x-www-form-urlencoded'
|
||||||
|
* and serializes the parameters if needed.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreInterceptor implements HttpInterceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize an object to be used in a request.
|
||||||
|
*
|
||||||
|
* @param obj Object to serialize.
|
||||||
|
* @param addNull Add null values to the serialized as empty parameters.
|
||||||
|
* @return Serialization of the object.
|
||||||
|
*/
|
||||||
|
static serialize(obj: any, addNull?: boolean): string {
|
||||||
|
let query = '';
|
||||||
|
let fullSubName;
|
||||||
|
let subValue;
|
||||||
|
let innerObj;
|
||||||
|
|
||||||
|
for (const name in obj) {
|
||||||
|
const value = obj[name];
|
||||||
|
|
||||||
|
if (value instanceof Array) {
|
||||||
|
for (let i = 0; i < value.length; ++i) {
|
||||||
|
subValue = value[i];
|
||||||
|
fullSubName = name + '[' + i + ']';
|
||||||
|
innerObj = {};
|
||||||
|
innerObj[fullSubName] = subValue;
|
||||||
|
query += this.serialize(innerObj) + '&';
|
||||||
|
}
|
||||||
|
} else if (value instanceof Object) {
|
||||||
|
for (const subName in value) {
|
||||||
|
subValue = value[subName];
|
||||||
|
fullSubName = name + '[' + subName + ']';
|
||||||
|
innerObj = {};
|
||||||
|
innerObj[fullSubName] = subValue;
|
||||||
|
query += this.serialize(innerObj) + '&';
|
||||||
|
}
|
||||||
|
} else if (addNull || (typeof value != 'undefined' && value !== null)) {
|
||||||
|
query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.length ? query.substr(0, query.length - 1) : query;
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
|
||||||
|
// Add the header and serialize the body if needed.
|
||||||
|
const newReq = req.clone({
|
||||||
|
headers: req.headers.set('Content-Type', 'application/x-www-form-urlencoded'),
|
||||||
|
body: typeof req.body == 'object' && String(req.body) != '[object File]' ?
|
||||||
|
CoreInterceptor.serialize(req.body) : req.body
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pass on the cloned request instead of the original request.
|
||||||
|
return next.handle(newReq);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { HttpResponse as AngularHttpResponse, HttpHeaders } from '@angular/common/http';
|
||||||
|
import { HTTPResponse as NativeHttpResponse } from '@ionic-native/http';
|
||||||
|
|
||||||
|
const HTTP_STATUS_MESSAGES = {
|
||||||
|
100: 'Continue',
|
||||||
|
101: 'Switching Protocol',
|
||||||
|
102: 'Processing',
|
||||||
|
103: 'Early Hints',
|
||||||
|
200: 'OK',
|
||||||
|
201: 'Created',
|
||||||
|
202: 'Accepted',
|
||||||
|
203: 'Non-Authoritative Information',
|
||||||
|
204: 'No Content',
|
||||||
|
205: 'Reset Content',
|
||||||
|
206: 'Partial Content',
|
||||||
|
207: 'Multi-Status',
|
||||||
|
208: 'Already Reported',
|
||||||
|
226: 'IM Used',
|
||||||
|
300: 'Multiple Choice',
|
||||||
|
301: 'Moved Permanently',
|
||||||
|
302: 'Found',
|
||||||
|
303: 'See Other',
|
||||||
|
304: 'Not Modified',
|
||||||
|
305: 'Use Proxy',
|
||||||
|
306: 'unused',
|
||||||
|
307: 'Temporary Redirect',
|
||||||
|
308: 'Permanent Redirect',
|
||||||
|
400: 'Bad Request',
|
||||||
|
401: 'Unauthorized',
|
||||||
|
402: 'Payment Required',
|
||||||
|
403: 'Forbidden',
|
||||||
|
404: 'Not Found',
|
||||||
|
405: 'Method Not Allowed',
|
||||||
|
406: 'Not Acceptable',
|
||||||
|
407: 'Proxy Authentication Required',
|
||||||
|
408: 'Request Timeout',
|
||||||
|
409: 'Conflict',
|
||||||
|
410: 'Gone',
|
||||||
|
411: 'Length Required',
|
||||||
|
412: 'Precondition Failed',
|
||||||
|
413: 'Payload Too Large',
|
||||||
|
414: 'URI Too Long',
|
||||||
|
415: 'Unsupported Media Type',
|
||||||
|
416: 'Range Not Satisfiable',
|
||||||
|
417: 'Expectation Failed',
|
||||||
|
418: 'I\'m a teapot',
|
||||||
|
421: 'Misdirected Request',
|
||||||
|
422: 'Unprocessable Entity',
|
||||||
|
423: 'Locked',
|
||||||
|
424: 'Failed Dependency',
|
||||||
|
425: 'Too Early',
|
||||||
|
426: 'Upgrade Required',
|
||||||
|
428: 'Precondition Required',
|
||||||
|
429: 'Too Many Requests',
|
||||||
|
431: 'Request Header Fields Too Large',
|
||||||
|
451: 'Unavailable For Legal Reasons',
|
||||||
|
500: 'Internal Server Error',
|
||||||
|
501: 'Not Implemented',
|
||||||
|
502: 'Bad Gateway',
|
||||||
|
503: 'Service Unavailable',
|
||||||
|
504: 'Gateway Timeout',
|
||||||
|
505: 'HTTP Version Not Supported',
|
||||||
|
506: 'Variant Also Negotiates',
|
||||||
|
507: 'Insufficient Storage',
|
||||||
|
508: 'Loop Detected',
|
||||||
|
510: 'Not Extended',
|
||||||
|
511: 'Network Authentication Required',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that adapts a Cordova plugin http response to an Angular http response.
|
||||||
|
*/
|
||||||
|
export class CoreNativeToAngularHttpResponse<T> extends AngularHttpResponse<T> {
|
||||||
|
|
||||||
|
constructor(protected nativeResponse: NativeHttpResponse) {
|
||||||
|
super({
|
||||||
|
body: nativeResponse.data,
|
||||||
|
headers: new HttpHeaders(nativeResponse.headers),
|
||||||
|
status: nativeResponse.status,
|
||||||
|
statusText: HTTP_STATUS_MESSAGES[nativeResponse.status] || '',
|
||||||
|
url: nativeResponse.url || ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { CoreUtils, PromiseDefer } from '@services/utils/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to add to the queue.
|
||||||
|
*/
|
||||||
|
export type CoreQueueRunnerFunction<T> = (...args: any[]) => T | Promise<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue item.
|
||||||
|
*/
|
||||||
|
export type CoreQueueRunnerItem<T = any> = {
|
||||||
|
/**
|
||||||
|
* Item ID.
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to execute.
|
||||||
|
*/
|
||||||
|
fn: CoreQueueRunnerFunction<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deferred with a promise resolved/rejected with the result of the function.
|
||||||
|
*/
|
||||||
|
deferred: PromiseDefer<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options to pass to add item.
|
||||||
|
*/
|
||||||
|
export type CoreQueueRunnerAddOptions = {
|
||||||
|
/**
|
||||||
|
* Whether to allow having multiple entries with same ID in the queue.
|
||||||
|
*/
|
||||||
|
allowRepeated?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A queue to prevent having too many concurrent executions.
|
||||||
|
*/
|
||||||
|
export class CoreQueueRunner {
|
||||||
|
protected queue: {[id: string]: CoreQueueRunnerItem} = {};
|
||||||
|
protected orderedQueue: CoreQueueRunnerItem[] = [];
|
||||||
|
protected numberRunning = 0;
|
||||||
|
|
||||||
|
constructor(protected maxParallel: number = 1) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get unique ID.
|
||||||
|
*
|
||||||
|
* @param id ID.
|
||||||
|
* @return Unique ID.
|
||||||
|
*/
|
||||||
|
protected getUniqueId(id: string): string {
|
||||||
|
let newId = id;
|
||||||
|
let num = 1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
newId = id + '-' + num;
|
||||||
|
num++;
|
||||||
|
} while (newId in this.queue);
|
||||||
|
|
||||||
|
return newId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process next item in the queue.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when next item has been treated.
|
||||||
|
*/
|
||||||
|
protected async processNextItem(): Promise<void> {
|
||||||
|
if (!this.orderedQueue.length || this.numberRunning >= this.maxParallel) {
|
||||||
|
// Queue is empty or max number of parallel runs reached, stop.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = this.orderedQueue.shift();
|
||||||
|
this.numberRunning++;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await item.fn();
|
||||||
|
|
||||||
|
item.deferred.resolve(result);
|
||||||
|
} catch (error) {
|
||||||
|
item.deferred.reject(error);
|
||||||
|
} finally {
|
||||||
|
delete this.queue[item.id];
|
||||||
|
this.numberRunning--;
|
||||||
|
|
||||||
|
this.processNextItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an item to the queue.
|
||||||
|
*
|
||||||
|
* @param id ID.
|
||||||
|
* @param fn Function to call.
|
||||||
|
* @param options Options.
|
||||||
|
* @return Promise resolved when the function has been executed.
|
||||||
|
*/
|
||||||
|
run<T>(id: string, fn: CoreQueueRunnerFunction<T>, options?: CoreQueueRunnerAddOptions): Promise<T> {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
if (id in this.queue) {
|
||||||
|
if (!options.allowRepeated) {
|
||||||
|
// Item already in queue, return its promise.
|
||||||
|
return this.queue[id].deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = this.getUniqueId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the item in the queue.
|
||||||
|
const item = {
|
||||||
|
id,
|
||||||
|
fn,
|
||||||
|
deferred: CoreUtils.instance.promiseDefer<T>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.queue[id] = item;
|
||||||
|
this.orderedQueue.push(item);
|
||||||
|
|
||||||
|
// Process next item if we haven't reached the max yet.
|
||||||
|
this.processNextItem();
|
||||||
|
|
||||||
|
return item.deferred.promise;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -12,9 +12,32 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Injector } from '@angular/core';
|
import { Injector, NgZone as NgZoneService } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
import { SplashScreen as SplashScreenPlugin } from '@ionic-native/splash-screen/ngx';
|
import { Platform as PlatformService } from '@ionic/angular';
|
||||||
|
|
||||||
|
import { Clipboard as ClipboardService } from '@ionic-native/clipboard/ngx';
|
||||||
|
import { Diagnostic as DiagnosticService } from '@ionic-native/diagnostic/ngx';
|
||||||
|
import { Device as DeviceService } from '@ionic-native/device/ngx';
|
||||||
|
import { File as FileService } from '@ionic-native/file/ngx';
|
||||||
|
import { FileOpener as FileOpenerService } from '@ionic-native/file-opener/ngx';
|
||||||
|
import { FileTransfer as FileTransferService } from '@ionic-native/file-transfer/ngx';
|
||||||
|
import { Geolocation as GeolocationService } from '@ionic-native/geolocation/ngx';
|
||||||
|
import { Globalization as GlobalizationService } from '@ionic-native/globalization/ngx';
|
||||||
|
import { InAppBrowser as InAppBrowserService } from '@ionic-native/in-app-browser/ngx';
|
||||||
|
import { Keyboard as KeyboardService } from '@ionic-native/keyboard/ngx';
|
||||||
|
import { LocalNotifications as LocalNotificationsService } from '@ionic-native/local-notifications/ngx';
|
||||||
|
import { Network as NetworkService } from '@ionic-native/network/ngx';
|
||||||
|
import { Push as PushService } from '@ionic-native/push/ngx';
|
||||||
|
import { QRScanner as QRScannerService } from '@ionic-native/qr-scanner/ngx';
|
||||||
|
import { StatusBar as StatusBarService } from '@ionic-native/status-bar/ngx';
|
||||||
|
import { SplashScreen as SplashScreenService } from '@ionic-native/splash-screen/ngx';
|
||||||
|
import { SQLite as SQLiteService } from '@ionic-native/sqlite/ngx';
|
||||||
|
import { WebIntent as WebIntentService } from '@ionic-native/web-intent/ngx';
|
||||||
|
import { Zip as ZipService } from '@ionic-native/zip/ngx';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { CoreSingletonsFactory, CoreInjectionToken, CoreSingletonClass } from '@classes/singletons-factory';
|
import { CoreSingletonsFactory, CoreInjectionToken, CoreSingletonClass } from '@classes/singletons-factory';
|
||||||
|
|
||||||
|
@ -39,4 +62,31 @@ export function makeSingleton<Service>(injectionToken: CoreInjectionToken<Servic
|
||||||
return factory.makeSingleton(injectionToken);
|
return factory.makeSingleton(injectionToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SplashScreen extends makeSingleton(SplashScreenPlugin) {}
|
// Convert ionic-native services to singleton.
|
||||||
|
export class Clipboard extends makeSingleton(ClipboardService) {}
|
||||||
|
export class Device extends makeSingleton(DeviceService) {}
|
||||||
|
export class Diagnostic extends makeSingleton(DiagnosticService) {}
|
||||||
|
export class File extends makeSingleton(FileService) {}
|
||||||
|
export class FileOpener extends makeSingleton(FileOpenerService) {}
|
||||||
|
export class FileTransfer extends makeSingleton(FileTransferService) {}
|
||||||
|
export class Geolocation extends makeSingleton(GeolocationService) {}
|
||||||
|
export class Globalization extends makeSingleton(GlobalizationService) {}
|
||||||
|
export class InAppBrowser extends makeSingleton(InAppBrowserService) {}
|
||||||
|
export class Keyboard extends makeSingleton(KeyboardService) {}
|
||||||
|
export class LocalNotifications extends makeSingleton(LocalNotificationsService) {}
|
||||||
|
export class Network extends makeSingleton(NetworkService) {}
|
||||||
|
export class Push extends makeSingleton(PushService) {}
|
||||||
|
export class QRScanner extends makeSingleton(QRScannerService) {}
|
||||||
|
export class StatusBar extends makeSingleton(StatusBarService) {}
|
||||||
|
export class SplashScreen extends makeSingleton(SplashScreenService) {}
|
||||||
|
export class SQLite extends makeSingleton(SQLiteService) {}
|
||||||
|
export class WebIntent extends makeSingleton(WebIntentService) {}
|
||||||
|
export class Zip extends makeSingleton(ZipService) {}
|
||||||
|
|
||||||
|
// Convert some Angular and Ionic injectables to singletons.
|
||||||
|
export class NgZone extends makeSingleton(NgZoneService) {}
|
||||||
|
export class Http extends makeSingleton(HttpClient) {}
|
||||||
|
export class Platform extends makeSingleton(PlatformService) {}
|
||||||
|
|
||||||
|
// Convert external libraries injectables.
|
||||||
|
export class Translate extends makeSingleton(TranslateService) {}
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"no-angle-bracket-type-assertion": false,
|
||||||
"no-console": [
|
"no-console": [
|
||||||
true,
|
true,
|
||||||
"debug",
|
"debug",
|
||||||
|
@ -58,12 +59,15 @@
|
||||||
],
|
],
|
||||||
"no-non-null-assertion": true,
|
"no-non-null-assertion": true,
|
||||||
"no-redundant-jsdoc": true,
|
"no-redundant-jsdoc": true,
|
||||||
|
"no-shadowed-variable": false,
|
||||||
"no-switch-case-fall-through": true,
|
"no-switch-case-fall-through": true,
|
||||||
|
"no-unused-expression": false,
|
||||||
"no-var-requires": false,
|
"no-var-requires": false,
|
||||||
"object-literal-key-quotes": [
|
"object-literal-key-quotes": [
|
||||||
true,
|
true,
|
||||||
"as-needed"
|
"as-needed"
|
||||||
],
|
],
|
||||||
|
"prefer-for-of": false,
|
||||||
"quotemark": [
|
"quotemark": [
|
||||||
true,
|
true,
|
||||||
"single"
|
"single"
|
||||||
|
@ -104,7 +108,8 @@
|
||||||
"options": [
|
"options": [
|
||||||
"ban-keywords",
|
"ban-keywords",
|
||||||
"check-format",
|
"check-format",
|
||||||
"allow-pascal-case"
|
"allow-pascal-case",
|
||||||
|
"allow-leading-underscore"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"whitespace": {
|
"whitespace": {
|
||||||
|
|
Loading…
Reference in New Issue