RIFT-16524: CATALOG COMPOSER : INTERNAL CONNECTION POINTS not getting populated in...
[osm/UI.git] / skyquake / plugins / composer / src / src / libraries / utils.js
1 /*
2 *
3 * Copyright 2016 RIFT.IO Inc
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 'use strict';
20
21 import _cloneDeep from 'lodash/cloneDeep';
22 import _isArray from 'lodash/isArray';
23 import _map from 'lodash/map';
24 import _flatten from 'lodash/flatten';
25 import _find from 'lodash/find';
26 import _toNumber from 'lodash/toNumber';
27 import _isNumber from 'lodash/isNumber';
28 import _clone from 'lodash/clone';
29
30 export default {
31 addAuthorizationStub(xhr) {
32 xhr.setRequestHeader('Authorization', 'Basic YWRtaW46YWRtaW4=');
33 },
34 getSearchParams(url) {
35 var a = document.createElement('a');
36 a.href = url;
37 var params = {};
38 var items = a.search.replace('?', '').split('&');
39 for (var i = 0; i < items.length; i++) {
40 if (items[i].length > 0) {
41 var key_value = items[i].split('=');
42 params[key_value[0]] = decodeURIComponent(key_value[1]);
43 }
44 }
45 return params;
46 },
47 parseJSONIgnoreErrors(txt) {
48 try {
49 return JSON.parse(txt);
50 } catch (ignore) {
51 return {};
52 }
53 },
54 resolvePath(obj, path) {
55 // supports a.b, a[1] and foo[bar], etc.
56 // where obj is ['nope', 'yes', {a: {b: 1}, foo: 2}]
57 // then [1] returns 'yes'; [2].a.b returns 1; [2].a[foo] returns 2;
58 path = path.split(/[\.\[\]]/).filter(d => d);
59 return path.reduce((r, p) => {
60 if (r) {
61 return r[p];
62 }
63 }, obj);
64 },
65 assignPathValue(obj, path, value) {
66 path = path.split(/[\.\[\]]/).filter(d => d);
67 // enable look-ahead to determine if type is array or object
68 const pathCopy = path.slice();
69 // last item in path used to assign value on the resolved object
70 const name = path.pop();
71 const resolvedObj = path.reduce((r, p, i) => {
72 if (typeof(r[p]) !== 'object') {
73 // look-ahead to see if next path item is a number
74 const isArray = !isNaN(parseInt(pathCopy[i + 1], 10));
75 r[p] = isArray ? [] : {}
76 }
77 return r[p];
78 }, obj);
79 resolvedObj[name] = value;
80 },
81 updatePathValue(obj, path, value, isCase) {
82 // todo: replace implementation of assignPathValue with this impl and
83 // remove updatePathValue (only need one function, not both)
84 // same as assignPathValue except removes property if value is undefined
85 path = path.split(/[\.\[\]]/).filter(d => d);
86 const isRemove = typeof value === 'undefined';
87 // enable look-ahead to determine if type is array or object
88 const pathCopy = path.slice();
89 // last item in path used to assign value on the resolved object
90 const name = path.pop();
91 const resolvedObj = path.reduce((r, p, i) => {
92 // look-ahead to see if next path item is a number
93 const index = parseInt(pathCopy[i + 1], 10);
94 const isArray = !isNaN(index);
95 if (typeof(r[p]) !== 'object') {
96 r[p] = isArray ? [] : {}
97 }
98 if (isRemove && ((i + 1) === path.length)) {
99 if (isArray) {
100 r[p] = r[p].filter((d, i) => i !== index);
101 } else {
102 if(isCase) {
103 delete r[name];
104 } else {
105 delete r[p][name];
106 }
107 }
108 }
109 if(isCase) {
110 return r;
111 } else {
112 return r[p];
113 }
114
115 }, obj);
116 if (!isRemove) {
117 resolvedObj[name] = value;
118 }
119 },
120 removePathValue(obj, path, isCase) {
121 // note updatePathValue removes value if third argument is undefined
122 return this.updatePathValue(obj, path, undefined, isCase);
123 },
124
125 suffixAsInteger: (field) => {
126 return (obj) =>{
127 const str = String(obj[field]);
128 const value = str.replace(str.replace(/[\d]+$/, ''), '');
129 return 1 + parseInt(value, 10) || 0;
130 };
131 },
132
133 toBiggestValue: (maxIndex, curIndex) => Math.max(maxIndex, curIndex),
134
135 isRelativePath (path) {
136 if (path.split('/')[0] == '..') {
137 return true;
138 }
139 return false;
140 },
141
142 getResults (topLevelObject, pathArray) {
143 let objectCopy = _cloneDeep(topLevelObject);
144 let i = pathArray.length;
145 let results = [];
146
147 while(pathArray[pathArray.length - i]) {
148 if (_isArray(objectCopy[pathArray[pathArray.length - i]])) {
149 if (i == 2) {
150 results = _map(objectCopy[pathArray[pathArray.length - i]], pathArray[pathArray.length - 1]);
151 } else {
152 objectCopy = objectCopy[pathArray[pathArray.length - i]];
153 }
154 } else if (_isArray(objectCopy)) {
155 objectCopy.map((object) => {
156 if (_isArray(object[pathArray[pathArray.length - i]])) {
157 if (i == 2) {
158 results = results.concat(_map(object[pathArray[pathArray.length - i]], pathArray[pathArray.length - 1]));
159 }
160 }
161 })
162 }
163 i--;
164 }
165
166 return results;
167 },
168
169 getAbsoluteResults (topLevelObject, pathArray) {
170 let i = pathArray.length;
171 let objectCopy = _cloneDeep(topLevelObject);
172 let results = [];
173
174 let fragment = pathArray[pathArray.length - i]
175
176 while (fragment) {
177 if (i == 1) {
178 // last fragment
179 if (_isArray(objectCopy)) {
180 // results will be obtained from a map
181 results = _map(objectCopy, fragment);
182 } else {
183 // object
184 if (fragment.match(/\[.*\]/g)) {
185 // contains a predicate
186 // shouldn't reach here
187 console.log('Something went wrong while resolving a leafref. Reached a leaf with predicate.');
188 } else {
189 // contains no predicate
190 results.push(objectCopy[fragment]);
191 }
192 }
193 } else {
194 if (_isArray(objectCopy)) {
195 // is array
196 objectCopy = _map(objectCopy, fragment);
197
198 // If any of the deeper object is an array, flatten the entire list.
199 // This would usually be a bad leafref going out of its scope.
200 // Log it too
201 for (let i = 0; i < objectCopy.length; i++) {
202 if (_isArray(objectCopy[i])) {
203 objectCopy = _flatten(objectCopy);
204 console.log('This might be a bad leafref. Verify with backend team.')
205 break;
206 }
207 }
208 } else {
209 // is object
210 if (fragment.match(/\[.*\]/g)) {
211 // contains a predicate
212 let predicateStr = fragment.match(/\[.*\]/g)[0];
213 // Clip leading [ and trailing ]
214 predicateStr = predicateStr.substr(1, predicateStr.length - 2);
215 const predicateKeyValue = predicateStr.split('=');
216 const predicateKey = predicateKeyValue[0];
217 const predicateValue = predicateKeyValue[1];
218 // get key for object to search into
219 let key = fragment.split('[')[0];
220 let searchObject = {};
221 searchObject[predicateKey] = predicateValue;
222 let found = _find(objectCopy[key], searchObject);
223 if (found) {
224 objectCopy = found;
225 } else {
226 // check for numerical value
227 if (predicateValue != "" &&
228 predicateValue != null &&
229 predicateValue != NaN &&
230 predicateValue != Infinity &&
231 predicateValue != -Infinity) {
232 let numericalPredicateValue = _toNumber(predicateValue);
233 if (_isNumber(numericalPredicateValue)) {
234 searchObject[predicateKey] = numericalPredicateValue;
235 found = _find(objectCopy[key], searchObject);
236 }
237 }
238 if (found) {
239 objectCopy = found;
240 } else {
241 return [];
242 }
243 }
244 } else {
245 // contains no predicate
246 objectCopy = objectCopy[fragment];
247 if (!objectCopy) {
248 // contains no value
249 break;
250 }
251 }
252 }
253 }
254 i--;
255 fragment = pathArray[pathArray.length - i];
256 }
257
258 return results;
259 },
260
261 resolveCurrentPredicate (leafRefPath, container, pathCopy) {
262 if (leafRefPath.indexOf('current()') != -1) {
263 // contains current
264
265 // Get the relative path from current
266 let relativePath = leafRefPath.match("current\\(\\)\/(.*)\]");
267 let relativePathArray = relativePath[1].split('/');
268
269 while (relativePathArray[0] == '..') {
270 pathCopy.pop();
271 relativePathArray.shift();
272 }
273
274 // Supports only one relative path up
275 // To support deeper paths, will need to massage the string more
276 // i.e. change '/'' to '.'
277 const searchPath = pathCopy.join('.').concat('.', relativePathArray[0]);
278 const searchValue = this.resolvePath(container.model, searchPath);
279
280 const predicateStr = leafRefPath.match("(current.*)\]")[1];
281 leafRefPath = leafRefPath.replace(predicateStr, searchValue);
282 }
283 return leafRefPath;
284 },
285
286 cleanupFieldKeyArray (fieldKeyArray) {
287 fieldKeyArray.map((fieldKey, fieldKeyIndex) => {
288 fieldKeyArray[fieldKeyIndex] = fieldKey.replace(/.*\/(.*)/, '$1');
289 });
290 return fieldKeyArray;
291 },
292
293 resolveLeafRefPath (catalogs, leafRefPath, fieldKey, path, container) {
294 let pathCopy = _clone(path);
295 // Strip any prefixes
296 let leafRefPathCopy = leafRefPath.replace(/[\w\d]*:/g, '');
297 // Strip any spaces
298 leafRefPathCopy = leafRefPathCopy.replace(/\s/g, '');
299
300 // resolve any current paths
301 leafRefPathCopy = this.resolveCurrentPredicate(leafRefPathCopy, container, pathCopy);
302
303 // Split on delimiter (/)
304 const pathArray = leafRefPathCopy.split('/');
305
306 let fieldKeyArray = fieldKey.split(':');
307
308 // strip prepending qualifiers from fieldKeys
309 fieldKeyArray = this.cleanupFieldKeyArray(fieldKeyArray);
310 let results = [];
311
312 // Check if relative path or not
313 // TODO: Below works but
314 // better to convert the pathCopy to absolute/rooted path
315 // and use the absolute module instead
316 if (this.isRelativePath(leafRefPathCopy)) {
317 let i = pathArray.length;
318 while ((pathArray[pathArray.length - i] == '..') && fieldKeyArray.length > 1) {
319 fieldKeyArray.splice(-1, 1);
320 if (!isNaN(Number(fieldKeyArray[fieldKeyArray.length - 1]))) {
321 // found a number, so an index. strip it
322 fieldKeyArray.splice(-1, 1);
323 }
324 i--;
325 }
326
327 // traversed all .. - now traverse down
328 if (fieldKeyArray.length == 1) {
329 for (let key in catalogs) {
330 for (let subKey in catalogs[key]) {
331 let found = _find(catalogs[key][subKey], {id: fieldKeyArray[0]});
332 if (found) {
333 results = this.getAbsoluteResults(found, pathArray.splice(-i, i));
334 return results;
335 }
336 }
337 }
338 } else if (fieldKeyArray.length == 2) {
339 for (let key in catalogs) {
340 for (let subKey in catalogs[key]) {
341 console.log(key, subKey);
342 var found = _find(catalogs[key][subKey], {id: fieldKeyArray[0]});
343 if (found) {
344 for (let foundKey in found) {
345 if (_isArray(found[foundKey])) {
346 let topLevel = _find(found[foundKey], {id: fieldKeyArray[1]});
347 if (topLevel) {
348 results = this.getAbsoluteResults(topLevel, pathArray.splice(-i, i));
349 return results;
350 }
351 } else {
352 if (foundKey == fieldKeyArray[1]) {
353 results = this.getAbsoluteResults(found[foundKey], pathArray.splice(-i, i));
354 return results;
355 }
356 }
357 }
358 }
359 }
360 }
361 } else if (fieldKeyArray.length == 3) {
362 for (let key in catalogs) {
363 for (let subKey in catalogs[key]) {
364 let found = _find(catalogs[key][subKey], {id: fieldKeyArray[0]});
365 if (found) {
366 for (let foundKey in found) {
367 if (_isArray(found[foundKey])) {
368 let topLevel = _find(found[foundKey], {id: fieldKeyArray[1]});
369 if (topLevel) {
370 results = this.getAbsoluteResults(topLevel, pathArray.splice(-i, i));
371 return results;
372 }
373 } else {
374 if (foundKey == fieldKeyArray[1]) {
375 results = this.getAbsoluteResults(found[foundKey], pathArray.splice(-i, i));
376 return results;
377 }
378 }
379 }
380 }
381 }
382 }
383 } else {
384 // not supported - too many levels deep ... maybe some day
385 console.log('The relative path is from a node too many levels deep from root. This is not supported at the time');
386 }
387 } else {
388 // absolute path
389 if (pathArray[0] == "") {
390 pathArray.splice(0, 1);
391 }
392
393 let catalogKey = pathArray[0];
394 let topLevelObject = {};
395
396 topLevelObject[catalogKey] = catalogs[catalogKey];
397
398 results = this.getAbsoluteResults(topLevelObject, pathArray);
399
400 return results;
401 }
402 }
403 }
404