@esri/solution-creator 4.1.2 → 5.0.0

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.
@@ -1,436 +1,439 @@
1
- "use strict";
2
- /** @license
3
- * Copyright 2018 Esri
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
- Object.defineProperty(exports, "__esModule", { value: true });
18
- exports._templatizeResources = exports._getWebMapFSDependencies = exports._addMapLayerIds = exports._addLayerIdToDatasourceUrl = exports._updateWebMapHashInfo = exports._getTemplateTypeHash = exports._getDatasourceInfos = exports.postProcessFieldReferences = exports.createItemTemplate = void 0;
19
- /**
20
- * Manages creation of the template of a Solution item via the REST API.
21
- *
22
- * @module createItemTemplate
23
- */
24
- const solution_common_1 = require("@esri/solution-common");
25
- const hub_common_1 = require("@esri/hub-common");
26
- const module_map_1 = require("./module-map");
27
- // ------------------------------------------------------------------------------------------------------------------ //
28
- /**
29
- * Creates template for an AGO item and its dependencies
30
- *
31
- * @param solutionItemId The solution to contain the item
32
- * @param itemId AGO id string
33
- * @param templateDictionary Hash of facts
34
- * @param srcAuthentication Credentials for requests to source items
35
- * @param destAuthentication Authentication for requesting information from AGO about items to be included in solution item
36
- * @param existingTemplates A collection of AGO item templates that can be referenced by newly-created templates
37
- * @returns A promise which resolves with an array of resources for the item and its dependencies
38
- * @private
39
- */
40
- function createItemTemplate(solutionItemId, itemId, templateDictionary, srcAuthentication, destAuthentication, existingTemplates, itemProgressCallback) {
41
- return new Promise(resolve => {
42
- // Check if item and its dependents are already in list or are queued
43
- if ((0, solution_common_1.findTemplateInList)(existingTemplates, itemId)) {
44
- resolve([]);
45
- }
46
- else {
47
- // Add the id as a placeholder to show that it is being fetched
48
- existingTemplates.push((0, solution_common_1.createPlaceholderTemplate)(itemId));
49
- itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Started, 0);
50
- // Fetch the item
51
- (0, solution_common_1.getItemBase)(itemId, srcAuthentication)
52
- .catch(() => {
53
- // If item query fails, try fetching item as a group
54
- // Change its placeholder from an empty type to the Group type so that we can later distinguish
55
- // between items and groups (the base info for a group doesn't include a type property)
56
- (0, solution_common_1.replaceTemplate)(existingTemplates, itemId, (0, solution_common_1.createPlaceholderTemplate)(itemId, "Group"));
57
- return (0, solution_common_1.getGroupBase)(itemId, srcAuthentication);
58
- })
59
- .then(itemInfo => {
60
- itemInfo = (0, solution_common_1.sanitizeJSON)(itemInfo);
61
- // Save the URL as a symbol
62
- if (itemInfo.url) {
63
- templateDictionary[itemInfo.url] = "{{" + itemInfo.id + ".url}}";
64
- itemInfo.origUrl = itemInfo.url;
65
- }
66
- const idTest = /^source-[0-9A-F]{32}/i;
67
- // Remove any source-itemId type keywords
68
- /* istanbul ignore else */
69
- if (Array.isArray(itemInfo.typeKeywords)) {
70
- itemInfo.typeKeywords = itemInfo.typeKeywords.filter(v => idTest.test(v) ? false : true);
71
- }
72
- // Remove any source-itemId tags
73
- /* istanbul ignore else */
74
- if (Array.isArray(itemInfo.tags)) {
75
- itemInfo.tags = itemInfo.tags.filter(v => idTest.test(v) ? false : true);
76
- }
77
- const placeholder = (0, solution_common_1.findTemplateInList)(existingTemplates, itemId);
78
- let itemType = placeholder.type;
79
- if (!itemType) {
80
- // Groups have this defined when their placeholder is created
81
- itemType = itemInfo.type;
82
- placeholder.type = itemType;
83
- }
84
- if (!itemInfo.type) {
85
- itemInfo.type = itemType; // Groups don't have this property, so we'll patch it in
86
- }
87
- placeholder.item = {
88
- ...itemInfo
89
- };
90
- // Interrupt process if progress callback returns `false`
91
- if (!itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Created, 1)) {
92
- itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Cancelled, 1);
93
- resolve((0, solution_common_1.fail)("Cancelled"));
94
- return;
95
- }
96
- const itemHandler = module_map_1.moduleMap[itemType];
97
- if (!itemHandler || itemHandler === module_map_1.UNSUPPORTED) {
98
- if (itemHandler === module_map_1.UNSUPPORTED) {
99
- itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Ignored, 1);
100
- resolve([]);
101
- }
102
- else {
103
- itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Failed, 1);
104
- placeholder.properties["failed"] = true;
105
- (0, solution_common_1.replaceTemplate)(existingTemplates, itemId, placeholder);
106
- resolve((0, solution_common_1.fail)("The type of AGO item " +
107
- itemId +
108
- " ('" +
109
- itemType +
110
- "') is not supported at this time"));
111
- }
112
- }
113
- else {
114
- // Handle original Story Maps with next-gen Story Maps
115
- /* istanbul ignore else */
116
- /* Not yet supported
117
- if (storyMap.isAStoryMap(itemType, itemInfo.url)) {
118
- itemHandler = storyMap;
119
- } */
120
- // Delegate the creation of the item to the handler
121
- itemHandler
122
- .convertItemToTemplate(solutionItemId, itemInfo, destAuthentication, srcAuthentication, templateDictionary)
123
- .then(itemTemplate => {
124
- let resourcePrepPromise = Promise.resolve([]);
125
- // If the item type is Quick Capture, then we already have the resource files and just need to
126
- // convert them into ISourceFile objects
127
- if (itemTemplate.type === "QuickCapture Project") {
128
- const resourceItemFiles = itemTemplate.resources.map((file) => {
129
- const fileParts = file.name.split("/");
130
- return {
131
- itemId: itemTemplate.itemId,
132
- file,
133
- folder: fileParts.length === 1 ? "" : fileParts[0],
134
- filename: fileParts.length === 1 ? fileParts[0] : fileParts[1],
135
- };
136
- });
137
- resourcePrepPromise = Promise.resolve(resourceItemFiles);
138
- }
139
- else {
140
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
141
- resourcePrepPromise = (0, solution_common_1.getItemResourcesPaths)(itemTemplate, solutionItemId, srcAuthentication, solution_common_1.SolutionTemplateFormatVersion).then((resourceItemFilePaths) => {
142
- itemTemplate.item.thumbnail = null; // not needed in this property; handled as a resource
143
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
144
- return (0, solution_common_1.getItemResourcesFilesFromPaths)(resourceItemFilePaths, srcAuthentication);
145
- });
146
- }
147
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
148
- resourcePrepPromise.then(async (resourceItemFiles) => {
149
- // Perform any custom processing needed on resource files
150
- await _templatizeResources(itemTemplate, resourceItemFiles, srcAuthentication);
151
- // update the template's resources
152
- itemTemplate.resources = resourceItemFiles.map((file) => file.folder + "/" + file.filename);
153
- // Set the value keyed by the id to the created template, replacing the placeholder template
154
- (0, solution_common_1.replaceTemplate)(existingTemplates, itemTemplate.itemId, itemTemplate);
155
- // Trace item dependencies
156
- if (itemTemplate.dependencies.length === 0) {
157
- itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Finished, 1);
158
- resolve(resourceItemFiles);
159
- }
160
- else {
161
- // Get its dependencies, asking each to get its dependents via
162
- // recursive calls to this function
163
- const dependentDfds = [];
164
- itemTemplate.dependencies.forEach(dependentId => {
165
- if (!(0, solution_common_1.findTemplateInList)(existingTemplates, dependentId)) {
166
- dependentDfds.push(createItemTemplate(solutionItemId, dependentId, templateDictionary, srcAuthentication, destAuthentication, existingTemplates, itemProgressCallback));
167
- }
168
- });
169
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
170
- Promise.all(dependentDfds).then((dependentResourceItemFiles) => {
171
- // Templatization of item and its dependencies done
172
- itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Finished, 1);
173
- resourceItemFiles = dependentResourceItemFiles.reduce((accumulator, currentValue) => accumulator.concat(currentValue), resourceItemFiles);
174
- resolve(resourceItemFiles);
175
- });
176
- }
177
- ;
178
- });
179
- }, error => {
180
- placeholder.properties["error"] = JSON.stringify(error);
181
- (0, solution_common_1.replaceTemplate)(existingTemplates, itemId, placeholder);
182
- itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Failed, 1);
183
- resolve([]);
184
- });
185
- }
186
- },
187
- // Id not found or item is not accessible
188
- () => {
189
- // mock hasInvalidDesignations so this will be processed at the end
190
- // as we do with living atlas layers
191
- const t = (0, solution_common_1.findTemplateInList)(existingTemplates, itemId);
192
- t.properties.hasInvalidDesignations = true;
193
- // Skip items that we cannot fetch per issue #859
194
- // Use finished rather than ignored
195
- // ignored will cause the template to be removed before we can check for hasInvalidDesignations
196
- itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Finished, 0);
197
- resolve([]);
198
- });
199
- }
200
- });
201
- }
202
- exports.createItemTemplate = createItemTemplate;
203
- /**
204
- * Templatizes field references within specific template types.
205
- * Currently only handles web applications
206
- *
207
- * @param templates List of solution templates
208
- * @returns A list of templates that have templatized field references
209
- */
210
- function postProcessFieldReferences(templates) {
211
- const datasourceInfos = _getDatasourceInfos(templates);
212
- const templateTypeHash = _getTemplateTypeHash(templates);
213
- return templates.map(template => {
214
- /* istanbul ignore else */
215
- if (template.type === "Web Mapping Application" ||
216
- template.type === "Dashboard" ||
217
- template.type === "Web Map") {
218
- const webMapFSDependencies = _getWebMapFSDependencies(template, templateTypeHash);
219
- const itemHandler = module_map_1.moduleMap[template.item.type];
220
- /* istanbul ignore else */
221
- if (itemHandler) {
222
- const dependencies = webMapFSDependencies.concat(template.dependencies);
223
- let dependentDatasources = datasourceInfos.filter(ds => {
224
- if (dependencies.indexOf(ds.itemId) > -1) {
225
- return ds;
226
- }
227
- });
228
- dependentDatasources = _addMapLayerIds(dependentDatasources, templateTypeHash);
229
- if (dependentDatasources.length > 0) {
230
- template = itemHandler.postProcessFieldReferences(template, dependentDatasources, template.item.type);
231
- }
232
- }
233
- }
234
- return template;
235
- });
236
- }
237
- exports.postProcessFieldReferences = postProcessFieldReferences;
238
- // ------------------------------------------------------------------------------------------------------------------ //
239
- /**
240
- * Get common properties that will support the templatization of field references
241
- *
242
- * @param templates List of solution templates
243
- * @returns A list of IDataSourceInfo objects with key properties
244
- * @private
245
- */
246
- function _getDatasourceInfos(templates) {
247
- const datasourceInfos = [];
248
- templates.forEach(t => {
249
- if (t.type === "Feature Service") {
250
- const layers = (0, hub_common_1.getProp)(t, "properties.layers") || [];
251
- const tables = (0, hub_common_1.getProp)(t, "properties.tables") || [];
252
- const layersAndTables = layers.concat(tables);
253
- layersAndTables.forEach(obj => {
254
- /* istanbul ignore else */
255
- if (!(0, solution_common_1.hasDatasource)(datasourceInfos, t.itemId, obj.id)) {
256
- datasourceInfos.push({
257
- itemId: t.itemId,
258
- layerId: obj.id,
259
- fields: obj.fields,
260
- basePath: t.itemId + ".layer" + obj.id + ".fields",
261
- url: (0, hub_common_1.getProp)(t, "item.url"),
262
- ids: [],
263
- relationships: obj.relationships || [],
264
- adminLayerInfo: obj.adminLayerInfo || {}
265
- });
266
- }
267
- });
268
- }
269
- });
270
- return datasourceInfos;
271
- }
272
- exports._getDatasourceInfos = _getDatasourceInfos;
273
- /**
274
- * Creates a simple lookup object to quickly understand an items type and dependencies
275
- * and associated web map layer ids based on itemId
276
- *
277
- * @param templates List of solution templates
278
- * @returns The lookup object with type, dependencies, and webmap layer info
279
- * @private
280
- */
281
- function _getTemplateTypeHash(templates) {
282
- const templateTypeHash = {};
283
- templates.forEach(template => {
284
- templateTypeHash[template.itemId] = {
285
- type: template.type,
286
- dependencies: template.dependencies
287
- };
288
- if (template.type === "Web Map") {
289
- _updateWebMapHashInfo(template, templateTypeHash[template.itemId]);
290
- }
291
- });
292
- return templateTypeHash;
293
- }
294
- exports._getTemplateTypeHash = _getTemplateTypeHash;
295
- /**
296
- * Updates the lookup object with webmap layer info
297
- * so we can know the id used within a map for a given feature service
298
- *
299
- * @param template A webmap solution template
300
- * @returns The lookup object with webmap layer info added
301
- * @private
302
- */
303
- function _updateWebMapHashInfo(template, hashItem) {
304
- const operationalLayers = (0, hub_common_1.getProp)(template, "data.operationalLayers") || [];
305
- const tables = (0, hub_common_1.getProp)(template, "data.tables") || [];
306
- const layersAndTables = operationalLayers.concat(tables);
307
- if (layersAndTables && layersAndTables.length > 0) {
308
- hashItem.layersAndTables = [];
309
- layersAndTables.forEach(layer => {
310
- const obj = {};
311
- let itemId;
312
- /* istanbul ignore else */
313
- if (layer.itemId) {
314
- itemId = layer.itemId;
315
- }
316
- /* istanbul ignore else */
317
- if (itemId) {
318
- obj[(0, solution_common_1.cleanLayerBasedItemId)(itemId)] = {
319
- id: layer.id,
320
- url: layer.url
321
- };
322
- hashItem.layersAndTables.push(obj);
323
- }
324
- });
325
- }
326
- }
327
- exports._updateWebMapHashInfo = _updateWebMapHashInfo;
328
- /**
329
- * Updates a templatized datasource URL with a layer id.
330
- *
331
- * @param dataSourceUrl Templatized datasource URL
332
- * @param layerId Layer id
333
- * @returns string Amended datasource URL
334
- * @private
335
- */
336
- function _addLayerIdToDatasourceUrl(datasourceUrl, layerId) {
337
- return datasourceUrl && !isNaN(layerId)
338
- ? datasourceUrl.replace(/[.]/, ".layer" + layerId + ".")
339
- : "";
340
- }
341
- exports._addLayerIdToDatasourceUrl = _addLayerIdToDatasourceUrl;
342
- /**
343
- * Updates the datasource info objects by passing the webmap layer IDs from the lookup hash
344
- * to the underlying feature service datasource infos
345
- *
346
- * @param datasourceInfos A webmap solution template
347
- * @param templateTypeHash A simple lookup object populated with key item info
348
- * @returns The updated datasource infos
349
- * @private
350
- */
351
- function _addMapLayerIds(datasourceInfos, templateTypeHash) {
352
- const webMapIds = Object.keys(templateTypeHash).filter(k => {
353
- if (templateTypeHash[k].type === "Web Map") {
354
- return templateTypeHash[k];
355
- }
356
- });
357
- return datasourceInfos.map(ds => {
358
- webMapIds.forEach(webMapId => {
359
- templateTypeHash[webMapId].layersAndTables.forEach((opLayer) => {
360
- const opLayerInfo = opLayer[ds.itemId];
361
- const url = _addLayerIdToDatasourceUrl(ds.url, ds.layerId);
362
- if (opLayerInfo &&
363
- url === opLayerInfo.url &&
364
- ds.ids.indexOf(opLayerInfo.id) < 0) {
365
- ds.ids.push(opLayerInfo.id);
366
- }
367
- });
368
- });
369
- return ds;
370
- });
371
- }
372
- exports._addMapLayerIds = _addMapLayerIds;
373
- /**
374
- * Get feature service item IDs from applications webmaps
375
- * As they are not explict dependencies of the application but are needed for field references
376
- *
377
- * @param template A webmap solution template
378
- * @param templateTypeHash A simple lookup object populated with key item info
379
- * @returns A list of feature service item IDs
380
- * @private
381
- */
382
- function _getWebMapFSDependencies(template, templateTypeHash) {
383
- const webMapFSDependencies = [];
384
- template.dependencies.forEach(dep => {
385
- const depObj = templateTypeHash[dep];
386
- if (depObj.type === "Web Map") {
387
- depObj.dependencies.forEach((depObjDependency) => {
388
- /* istanbul ignore else */
389
- if (templateTypeHash[depObjDependency].type === "Feature Service") {
390
- webMapFSDependencies.push(depObjDependency);
391
- }
392
- });
393
- }
394
- });
395
- return webMapFSDependencies;
396
- }
397
- exports._getWebMapFSDependencies = _getWebMapFSDependencies;
398
- /**
399
- * Perform templatizations needed in an item's resources
400
- *
401
- * @param itemTemplate Item being templatized
402
- * @param resourceItemFiles Resources for the item; these resources are modified as needed
403
- * by the templatization
404
- * @param srcAuthentication Credentials for requests to source items
405
- *
406
- * @returns A promise that resolves when all templatization has completed
407
- */
408
- function _templatizeResources(itemTemplate, resourceItemFiles, srcAuthentication) {
409
- const synchronizePromises = [];
410
- if (itemTemplate.type === "Vector Tile Service") {
411
- // Get the root.json files
412
- const rootJsonResources = resourceItemFiles.filter(file => file.filename === "root.json");
413
- const resourcePath = srcAuthentication.portal + "/content/items/" + itemTemplate.itemId;
414
- const templatizedResourcePath = "{{" + itemTemplate.itemId + ".url}}";
415
- const replacer = new RegExp(resourcePath, "g");
416
- // Templatize the paths in the files that reference the source item id
417
- rootJsonResources.forEach(rootFileResource => {
418
- synchronizePromises.push(new Promise(resolve => {
419
- // Read the file
420
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
421
- (0, solution_common_1.blobToJson)(rootFileResource.file)
422
- .then(fileJson => {
423
- // Templatize by turning JSON into string, replacing paths with template, and re-JSONing
424
- const updatedFileJson = JSON.parse(JSON.stringify(fileJson)
425
- .replace(replacer, templatizedResourcePath));
426
- // Write the changes back into the file
427
- rootFileResource.file = (0, solution_common_1.jsonToFile)(updatedFileJson, rootFileResource.filename);
428
- resolve(null);
429
- });
430
- }));
431
- });
432
- }
433
- return Promise.all(synchronizePromises);
434
- }
435
- exports._templatizeResources = _templatizeResources;
1
+ "use strict";
2
+ /** @license
3
+ * Copyright 2018 Esri
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
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports._templatizeResources = exports._getWebMapFSDependencies = exports._addMapLayerIds = exports._addLayerIdToDatasourceUrl = exports._updateWebMapHashInfo = exports._getTemplateTypeHash = exports._getDatasourceInfos = exports.postProcessFieldReferences = exports.createItemTemplate = void 0;
19
+ /**
20
+ * Manages creation of the template of a Solution item via the REST API.
21
+ *
22
+ * @module createItemTemplate
23
+ */
24
+ const solution_common_1 = require("@esri/solution-common");
25
+ const hub_common_1 = require("@esri/hub-common");
26
+ const module_map_1 = require("./module-map");
27
+ // ------------------------------------------------------------------------------------------------------------------ //
28
+ /**
29
+ * Creates template for an AGO item and its dependencies
30
+ *
31
+ * @param solutionItemId The solution to contain the item
32
+ * @param itemId AGO id string
33
+ * @param templateDictionary Hash of facts
34
+ * @param srcAuthentication Credentials for requests to source items
35
+ * @param destAuthentication Authentication for requesting information from AGO about items to be included in solution item
36
+ * @param existingTemplates A collection of AGO item templates that can be referenced by newly-created templates
37
+ * @returns A promise which resolves with an array of resources for the item and its dependencies
38
+ * @private
39
+ */
40
+ function createItemTemplate(solutionItemId, itemId, templateDictionary, srcAuthentication, destAuthentication, existingTemplates, itemProgressCallback) {
41
+ return new Promise(resolve => {
42
+ // Check if item and its dependents are already in list or are queued
43
+ if ((0, solution_common_1.findTemplateInList)(existingTemplates, itemId)) {
44
+ resolve([]);
45
+ }
46
+ else {
47
+ // Add the id as a placeholder to show that it is being fetched
48
+ existingTemplates.push((0, solution_common_1.createPlaceholderTemplate)(itemId));
49
+ itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Started, 0);
50
+ // Fetch the item
51
+ (0, solution_common_1.getItemBase)(itemId, srcAuthentication)
52
+ .catch(() => {
53
+ // If item query fails, try fetching item as a group
54
+ // Change its placeholder from an empty type to the Group type so that we can later distinguish
55
+ // between items and groups (the base info for a group doesn't include a type property)
56
+ (0, solution_common_1.replaceTemplate)(existingTemplates, itemId, (0, solution_common_1.createPlaceholderTemplate)(itemId, "Group"));
57
+ return (0, solution_common_1.getGroupBase)(itemId, srcAuthentication);
58
+ })
59
+ .then(itemInfo => {
60
+ itemInfo = (0, solution_common_1.sanitizeJSON)(itemInfo);
61
+ // Save the URL as a symbol
62
+ if (itemInfo.url) {
63
+ templateDictionary[itemInfo.url] = "{{" + itemInfo.id + ".url}}";
64
+ itemInfo.origUrl = itemInfo.url;
65
+ }
66
+ const idTest = /^source-[0-9A-F]{32}/i;
67
+ // Remove any source-itemId type keywords
68
+ /* istanbul ignore else */
69
+ if (Array.isArray(itemInfo.typeKeywords)) {
70
+ itemInfo.typeKeywords = itemInfo.typeKeywords.filter(v => idTest.test(v) ? false : true);
71
+ }
72
+ // Remove any source-itemId tags
73
+ /* istanbul ignore else */
74
+ if (Array.isArray(itemInfo.tags)) {
75
+ itemInfo.tags = itemInfo.tags.filter(v => idTest.test(v) ? false : true);
76
+ }
77
+ const placeholder = (0, solution_common_1.findTemplateInList)(existingTemplates, itemId);
78
+ let itemType = placeholder.type;
79
+ if (!itemType) {
80
+ // Groups have this defined when their placeholder is created
81
+ itemType = itemInfo.type;
82
+ placeholder.type = itemType;
83
+ }
84
+ if (!itemInfo.type) {
85
+ itemInfo.type = itemType; // Groups don't have this property, so we'll patch it in
86
+ }
87
+ placeholder.item = {
88
+ ...itemInfo
89
+ };
90
+ // Interrupt process if progress callback returns `false`
91
+ if (!itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Created, 1)) {
92
+ itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Cancelled, 1);
93
+ resolve((0, solution_common_1.fail)("Cancelled"));
94
+ return;
95
+ }
96
+ const itemHandler = module_map_1.moduleMap[itemType];
97
+ if (!itemHandler || itemHandler === module_map_1.UNSUPPORTED) {
98
+ if (itemHandler === module_map_1.UNSUPPORTED) {
99
+ itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Ignored, 1);
100
+ resolve([]);
101
+ }
102
+ else {
103
+ itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Failed, 1);
104
+ placeholder.properties["failed"] = true;
105
+ (0, solution_common_1.replaceTemplate)(existingTemplates, itemId, placeholder);
106
+ resolve((0, solution_common_1.fail)("The type of AGO item " +
107
+ itemId +
108
+ " ('" +
109
+ itemType +
110
+ "') is not supported at this time"));
111
+ }
112
+ }
113
+ else {
114
+ // Handle original Story Maps with next-gen Story Maps
115
+ /* istanbul ignore else */
116
+ /* Not yet supported
117
+ if (storyMap.isAStoryMap(itemType, itemInfo.url)) {
118
+ itemHandler = storyMap;
119
+ } */
120
+ // Delegate the creation of the item to the handler
121
+ itemHandler
122
+ .convertItemToTemplate(solutionItemId, itemInfo, destAuthentication, srcAuthentication, templateDictionary)
123
+ .then(itemTemplate => {
124
+ let resourcePrepPromise = Promise.resolve([]);
125
+ // If the item type is Quick Capture, then we already have the resource files and just need to
126
+ // convert them into ISourceFile objects
127
+ if (itemTemplate.type === "QuickCapture Project") {
128
+ // Fetch thumbnail
129
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
130
+ resourcePrepPromise = (0, solution_common_1.getItemResourcesFilesFromPaths)([(0, solution_common_1.generateSourceThumbnailPath)(srcAuthentication.portal, itemTemplate.itemId, itemTemplate.item.thumbnail)], srcAuthentication).then((thumbnailFile) => {
131
+ itemTemplate.item.thumbnail = null; // not needed in this property; handled as a resource
132
+ return itemTemplate.resources.map((file) => {
133
+ return {
134
+ itemId: itemTemplate.itemId,
135
+ file,
136
+ folder: itemTemplate.itemId,
137
+ filename: file.name
138
+ };
139
+ }).concat(thumbnailFile);
140
+ });
141
+ }
142
+ else {
143
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
144
+ resourcePrepPromise = (0, solution_common_1.getItemResourcesPaths)(itemTemplate, solutionItemId, srcAuthentication, solution_common_1.SolutionTemplateFormatVersion).then((resourceItemFilePaths) => {
145
+ itemTemplate.item.thumbnail = null; // not needed in this property; handled as a resource
146
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
147
+ return (0, solution_common_1.getItemResourcesFilesFromPaths)(resourceItemFilePaths, srcAuthentication);
148
+ });
149
+ }
150
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
151
+ resourcePrepPromise.then(async (resourceItemFiles) => {
152
+ // Perform any custom processing needed on resource files
153
+ await _templatizeResources(itemTemplate, resourceItemFiles, srcAuthentication);
154
+ // update the template's resources
155
+ itemTemplate.resources = resourceItemFiles.map((file) => file.folder + "/" + file.filename);
156
+ // Set the value keyed by the id to the created template, replacing the placeholder template
157
+ (0, solution_common_1.replaceTemplate)(existingTemplates, itemTemplate.itemId, itemTemplate);
158
+ // Trace item dependencies
159
+ if (itemTemplate.dependencies.length === 0) {
160
+ itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Finished, 1);
161
+ resolve(resourceItemFiles);
162
+ }
163
+ else {
164
+ // Get its dependencies, asking each to get its dependents via
165
+ // recursive calls to this function
166
+ const dependentDfds = [];
167
+ itemTemplate.dependencies.forEach(dependentId => {
168
+ if (!(0, solution_common_1.findTemplateInList)(existingTemplates, dependentId)) {
169
+ dependentDfds.push(createItemTemplate(solutionItemId, dependentId, templateDictionary, srcAuthentication, destAuthentication, existingTemplates, itemProgressCallback));
170
+ }
171
+ });
172
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
173
+ Promise.all(dependentDfds).then((dependentResourceItemFiles) => {
174
+ // Templatization of item and its dependencies done
175
+ itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Finished, 1);
176
+ resourceItemFiles = dependentResourceItemFiles.reduce((accumulator, currentValue) => accumulator.concat(currentValue), resourceItemFiles);
177
+ resolve(resourceItemFiles);
178
+ });
179
+ }
180
+ ;
181
+ });
182
+ }, error => {
183
+ placeholder.properties["error"] = JSON.stringify(error);
184
+ (0, solution_common_1.replaceTemplate)(existingTemplates, itemId, placeholder);
185
+ itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Failed, 1);
186
+ resolve([]);
187
+ });
188
+ }
189
+ },
190
+ // Id not found or item is not accessible
191
+ () => {
192
+ // mock hasInvalidDesignations so this will be processed at the end
193
+ // as we do with living atlas layers
194
+ const t = (0, solution_common_1.findTemplateInList)(existingTemplates, itemId);
195
+ t.properties.hasInvalidDesignations = true;
196
+ // Skip items that we cannot fetch per issue #859
197
+ // Use finished rather than ignored
198
+ // ignored will cause the template to be removed before we can check for hasInvalidDesignations
199
+ itemProgressCallback(itemId, solution_common_1.EItemProgressStatus.Finished, 0);
200
+ resolve([]);
201
+ });
202
+ }
203
+ });
204
+ }
205
+ exports.createItemTemplate = createItemTemplate;
206
+ /**
207
+ * Templatizes field references within specific template types.
208
+ * Currently only handles web applications
209
+ *
210
+ * @param templates List of solution templates
211
+ * @returns A list of templates that have templatized field references
212
+ */
213
+ function postProcessFieldReferences(templates) {
214
+ const datasourceInfos = _getDatasourceInfos(templates);
215
+ const templateTypeHash = _getTemplateTypeHash(templates);
216
+ return templates.map(template => {
217
+ /* istanbul ignore else */
218
+ if (template.type === "Web Mapping Application" ||
219
+ template.type === "Dashboard" ||
220
+ template.type === "Web Map") {
221
+ const webMapFSDependencies = _getWebMapFSDependencies(template, templateTypeHash);
222
+ const itemHandler = module_map_1.moduleMap[template.item.type];
223
+ /* istanbul ignore else */
224
+ if (itemHandler) {
225
+ const dependencies = webMapFSDependencies.concat(template.dependencies);
226
+ let dependentDatasources = datasourceInfos.filter(ds => {
227
+ if (dependencies.indexOf(ds.itemId) > -1) {
228
+ return ds;
229
+ }
230
+ });
231
+ dependentDatasources = _addMapLayerIds(dependentDatasources, templateTypeHash);
232
+ if (dependentDatasources.length > 0) {
233
+ template = itemHandler.postProcessFieldReferences(template, dependentDatasources, template.item.type);
234
+ }
235
+ }
236
+ }
237
+ return template;
238
+ });
239
+ }
240
+ exports.postProcessFieldReferences = postProcessFieldReferences;
241
+ // ------------------------------------------------------------------------------------------------------------------ //
242
+ /**
243
+ * Get common properties that will support the templatization of field references
244
+ *
245
+ * @param templates List of solution templates
246
+ * @returns A list of IDataSourceInfo objects with key properties
247
+ * @private
248
+ */
249
+ function _getDatasourceInfos(templates) {
250
+ const datasourceInfos = [];
251
+ templates.forEach(t => {
252
+ if (t.type === "Feature Service") {
253
+ const layers = (0, hub_common_1.getProp)(t, "properties.layers") || [];
254
+ const tables = (0, hub_common_1.getProp)(t, "properties.tables") || [];
255
+ const layersAndTables = layers.concat(tables);
256
+ layersAndTables.forEach(obj => {
257
+ /* istanbul ignore else */
258
+ if (!(0, solution_common_1.hasDatasource)(datasourceInfos, t.itemId, obj.id)) {
259
+ datasourceInfos.push({
260
+ itemId: t.itemId,
261
+ layerId: obj.id,
262
+ fields: obj.fields,
263
+ basePath: t.itemId + ".layer" + obj.id + ".fields",
264
+ url: (0, hub_common_1.getProp)(t, "item.url"),
265
+ ids: [],
266
+ relationships: obj.relationships || [],
267
+ adminLayerInfo: obj.adminLayerInfo || {}
268
+ });
269
+ }
270
+ });
271
+ }
272
+ });
273
+ return datasourceInfos;
274
+ }
275
+ exports._getDatasourceInfos = _getDatasourceInfos;
276
+ /**
277
+ * Creates a simple lookup object to quickly understand an items type and dependencies
278
+ * and associated web map layer ids based on itemId
279
+ *
280
+ * @param templates List of solution templates
281
+ * @returns The lookup object with type, dependencies, and webmap layer info
282
+ * @private
283
+ */
284
+ function _getTemplateTypeHash(templates) {
285
+ const templateTypeHash = {};
286
+ templates.forEach(template => {
287
+ templateTypeHash[template.itemId] = {
288
+ type: template.type,
289
+ dependencies: template.dependencies
290
+ };
291
+ if (template.type === "Web Map") {
292
+ _updateWebMapHashInfo(template, templateTypeHash[template.itemId]);
293
+ }
294
+ });
295
+ return templateTypeHash;
296
+ }
297
+ exports._getTemplateTypeHash = _getTemplateTypeHash;
298
+ /**
299
+ * Updates the lookup object with webmap layer info
300
+ * so we can know the id used within a map for a given feature service
301
+ *
302
+ * @param template A webmap solution template
303
+ * @returns The lookup object with webmap layer info added
304
+ * @private
305
+ */
306
+ function _updateWebMapHashInfo(template, hashItem) {
307
+ const operationalLayers = (0, hub_common_1.getProp)(template, "data.operationalLayers") || [];
308
+ const tables = (0, hub_common_1.getProp)(template, "data.tables") || [];
309
+ const layersAndTables = operationalLayers.concat(tables);
310
+ if (layersAndTables && layersAndTables.length > 0) {
311
+ hashItem.layersAndTables = [];
312
+ layersAndTables.forEach(layer => {
313
+ const obj = {};
314
+ let itemId;
315
+ /* istanbul ignore else */
316
+ if (layer.itemId) {
317
+ itemId = layer.itemId;
318
+ }
319
+ /* istanbul ignore else */
320
+ if (itemId) {
321
+ obj[(0, solution_common_1.cleanLayerBasedItemId)(itemId)] = {
322
+ id: layer.id,
323
+ url: layer.url
324
+ };
325
+ hashItem.layersAndTables.push(obj);
326
+ }
327
+ });
328
+ }
329
+ }
330
+ exports._updateWebMapHashInfo = _updateWebMapHashInfo;
331
+ /**
332
+ * Updates a templatized datasource URL with a layer id.
333
+ *
334
+ * @param dataSourceUrl Templatized datasource URL
335
+ * @param layerId Layer id
336
+ * @returns string Amended datasource URL
337
+ * @private
338
+ */
339
+ function _addLayerIdToDatasourceUrl(datasourceUrl, layerId) {
340
+ return datasourceUrl && !isNaN(layerId)
341
+ ? datasourceUrl.replace(/[.]/, ".layer" + layerId + ".")
342
+ : "";
343
+ }
344
+ exports._addLayerIdToDatasourceUrl = _addLayerIdToDatasourceUrl;
345
+ /**
346
+ * Updates the datasource info objects by passing the webmap layer IDs from the lookup hash
347
+ * to the underlying feature service datasource infos
348
+ *
349
+ * @param datasourceInfos A webmap solution template
350
+ * @param templateTypeHash A simple lookup object populated with key item info
351
+ * @returns The updated datasource infos
352
+ * @private
353
+ */
354
+ function _addMapLayerIds(datasourceInfos, templateTypeHash) {
355
+ const webMapIds = Object.keys(templateTypeHash).filter(k => {
356
+ if (templateTypeHash[k].type === "Web Map") {
357
+ return templateTypeHash[k];
358
+ }
359
+ });
360
+ return datasourceInfos.map(ds => {
361
+ webMapIds.forEach(webMapId => {
362
+ templateTypeHash[webMapId].layersAndTables.forEach((opLayer) => {
363
+ const opLayerInfo = opLayer[ds.itemId];
364
+ const url = _addLayerIdToDatasourceUrl(ds.url, ds.layerId);
365
+ if (opLayerInfo &&
366
+ url === opLayerInfo.url &&
367
+ ds.ids.indexOf(opLayerInfo.id) < 0) {
368
+ ds.ids.push(opLayerInfo.id);
369
+ }
370
+ });
371
+ });
372
+ return ds;
373
+ });
374
+ }
375
+ exports._addMapLayerIds = _addMapLayerIds;
376
+ /**
377
+ * Get feature service item IDs from applications webmaps
378
+ * As they are not explict dependencies of the application but are needed for field references
379
+ *
380
+ * @param template A webmap solution template
381
+ * @param templateTypeHash A simple lookup object populated with key item info
382
+ * @returns A list of feature service item IDs
383
+ * @private
384
+ */
385
+ function _getWebMapFSDependencies(template, templateTypeHash) {
386
+ const webMapFSDependencies = [];
387
+ template.dependencies.forEach(dep => {
388
+ const depObj = templateTypeHash[dep];
389
+ if (depObj.type === "Web Map") {
390
+ depObj.dependencies.forEach((depObjDependency) => {
391
+ /* istanbul ignore else */
392
+ if (templateTypeHash[depObjDependency].type === "Feature Service") {
393
+ webMapFSDependencies.push(depObjDependency);
394
+ }
395
+ });
396
+ }
397
+ });
398
+ return webMapFSDependencies;
399
+ }
400
+ exports._getWebMapFSDependencies = _getWebMapFSDependencies;
401
+ /**
402
+ * Perform templatizations needed in an item's resources
403
+ *
404
+ * @param itemTemplate Item being templatized
405
+ * @param resourceItemFiles Resources for the item; these resources are modified as needed
406
+ * by the templatization
407
+ * @param srcAuthentication Credentials for requests to source items
408
+ *
409
+ * @returns A promise that resolves when all templatization has completed
410
+ */
411
+ function _templatizeResources(itemTemplate, resourceItemFiles, srcAuthentication) {
412
+ const synchronizePromises = [];
413
+ if (itemTemplate.type === "Vector Tile Service") {
414
+ // Get the root.json files
415
+ const rootJsonResources = resourceItemFiles.filter(file => file.filename === "root.json");
416
+ const resourcePath = srcAuthentication.portal + "/content/items/" + itemTemplate.itemId;
417
+ const templatizedResourcePath = "{{" + itemTemplate.itemId + ".url}}";
418
+ const replacer = new RegExp(resourcePath, "g");
419
+ // Templatize the paths in the files that reference the source item id
420
+ rootJsonResources.forEach(rootFileResource => {
421
+ synchronizePromises.push(new Promise(resolve => {
422
+ // Read the file
423
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
424
+ (0, solution_common_1.blobToJson)(rootFileResource.file)
425
+ .then(fileJson => {
426
+ // Templatize by turning JSON into string, replacing paths with template, and re-JSONing
427
+ const updatedFileJson = JSON.parse(JSON.stringify(fileJson)
428
+ .replace(replacer, templatizedResourcePath));
429
+ // Write the changes back into the file
430
+ rootFileResource.file = (0, solution_common_1.jsonToFile)(updatedFileJson, rootFileResource.filename);
431
+ resolve(null);
432
+ });
433
+ }));
434
+ });
435
+ }
436
+ return Promise.all(synchronizePromises);
437
+ }
438
+ exports._templatizeResources = _templatizeResources;
436
439
  //# sourceMappingURL=createItemTemplate.js.map