2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 import _extend from 'lodash/extend.js';
22 import _clone from 'lodash/clone.js';
23 import _defaults from 'lodash/defaults.js';
24 import $ from 'jquery';
25 import uuid from 'uuid-js';
26 import md5 from 'md5';
28 import store from 'sdc-app/AppStore.js';
29 import {actionTypes as LoaderConstants} from 'nfvo-components/loader/LoaderConstants.js';
30 import Configuration from 'sdc-app/config/Configuration.js';
31 import errorResponseHandler from './ErrorResponseHandler.js';
39 const AUTHORIZATION_HEADER = 'X-AUTH-TOKEN';
40 const STORAGE_AUTH_KEY = 'sdc-auth-token';
41 const REQUEST_ID_HEADER = 'X-ECOMP-RequestID';
42 const CONTENT_MD5_HEADER = 'Content-MD5';
43 const namedParam = /{(\w+)}/g;
44 const queryParamsNames = {
45 pageStart: 'pageStart',
47 sortField: 'sortField',
53 // jQuery binary transport to download files through XHR
54 // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
55 // https://github.com/henrya/js-jquery/tree/master/BinaryTransport
56 $.ajaxTransport('+binary', function (options/*, originalOptions , jqXHR*/) {
57 // check for conditions and support for blob / arraybuffer response type
58 if (window.FormData && ((options.dataType && (options.dataType === 'binary')) ||
59 (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) ||
60 (window.Blob && options.data instanceof Blob))))
63 // create new XMLHttpRequest
64 send: function (headers, callback) {
65 // setup all variables
66 var xhr = new XMLHttpRequest(),
69 async = options.async || true,
70 // blob or arraybuffer. Default is blob
71 dataType = options.responseType || 'blob',
72 data = options.data || null,
73 username = options.username || null,
74 password = options.password || null;
76 xhr.addEventListener('load', function () {
78 data[options.dataType] = xhr.response;
79 // make callback and send data
80 callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders());
83 xhr.open(type, url, async, username, password);
85 // setup custom headers
86 for (var i in headers) {
87 xhr.setRequestHeader(i, headers[i]);
90 xhr.responseType = dataType;
99 $(document).ajaxStart(()=> store.dispatch({type: LoaderConstants.SHOW}));
100 $(document).ajaxStop(()=> store.dispatch({type: LoaderConstants.HIDE}));
102 function urlError() {
103 throw new Error('A "url" property or function must be specified');
106 export function makeQueryParams(options) {
108 if (options.pagination) {
109 qParams[queryParamsNames.pageStart] = options.pagination.pageStart;
110 qParams[queryParamsNames.pageSize] = options.pagination.pageSize;
112 if (options.sorting) {
113 qParams[queryParamsNames.sortField] = options.sorting.sortField;
114 qParams[queryParamsNames.sortDir] = options.sorting.sortDir;
116 if (options.filtering) {
117 qParams[queryParamsNames.filtering] = JSON.stringify(options.filtering);
120 return _defaults(qParams, options.qParams);
123 function appendQueryParam(p, value) {
126 if (value instanceof Array) {
127 if (value.length === 1) {
128 str = appendQueryParam(p, value[0]);
129 } else if (value.length > 1) {
130 str = appendQueryParam(p, value.shift()) + '&' + appendQueryParam(p, value);
133 str = p + '=' + encodeURIComponent(value);
139 function appendQueryString(url, qParams) {
141 for (var param in qParams) {
142 str += (str ? '&' : '') + appendQueryParam(param, qParams[param]);
144 return url + (str ? '?' : '') + str;
147 function composeURL(baseUrl, options) {
148 var url = baseUrl || urlError();
153 var qParams = makeQueryParams(options);
154 url = appendQueryString(url, qParams);
156 var matches = url.match(namedParam);
158 for (var i = 0; i < matches.length; i++) {
159 var param = matches[i].substring(1, matches[i].length - 1);
160 var value = (options.params && options.params[param]);
162 if (value === undefined) {
163 value = options[param];
165 url = url.replace(matches[i], encodeURIComponent(value));
172 function applyMD5Header(options, data) {
174 let headers = options.headers;
175 headers[CONTENT_MD5_HEADER] = window.btoa(md5(JSON.stringify(data)).toLowerCase());
179 function applySecurity(options, data) {
180 var headers = options.headers || (options.headers = {});
182 var authToken = localStorage.getItem(STORAGE_AUTH_KEY);
184 headers[AUTHORIZATION_HEADER] = authToken;
187 var attApiHeaders = Configuration.get('ATTApiHeaders'),
188 attUidHeader = attApiHeaders && attApiHeaders.userId;
190 headers[attUidHeader.name] = attUidHeader.value;
193 headers[REQUEST_ID_HEADER] = uuid.create().toString();
195 applyMD5Header(options, data);
198 function handleResponse(options) {
199 var authToken = options.xhr.getResponseHeader(AUTHORIZATION_HEADER);
200 var prevToken = options.headers && options.headers[AUTHORIZATION_HEADER];
201 if (authToken && authToken !== prevToken) {
202 if (authToken === 'null') {
203 localStorage.removeItem(STORAGE_AUTH_KEY);
205 localStorage.setItem(STORAGE_AUTH_KEY, authToken);
210 function sync(baseUrl, method, options, data) {
212 options = options ? _clone(options) : {};
214 var type = methodMap[method];
215 _defaults(options || (options = {}));
220 params.url = composeURL(baseUrl, options);
222 if ((method === 'create' || method === 'update') && data instanceof FormData) {
223 params.contentType = 'multipart/form-data';
226 else if (method === 'create' || method === 'update') {
227 params.contentType = 'application/json';
228 params.data = JSON.stringify(data);
231 if (params.type !== 'GET') {
232 params.processData = false;
234 var success = options.success;
235 options.success = function (resp) {
237 handleResponse(options);
238 success.call(options.context, _clone(resp), resp, options);
242 options.error = options.error || errorResponseHandler;
244 if (typeof options.progressCallback === 'function' && options.fileSize) {
245 const {fileSize} = options;
246 options.xhrFields = {
247 // add listener to XMLHTTPRequest object directly for progress (jquery doesn't have this yet)
248 onprogress: function (progress) {
249 // calculate upload progress
250 let percentage = Math.floor((progress.loaded / fileSize) * 100);
251 // log upload progress to console
252 //console.log('progress', percentage);
253 options.progressCallback(percentage);
254 if (percentage === 100) {
255 console.log('DONE!');
261 applySecurity(options, data);
264 console.log('--> Making REST call (' + type + '): ' + params.url);
266 var xhr = options.xhr = $.ajax(_extend(params, options));
272 fetch(baseUrl, options) {
273 return sync(baseUrl, 'read', options);
276 save(baseUrl, data, options) {
277 return sync(baseUrl, 'update', options, data);
280 create(baseUrl, data, options) {
281 return sync(baseUrl, 'create', options, data);
284 destroy(baseUrl, options) {
285 return sync(baseUrl, 'delete', options);