@esri/solution-simple-types 4.1.2 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/cjs/dashboard.d.ts +93 -93
  2. package/dist/cjs/dashboard.js +352 -352
  3. package/dist/cjs/helpers/convert-item-to-template.d.ts +46 -46
  4. package/dist/cjs/helpers/convert-item-to-template.js +235 -209
  5. package/dist/cjs/helpers/convert-item-to-template.js.map +1 -1
  6. package/dist/cjs/helpers/create-item-from-template.d.ts +17 -17
  7. package/dist/cjs/helpers/create-item-from-template.js +153 -138
  8. package/dist/cjs/helpers/create-item-from-template.js.map +1 -1
  9. package/dist/cjs/helpers/notebook-helpers.d.ts +19 -19
  10. package/dist/cjs/helpers/notebook-helpers.js +24 -24
  11. package/dist/cjs/helpers/quickcapture-helpers.d.ts +18 -18
  12. package/dist/cjs/helpers/quickcapture-helpers.js +22 -22
  13. package/dist/cjs/helpers/simple-type-helpers.d.ts +18 -18
  14. package/dist/cjs/helpers/simple-type-helpers.js +22 -22
  15. package/dist/cjs/helpers/update-notebook-data.d.ts +17 -17
  16. package/dist/cjs/helpers/update-notebook-data.js +27 -27
  17. package/dist/cjs/index.d.ts +27 -27
  18. package/dist/cjs/index.js +36 -36
  19. package/dist/cjs/notebook.d.ts +66 -66
  20. package/dist/cjs/notebook.js +133 -133
  21. package/dist/cjs/oic.d.ts +53 -53
  22. package/dist/cjs/oic.js +170 -170
  23. package/dist/cjs/quickcapture.d.ts +76 -87
  24. package/dist/cjs/quickcapture.js +143 -192
  25. package/dist/cjs/quickcapture.js.map +1 -1
  26. package/dist/cjs/simple-types.d.ts +57 -57
  27. package/dist/cjs/simple-types.js +94 -94
  28. package/dist/cjs/webmap.d.ts +102 -102
  29. package/dist/cjs/webmap.js +325 -325
  30. package/dist/cjs/webmappingapplication.d.ts +176 -176
  31. package/dist/cjs/webmappingapplication.js +674 -674
  32. package/dist/cjs/webmappingapplication.js.map +1 -1
  33. package/dist/cjs/workforce.d.ts +34 -34
  34. package/dist/cjs/workforce.js +44 -44
  35. package/dist/esm/dashboard.d.ts +93 -93
  36. package/dist/esm/dashboard.js +339 -339
  37. package/dist/esm/helpers/convert-item-to-template.d.ts +46 -46
  38. package/dist/esm/helpers/convert-item-to-template.js +228 -202
  39. package/dist/esm/helpers/convert-item-to-template.js.map +1 -1
  40. package/dist/esm/helpers/create-item-from-template.d.ts +17 -17
  41. package/dist/esm/helpers/create-item-from-template.js +148 -133
  42. package/dist/esm/helpers/create-item-from-template.js.map +1 -1
  43. package/dist/esm/helpers/notebook-helpers.d.ts +19 -19
  44. package/dist/esm/helpers/notebook-helpers.js +20 -20
  45. package/dist/esm/helpers/quickcapture-helpers.d.ts +18 -18
  46. package/dist/esm/helpers/quickcapture-helpers.js +19 -19
  47. package/dist/esm/helpers/simple-type-helpers.d.ts +18 -18
  48. package/dist/esm/helpers/simple-type-helpers.js +19 -19
  49. package/dist/esm/helpers/update-notebook-data.d.ts +17 -17
  50. package/dist/esm/helpers/update-notebook-data.js +23 -23
  51. package/dist/esm/index.d.ts +27 -27
  52. package/dist/esm/index.js +27 -27
  53. package/dist/esm/notebook.d.ts +66 -66
  54. package/dist/esm/notebook.js +123 -123
  55. package/dist/esm/oic.d.ts +53 -53
  56. package/dist/esm/oic.js +162 -162
  57. package/dist/esm/quickcapture.d.ts +76 -87
  58. package/dist/esm/quickcapture.js +131 -179
  59. package/dist/esm/quickcapture.js.map +1 -1
  60. package/dist/esm/simple-types.d.ts +57 -57
  61. package/dist/esm/simple-types.js +86 -86
  62. package/dist/esm/webmap.d.ts +102 -102
  63. package/dist/esm/webmap.js +312 -312
  64. package/dist/esm/webmappingapplication.d.ts +176 -176
  65. package/dist/esm/webmappingapplication.js +648 -648
  66. package/dist/esm/webmappingapplication.js.map +1 -1
  67. package/dist/esm/workforce.d.ts +34 -34
  68. package/dist/esm/workforce.js +38 -38
  69. package/package.json +7 -7
