blob: 24734739a2a77f396ac5bd73f56f3b79c050c5d7 [file] [log] [blame]
Michael Landoefa037d2017-02-19 12:57:33 +02001/*-
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
21import _extend from 'lodash/extend.js';
22import _clone from 'lodash/clone.js';
23import _defaults from 'lodash/defaults.js';
24import $ from 'jquery';
25import uuid from 'uuid-js';
26import md5 from 'md5';
27
28import store from 'sdc-app/AppStore.js';
29import {actionTypes as LoaderConstants} from 'nfvo-components/loader/LoaderConstants.js';
30import Configuration from 'sdc-app/config/Configuration.js';
31import errorResponseHandler from './ErrorResponseHandler.js';
32
33const methodMap = {
34 'create': 'POST',
35 'update': 'PUT',
36 'delete': 'DELETE',
37 'read': 'GET'
38};
39const AUTHORIZATION_HEADER = 'X-AUTH-TOKEN';
40const STORAGE_AUTH_KEY = 'sdc-auth-token';
41const REQUEST_ID_HEADER = 'X-ECOMP-RequestID';
42const CONTENT_MD5_HEADER = 'Content-MD5';
43const namedParam = /{(\w+)}/g;
44const 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
102function urlError() {
103 throw new Error('A "url" property or function must be specified');
104};
105
106export 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
123function 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
139function 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
147function 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
172function 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
179function 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
198function 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
210function 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
270export 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};