301 lines
6.4 KiB
JavaScript
301 lines
6.4 KiB
JavaScript
|
|
const VnObject = require('./object');
|
|
const JsonException = require('./json-exception');
|
|
|
|
/**
|
|
* Handler for JSON rest connections.
|
|
*/
|
|
module.exports = new Class({
|
|
Extends: VnObject,
|
|
|
|
_connected: false,
|
|
_requestsCount: 0,
|
|
token: null,
|
|
|
|
/**
|
|
* Initilizes the connection object.
|
|
*/
|
|
initialize() {
|
|
VnObject.prototype.initialize.call(this);
|
|
this.fetchToken();
|
|
},
|
|
|
|
fetchToken() {
|
|
let token = null;
|
|
|
|
if (sessionStorage.getItem('vnToken'))
|
|
token = sessionStorage.getItem('vnToken');
|
|
if (localStorage.getItem('vnToken'))
|
|
token = localStorage.getItem('vnToken');
|
|
|
|
this.token = token;
|
|
},
|
|
|
|
clearToken() {
|
|
this.token = null;
|
|
localStorage.removeItem('vnToken');
|
|
sessionStorage.removeItem('vnToken');
|
|
},
|
|
|
|
/**
|
|
* Opens the connection to the REST service.
|
|
*
|
|
* @param {String} user The user name
|
|
* @param {String} password The user password
|
|
* @param {Boolean} remember Specifies if the user should be remembered
|
|
* @return {Promise} Resolved when operation is done
|
|
*/
|
|
async open(user, pass, remember) {
|
|
let params;
|
|
|
|
if (user !== null && user !== undefined) {
|
|
params = {
|
|
user,
|
|
password: pass
|
|
};
|
|
} else
|
|
params = null;
|
|
|
|
try {
|
|
const json = await this.post('Accounts/login', params);
|
|
|
|
const storage = remember ? localStorage : sessionStorage;
|
|
storage.setItem('vnToken', json.token);
|
|
|
|
this.token = json.token;
|
|
this._connected = true;
|
|
this.emit('openned');
|
|
} catch(err) {
|
|
this._closeClient();
|
|
throw err;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Closes the connection to the REST service.
|
|
*
|
|
* @return {Promise} Resolved when operation is done
|
|
*/
|
|
async close() {
|
|
const token = this.token;
|
|
this._closeClient();
|
|
this.emit('closed');
|
|
|
|
if (token) {
|
|
const config = {
|
|
headers: {'Authorization': token}
|
|
};
|
|
await this.send('user/logout', null, config);
|
|
await this.post('Accounts/logout', null, config);
|
|
}
|
|
},
|
|
|
|
|
|
_closeClient() {
|
|
this._connected = false;
|
|
this.clearToken();
|
|
},
|
|
|
|
/**
|
|
* Executes the specified REST service with the given params and calls
|
|
* the callback when response is received.
|
|
*
|
|
* @param {String} url The service path
|
|
* @param {Object} params The params to pass to the service
|
|
* @return {Object} The parsed JSON response
|
|
*/
|
|
async send(url, params) {
|
|
if (!params) params = {};
|
|
params.srv = `json:${url}`;
|
|
return this.sendWithUrl('POST', '.', params);
|
|
},
|
|
|
|
async sendForm(form) {
|
|
const params = {};
|
|
const elements = form.elements;
|
|
|
|
for (var i = 0; i < elements.length; i++)
|
|
if (elements[i].name)
|
|
params[elements[i].name] = elements[i].value;
|
|
|
|
return this.sendWithUrl('POST', form.action, params);
|
|
},
|
|
|
|
async sendFormMultipart(form) {
|
|
return this.request({
|
|
method: 'POST',
|
|
url: form.action,
|
|
data: new FormData(form)
|
|
});
|
|
},
|
|
|
|
async sendFormData(formData) {
|
|
return this.request({
|
|
method: 'POST',
|
|
url: '',
|
|
data: formData
|
|
});
|
|
},
|
|
|
|
/*
|
|
* Called when REST response is received.
|
|
*/
|
|
async sendWithUrl(method, url, params, config) {
|
|
config = Object.assign({}, config, {
|
|
method,
|
|
url,
|
|
data: Vn.Url.makeUri(params)
|
|
});
|
|
config.headers = Object.assign({}, config.headers, {
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
});
|
|
return this.request(config);
|
|
},
|
|
|
|
async get(url, config) {
|
|
config = Object.assign({}, config, {
|
|
method: 'GET',
|
|
url: `api/${url}`
|
|
});
|
|
return this.request(config);
|
|
},
|
|
|
|
async post(url, data, config) {
|
|
return this.requestData('POST', url, data, config);
|
|
},
|
|
|
|
async patch(url, data, config) {
|
|
return this.requestData('PATCH', url, data, config);
|
|
},
|
|
|
|
async requestData(method, url, data, config) {
|
|
config = Object.assign({}, config, {
|
|
method,
|
|
url: `api/${url}`,
|
|
data: data && JSON.stringify(data),
|
|
});
|
|
|
|
config.headers = Object.assign({}, config.headers, {
|
|
'Content-Type': 'application/json;charset=utf-8'
|
|
});
|
|
return this.request(config);
|
|
},
|
|
|
|
async request(config) {
|
|
const request = new XMLHttpRequest();
|
|
request.open(config.method, config.url, true);
|
|
if (this.token)
|
|
request.setRequestHeader('Authorization', this.token);
|
|
|
|
const headers = config.headers;
|
|
if (headers)
|
|
for (const header in headers)
|
|
request.setRequestHeader(header, headers[header]);
|
|
|
|
const promise = new Promise((resolve, reject) => {
|
|
request.onreadystatechange =
|
|
() => this._onStateChange(request, resolve, reject);
|
|
});
|
|
|
|
request.send(config.data);
|
|
|
|
this._requestsCount++;
|
|
|
|
if (this._requestsCount === 1)
|
|
this.emit('loading-changed', true);
|
|
|
|
return promise;
|
|
},
|
|
|
|
_onStateChange(request, resolve, reject) {
|
|
if (request.readyState !== 4)
|
|
return;
|
|
|
|
this._requestsCount--;
|
|
|
|
if (this._requestsCount === 0)
|
|
this.emit('loading-changed', false);
|
|
|
|
let data = null;
|
|
let error = null;
|
|
try {
|
|
if (request.status == 0) {
|
|
const err = new JsonException();
|
|
err.message = _('The server does not respond, please check your Internet connection');
|
|
err.statusCode = request.status;
|
|
throw err;
|
|
}
|
|
|
|
let contentType = null;
|
|
|
|
try {
|
|
contentType = request
|
|
.getResponseHeader('Content-Type')
|
|
.split(';')[0]
|
|
.trim();
|
|
} catch (err) {
|
|
console.warn(err);
|
|
}
|
|
|
|
if (contentType != 'application/json') {
|
|
const err = new JsonException();
|
|
err.message = request.statusText;
|
|
err.statusCode = request.status;
|
|
throw err;
|
|
}
|
|
|
|
let json;
|
|
let jsData;
|
|
|
|
if (request.responseText)
|
|
json = JSON.parse(request.responseText);
|
|
if (json)
|
|
jsData = json.data || json;
|
|
|
|
if (request.status >= 200 && request.status < 300) {
|
|
data = jsData;
|
|
} else {
|
|
let exception = jsData.exception;
|
|
let error = jsData.error;
|
|
let err = new JsonException();
|
|
|
|
if (exception) {
|
|
exception = exception
|
|
.replace(/\\/g, '.')
|
|
.replace(/Exception$/, 'Error')
|
|
.replace(/^Vn\.Web\./, '');
|
|
|
|
err.exception = exception;
|
|
err.message = jsData.message;
|
|
err.code = jsData.code;
|
|
err.file = jsData.file;
|
|
err.line = jsData.line;
|
|
err.trace = jsData.trace;
|
|
err.statusCode = request.status;
|
|
} else if (error) {
|
|
err.message = error.message;
|
|
err.code = error.code;
|
|
err.statusCode = request.status;
|
|
} else {
|
|
err.message = request.statusText;
|
|
err.statusCode = request.status;
|
|
}
|
|
|
|
throw err;
|
|
}
|
|
} catch (e) {
|
|
data = null;
|
|
error = e;
|
|
}
|
|
|
|
if (error) {
|
|
if (error.exception == 'SessionExpired')
|
|
this.clearToken();
|
|
|
|
this.emit('error', error);
|
|
reject(error);
|
|
} else
|
|
resolve(data);
|
|
}
|
|
});
|