Add new code new version
[sdc.git] / openecomp-ui / src / nfvo-utils / RestAPIUtil.js
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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=========================================================
19  */
20
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';
27
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';
32
33 const methodMap = {
34         'create': 'POST',
35         'update': 'PUT',
36         'delete': 'DELETE',
37         'read': 'GET'
38 };
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',
46         pageSize: 'pageSize',
47         sortField: 'sortField',
48         sortDir: 'sortDir',
49         filtering: 'filter'
50 };
51
52
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))))
61         ) {
62                 return {
63                         // create new XMLHttpRequest
64                         send: function (headers, callback) {
65                                 // setup all variables
66                                 var xhr = new XMLHttpRequest(),
67                                         url = options.url,
68                                         type = options.type,
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;
75
76                                 xhr.addEventListener('load', function () {
77                                         var data = {};
78                                         data[options.dataType] = xhr.response;
79                                         // make callback and send data
80                                         callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders());
81                                 });
82
83                                 xhr.open(type, url, async, username, password);
84
85                                 // setup custom headers
86                                 for (var i in headers) {
87                                         xhr.setRequestHeader(i, headers[i]);
88                                 }
89
90                                 xhr.responseType = dataType;
91                                 xhr.send(data);
92                         },
93                         abort: function () {
94                         }
95                 };
96         }
97 });
98
99 $(document).ajaxStart(()=> store.dispatch({type: LoaderConstants.SHOW}));
100 $(document).ajaxStop(()=> store.dispatch({type: LoaderConstants.HIDE}));
101
102 function urlError() {
103         throw new Error('A "url" property or function must be specified');
104 };
105
106 export function makeQueryParams(options) {
107         var qParams = {};
108         if (options.pagination) {
109                 qParams[queryParamsNames.pageStart] = options.pagination.pageStart;
110                 qParams[queryParamsNames.pageSize] = options.pagination.pageSize;
111         }
112         if (options.sorting) {
113                 qParams[queryParamsNames.sortField] = options.sorting.sortField;
114                 qParams[queryParamsNames.sortDir] = options.sorting.sortDir;
115         }
116         if (options.filtering) {
117                 qParams[queryParamsNames.filtering] = JSON.stringify(options.filtering);
118         }
119
120         return _defaults(qParams, options.qParams);
121 }
122
123 function appendQueryParam(p, value) {
124         var str = '';
125
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);
131                 }
132         } else {
133                 str = p + '=' + encodeURIComponent(value);
134         }
135
136         return str;
137 }
138
139 function appendQueryString(url, qParams) {
140         var str = '';
141         for (var param in qParams) {
142                 str += (str ? '&' : '') + appendQueryParam(param, qParams[param]);
143         }
144         return url + (str ? '?' : '') + str;
145 }
146
147 function composeURL(baseUrl, options) {
148         var url = baseUrl || urlError();
149         if (options.url) {
150                 delete options.url;
151         }
152
153         var qParams = makeQueryParams(options);
154         url = appendQueryString(url, qParams);
155
156         var matches = url.match(namedParam);
157         if (matches) {
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]);
161
162                         if (value === undefined) {
163                                 value = options[param];
164                         }
165                         url = url.replace(matches[i], encodeURIComponent(value));
166                 }
167         }
168
169         return url;
170 }
171
172 function applyMD5Header(options, data) {
173         if (options.md5) {
174                 let headers = options.headers;
175                 headers[CONTENT_MD5_HEADER] = window.btoa(md5(JSON.stringify(data)).toLowerCase());
176         }
177 }
178
179 function applySecurity(options, data) {
180         var headers = options.headers || (options.headers = {});
181
182         var authToken = localStorage.getItem(STORAGE_AUTH_KEY);
183         if (authToken) {
184                 headers[AUTHORIZATION_HEADER] = authToken;
185         }
186
187         var attApiHeaders = Configuration.get('ATTApiHeaders'),
188                 attUidHeader = attApiHeaders && attApiHeaders.userId;
189         if (attUidHeader) {
190                 headers[attUidHeader.name] = attUidHeader.value;
191         }
192
193         headers[REQUEST_ID_HEADER] = uuid.create().toString();
194
195         applyMD5Header(options, data);
196 }
197
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);
204                 } else {
205                         localStorage.setItem(STORAGE_AUTH_KEY, authToken);
206                 }
207         }
208 }
209
210 function sync(baseUrl, method, options, data) {
211
212         options = options ? _clone(options) : {};
213
214         var type = methodMap[method];
215         _defaults(options || (options = {}));
216         var params = {
217                 type: type,
218                 dataType: 'json'
219         };
220         params.url = composeURL(baseUrl, options);
221
222         if ((method === 'create' || method === 'update') && data instanceof FormData) {
223                 params.contentType = 'multipart/form-data';
224                 params.data = data;
225         }
226         else if (method === 'create' || method === 'update') {
227                 params.contentType = 'application/json';
228                 params.data = JSON.stringify(data);
229         }
230
231         if (params.type !== 'GET') {
232                 params.processData = false;
233         }
234         var success = options.success;
235         options.success = function (resp) {
236                 if (success) {
237                         handleResponse(options);
238                         success.call(options.context, _clone(resp), resp, options);
239                 }
240         };
241
242         options.error = options.error || errorResponseHandler;
243
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!');
256                                 }
257                         }
258                 };
259         }
260
261         applySecurity(options, data);
262
263         if (DEBUG) {
264                 console.log('--> Making REST call (' + type + '): ' + params.url);
265         }
266         var xhr = options.xhr = $.ajax(_extend(params, options));
267         return xhr;
268 }
269
270 export default {
271
272         fetch(baseUrl, options) {
273                 return sync(baseUrl, 'read', options);
274         },
275
276         save(baseUrl, data, options) {
277                 return sync(baseUrl, 'update', options, data);
278         },
279
280         create(baseUrl, data, options) {
281                 return sync(baseUrl, 'create', options, data);
282         },
283
284         destroy(baseUrl, options) {
285                 return sync(baseUrl, 'delete', options);
286         }
287
288 };