geoportal-access-lib
Version:
French Geoportal resources access library
468 lines (421 loc) • 19.2 kB
JavaScript
import Logger from "../Utils/LoggerByDefault";
import Helper from "../Utils/Helper";
import _ from "../Utils/MessagesResources";
import Protocol from "../Protocols/Protocol";
import ErrorService from "../Exceptions/ErrorService";
// import DefaultUrlService from "./DefaultUrlService";
// package.json (extract version)
import Pkg from "../../package.json";
/**
* @classdesc
* Composant Service
*
* @constructor
* @alias Gp.Services.CommonService
* @param {Object} options - options communes à tous les services
*
* @param {String} [options.serverUrl] - URL d'accès au service. Par défaut "https://data.geopf.fr/SERVICE/".
* Permet de forcer l'utilisation d'un service équivalent déployé derrière une éventuelle autre URL d'accès.
* Si ce paramètre est renseigné alors, le paramètre par défaut est ignoré.
*
* @param {String} [options.protocol] - Le protocole à utiliser pour récupérer les informations du service :
* peut valoir 'JSONP' ou 'XHR'.
* Par défaut, c'est le protocole XHR qui sera utilisé.
* Attention, le protocole JSONP n'est pas valide dans un environnement NodeJS (Utilisation du mode XHR).
*
* @param {Boolean} [options.ssl] - Indique si l'on souhaite intérroger les services en https.
* Ce paramètre ne fonctionne que pour une utilisation hors navigateur (ex. NodeJS).
* Sur un navigateur, le protocole est automatiquement extrait de l'url du site...
* Par défaut, on utilise le protocole http (ssl=false).
*
* @param {String} [options.proxyURL] - Le proxy à utiliser pour pallier au problème de cross-domain dans le cas d'une requête XHR.
* Utile si le paramètre 'protocol' vaut 'XHR', il ne sera pas pris en compte si protocol vaut JSONP.
*
* @param {String} [options.callbackSuffix] - Suffixe de la fonction de callback à utiliser, dans le cas du protocole JSONP.
* Par défaut, la fonction de callback portera un nom du type "callback"+ID, où ID est soit un identifiant unique généré à chaque requête,
* soit le paramètre callbackSuffix s'il est spécifié. Par exemple, si callbackSuffix="_2", la fonction sera "callback_2 ()".
* Utile pour utiliser une réponse déjà encapsulée dans une fonction de callback, dont le nom est connu
* Utile seulement si le paramètre 'protocol' vaut 'JSONP', il ne sera pas pris en compte si protocol vaut 'XHR'.
*
* @param {String} [options.httpMethod] - La méthode HTTP
* à utiliser dans le cas d'une requête XHR : peut valoir 'GET' ou 'POST'.
* Non pris en compte si 'protocol' vaut JSONP qui fonctionne obligatoirement en GET.
* Par défaut, c'est la méthode GET qui est utilisée.
*
* @param {String} [options.contentType] - Content-Type de la requete
* à utiliser dans le cas d'une requête XHR en mode POST.
* Non pris en compte si 'protocol' vaut JSONP et/ou la méthode HTTP vaut GET.
* Par défaut, c'est la méthode GET qui est utilisée donc on n'utilise pas de Content-Type.
*
* @param {Number} [options.timeOut] - Délai d'attente maximal (en ms) de la réponse du service (à partir de l'envoi de la requête).
* Par défaut, aucun timeOut n'est pris en compte (timeoutDelay= 0).
*
* @param {Boolean} [options.rawResponse] - Indique si l'on souhaite que la réponse du service ne soit pas parsée par l'API avant d'être restituée.
* (Cf. paramètre « onSuccess » pour plus de détails).
*
* @param {Function} [options.onSuccess] - Fonction appelée lorsque le service répond correctement à la requête
* (code HTTP 200, sans message d'erreur).
* Cette fonction prend en paramètre la réponse du service,
* soit sous la forme d'un Object Javascript formaté par le parseur dédié à la syntaxe du service (comportement par défaut) ;
* soit brute au format String non prétraité si le paramètre « rawResponse » a été précisé avec la valeur « true ».
*
* @param {Function} [options.onFailure] - Fonction appelée lorsque le service ne répond pas correctement
* (code HTTP de retour différent de 200 ou pas de réponse).
*
* @param {Function} [options.onBeforeParse] - Fonction appelée avant le parsing de la réponse
* Permet de modifier la réponse avant parsing et la fonction doit retourner une String.
* Cette fonction prend en paramètre la réponse telle que renvoyée par le service
* (cad au format json ou xml).
* Pour le JSONP, si le paramètre "rawResponse" a été précisé avec la valeur "true",
* la fonction prend en paramètre un Object JavaScript contenant la réponse XML.
*
* @example
* var options = {
* serverUrl : 'http://localhost/service/',
* protocol : 'JSONP', // JSONP|XHR
* ssl : false,
* proxyURL : null,
* callbackName : null,
* httpMethod : 'GET', // GET|POST
* timeOut : 10000, // ms
* rawResponse : false, // true|false
* scope : null, // this
* onSuccess : function (response) {},
* onFailure : function (error) {},
* onBeforeParse : function (rawResponse) {}
* };
*/
function CommonService (options) {
if (!(this instanceof CommonService)) {
throw new TypeError(_.getMessage("CLASS_CONSTRUCTOR"));
}
this.logger = Logger.getLogger("CommonService");
this.logger.trace("[Constructeur CommonService (options)]");
// #####################
// récupération des options par défaut pour les paramètres optionnels
// #####################
/**
* Options du service
* @type {Object}
*/
this.options = {
// protocol : "JSONP",
protocol : "XHR",
ssl : true,
proxyURL : "",
// callbackName : "",
callbackSuffix : null,
httpMethod : "GET",
timeOut : 0,
rawResponse : false,
scope : this,
/**
* callback par defaut pour la reponse
* @param {Object} response - response
* @private
*/
onSuccess : function (response) {
console.log("onSuccess - la reponse est la suivante : ", response);
},
/**
* callback par defaut pour les erreurs
* @param {Object} error - error
* @private
*/
onFailure : function (error) {
if (error.status === 200 || !error.status) {
console.log("onFailure : ", error.message);
} else {
console.log("onFailure - Erreur (", error.status, ") : ", error.message);
}
}
};
// et on ajoute les options en paramètre aux options par défaut
for (var opt in options) {
if (options.hasOwnProperty(opt)) {
this.options[opt] = options[opt];
}
}
// #####################
// analyse des options
// #####################
// modification de la fonction de callback onSuccess dans le cas où la réponse brute est demandée
if (this.options.rawResponse && !this.options.onSuccess) {
/**
* callback par defaut pour la reponse
* @param {Object} response - response
* @private
*/
this.options.onSuccess = function (response) {
console.log("onSuccess - la réponse brute du service est la suivante : ", response);
};
}
// gestion du callback onSuccess
var bOnSuccess = !!(this.options.onSuccess !== null && typeof this.options.onSuccess === "function");
if (!bOnSuccess) {
throw new Error(_.getMessage("PARAM_MISSING", "onSuccess()"));
}
// gestion de la methode HTTP
this.options.httpMethod = (typeof options.httpMethod === "string") ? options.httpMethod.toUpperCase() : "GET";
switch (this.options.httpMethod) {
case "POST":
case "GET":
break;
case "PUT":
case "DELETE":
case "HEAD":
case "OPTIONS":
throw new Error(_.getMessage("PARAM_NOT_SUPPORT", "httpMethod"));
default:
throw new Error(_.getMessage("PARAM_UNKNOWN", "httpMethod"));
}
// gestion du protocole
// this.options.protocol = (typeof options.protocol === "string" ) ? options.protocol.toUpperCase() : "JSONP";
this.options.protocol = (typeof options.protocol === "string") ? options.protocol.toUpperCase() : "XHR";
switch (this.options.protocol) {
case "JSONP":
case "XHR":
break;
default:
throw new Error(_.getMessage("PARAM_UNKNOWN", "protocol"));
}
// on determine l'environnement d'execution : browser ou non ?
// et on lance une exception sur l'utilisation du protocole JSONP pour nodeJS...
if (typeof window === "undefined" && this.options.protocol === "JSONP") {
throw new Error(_.getMessage("PARAM_NOT_SUPPORT_NODEJS", "protocol=JSONP (instead use XHR)"));
}
// le protocole JSONP ne fonctionne qu'en GET.
if (this.options.protocol === "JSONP") {
this.options.httpMethod = "GET";
}
// gestion du cache
this.options.nocache = options.nocache || false;
// #####################
// attributs d'instances
// #####################
/**
* Format de réponse du service
*/
this.options.outputFormat = null;
/**
* Requête envoyée au service
*/
this.request = null;
/**
* Reponse du service
*/
this.response = null;
}
/**
* @lends module:CommonService
*/
CommonService.prototype = {
/*
* Constructeur (alias)
*/
constructor : CommonService,
/**
* Appel du service Géoportail
*/
call : function () {
/* jshint validthis : true */
this.logger.trace("CommonService::call ()");
var context = this;
/** fonction d'execution */
function run () {
this.logger.trace("CommonService::run ()");
this.buildRequest.call(context, onError, onBuildRequest);
}
run.call(context);
// callback de fin de construction de la requête
function onBuildRequest (result) {
this.logger.trace("CommonService::onBuildRequest : ", result);
this.callService.call(context, onError, onCallService);
}
// callback de fin d'appel au service
function onCallService (result) {
this.logger.trace("CommonService::onCallService : ", result);
this.analyzeResponse.call(context, onError, onAnalyzeResponse);
}
// callback de fin de lecture de la reponse
function onAnalyzeResponse (result) {
this.logger.trace("CommonService::onAnalyzeResponse : ", result);
if (result) {
this.options.onSuccess.call(this, result);
} else {
return onError.call(this, new ErrorService("Analyse de la reponse en échec !?"));
}
}
// callback de gestion des erreurs : renvoit un objet de type ErrorService
function onError (error) {
this.logger.trace("CommonService::onError()");
// error : l'objet est du type ErrorService ou Error
var e = error;
if (!(e instanceof ErrorService)) {
e = new ErrorService(error.message);
}
this.options.onFailure.call(this, e);
}
},
/**
* Création de la requête
* @param {Function} error - callback
* @param {Function} success - callback
*/
buildRequest : function (error, success) {
// INFO
this.logger.error("overwritten method !");
// retourne l'objet 'this.request'
if (error) {
error.call(this, "This method must be overwritten !");
}
success.call(this, "This method must be overwritten !");
},
/**
* Appel du service
* @param {Function} error - callback
* @param {Function} success - callback
*/
callService : function (error, success) {
// INFO
// retourne l'objet 'this.response'
// NOTES
// Pour le mode XHR, on recupère une reponse sous forme d'un json ou xml (#document).
// Pour le mode JSONP, on a toujours un objet JSON mais sous 2 formes :
// - natif
// - XML encapsulé :
// {http : {status:200, error:null},xml :'réponse du service'}
// {http : {status:400, error:'reponse du service'},xml :null}
// En XHR, la reponse est directement sauvegardée dans 'this.response'.
// Par contre, en JSONP, on doit analyser la reponse (status ou non vide),
// et ne renvoyer que le contenu (xml ou l'objet)
// gestion de la proxification du service
var strUrlProxified = null;
var strData = this.request;
// a t on mis en place un proxy ?
// la proxyfication est valable uniquement en mode XHR !
var bUrlProxified = !!(this.options.proxyURL && this.options.protocol === "XHR");
// rajout de l'option gpbibaccess
// INFO : acces au numero de version de package.conf aprés compilation !
var requestMetaOptions = {
"gp-access-lib" : Pkg.version
};
if (this.options.apiKey) {
requestMetaOptions.apiKey = this.options.apiKey;
}
this.options.serverUrl = Helper.normalyzeUrl(this.options.serverUrl, requestMetaOptions, false);
// si le proxy est renseigné, on proxifie l'url du service
if (bUrlProxified) {
if (this.options.httpMethod === "GET") {
strUrlProxified = this.options.proxyURL + Helper.normalyzeUrl(this.options.serverUrl, this.request, true);
strData = null;
}
if (this.options.httpMethod === "POST") {
strUrlProxified = this.options.proxyURL + Helper.normalyzeUrl(this.options.serverUrl, null, true);
strData = this.request;
}
}
// contexte du composant spécifique !
var self = this;
var options = {
url : strUrlProxified || this.options.serverUrl,
method : this.options.httpMethod,
protocol : this.options.protocol,
timeOut : this.options.timeOut || 0,
format : this.options.outputFormat, // ceci declenche le parsing de la reponse du service, mais on souhaite toujours une reponse brute (string) !
nocache : this.options.nocache || false, // ceci permet d'ajouter un timestamp dans la requête
wrap : this.options.protocol !== "XHR", // ceci declenche l'encapsulation de la reponse XML du service dans du JSON, mais pas en mode XHR !
callbackSuffix : this.options.callbackSuffix,
// callbackName : this.options.callbackName || null,
data : strData,
headers : null, // TODO...
content : this.options.contentType || "application/xml",
scope : this.options.scope || this,
// callback de reponse
onResponse : function (response) {
self.logger.trace("callService::onResponse()");
// le contenu de la reponse à renvoyer !
var content = null;
// XHR : on renvoie toujours la reponse brute du service (json ou xml)
// au parser du composant...
if (self.options.protocol === "XHR") {
self.logger.trace("Response XHR", response);
content = response; // par defaut, la reponse du service !
}
// JSONP : on pre-analyse la reponse brute du service (encapsuler ou pas)
// avant de l'envoyer au parser du composant...
if (self.options.protocol === "JSONP") {
self.logger.trace("Response JSON", response);
if (response) {
if (response.http) {
// reponse encapsulée :
// ex. reponse du service en xml
// > {http : {status:200, error:null},xml :'réponse du service'}
if (response.http.status !== 200) {
error.call(self, new ErrorService({
status : response.http.status,
message : response.http.error,
type : ErrorService.TYPE_SRVERR
}));
return;
} else {
content = response.xml; // par defaut !
if (self.options.rawResponse) {
content = response;
}
}
} else {
// reponse non encapsulée :
// ex. reponse du service en json ou xml
content = response;
}
} else {
error.call(self, new ErrorService("Le contenu de la reponse est vide !?"));
return;
}
}
// si on souhaite parser la reponse du service
if (typeof self.options.onBeforeParse === "function") {
var newResponse = self.options.onBeforeParse(content);
if (typeof newResponse === "string") {
// la reponse parsée par l'utilisateur est retournée sous
// forme de string !
content = newResponse;
}
}
// sauvegarde de la reponse dans l'objet parent (CommonService)
self.response = content;
// on renvoie la reponse...
success.call(self, content);
},
// callback des erreurs
onFailure : function (e) {
self.logger.trace("callService::onFailure()");
// on est forcement sur une erreur levée par un service !
e.type = ErrorService.TYPE_SRVERR;
error.call(self, new ErrorService(e));
},
// callback de timeOut
onTimeOut : function () {
self.logger.trace("callService::onTimeOut()");
error.call(self, new ErrorService("TimeOut!"));
}
};
Protocol.send(options);
},
/**
* Analyse de la réponse
* @param {Function} error - callback
* @param {Function} success - callback
*/
analyzeResponse : function (error, success) {
// INFO
this.logger.error("overwritten method !");
// retourne l'objet spécifique au type de composant (json)
if (error) {
error.call(this, "This method must be overwritten !");
}
success.call(this, "This method must be overwritten !");
}
};
export default CommonService;