@@ -1,649 +1,649 @@
1
- /** @license
2
- * Copyright 2020 Esri
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
- import * as common from "@esri/solution-common";
17
- // ------------------------------------------------------------------------------------------------------------------ //
18
- /**
19
- * Converts a web mapping application item into a template.
20
- *
21
- * @param itemInfo Info about the item
22
- * @param destAuthentication Credentials for requests to the destination organization
23
- * @param srcAuthentication Credentials for requests to source items
24
- * @param templateDictionary Hash of key details used for variable replacement
25
- * @returns A promise that will resolve when the template has been created
26
- */
27
- export function convertItemToTemplate(itemTemplate, destAuthentication, srcAuthentication, templateDictionary) {
28
- return new Promise((resolve, reject) => {
29
- // Remove org base URL and app id, e.g.,
30
- // http://anOrg.maps.arcgis.com/apps/CrowdsourcePolling/index.html?appid=6fc5992522d34a6b5ce80d17835eea21
31
- // to
32
- // <placeholder(SERVER_NAME)>/apps/CrowdsourcePolling/index.html?appid={{<itemId>.id}}
33
- // Need to add placeholder server name because otherwise AGOL makes URL null
34
- let portalUrl = "";
35
- if (itemTemplate.item.url) {
36
- const templatizedUrl = itemTemplate.item.url;
37
- const iSep = templatizedUrl.indexOf("//");
38
- itemTemplate.item.url =
39
- common.placeholder(common.SERVER_NAME) + // add placeholder server name
40
- templatizedUrl.substring(templatizedUrl.indexOf("/", iSep + 2), templatizedUrl.lastIndexOf("=") + 1) +
41
- itemTemplate.item.id; // templatized id
42
- portalUrl = templatizedUrl.replace(templatizedUrl.substring(templatizedUrl.indexOf("/", iSep + 2)), "");
43
- }
44
- // Extract dependencies
45
- itemTemplate.dependencies = _extractDependencies(itemTemplate);
46
- // Set the folder
47
- common.setProp(itemTemplate, "data.folderId", "{{folderId}}");
48
- // Set the map or group after we've extracted them as dependencies
49
- _templatizeIdPaths(itemTemplate, [
50
- "data.map.itemId",
51
- "data.map.appProxy.mapItemId",
52
- "data.values.webmap",
53
- "data.values.group"
54
- ]);
55
- // force the appItemId to be pulled directly from the template item
56
- // this is to address solution.js #124
57
- _templatizeIdPath(itemTemplate, "data.appItemId", itemTemplate.itemId);
58
- setValues(itemTemplate, [
59
- "data.logo",
60
- "data.map.portalUrl",
61
- "data.portalUrl",
62
- "data.httpProxy.url"
63
- ], common.placeholder(common.SERVER_NAME));
64
- common.setProp(itemTemplate, "data.geometryService", common.placeholder(common.GEOMETRY_SERVER_NAME));
65
- templatizeDatasources(itemTemplate, srcAuthentication, portalUrl, templateDictionary).then(() => {
66
- templatizeWidgets(itemTemplate, srcAuthentication, portalUrl, "data.widgetPool.widgets").then(_itemTemplate => {
67
- templatizeWidgets(_itemTemplate, srcAuthentication, portalUrl, "data.widgetOnScreen.widgets", true).then(updatedItemTemplate => {
68
- templatizeValues(updatedItemTemplate, srcAuthentication, portalUrl, "data.values").then(_updatedItemTemplate => {
69
- resolve(_updatedItemTemplate);
70
- }, e => reject(common.fail(e)));
71
- }, e => reject(common.fail(e)));
72
- }, e => reject(common.fail(e)));
73
- }, e => reject(common.fail(e)));
74
- });
75
- }
76
- /**
77
- * Converts web mapping application datasources to variables for deployment
78
- *
79
- * @param itemTemplate The solution item template
80
- * @param authentication Credentials for requests
81
- * @param portalUrl Rest Url of the portal to perform the search
82
- * @param templateDictionary Hash of key details used for variable replacement
83
- * @returns A promise that will resolve with the created template
84
- */
85
- export function templatizeDatasources(itemTemplate, authentication, portalUrl, templateDictionary) {
86
- return new Promise((resolve, reject) => {
87
- const dataSources = common.getProp(itemTemplate, "data.dataSource.dataSources");
88
- if (dataSources && Object.keys(dataSources).length > 0) {
89
- const pendingRequests = new Array();
90
- Object.keys(dataSources).forEach(k => {
91
- const ds = dataSources[k];
92
- common.setProp(ds, "portalUrl", common.placeholder(common.SERVER_NAME));
93
- const itemId = common.getProp(ds, "itemId");
94
- if (common.getProp(ds, "url")) {
95
- if (itemId) {
96
- const layerId = ds.url.substr(ds.url.lastIndexOf("/") + 1);
97
- common.cacheLayerInfo(layerId.toString(), itemId, ds.url, templateDictionary);
98
- ds.itemId = common.templatizeTerm(itemId, itemId, ".layer" + layerId + ".itemId");
99
- }
100
- const urlResults = findUrls(ds.url, portalUrl, [], [], authentication);
101
- pendingRequests.push(new Promise((resolveReq, rejectReq) => {
102
- handleServiceRequests(urlResults.serviceRequests, urlResults.requestUrls, urlResults.testString).then(response => {
103
- ds.url = response;
104
- resolveReq();
105
- }, e => rejectReq(common.fail(e)));
106
- }));
107
- }
108
- else {
109
- if (itemId) {
110
- ds.itemId = common.templatizeTerm(itemId, itemId, ".itemId");
111
- }
112
- }
113
- });
114
- Promise.all(pendingRequests).then(() => resolve(itemTemplate), e => reject(common.fail(e)));
115
- }
116
- else {
117
- resolve(itemTemplate);
118
- }
119
- });
120
- }
121
- export function templatizeWidgets(itemTemplate, authentication, portalUrl, widgetPath, isOnScreen = false) {
122
- return new Promise((resolve, reject) => {
123
- // update widgets
124
- const widgets = common.getProp(itemTemplate, widgetPath) || [];
125
- let serviceRequests = [];
126
- let requestUrls = [];
127
- widgets.forEach(widget => {
128
- /* istanbul ignore else */
129
- if (!isOnScreen && common.getProp(widget, "icon")) {
130
- setValues(widget, ["icon"], common.placeholder(common.SERVER_NAME));
131
- }
132
- const config = widget.config;
133
- if (config) {
134
- const sConfig = JSON.stringify(config);
135
- const urlResults = findUrls(sConfig, portalUrl, requestUrls, serviceRequests, authentication);
136
- widget.config = JSON.parse(urlResults.testString);
137
- serviceRequests = urlResults.serviceRequests;
138
- requestUrls = urlResults.requestUrls;
139
- }
140
- });
141
- if (serviceRequests.length > 0) {
142
- const sWidgets = JSON.stringify(widgets);
143
- handleServiceRequests(serviceRequests, requestUrls, sWidgets).then(response => {
144
- common.setProp(itemTemplate, widgetPath, JSON.parse(response));
145
- resolve(itemTemplate);
146
- }, e => reject(common.fail(e)));
147
- }
148
- else {
149
- resolve(itemTemplate);
150
- }
151
- });
152
- }
153
- export function templatizeValues(itemTemplate, authentication, portalUrl, widgetPath) {
154
- return new Promise((resolve, reject) => {
155
- // update properties of values collection for web app templates
156
- let values = common.getProp(itemTemplate, widgetPath);
157
- let serviceRequests = [];
158
- let requestUrls = [];
159
- if (values) {
160
- if (common.getProp(values, "icon")) {
161
- setValues(values, ["icon"], common.placeholder(common.SERVER_NAME));
162
- }
163
- const sConfig = JSON.stringify(values);
164
- const urlResults = findUrls(sConfig, portalUrl, requestUrls, serviceRequests, authentication);
165
- values = JSON.parse(urlResults.testString);
166
- serviceRequests = urlResults.serviceRequests;
167
- requestUrls = urlResults.requestUrls;
168
- }
169
- if (serviceRequests.length > 0) {
170
- const sWidgets = JSON.stringify(values);
171
- handleServiceRequests(serviceRequests, requestUrls, sWidgets).then(response => {
172
- common.setProp(itemTemplate, widgetPath, JSON.parse(response));
173
- resolve(itemTemplate);
174
- }, e => reject(common.fail(e)));
175
- }
176
- else {
177
- resolve(itemTemplate);
178
- }
179
- });
180
- }
181
- export function handleServiceRequests(serviceRequests, requestUrls, objString) {
182
- return new Promise((resolve, reject) => {
183
- if (serviceRequests && serviceRequests.length > 0) {
184
- let i = 0;
185
- Promise.all(serviceRequests).then(serviceResponses => {
186
- serviceResponses.forEach(serviceResponse => {
187
- if (common.getProp(serviceResponse, "serviceItemId")) {
188
- const serviceTemplate = "{{" +
189
- serviceResponse.serviceItemId +
190
- (serviceResponse.hasOwnProperty("id")
191
- ? ".layer" + serviceResponse.id
192
- : "") +
193
- ".url}}";
194
- objString = replaceUrl(objString, requestUrls[i], serviceTemplate, true);
195
- }
196
- i++;
197
- });
198
- resolve(objString);
199
- }, e => reject(common.fail(e)));
200
- }
201
- else {
202
- resolve(objString);
203
- }
204
- });
205
- }
206
- export function findUrls(testString, portalUrl, requestUrls, serviceRequests, authentication) {
207
- const options = {
208
- f: "json",
209
- authentication: authentication
210
- };
211
- // test for URLs
212
- const results = testString.match(/(\bhttps?:\/\/[-A-Z0-9/._]*)/gim);
213
- if (results && results.length) {
214
- results.forEach((url) => {
215
- if (url.indexOf("NAServer") > -1) {
216
- testString = replaceUrl(testString, url, common.placeholder(common.NA_SERVER_NAME));
217
- }
218
- else if (url.indexOf("GeocodeServer") > -1) {
219
- testString = replaceUrl(testString, url, common.placeholder(common.GEOCODE_SERVER_NAME));
220
- }
221
- else if (portalUrl && url.indexOf(portalUrl) > -1) {
222
- testString = replaceUrl(testString, portalUrl, common.placeholder(common.SERVER_NAME));
223
- }
224
- else if (url.indexOf("FeatureServer") > -1) {
225
- if (requestUrls.indexOf(url) === -1) {
226
- requestUrls.push(url);
227
- serviceRequests.push(common.rest_request(url, options));
228
- }
229
- }
230
- });
231
- }
232
- return {
233
- testString,
234
- requestUrls,
235
- serviceRequests
236
- };
237
- }
238
- /**
239
- * Replace url with templatized url value
240
- *
241
- * @param obj can be a single url string or a stringified JSON object
242
- * @param url the current url we are testing for
243
- * @param newUrl the templatized url
244
- * @param validateFullUrl should only replace url when we have a full match.
245
- * This property is only relevant when the obj is a stringified JSON object.
246
- *
247
- * @returns the obj with any instances of the url replaced
248
- */
249
- export function replaceUrl(obj, url, newUrl, validateFullUrl = false) {
250
- const enforceFullUrl = validateFullUrl && obj.indexOf('"') > -1;
251
- const re = new RegExp(enforceFullUrl ? '"' + url + '"' : url, "gmi");
252
- return obj.replace(re, enforceFullUrl ? '"' + newUrl + '"' : newUrl);
253
- }
254
- export function setValues(itemTemplate, paths, base) {
255
- paths.forEach(path => {
256
- const url = common.getProp(itemTemplate, path);
257
- if (url) {
258
- const subString = url.substring(url.indexOf("/", url.indexOf("//") + 2));
259
- common.setProp(itemTemplate, path, subString !== url ? base + subString : base);
260
- }
261
- });
262
- }
263
- export function fineTuneCreatedItem(originalTemplate, newlyCreatedItem, templateDictionary, destinationAuthentication) {
264
- return new Promise(resolve => {
265
- // If this is a Web AppBuilder application, we will create a Code Attachment for downloading
266
- if (common.hasAnyKeyword(originalTemplate, [
267
- "WAB2D",
268
- "WAB3D",
269
- "Web AppBuilder"
270
- ])) {
271
- // Update item so properties like appItemId can now be set now that we know the new apps ID
272
- const updateOptions = {
273
- id: newlyCreatedItem.itemId,
274
- url: newlyCreatedItem.item.url,
275
- data: newlyCreatedItem.data
276
- };
277
- const updateDef = common.updateItem(updateOptions, destinationAuthentication);
278
- const itemInfo = {
279
- tags: originalTemplate.item.tags,
280
- title: originalTemplate.item.title,
281
- type: "Code Attachment",
282
- typeKeywords: ["Code", "Javascript", "Web Mapping Application"],
283
- relationshipType: "WMA2Code",
284
- originItemId: newlyCreatedItem.itemId,
285
- url: common.checkUrlPathTermination(common.replaceInTemplate(common.placeholder(common.SERVER_NAME), templateDictionary)) +
286
- "sharing/rest/content/items/" +
287
- newlyCreatedItem.itemId +
288
- "/package"
289
- };
290
- const createItemWithDataDef = common.createItemWithData(itemInfo, {}, destinationAuthentication, templateDictionary.folderId);
291
- Promise.all([updateDef, createItemWithDataDef]).then(() => resolve(null), () => resolve(null));
292
- }
293
- else {
294
- // Otherwise, nothing extra needed
295
- resolve(null);
296
- }
297
- });
298
- }
299
- // ------------------------------------------------------------------------------------------------------------------ //
300
- /**
301
- * Gets the ids of the dependencies of an AGOL webapp item.
302
- *
303
- * @param fullItem A webapp item whose dependencies are sought
304
- * @returns A promise that will resolve with list of dependent ids
305
- * @private
306
- */
307
- export function _extractDependencies(model) {
308
- let processor = _getGenericWebAppDependencies;
309
- /*
310
- if (common.hasTypeKeyword(model, "Story Map")) {
311
- processor = getStoryMapDependencies;
312
- }
313
- */
314
- if (common.hasAnyKeyword(model, ["WAB2D", "WAB3D", "Web AppBuilder"])) {
315
- processor = _getWABDependencies;
316
- }
317
- return processor(model);
318
- }
319
- /**
320
- * Generic Web App Dependencies
321
- *
322
- * @private
323
- */
324
- export function _getGenericWebAppDependencies(model) {
325
- const props = ["data.values.webmap", "data.values.group"];
326
- return common.getProps(model, props);
327
- }
328
- //???
329
- export function _getWABDependencies(model) {
330
- const deps = [];
331
- const v = common.getProp(model, "data.map.itemId");
332
- if (v) {
333
- deps.push(v);
334
- }
335
- const dataSources = common.getProp(model, "data.dataSource.dataSources");
336
- if (dataSources) {
337
- Object.keys(dataSources).forEach(k => {
338
- const ds = dataSources[k];
339
- if (ds.itemId) {
340
- deps.push(ds.itemId);
341
- }
342
- });
343
- }
344
- return deps;
345
- }
346
- /**
347
- * Templatizes id properties for the paths provided
348
- *
349
- * @param itemTemplate The solution item template
350
- * @param paths A list of property paths that contain ids
351
- * @private
352
- */
353
- export function _templatizeIdPaths(itemTemplate, paths) {
354
- paths.forEach(path => {
355
- const id = common.getProp(itemTemplate, path);
356
- _templatizeIdPath(itemTemplate, path, id);
357
- });
358
- }
359
- /**
360
- * Templatizes id property for the path provided
361
- *
362
- * @param itemTemplate The solution item template
363
- * @param path A path to an id property
364
- * @param id The base id to use when templatizing
365
- * @private
366
- */
367
- export function _templatizeIdPath(itemTemplate, path, id) {
368
- common.setProp(itemTemplate, path, common.templatizeTerm(id, id, ".itemId"));
369
- }
370
- /**
371
- * Templatize field references for datasources and widgets.
372
- *
373
- * @param solutionTemplate The solution item template
374
- * @param datasourceInfos A list of datasource info objects that contain key values to templatize field references
375
- * @returns The solutionTemplate with templatized field references
376
- */
377
- export function postProcessFieldReferences(solutionTemplate, datasourceInfos) {
378
- // handle datasources common for WAB apps
379
- const dataSources = common.getProp(solutionTemplate, "data.dataSource.dataSources");
380
- if (dataSources && Object.keys(dataSources).length > 0) {
381
- Object.keys(dataSources).forEach(k => {
382
- const ds = dataSources[k];
383
- dataSources[k] = _templatizeObject(ds, datasourceInfos);
384
- });
385
- common.setProp(solutionTemplate, "data.dataSource.dataSources", dataSources);
386
- }
387
- // handle widgets common for WAB apps
388
- const paths = [
389
- "data.widgetPool.widgets",
390
- "data.widgetOnScreen.widgets"
391
- ];
392
- paths.forEach(path => {
393
- const widgets = common.getProp(solutionTemplate, path);
394
- if (widgets) {
395
- common.setProp(solutionTemplate, path, _templatizeObjectArray(widgets, datasourceInfos));
396
- }
397
- });
398
- // handle values common for web app templates
399
- const values = common.getProp(solutionTemplate, "data.values");
400
- if (values) {
401
- common.setProp(solutionTemplate, "data.values", _templatizeObject(values, datasourceInfos));
402
- }
403
- return solutionTemplate;
404
- }
405
- /**
406
- * Templatize field references for given dataSource from the web application.
407
- *
408
- * @param obj The dataSource or widget object from the web application.
409
- * @param datasourceInfos A list of datasource info objects that contain key values to templatize field references
410
- * @returns The dataSource with templatized field references
411
- * @private
412
- */
413
- export function _templatizeObject(obj, datasourceInfos, templatizeKeys = false) {
414
- obj = _prioritizedTests(obj, datasourceInfos, templatizeKeys);
415
- const replaceOrder = _getReplaceOrder(obj, datasourceInfos);
416
- replaceOrder.forEach(ds => {
417
- obj = common.templatizeFieldReferences(obj, ds.fields, ds.basePath, templatizeKeys);
418
- });
419
- return obj;
420
- }
421
- /**
422
- * Templatize field references from an array of various objects from the web application.
423
- *
424
- * @param objects A list of widgets or objects from the web application that may contain field references.
425
- * @param datasourceInfos A list of datasource info objects that contain key values to templatize field references
426
- * @returns The widgets with templatized field references
427
- * @private
428
- */
429
- export function _templatizeObjectArray(objects, datasourceInfos) {
430
- const updateKeyObjects = ["SmartEditor", "Screening"];
431
- return objects.map(obj => {
432
- // only templatize the config and lower
433
- if (obj.config) {
434
- const templatizeKeys = updateKeyObjects.indexOf(obj.name) > -1;
435
- obj.config = _templatizeObject(obj.config, datasourceInfos, templatizeKeys);
436
- }
437
- return obj;
438
- });
439
- }
440
- /**
441
- * Gets an order for testing wit the various datasource info objects against the widget or dataSource.
442
- * A widget or dataSource that contain a layers url or webmap layer id are more likely
443
- * to have field references from that layer.
444
- *
445
- * @param obj The dataSource or widget object from the web application.
446
- * @param datasourceInfos A list of datasource info objects that contain key values to templatize field references
447
- * @returns A list of datasourceInfo objects sorted based on the presence of a layers url or id
448
- * @private
449
- */
450
- export function _getReplaceOrder(obj, datasourceInfos) {
451
- const objString = JSON.stringify(obj);
452
- // If we don't find any layer url, web map layer id, service url, agol itemId then remove the datasource.
453
- const _datasourceInfos = datasourceInfos.filter(ds => _getSortOrder(ds, objString) < 4);
454
- return _datasourceInfos.sort((a, b) => {
455
- return _getSortOrder(a, objString) - _getSortOrder(b, objString);
456
- });
457
- }
458
- /**
459
- * Determine an order for checking field names against a dataSource or widget.
460
- * Sort order preference is set in this order: layer url, web map layer id, service url, agol itemId
461
- *
462
- * @param datasourceInfo The datasource object with key properties about the service.
463
- * @param testString A stringified version of a widget or dataSource
464
- * @returns The prioritized order for testing
465
- * @private
466
- */
467
- export function _getSortOrder(datasourceInfo, testString) {
468
- const url = datasourceInfo.url;
469
- const itemId = datasourceInfo.itemId;
470
- const layerId = datasourceInfo.layerId;
471
- // if we have the url and the layerID and its found prioritize it first
472
- // else if we find the maps layer id prioritze it first
473
- let layerUrlTest;
474
- if (url && !isNaN(layerId)) {
475
- layerUrlTest = new RegExp(url.replace(/[.]/, ".layer" + layerId + "."), "gm");
476
- }
477
- if (layerUrlTest && layerUrlTest.test(testString)) {
478
- return 1;
479
- }
480
- else if (datasourceInfo.ids.length > 0) {
481
- if (datasourceInfo.ids.some(id => {
482
- const layerMapIdTest = new RegExp(id, "gm");
483
- return layerMapIdTest.test(testString);
484
- })) {
485
- return 1;
486
- }
487
- }
488
- // if neither full layer url or map layer id are found...check to see if we can
489
- // find the base service url
490
- if (url) {
491
- const serviceUrlTest = new RegExp(url, "gm");
492
- if (serviceUrlTest.test(testString)) {
493
- return 2;
494
- }
495
- }
496
- // if none of the above see if we can find an AGOL item id reference
497
- if (itemId) {
498
- const itemIdTest = new RegExp(itemId, "gm");
499
- if (itemIdTest.test(testString)) {
500
- return 3;
501
- }
502
- }
503
- return 4;
504
- }
505
- /**
506
- * These tests will run prior to the tests associated with the higher level tests based on sort order.
507
- * The tests work more like cloning an object where we go through and review each individual property.
508
- * If we find a url or webmap layer id we will templatize the parent object that contains this property.
509
- * Many widgets will store one of these two properties in an object that will also contain various field references.
510
- *
511
- * @param obj The dataSource or widget object from the application
512
- * @param datasourceInfos A list of datasource info objects that contain key values to templatize field references
513
- * @returns An updated instance of the dataSource or widget with as many field references templatized as possible.
514
- * @private
515
- */
516
- export function _prioritizedTests(obj, datasourceInfos, templatizeKeys) {
517
- const objString = JSON.stringify(obj);
518
- const hasDatasources = datasourceInfos.filter(ds => {
519
- let urlTest;
520
- if (ds.url && !isNaN(ds.layerId)) {
521
- urlTest = new RegExp(ds.url.replace(/[.]/, ".layer" + ds.layerId + "."), "gm");
522
- }
523
- let hasMapLayerId = false;
524
- if (ds.ids.length > 0) {
525
- hasMapLayerId = ds.ids.some(id => {
526
- const idTest = new RegExp(id, "gm");
527
- return idTest.test(objString);
528
- });
529
- }
530
- if (hasMapLayerId || (urlTest && urlTest.test(objString))) {
531
- return ds;
532
- }
533
- });
534
- if (hasDatasources.length > 0) {
535
- hasDatasources.forEach(ds => {
536
- // specific url reference is the most common
537
- obj = _templatizeParentByURL(obj, ds, templatizeKeys);
538
- if (ds.ids.length > 0) {
539
- // the second most common is to use the layerId from the webmap
540
- ds.ids.forEach(id => {
541
- obj = _templatizeParentByWebMapLayerId(obj, ds, id, templatizeKeys);
542
- });
543
- }
544
- });
545
- }
546
- return obj;
547
- }
548
- /**
549
- * This is very close to common.cloneObject but will test if an object
550
- * has one of the datasource urls as a property. If it finds one it will
551
- * templatize it's parent based on the fields from that datasource
552
- *
553
- * @param obj The dataSource or widget object from the application
554
- * @param ds A datasourceInfo object to use for testing against the current dataSource or widget
555
- * @returns The updated instance of the object with as many field references templatized as possible
556
- * @private
557
- */
558
- export function _templatizeParentByURL(obj, ds, templatizeKeys) {
559
- let clone = {};
560
- const url = ds.url;
561
- const layerId = ds.layerId;
562
- let urlTest;
563
- if (url && !isNaN(layerId)) {
564
- urlTest = new RegExp(url.replace(/[.]/, ".layer" + layerId + "."), "gm");
565
- }
566
- if (Array.isArray(obj)) {
567
- clone = obj.map(c => {
568
- return _templatizeParentByURL(c, ds, templatizeKeys);
569
- });
570
- }
571
- else if (typeof obj === "object") {
572
- for (const i in obj) {
573
- if (obj[i] != null && typeof obj[i] === "object") {
574
- clone[i] = _templatizeParentByURL(obj[i], ds, templatizeKeys);
575
- }
576
- else {
577
- if (urlTest && urlTest.test(obj[i])) {
578
- obj = common.templatizeFieldReferences(obj, ds.fields, ds.basePath, templatizeKeys);
579
- }
580
- clone[i] = obj[i];
581
- }
582
- }
583
- }
584
- else {
585
- clone = obj;
586
- }
587
- return clone;
588
- }
589
- /**
590
- * This is very close to common.cloneObject but will test if an object
591
- * has one of the datasource webmap layer ids as a property. If it finds one it will
592
- * templatize it's parent based on the fields from that datasource.
593
- *
594
- * @param obj The dataSource or widget object from the application
595
- * @param ds A datasourceInfo object to use for testing against the current dataSource or widget
596
- * @param id A webmap layer id to test with.
597
- * @returns The updated instance of the object with as many field references templatized as possible
598
- * @private
599
- */
600
- export function _templatizeParentByWebMapLayerId(obj, ds, id, templatizeKeys) {
601
- let clone = {};
602
- const idTest = new RegExp(id, "gm");
603
- if (Array.isArray(obj)) {
604
- clone = obj.map(c => {
605
- return _templatizeParentByWebMapLayerId(c, ds, id, templatizeKeys);
606
- });
607
- }
608
- else if (typeof obj === "object") {
609
- for (const i in obj) {
610
- if (obj[i] !== null) {
611
- // In some web application templates they store a stringified version of an object that can
612
- // contain multiple layer references at a very high level on the main values collection.
613
- // This was causing many other more typical layer references to be set incorrectly as the first
614
- // layerId found in this high level string would be used against the main object.
615
- let parsedProp;
616
- try {
617
- parsedProp = JSON.parse(obj[i]);
618
- }
619
- catch (error) {
620
- parsedProp = undefined;
621
- }
622
- if (parsedProp && typeof parsedProp === "object") {
623
- clone[i] = JSON.stringify(_templatizeParentByWebMapLayerId(parsedProp, ds, id, templatizeKeys));
624
- }
625
- else if (typeof obj[i] === "object") {
626
- // some widgets store the layerId as a key to a collection of details that contain field references
627
- if (idTest.test(i) && templatizeKeys) {
628
- obj[i] = common.templatizeFieldReferences(obj[i], ds.fields, ds.basePath, templatizeKeys);
629
- }
630
- clone[i] = _templatizeParentByWebMapLayerId(obj[i], ds, id, templatizeKeys);
631
- }
632
- else {
633
- if (idTest.test(obj[i])) {
634
- obj = common.templatizeFieldReferences(obj, ds.fields, ds.basePath, templatizeKeys);
635
- }
636
- clone[i] = obj[i];
637
- }
638
- }
639
- else {
640
- clone[i] = obj[i];
641
- }
642
- }
643
- }
644
- else {
645
- clone = obj;
646
- }
647
- return clone;
648
- }
1
+ /** @license
2
+ * Copyright 2020 Esri
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import * as common from "@esri/solution-common";
17
+ // ------------------------------------------------------------------------------------------------------------------ //
18
+ /**
19
+ * Converts a web mapping application item into a template.
20
+ *
21
+ * @param itemInfo Info about the item
22
+ * @param destAuthentication Credentials for requests to the destination organization
23
+ * @param srcAuthentication Credentials for requests to source items
24
+ * @param templateDictionary Hash of key details used for variable replacement
25
+ * @returns A promise that will resolve when the template has been created
26
+ */
27
+ export function convertItemToTemplate(itemTemplate, destAuthentication, srcAuthentication, templateDictionary) {
28
+ return new Promise((resolve, reject) => {
29
+ // Remove org base URL and app id, e.g.,
30
+ // http://anOrg.maps.arcgis.com/apps/CrowdsourcePolling/index.html?appid=6fc5992522d34a6b5ce80d17835eea21
31
+ // to
32
+ // <placeholder(SERVER_NAME)>/apps/CrowdsourcePolling/index.html?appid={{<itemId>.id}}
33
+ // Need to add placeholder server name because otherwise AGOL makes URL null
34
+ let portalUrl = "";
35
+ if (itemTemplate.item.url) {
36
+ const templatizedUrl = itemTemplate.item.url;
37
+ const iSep = templatizedUrl.indexOf("//");
38
+ itemTemplate.item.url =
39
+ common.placeholder(common.SERVER_NAME) + // add placeholder server name
40
+ templatizedUrl.substring(templatizedUrl.indexOf("/", iSep + 2), templatizedUrl.lastIndexOf("=") + 1) +
41
+ itemTemplate.item.id; // templatized id
42
+ portalUrl = templatizedUrl.replace(templatizedUrl.substring(templatizedUrl.indexOf("/", iSep + 2)), "");
43
+ }
44
+ // Extract dependencies
45
+ itemTemplate.dependencies = _extractDependencies(itemTemplate);
46
+ // Set the folder
47
+ common.setProp(itemTemplate, "data.folderId", "{{folderId}}");
48
+ // Set the map or group after we've extracted them as dependencies
49
+ _templatizeIdPaths(itemTemplate, [
50
+ "data.map.itemId",
51
+ "data.map.appProxy.mapItemId",
52
+ "data.values.webmap",
53
+ "data.values.group"
54
+ ]);
55
+ // force the appItemId to be pulled directly from the template item
56
+ // this is to address solution.js #124
57
+ _templatizeIdPath(itemTemplate, "data.appItemId", itemTemplate.itemId);
58
+ setValues(itemTemplate, [
59
+ "data.logo",
60
+ "data.map.portalUrl",
61
+ "data.portalUrl",
62
+ "data.httpProxy.url"
63
+ ], common.placeholder(common.SERVER_NAME));
64
+ common.setProp(itemTemplate, "data.geometryService", common.placeholder(common.GEOMETRY_SERVER_NAME));
65
+ templatizeDatasources(itemTemplate, srcAuthentication, portalUrl, templateDictionary).then(() => {
66
+ templatizeWidgets(itemTemplate, srcAuthentication, portalUrl, "data.widgetPool.widgets").then(_itemTemplate => {
67
+ templatizeWidgets(_itemTemplate, srcAuthentication, portalUrl, "data.widgetOnScreen.widgets", true).then(updatedItemTemplate => {
68
+ templatizeValues(updatedItemTemplate, srcAuthentication, portalUrl, "data.values").then(_updatedItemTemplate => {
69
+ resolve(_updatedItemTemplate);
70
+ }, e => reject(common.fail(e)));
71
+ }, e => reject(common.fail(e)));
72
+ }, e => reject(common.fail(e)));
73
+ }, e => reject(common.fail(e)));
74
+ });
75
+ }
76
+ /**
77
+ * Converts web mapping application datasources to variables for deployment
78
+ *
79
+ * @param itemTemplate The solution item template
80
+ * @param authentication Credentials for requests
81
+ * @param portalUrl Rest Url of the portal to perform the search
82
+ * @param templateDictionary Hash of key details used for variable replacement
83
+ * @returns A promise that will resolve with the created template
84
+ */
85
+ export function templatizeDatasources(itemTemplate, authentication, portalUrl, templateDictionary) {
86
+ return new Promise((resolve, reject) => {
87
+ const dataSources = common.getProp(itemTemplate, "data.dataSource.dataSources");
88
+ if (dataSources && Object.keys(dataSources).length > 0) {
89
+ const pendingRequests = new Array();
90
+ Object.keys(dataSources).forEach(k => {
91
+ const ds = dataSources[k];
92
+ common.setProp(ds, "portalUrl", common.placeholder(common.SERVER_NAME));
93
+ const itemId = common.getProp(ds, "itemId");
94
+ if (common.getProp(ds, "url")) {
95
+ if (itemId) {
96
+ const layerId = ds.url.substr(ds.url.lastIndexOf("/") + 1);
97
+ common.cacheLayerInfo(layerId.toString(), itemId, ds.url, templateDictionary);
98
+ ds.itemId = common.templatizeTerm(itemId, itemId, ".layer" + layerId + ".itemId");
99
+ }
100
+ const urlResults = findUrls(ds.url, portalUrl, [], [], authentication);
101
+ pendingRequests.push(new Promise((resolveReq, rejectReq) => {
102
+ handleServiceRequests(urlResults.serviceRequests, urlResults.requestUrls, urlResults.testString).then(response => {
103
+ ds.url = response;
104
+ resolveReq();
105
+ }, e => rejectReq(common.fail(e)));
106
+ }));
107
+ }
108
+ else {
109
+ if (itemId) {
110
+ ds.itemId = common.templatizeTerm(itemId, itemId, ".itemId");
111
+ }
112
+ }
113
+ });
114
+ Promise.all(pendingRequests).then(() => resolve(itemTemplate), e => reject(common.fail(e)));
115
+ }
116
+ else {
117
+ resolve(itemTemplate);
118
+ }
119
+ });
120
+ }
121
+ export function templatizeWidgets(itemTemplate, authentication, portalUrl, widgetPath, isOnScreen = false) {
122
+ return new Promise((resolve, reject) => {
123
+ // update widgets
124
+ const widgets = common.getProp(itemTemplate, widgetPath) || [];
125
+ let serviceRequests = [];
126
+ let requestUrls = [];
127
+ widgets.forEach(widget => {
128
+ /* istanbul ignore else */
129
+ if (!isOnScreen && common.getProp(widget, "icon")) {
130
+ setValues(widget, ["icon"], common.placeholder(common.SERVER_NAME));
131
+ }
132
+ const config = widget.config;
133
+ if (config) {
134
+ const sConfig = JSON.stringify(config);
135
+ const urlResults = findUrls(sConfig, portalUrl, requestUrls, serviceRequests, authentication);
136
+ widget.config = JSON.parse(urlResults.testString);
137
+ serviceRequests = urlResults.serviceRequests;
138
+ requestUrls = urlResults.requestUrls;
139
+ }
140
+ });
141
+ if (serviceRequests.length > 0) {
142
+ const sWidgets = JSON.stringify(widgets);
143
+ handleServiceRequests(serviceRequests, requestUrls, sWidgets).then(response => {
144
+ common.setProp(itemTemplate, widgetPath, JSON.parse(response));
145
+ resolve(itemTemplate);
146
+ }, e => reject(common.fail(e)));
147
+ }
148
+ else {
149
+ resolve(itemTemplate);
150
+ }
151
+ });
152
+ }
153
+ export function templatizeValues(itemTemplate, authentication, portalUrl, widgetPath) {
154
+ return new Promise((resolve, reject) => {
155
+ // update properties of values collection for web app templates
156
+ let values = common.getProp(itemTemplate, widgetPath);
157
+ let serviceRequests = [];
158
+ let requestUrls = [];
159
+ if (values) {
160
+ if (common.getProp(values, "icon")) {
161
+ setValues(values, ["icon"], common.placeholder(common.SERVER_NAME));
162
+ }
163
+ const sConfig = JSON.stringify(values);
164
+ const urlResults = findUrls(sConfig, portalUrl, requestUrls, serviceRequests, authentication);
165
+ values = JSON.parse(urlResults.testString);
166
+ serviceRequests = urlResults.serviceRequests;
167
+ requestUrls = urlResults.requestUrls;
168
+ }
169
+ if (serviceRequests.length > 0) {
170
+ const sWidgets = JSON.stringify(values);
171
+ handleServiceRequests(serviceRequests, requestUrls, sWidgets).then(response => {
172
+ common.setProp(itemTemplate, widgetPath, JSON.parse(response));
173
+ resolve(itemTemplate);
174
+ }, e => reject(common.fail(e)));
175
+ }
176
+ else {
177
+ resolve(itemTemplate);
178
+ }
179
+ });
180
+ }
181
+ export function handleServiceRequests(serviceRequests, requestUrls, objString) {
182
+ return new Promise((resolve, reject) => {
183
+ if (serviceRequests && serviceRequests.length > 0) {
184
+ let i = 0;
185
+ Promise.all(serviceRequests).then(serviceResponses => {
186
+ serviceResponses.forEach(serviceResponse => {
187
+ if (common.getProp(serviceResponse, "serviceItemId")) {
188
+ const serviceTemplate = "{{" +
189
+ serviceResponse.serviceItemId +
190
+ (serviceResponse.hasOwnProperty("id")
191
+ ? ".layer" + serviceResponse.id
192
+ : "") +
193
+ ".url}}";
194
+ objString = replaceUrl(objString, requestUrls[i], serviceTemplate, true);
195
+ }
196
+ i++;
197
+ });
198
+ resolve(objString);
199
+ }, e => reject(common.fail(e)));
200
+ }
201
+ else {
202
+ resolve(objString);
203
+ }
204
+ });
205
+ }
206
+ export function findUrls(testString, portalUrl, requestUrls, serviceRequests, authentication) {
207
+ const options = {
208
+ f: "json",
209
+ authentication: authentication
210
+ };
211
+ // test for URLs
212
+ const results = testString.match(/(\bhttps?:\/\/[-A-Z0-9/._]*)/gim);
213
+ if (results && results.length) {
214
+ results.forEach((url) => {
215
+ if (url.indexOf("NAServer") > -1) {
216
+ testString = replaceUrl(testString, url, common.placeholder(common.NA_SERVER_NAME));
217
+ }
218
+ else if (url.indexOf("GeocodeServer") > -1) {
219
+ testString = replaceUrl(testString, url, common.placeholder(common.GEOCODE_SERVER_NAME));
220
+ }
221
+ else if (portalUrl && url.indexOf(portalUrl) > -1) {
222
+ testString = replaceUrl(testString, portalUrl, common.placeholder(common.SERVER_NAME));
223
+ }
224
+ else if (url.indexOf("FeatureServer") > -1) {
225
+ if (requestUrls.indexOf(url) === -1) {
226
+ requestUrls.push(url);
227
+ serviceRequests.push(common.rest_request(url, options));
228
+ }
229
+ }
230
+ });
231
+ }
232
+ return {
233
+ testString,
234
+ requestUrls,
235
+ serviceRequests
236
+ };
237
+ }
238
+ /**
239
+ * Replace url with templatized url value
240
+ *
241
+ * @param obj can be a single url string or a stringified JSON object
242
+ * @param url the current url we are testing for
243
+ * @param newUrl the templatized url
244
+ * @param validateFullUrl should only replace url when we have a full match.
245
+ * This property is only relevant when the obj is a stringified JSON object.
246
+ *
247
+ * @returns the obj with any instances of the url replaced
248
+ */
249
+ export function replaceUrl(obj, url, newUrl, validateFullUrl = false) {
250
+ const enforceFullUrl = validateFullUrl && obj.indexOf('"') > -1;
251
+ const re = new RegExp(enforceFullUrl ? '"' + url + '"' : url, "gmi");
252
+ return obj.replace(re, enforceFullUrl ? '"' + newUrl + '"' : newUrl);
253
+ }
254
+ export function setValues(itemTemplate, paths, base) {
255
+ paths.forEach(path => {
256
+ const url = common.getProp(itemTemplate, path);
257
+ if (url) {
258
+ const subString = url.substring(url.indexOf("/", url.indexOf("//") + 2));
259
+ common.setProp(itemTemplate, path, subString !== url ? base + subString : base);
260
+ }
261
+ });
262
+ }
263
+ export function fineTuneCreatedItem(originalTemplate, newlyCreatedItem, templateDictionary, destinationAuthentication) {
264
+ return new Promise(resolve => {
265
+ // If this is a Web AppBuilder application, we will create a Code Attachment for downloading
266
+ if (common.hasAnyKeyword(originalTemplate, [
267
+ "WAB2D",
268
+ "WAB3D",
269
+ "Web AppBuilder"
270
+ ])) {
271
+ // Update item so properties like appItemId can now be set now that we know the new apps ID
272
+ const updateOptions = {
273
+ id: newlyCreatedItem.itemId,
274
+ url: newlyCreatedItem.item.url,
275
+ data: newlyCreatedItem.data
276
+ };
277
+ const updateDef = common.updateItem(updateOptions, destinationAuthentication);
278
+ const itemInfo = {
279
+ tags: originalTemplate.item.tags,
280
+ title: originalTemplate.item.title,
281
+ type: "Code Attachment",
282
+ typeKeywords: ["Code", "Javascript", "Web Mapping Application"],
283
+ relationshipType: "WMA2Code",
284
+ originItemId: newlyCreatedItem.itemId,
285
+ url: common.checkUrlPathTermination(common.replaceInTemplate(common.placeholder(common.SERVER_NAME), templateDictionary)) +
286
+ "sharing/rest/content/items/" +
287
+ newlyCreatedItem.itemId +
288
+ "/package"
289
+ };
290
+ const createItemWithDataDef = common.createItemWithData(itemInfo, {}, destinationAuthentication, templateDictionary.folderId);
291
+ Promise.all([updateDef, createItemWithDataDef]).then(() => resolve(null), () => resolve(null));
292
+ }
293
+ else {
294
+ // Otherwise, nothing extra needed
295
+ resolve(null);
296
+ }
297
+ });
298
+ }
299
+ // ------------------------------------------------------------------------------------------------------------------ //
300
+ /**
301
+ * Gets the ids of the dependencies of an AGOL webapp item.
302
+ *
303
+ * @param fullItem A webapp item whose dependencies are sought
304
+ * @returns A promise that will resolve with list of dependent ids
305
+ * @private
306
+ */
307
+ export function _extractDependencies(model) {
308
+ let processor = _getGenericWebAppDependencies;
309
+ /*
310
+ if (common.hasTypeKeyword(model, "Story Map")) {
311
+ processor = getStoryMapDependencies;
312
+ }
313
+ */
314
+ if (common.hasAnyKeyword(model, ["WAB2D", "WAB3D", "Web AppBuilder"])) {
315
+ processor = _getWABDependencies;
316
+ }
317
+ return processor(model);
318
+ }
319
+ /**
320
+ * Generic Web App Dependencies
321
+ *
322
+ * @private
323
+ */
324
+ export function _getGenericWebAppDependencies(model) {
325
+ const props = ["data.values.webmap", "data.values.group"];
326
+ return common.getProps(model, props);
327
+ }
328
+ //TODO: function doc
329
+ export function _getWABDependencies(model) {
330
+ const deps = [];
331
+ const v = common.getProp(model, "data.map.itemId");
332
+ if (v) {
333
+ deps.push(v);
334
+ }
335
+ const dataSources = common.getProp(model, "data.dataSource.dataSources");
336
+ if (dataSources) {
337
+ Object.keys(dataSources).forEach(k => {
338
+ const ds = dataSources[k];
339
+ if (ds.itemId) {
340
+ deps.push(ds.itemId);
341
+ }
342
+ });
343
+ }
344
+ return deps;
345
+ }
346
+ /**
347
+ * Templatizes id properties for the paths provided
348
+ *
349
+ * @param itemTemplate The solution item template
350
+ * @param paths A list of property paths that contain ids
351
+ * @private
352
+ */
353
+ export function _templatizeIdPaths(itemTemplate, paths) {
354
+ paths.forEach(path => {
355
+ const id = common.getProp(itemTemplate, path);
356
+ _templatizeIdPath(itemTemplate, path, id);
357
+ });
358
+ }
359
+ /**
360
+ * Templatizes id property for the path provided
361
+ *
362
+ * @param itemTemplate The solution item template
363
+ * @param path A path to an id property
364
+ * @param id The base id to use when templatizing
365
+ * @private
366
+ */
367
+ export function _templatizeIdPath(itemTemplate, path, id) {
368
+ common.setProp(itemTemplate, path, common.templatizeTerm(id, id, ".itemId"));
369
+ }
370
+ /**
371
+ * Templatize field references for datasources and widgets.
372
+ *
373
+ * @param solutionTemplate The solution item template
374
+ * @param datasourceInfos A list of datasource info objects that contain key values to templatize field references
375
+ * @returns The solutionTemplate with templatized field references
376
+ */
377
+ export function postProcessFieldReferences(solutionTemplate, datasourceInfos) {
378
+ // handle datasources common for WAB apps
379
+ const dataSources = common.getProp(solutionTemplate, "data.dataSource.dataSources");
380
+ if (dataSources && Object.keys(dataSources).length > 0) {
381
+ Object.keys(dataSources).forEach(k => {
382
+ const ds = dataSources[k];
383
+ dataSources[k] = _templatizeObject(ds, datasourceInfos);
384
+ });
385
+ common.setProp(solutionTemplate, "data.dataSource.dataSources", dataSources);
386
+ }
387
+ // handle widgets common for WAB apps
388
+ const paths = [
389
+ "data.widgetPool.widgets",
390
+ "data.widgetOnScreen.widgets"
391
+ ];
392
+ paths.forEach(path => {
393
+ const widgets = common.getProp(solutionTemplate, path);
394
+ if (widgets) {
395
+ common.setProp(solutionTemplate, path, _templatizeObjectArray(widgets, datasourceInfos));
396
+ }
397
+ });
398
+ // handle values common for web app templates
399
+ const values = common.getProp(solutionTemplate, "data.values");
400
+ if (values) {
401
+ common.setProp(solutionTemplate, "data.values", _templatizeObject(values, datasourceInfos));
402
+ }
403
+ return solutionTemplate;
404
+ }
405
+ /**
406
+ * Templatize field references for given dataSource from the web application.
407
+ *
408
+ * @param obj The dataSource or widget object from the web application.
409
+ * @param datasourceInfos A list of datasource info objects that contain key values to templatize field references
410
+ * @returns The dataSource with templatized field references
411
+ * @private
412
+ */
413
+ export function _templatizeObject(obj, datasourceInfos, templatizeKeys = false) {
414
+ obj = _prioritizedTests(obj, datasourceInfos, templatizeKeys);
415
+ const replaceOrder = _getReplaceOrder(obj, datasourceInfos);
416
+ replaceOrder.forEach(ds => {
417
+ obj = common.templatizeFieldReferences(obj, ds.fields, ds.basePath, templatizeKeys);
418
+ });
419
+ return obj;
420
+ }
421
+ /**
422
+ * Templatize field references from an array of various objects from the web application.
423
+ *
424
+ * @param objects A list of widgets or objects from the web application that may contain field references.
425
+ * @param datasourceInfos A list of datasource info objects that contain key values to templatize field references
426
+ * @returns The widgets with templatized field references
427
+ * @private
428
+ */
429
+ export function _templatizeObjectArray(objects, datasourceInfos) {
430
+ const updateKeyObjects = ["SmartEditor", "Screening"];
431
+ return objects.map(obj => {
432
+ // only templatize the config and lower
433
+ if (obj.config) {
434
+ const templatizeKeys = updateKeyObjects.indexOf(obj.name) > -1;
435
+ obj.config = _templatizeObject(obj.config, datasourceInfos, templatizeKeys);
436
+ }
437
+ return obj;
438
+ });
439
+ }
440
+ /**
441
+ * Gets an order for testing wit the various datasource info objects against the widget or dataSource.
442
+ * A widget or dataSource that contain a layers url or webmap layer id are more likely
443
+ * to have field references from that layer.
444
+ *
445
+ * @param obj The dataSource or widget object from the web application.
446
+ * @param datasourceInfos A list of datasource info objects that contain key values to templatize field references
447
+ * @returns A list of datasourceInfo objects sorted based on the presence of a layers url or id
448
+ * @private
449
+ */
450
+ export function _getReplaceOrder(obj, datasourceInfos) {
451
+ const objString = JSON.stringify(obj);
452
+ // If we don't find any layer url, web map layer id, service url, agol itemId then remove the datasource.
453
+ const _datasourceInfos = datasourceInfos.filter(ds => _getSortOrder(ds, objString) < 4);
454
+ return _datasourceInfos.sort((a, b) => {
455
+ return _getSortOrder(a, objString) - _getSortOrder(b, objString);
456
+ });
457
+ }
458
+ /**
459
+ * Determine an order for checking field names against a dataSource or widget.
460
+ * Sort order preference is set in this order: layer url, web map layer id, service url, agol itemId
461
+ *
462
+ * @param datasourceInfo The datasource object with key properties about the service.
463
+ * @param testString A stringified version of a widget or dataSource
464
+ * @returns The prioritized order for testing
465
+ * @private
466
+ */
467
+ export function _getSortOrder(datasourceInfo, testString) {
468
+ const url = datasourceInfo.url;
469
+ const itemId = datasourceInfo.itemId;
470
+ const layerId = datasourceInfo.layerId;
471
+ // if we have the url and the layerID and its found prioritize it first
472
+ // else if we find the maps layer id prioritze it first
473
+ let layerUrlTest;
474
+ if (url && !isNaN(layerId)) {
475
+ layerUrlTest = new RegExp(url.replace(/[.]/, ".layer" + layerId + "."), "gm");
476
+ }
477
+ if (layerUrlTest && layerUrlTest.test(testString)) {
478
+ return 1;
479
+ }
480
+ else if (datasourceInfo.ids.length > 0) {
481
+ if (datasourceInfo.ids.some(id => {
482
+ const layerMapIdTest = new RegExp(id, "gm");
483
+ return layerMapIdTest.test(testString);
484
+ })) {
485
+ return 1;
486
+ }
487
+ }
488
+ // if neither full layer url or map layer id are found...check to see if we can
489
+ // find the base service url
490
+ if (url) {
491
+ const serviceUrlTest = new RegExp(url, "gm");
492
+ if (serviceUrlTest.test(testString)) {
493
+ return 2;
494
+ }
495
+ }
496
+ // if none of the above see if we can find an AGOL item id reference
497
+ if (itemId) {
498
+ const itemIdTest = new RegExp(itemId, "gm");
499
+ if (itemIdTest.test(testString)) {
500
+ return 3;
501
+ }
502
+ }
503
+ return 4;
504
+ }
505
+ /**
506
+ * These tests will run prior to the tests associated with the higher level tests based on sort order.
507
+ * The tests work more like cloning an object where we go through and review each individual property.
508
+ * If we find a url or webmap layer id we will templatize the parent object that contains this property.
509
+ * Many widgets will store one of these two properties in an object that will also contain various field references.
510
+ *
511
+ * @param obj The dataSource or widget object from the application
512
+ * @param datasourceInfos A list of datasource info objects that contain key values to templatize field references
513
+ * @returns An updated instance of the dataSource or widget with as many field references templatized as possible.
514
+ * @private
515
+ */
516
+ export function _prioritizedTests(obj, datasourceInfos, templatizeKeys) {
517
+ const objString = JSON.stringify(obj);
518
+ const hasDatasources = datasourceInfos.filter(ds => {
519
+ let urlTest;
520
+ if (ds.url && !isNaN(ds.layerId)) {
521
+ urlTest = new RegExp(ds.url.replace(/[.]/, ".layer" + ds.layerId + "."), "gm");
522
+ }
523
+ let hasMapLayerId = false;
524
+ if (ds.ids.length > 0) {
525
+ hasMapLayerId = ds.ids.some(id => {
526
+ const idTest = new RegExp(id, "gm");
527
+ return idTest.test(objString);
528
+ });
529
+ }
530
+ if (hasMapLayerId || (urlTest && urlTest.test(objString))) {
531
+ return ds;
532
+ }
533
+ });
534
+ if (hasDatasources.length > 0) {
535
+ hasDatasources.forEach(ds => {
536
+ // specific url reference is the most common
537
+ obj = _templatizeParentByURL(obj, ds, templatizeKeys);
538
+ if (ds.ids.length > 0) {
539
+ // the second most common is to use the layerId from the webmap
540
+ ds.ids.forEach(id => {
541
+ obj = _templatizeParentByWebMapLayerId(obj, ds, id, templatizeKeys);
542
+ });
543
+ }
544
+ });
545
+ }
546
+ return obj;
547
+ }
548
+ /**
549
+ * This is very close to common.cloneObject but will test if an object
550
+ * has one of the datasource urls as a property. If it finds one it will
551
+ * templatize it's parent based on the fields from that datasource
552
+ *
553
+ * @param obj The dataSource or widget object from the application
554
+ * @param ds A datasourceInfo object to use for testing against the current dataSource or widget
555
+ * @returns The updated instance of the object with as many field references templatized as possible
556
+ * @private
557
+ */
558
+ export function _templatizeParentByURL(obj, ds, templatizeKeys) {
559
+ let clone = {};
560
+ const url = ds.url;
561
+ const layerId = ds.layerId;
562
+ let urlTest;
563
+ if (url && !isNaN(layerId)) {
564
+ urlTest = new RegExp(url.replace(/[.]/, ".layer" + layerId + "."), "gm");
565
+ }
566
+ if (Array.isArray(obj)) {
567
+ clone = obj.map(c => {
568
+ return _templatizeParentByURL(c, ds, templatizeKeys);
569
+ });
570
+ }
571
+ else if (typeof obj === "object") {
572
+ for (const i in obj) {
573
+ if (obj[i] != null && typeof obj[i] === "object") {
574
+ clone[i] = _templatizeParentByURL(obj[i], ds, templatizeKeys);
575
+ }
576
+ else {
577
+ if (urlTest && urlTest.test(obj[i])) {
578
+ obj = common.templatizeFieldReferences(obj, ds.fields, ds.basePath, templatizeKeys);
579
+ }
580
+ clone[i] = obj[i];
581
+ }
582
+ }
583
+ }
584
+ else {
585
+ clone = obj;
586
+ }
587
+ return clone;
588
+ }
589
+ /**
590
+ * This is very close to common.cloneObject but will test if an object
591
+ * has one of the datasource webmap layer ids as a property. If it finds one it will
592
+ * templatize it's parent based on the fields from that datasource.
593
+ *
594
+ * @param obj The dataSource or widget object from the application
595
+ * @param ds A datasourceInfo object to use for testing against the current dataSource or widget
596
+ * @param id A webmap layer id to test with.
597
+ * @returns The updated instance of the object with as many field references templatized as possible
598
+ * @private
599
+ */
600
+ export function _templatizeParentByWebMapLayerId(obj, ds, id, templatizeKeys) {
601
+ let clone = {};
602
+ const idTest = new RegExp(id, "gm");
603
+ if (Array.isArray(obj)) {
604
+ clone = obj.map(c => {
605
+ return _templatizeParentByWebMapLayerId(c, ds, id, templatizeKeys);
606
+ });
607
+ }
608
+ else if (typeof obj === "object") {
609
+ for (const i in obj) {
610
+ if (obj[i] !== null) {
611
+ // In some web application templates they store a stringified version of an object that can
612
+ // contain multiple layer references at a very high level on the main values collection.
613
+ // This was causing many other more typical layer references to be set incorrectly as the first
614
+ // layerId found in this high level string would be used against the main object.
615
+ let parsedProp;
616
+ try {
617
+ parsedProp = JSON.parse(obj[i]);
618
+ }
619
+ catch (error) {
620
+ parsedProp = undefined;
621
+ }
622
+ if (parsedProp && typeof parsedProp === "object") {
623
+ clone[i] = JSON.stringify(_templatizeParentByWebMapLayerId(parsedProp, ds, id, templatizeKeys));
624
+ }
625
+ else if (typeof obj[i] === "object") {
626
+ // some widgets store the layerId as a key to a collection of details that contain field references
627
+ if (idTest.test(i) && templatizeKeys) {
628
+ obj[i] = common.templatizeFieldReferences(obj[i], ds.fields, ds.basePath, templatizeKeys);
629
+ }
630
+ clone[i] = _templatizeParentByWebMapLayerId(obj[i], ds, id, templatizeKeys);
631
+ }
632
+ else {
633
+ if (idTest.test(obj[i])) {
634
+ obj = common.templatizeFieldReferences(obj, ds.fields, ds.basePath, templatizeKeys);
635
+ }
636
+ clone[i] = obj[i];
637
+ }
638
+ }
639
+ else {
640
+ clone[i] = obj[i];
641
+ }
642
+ }
643
+ }
644
+ else {
645
+ clone = obj;
646
+ }
647
+ return clone;
648
+ }
649
649
  //# sourceMappingURL=webmappingapplication.js.map