217 lines
5.2 KiB
JavaScript
217 lines
5.2 KiB
JavaScript
/* classe API */
|
|
export class APIClient{
|
|
|
|
|
|
|
|
/* (1) Constructs an API client manager
|
|
*
|
|
* @_hostname<String> Server hostname (without http, port number, etc)
|
|
* @_baseuri<String> Server base URI
|
|
* @_ssl<bool> [OPT] Whether SSL is activated (http vs https) (default: https)
|
|
* @_port<int> [OPT] optional HTTP port (default: none)
|
|
*
|
|
* @return http_url<String> Built http_url
|
|
*
|
|
---------------------------------------------------------*/
|
|
constructor(_hostname, _baseuri, _ssl=true, _port=null){
|
|
|
|
/* (1) Build URL parts */
|
|
this.http_parts = {
|
|
|
|
ssl: _ssl === true,
|
|
hostname: _hostname,
|
|
baseuri: this.fix_uri(_baseuri),
|
|
port: !isNaN(_port) ? _port: null
|
|
|
|
};
|
|
|
|
this.xhr_stack = []; // Ajax request stack
|
|
this.buffer = null; // Last request buffer
|
|
|
|
}
|
|
|
|
|
|
fix_uri(_uri){
|
|
|
|
if( typeof _uri !== 'string' )
|
|
return '';
|
|
|
|
return _uri.split('/').filter((r) => r.trim().length).join('/')
|
|
|
|
}
|
|
|
|
|
|
/* (2) HTTP url dynamic getter
|
|
*
|
|
* @_uri<String> [OPT] optional URI string
|
|
* @_token<String> [OPT] optional HTTP token
|
|
*
|
|
* @return http_url<String> Built http_url
|
|
*
|
|
---------------------------------------------------------*/
|
|
build_url(_uri, _token=null){
|
|
|
|
/* (1) Initialize URL buffer */
|
|
let bufurl = 'http';
|
|
|
|
/* (2) Manage @ssl */
|
|
this.http_parts.ssl && ( bufurl = bufurl.concat('s') );
|
|
bufurl = bufurl.concat('://');
|
|
|
|
/* (3) Manage token */
|
|
( typeof _token === 'string' ) && ( bufurl = bufurl.concat(`${_token}@`) );
|
|
|
|
/* (4) Manage hostname */
|
|
bufurl = bufurl.concat(this.http_parts.hostname);
|
|
|
|
/* (5) Manage port */
|
|
( this.http_parts.port !== null ) && ( bufurl = bufurl.concat(`:${this.http_parts.port}`) );
|
|
|
|
/* (6) Base uri */
|
|
bufurl = bufurl.concat(`/${this.http_parts.baseuri}/`);
|
|
|
|
/* (7) Manage URI */
|
|
bufurl = bufurl.concat( this.fix_uri(_uri) );
|
|
|
|
return bufurl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Server Transaction
|
|
*
|
|
* @param _path<String> target path (format "HTTP_METHOD uri/uri/uri")
|
|
* @param _args<Object> formdata object (as raw object)
|
|
* @param _callback<Function> Response callback
|
|
* @param _token<String> [OPT] http token
|
|
*
|
|
***************************************************************************************************
|
|
*
|
|
* @usecase
|
|
* 1. api.call(
|
|
* 2. 'PUT newspaper/article/4'
|
|
* 3. { content: "new content" },
|
|
* 4. (r) => alert(r.error),
|
|
* 5. 'sometoken'
|
|
* 6. );
|
|
*
|
|
*/
|
|
call(_path, _args, _callback, _token=null){
|
|
|
|
|
|
/* (1) Argument management
|
|
---------------------------------------------------------*/
|
|
/* (1) Set default callback if @callback not callable */
|
|
if( !(_callback instanceof Function) )
|
|
_callback = function(r){ console.warn('The API callback function is missing, default callback set.', 'Response', r); };
|
|
|
|
/* (2) Check @path format */
|
|
if( !/^([A-Z]+) (.+)/i.test(_path) ){
|
|
_callback({ error: -1 });
|
|
return false;
|
|
}
|
|
|
|
var http_method = RegExp.$1;
|
|
var http_uri = RegExp.$2;
|
|
|
|
/* (3) Default @_token */
|
|
if( typeof _token !== 'string' )
|
|
_token = null;
|
|
|
|
|
|
|
|
|
|
/* (3) Create form data
|
|
---------------------------------------------------------*/
|
|
/* (1) Create virtual form */
|
|
var form_data = new FormData();
|
|
|
|
/* (2) Add attributes */
|
|
for( var key in _args ){
|
|
|
|
// {2.1} If a file -> send as it //
|
|
if( _args[key] instanceof File )
|
|
form_data.append(key, _args[key]);
|
|
|
|
// {2.2} Else -> JSON stringify //
|
|
else
|
|
form_data.append(key, JSON.stringify(_args[key]));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (4) Create XHR request
|
|
---------------------------------------------------------*/
|
|
/* (1) Clean ended requests */
|
|
for( var i = this.xhr_stack.length-1 ; i >= 0 ; i-- ){
|
|
|
|
if( this.xhr_stack[i] != null )
|
|
break;
|
|
|
|
this.xhr_stack.pop();
|
|
|
|
}
|
|
|
|
/* (2) Push a new entry -> fetch its index */
|
|
i = this.xhr_stack.push(null) - 1;
|
|
|
|
/* (3) Create XHR object */
|
|
this.xhr_stack[i] = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHttpRequest');
|
|
|
|
|
|
|
|
/* (5) Bind response event
|
|
---------------------------------------------------------*/
|
|
this.xhr_stack[i].onreadystatechange = function(i, parent){
|
|
|
|
/* (1) If request over */
|
|
if( this[i].readyState === 4 ){
|
|
|
|
/* (2) Update buffer (for debug) */
|
|
parent.buffer = this[i].responseText;
|
|
|
|
/* (3) If request success */
|
|
if( [0, 200].indexOf(this[i].status) > -1 ){
|
|
|
|
|
|
/* (3.1) Create default response (if JSON error) */
|
|
var response = { error: -2 };
|
|
|
|
/* (3.2) Try to parse JSON */
|
|
try{ response = JSON.parse(this[i].responseText); }catch(e){}
|
|
|
|
/* (3.3) Launch @_callback with response */
|
|
_callback(response);
|
|
|
|
/* (4) If request error */
|
|
}else
|
|
_callback({ error: -3 });
|
|
|
|
/* (5) Notify current xhr instance is done */
|
|
this[i] = null;
|
|
|
|
}
|
|
|
|
}.bind(this.xhr_stack, i, this);
|
|
|
|
|
|
/* (6) Finish & send request
|
|
---------------------------------------------------------*/
|
|
/* (1) Open the XHR */
|
|
console.log(http_method, this.build_url(http_uri, _token));
|
|
this.xhr_stack[i].open(http_method, this.build_url(http_uri, _token), true);
|
|
|
|
/* (2) Custom header to notify we're using Ajax */
|
|
this.xhr_stack[i].setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
|
|
|
/* (3) Make the call */
|
|
this.xhr_stack[i].send(form_data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
} |