@esri/solution-deployer 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.
Files changed (41) hide show
  1. package/dist/cjs/deploySolutionFromTemplate.d.ts +48 -48
  2. package/dist/cjs/deploySolutionFromTemplate.js +331 -331
  3. package/dist/cjs/deploySolutionFromTemplate.js.map +1 -1
  4. package/dist/cjs/deploySolutionItems.d.ts +224 -224
  5. package/dist/cjs/deploySolutionItems.js +853 -849
  6. package/dist/cjs/deploySolutionItems.js.map +1 -1
  7. package/dist/cjs/deployer.d.ts +34 -34
  8. package/dist/cjs/deployer.js +101 -101
  9. package/dist/cjs/deployerUtils.d.ts +47 -47
  10. package/dist/cjs/deployerUtils.js +123 -123
  11. package/dist/cjs/helpers/post-process.d.ts +29 -29
  12. package/dist/cjs/helpers/post-process.js +61 -61
  13. package/dist/cjs/helpers/share-templates-to-groups.d.ts +24 -24
  14. package/dist/cjs/helpers/share-templates-to-groups.js +64 -64
  15. package/dist/cjs/helpers/sortTemplates.d.ts +23 -23
  16. package/dist/cjs/helpers/sortTemplates.js +14 -14
  17. package/dist/cjs/index.d.ts +24 -24
  18. package/dist/cjs/index.js +27 -27
  19. package/dist/cjs/module-map.d.ts +23 -23
  20. package/dist/cjs/module-map.js +195 -195
  21. package/dist/esm/deploySolutionFromTemplate.d.ts +48 -48
  22. package/dist/esm/deploySolutionFromTemplate.js +317 -317
  23. package/dist/esm/deploySolutionFromTemplate.js.map +1 -1
  24. package/dist/esm/deploySolutionItems.d.ts +224 -224
  25. package/dist/esm/deploySolutionItems.js +830 -826
  26. package/dist/esm/deploySolutionItems.js.map +1 -1
  27. package/dist/esm/deployer.d.ts +34 -34
  28. package/dist/esm/deployer.js +96 -96
  29. package/dist/esm/deployerUtils.d.ts +47 -47
  30. package/dist/esm/deployerUtils.js +115 -115
  31. package/dist/esm/helpers/post-process.d.ts +29 -29
  32. package/dist/esm/helpers/post-process.js +57 -57
  33. package/dist/esm/helpers/share-templates-to-groups.d.ts +24 -24
  34. package/dist/esm/helpers/share-templates-to-groups.js +60 -60
  35. package/dist/esm/helpers/sortTemplates.d.ts +23 -23
  36. package/dist/esm/helpers/sortTemplates.js +10 -10
  37. package/dist/esm/index.d.ts +24 -24
  38. package/dist/esm/index.js +24 -24
  39. package/dist/esm/module-map.d.ts +23 -23
  40. package/dist/esm/module-map.js +191 -191
  41. package/package.json +12 -12
@@ -1,318 +1,318 @@
1
- /** @license
2
- * Copyright 2018 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
- import * as deployItems from "./deploySolutionItems";
18
- import { getWithDefault } from "@esri/hub-common";
19
- import * as portal from "@esri/arcgis-rest-portal";
20
- import { postProcess } from "./helpers/post-process";
21
- import { sortTemplates } from "./helpers/sortTemplates";
22
- import { setCreateProp } from "@esri/solution-common";
23
- // NOTE: Moved to separate file to allow stubbing in main deploySolution tests
24
- export function deploySolutionFromTemplate(templateSolutionId, solutionTemplateBase, solutionTemplateData, authentication, options) {
25
- options.storageVersion = common.extractSolutionVersion(solutionTemplateData);
26
- return new Promise((resolve, reject) => {
27
- // It is possible to provide a separate authentication for the source
28
- const storageAuthentication = options.storageAuthentication
29
- ? options.storageAuthentication
30
- : authentication;
31
- // Replacement dictionary and high-level deployment ids for cleanup
32
- // TODO: Extract all templateDictionary prep into a separate function
33
- const templateDictionary = options.templateDictionary ?? {};
34
- let deployedFolderId;
35
- let deployedSolutionId;
36
- _applySourceToDeployOptions(options, solutionTemplateBase, templateDictionary, authentication);
37
- if (options.additionalTypeKeywords) {
38
- solutionTemplateBase.typeKeywords = [].concat(solutionTemplateBase.typeKeywords, options.additionalTypeKeywords);
39
- }
40
- // Get the thumbnail file
41
- let thumbFilename = "thumbnail";
42
- let thumbDef = Promise.resolve(null);
43
- if (!options.thumbnail && options.thumbnailurl) {
44
- // Figure out the thumbnail's filename
45
- thumbFilename =
46
- common.getFilenameFromUrl(options.thumbnailurl) || thumbFilename;
47
- const thumbnailurl = common.appendQueryParam(options.thumbnailurl, "w=400");
48
- delete options.thumbnailurl;
49
- // Fetch the thumbnail
50
- thumbDef = common.getBlobAsFile(thumbnailurl, thumbFilename, storageAuthentication, [400]);
51
- }
52
- _replaceParamVariables(solutionTemplateData, templateDictionary);
53
- // Get information about deployment environment
54
- Promise.all([
55
- common.getPortal("", authentication),
56
- common.getUser(authentication),
57
- common.getFoldersAndGroups(authentication),
58
- thumbDef
59
- ])
60
- .then(responses => {
61
- const [portalResponse, userResponse, foldersAndGroupsResponse, thumbnailFile] = responses;
62
- if (!options.thumbnail && thumbnailFile) {
63
- options.thumbnail = thumbnailFile;
64
- }
65
- // update template items with source-itemId type keyword
66
- solutionTemplateData.templates = _addSourceId(solutionTemplateData.templates);
67
- templateDictionary.isPortal = portalResponse.isPortal;
68
- templateDictionary.organization = Object.assign(templateDictionary.organization || {}, portalResponse);
69
- // TODO: Add more computed properties here
70
- // portal: portalResponse
71
- // orgextent as bbox for assignment onto items
72
- // more info in #266 https://github.com/Esri/solution.js/issues/266
73
- templateDictionary.portalBaseUrl = _getPortalBaseUrl(portalResponse, authentication);
74
- templateDictionary.user = userResponse;
75
- templateDictionary.user.folders = foldersAndGroupsResponse.folders;
76
- templateDictionary.user.groups = foldersAndGroupsResponse.groups.filter((group) => group.owner === templateDictionary.user.username);
77
- // if we have tracking views and the user is not admin or the org doesn't support tracking an error is thrown
78
- common.setLocationTrackingEnabled(portalResponse, userResponse, templateDictionary, solutionTemplateData.templates);
79
- const trackingOwnerPromise = common.getTackingServiceOwner(templateDictionary, authentication);
80
- // Create a folder to hold the deployed solution. We use the solution name, appending a sequential
81
- // suffix if the folder exists, e.g.,
82
- // * Manage Right of Way Activities
83
- // * Manage Right of Way Activities 1
84
- // * Manage Right of Way Activities 2
85
- const folderPromise = common.createUniqueFolder(solutionTemplateBase.title, templateDictionary, authentication);
86
- // Apply the portal extents to the solution
87
- const portalExtent = portalResponse.defaultExtent;
88
- const extentsPromise = common.convertExtentWithFallback(portalExtent, undefined, { wkid: 4326 }, portalResponse.helperServices.geometry.url, authentication);
89
- // Await completion of async actions: folder creation & extents conversion
90
- return Promise.all([folderPromise, extentsPromise, trackingOwnerPromise]);
91
- })
92
- .then(responses => {
93
- const [folderResponse, wgs84Extent, trackingOwnerResponse] = responses;
94
- deployedFolderId = folderResponse.folder.id;
95
- templateDictionary.folderId = deployedFolderId;
96
- templateDictionary.solutionItemExtent =
97
- wgs84Extent.xmin +
98
- "," +
99
- wgs84Extent.ymin +
100
- "," +
101
- wgs84Extent.xmax +
102
- "," +
103
- wgs84Extent.ymax;
104
- // Hub Solutions depend on organization defaultExtentBBox as a nested array not a string
105
- templateDictionary.organization.defaultExtentBBox = [
106
- [wgs84Extent.xmin, wgs84Extent.ymin],
107
- [wgs84Extent.xmax, wgs84Extent.ymax]
108
- ];
109
- // update templateDictionary to indicate if the user owns the tracking service
110
- // this will affect how we handle group sharing
111
- /* istanbul ignore else */
112
- if (templateDictionary.locationTrackingEnabled) {
113
- setCreateProp(templateDictionary, "locationTracking.userIsOwner", trackingOwnerResponse);
114
- }
115
- // Create a deployed Solution item
116
- const createSolutionItemBase = {
117
- ...common.sanitizeJSON(solutionTemplateBase),
118
- type: "Solution",
119
- typeKeywords: ["Solution"]
120
- };
121
- if (options.additionalTypeKeywords) {
122
- createSolutionItemBase.typeKeywords = ["Solution"].concat(options.additionalTypeKeywords);
123
- }
124
- // Create deployed solution item
125
- createSolutionItemBase.thumbnail = options.thumbnail;
126
- return common.createItemWithData(createSolutionItemBase, {}, authentication, deployedFolderId);
127
- })
128
- .then(createSolutionResponse => {
129
- deployedSolutionId = createSolutionResponse.id;
130
- // Protect the solution item
131
- const protectOptions = {
132
- id: deployedSolutionId,
133
- authentication
134
- };
135
- return portal.protectItem(protectOptions);
136
- })
137
- .then(() => {
138
- // TODO: Attach the whole solution model so we can
139
- // have stuff like `{{solution.item.title}}
140
- templateDictionary.solutionItemId = deployedSolutionId;
141
- solutionTemplateBase.id = deployedSolutionId;
142
- solutionTemplateBase.tryitUrl = _checkedReplaceAll(solutionTemplateBase.tryitUrl, templateSolutionId, deployedSolutionId);
143
- solutionTemplateBase.url = _checkedReplaceAll(solutionTemplateBase.url, templateSolutionId, deployedSolutionId);
144
- // Handle the contained item templates
145
- return deployItems.deploySolutionItems(storageAuthentication.portal, templateSolutionId, solutionTemplateData.templates, storageAuthentication, templateDictionary, deployedSolutionId, authentication, options);
146
- })
147
- .then((clonedSolutionsResponse) => {
148
- solutionTemplateData.templates = solutionTemplateData.templates.map((itemTemplate) => {
149
- // Update ids present in template dictionary
150
- itemTemplate.itemId = common.getProp(templateDictionary, `${itemTemplate.itemId}.itemId`);
151
- // Update the dependencies hash to point to the new item ids
152
- itemTemplate.dependencies = itemTemplate.dependencies.map((id) => getWithDefault(templateDictionary, `${id}.itemId`, id));
153
- return itemTemplate;
154
- });
155
- // Sort the templates into build order, which is provided by clonedSolutionsResponse
156
- sortTemplates(solutionTemplateData.templates, clonedSolutionsResponse.map(response => response.id));
157
- // Wrap up with post-processing, in which we deal with groups and cycle remnants
158
- return postProcess(deployedSolutionId, solutionTemplateData.templates, clonedSolutionsResponse, authentication, templateDictionary);
159
- })
160
- .then(() => {
161
- // Update solution item using internal representation & and the updated data JSON
162
- solutionTemplateBase.typeKeywords = [].concat(solutionTemplateBase.typeKeywords, ["Deployed"]);
163
- const iTemplateKeyword = solutionTemplateBase.typeKeywords.indexOf("Template");
164
- /* istanbul ignore else */
165
- if (iTemplateKeyword >= 0) {
166
- solutionTemplateBase.typeKeywords.splice(iTemplateKeyword, 1);
167
- }
168
- solutionTemplateData.templates = solutionTemplateData.templates.map((itemTemplate) => _purgeTemplateProperties(itemTemplate));
169
- solutionTemplateData.templates = _updateGroupReferences(solutionTemplateData.templates, templateDictionary);
170
- // Update solution items data using template dictionary, and then update the
171
- // itemId & dependencies in each item template
172
- solutionTemplateBase.data = common.replaceInTemplate(solutionTemplateData, templateDictionary);
173
- // Write any user defined params to the solution
174
- /* istanbul ignore else */
175
- if (templateDictionary.params) {
176
- solutionTemplateBase.data.params = templateDictionary.params;
177
- }
178
- return common.updateItem(solutionTemplateBase, authentication, deployedFolderId);
179
- })
180
- .then(() => resolve(solutionTemplateBase.id), reject);
181
- });
182
- }
183
- /**
184
- * Add source-id to items/groups typeKeywords
185
- *
186
- * @param template the array of solution data templates
187
- * @private
188
- */
189
- export function _addSourceId(templates) {
190
- return templates.map((template) => {
191
- /* istanbul ignore else */
192
- if (template.item) {
193
- const typeKeywords = template.item.typeKeywords || [];
194
- typeKeywords.push("source-" + template.itemId);
195
- template.item.typeKeywords = typeKeywords;
196
- }
197
- return template;
198
- });
199
- }
200
- /**
201
- * Update the deployOptions with the group properties
202
- *
203
- * @param deployOptions
204
- * @param sourceInfo
205
- * @param authentication
206
- * @param isGroup Boolean to indicate if the files are associated with a group or item
207
- * @private
208
- */
209
- export function _applySourceToDeployOptions(deployOptions, solutionTemplateBase, templateDictionary, authentication) {
210
- // Deploy a solution from the template's contents,
211
- // using the template's information as defaults for the deployed solution item
212
- ["title", "snippet", "description", "tags"].forEach(prop => {
213
- deployOptions[prop] = deployOptions[prop] ?? solutionTemplateBase[prop];
214
- if (deployOptions[prop]) {
215
- solutionTemplateBase[prop] = deployOptions[prop];
216
- // carry these options forward on the templateDict
217
- templateDictionary[prop] = deployOptions[prop];
218
- }
219
- });
220
- if (!deployOptions.thumbnailurl && solutionTemplateBase.thumbnail) {
221
- // Get the full path to the thumbnail
222
- deployOptions.thumbnailurl = common.generateSourceThumbnailUrl(authentication.portal, solutionTemplateBase.id, solutionTemplateBase.thumbnail);
223
- delete solutionTemplateBase.thumbnail;
224
- }
225
- return deployOptions;
226
- }
227
- //???
228
- export function _replaceParamVariables(solutionTemplateData, templateDictionary) {
229
- // a custom params object can be passed in with the options to deploy a solution
230
- // in most cases we can defer to the item type handlers to use these values
231
- // for variable replacement
232
- // for spatial reference specifically we need to replace up front so the default extent
233
- // logic can execute as expected
234
- solutionTemplateData.templates = solutionTemplateData.templates.map((template) => {
235
- // can't do this as it causes other values that don't exist in the dict yet to revert to defaults they may have defined
236
- // return common.replaceInTemplate(template, templateDictionary);
237
- /* istanbul ignore else */
238
- if (template.type === "Feature Service") {
239
- const paramsLookup = "params.";
240
- const wkidItemPath = "item.spatialReference.wkid";
241
- template = _updateProp(template, wkidItemPath, paramsLookup, templateDictionary);
242
- const wkidServicePath = "properties.service.spatialReference.wkid";
243
- template = _updateProp(template, wkidServicePath, paramsLookup, templateDictionary);
244
- }
245
- return template;
246
- });
247
- }
248
- //???
249
- export function _updateProp(template, path, lookup, templateDictionary) {
250
- const wkid = common.getProp(template, path);
251
- /* istanbul ignore else */
252
- if (wkid && typeof wkid === "string" && wkid.indexOf(lookup) > -1) {
253
- common.setProp(template, path, common.replaceInTemplate(wkid, templateDictionary));
254
- }
255
- return template;
256
- }
257
- //???
258
- export function _checkedReplaceAll(template, oldValue, newValue) {
259
- let newTemplate;
260
- if (template && template.indexOf(oldValue) > -1) {
261
- const re = new RegExp(oldValue, "g");
262
- newTemplate = template.replace(re, newValue);
263
- }
264
- else {
265
- newTemplate = template;
266
- }
267
- return newTemplate;
268
- }
269
- //???
270
- export function _getPortalBaseUrl(portalResponse, authentication) {
271
- // As of Spring 2020, only HTTPS (see
272
- // https://www.esri.com/arcgis-blog/products/product/administration/2019-arcgis-transport-security-improvements/)
273
- const scheme = "https"; // portalResponse.allSSL ? "https" : "http";
274
- const urlKey = common.getProp(portalResponse, "urlKey");
275
- const customBaseUrl = common.getProp(portalResponse, "customBaseUrl");
276
- const enterpriseBaseUrl = common.getProp(portalResponse, "portalHostname");
277
- return urlKey && customBaseUrl
278
- ? `${scheme}://${urlKey}.${customBaseUrl}`
279
- : enterpriseBaseUrl
280
- ? `${scheme}://${enterpriseBaseUrl}`
281
- : authentication.portal.replace("/sharing/rest", "");
282
- }
283
- //???
284
- export function _updateGroupReferences(itemTemplates, templateDictionary) {
285
- const groupIds = itemTemplates.reduce((result, t) => {
286
- if (t.type === "Group") {
287
- result.push(t.itemId);
288
- }
289
- return result;
290
- }, []);
291
- Object.keys(templateDictionary).forEach(k => {
292
- const newId = templateDictionary[k].itemId;
293
- if (groupIds.indexOf(newId) > -1) {
294
- itemTemplates.forEach(t => {
295
- t.groups = t.groups.map((id) => (id === k ? newId : id));
296
- });
297
- }
298
- });
299
- return itemTemplates;
300
- }
301
- //???
302
- export function _purgeTemplateProperties(itemTemplate) {
303
- const retainProps = ["itemId", "type", "dependencies", "groups"];
304
- const deleteProps = Object.keys(itemTemplate).filter(k => retainProps.indexOf(k) < 0);
305
- common.deleteProps(itemTemplate, deleteProps);
306
- return itemTemplate;
307
- }
308
- /**
309
- * Returns a match of a supplied id with the suffix ".itemId" in the template dictionary.
310
- *
311
- * @param id Id to look for
312
- * @param templateDictionary Hash mapping property names to replacement values
313
- * @returns Match in template dictionary or original id
314
- */
315
- export function _getNewItemId(id, templateDictionary) {
316
- return common.getProp(templateDictionary, id + ".itemId") ?? id;
317
- }
1
+ /** @license
2
+ * Copyright 2018 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
+ import * as deployItems from "./deploySolutionItems";
18
+ import { getWithDefault } from "@esri/hub-common";
19
+ import * as portal from "@esri/arcgis-rest-portal";
20
+ import { postProcess } from "./helpers/post-process";
21
+ import { sortTemplates } from "./helpers/sortTemplates";
22
+ import { setCreateProp } from "@esri/solution-common";
23
+ // NOTE: Moved to separate file to allow stubbing in main deploySolution tests
24
+ export function deploySolutionFromTemplate(templateSolutionId, solutionTemplateBase, solutionTemplateData, authentication, options) {
25
+ options.storageVersion = common.extractSolutionVersion(solutionTemplateData);
26
+ return new Promise((resolve, reject) => {
27
+ // It is possible to provide a separate authentication for the source
28
+ const storageAuthentication = options.storageAuthentication
29
+ ? options.storageAuthentication
30
+ : authentication;
31
+ // Replacement dictionary and high-level deployment ids for cleanup
32
+ // TODO: Extract all templateDictionary prep into a separate function
33
+ const templateDictionary = options.templateDictionary ?? {};
34
+ let deployedFolderId;
35
+ let deployedSolutionId;
36
+ _applySourceToDeployOptions(options, solutionTemplateBase, templateDictionary, authentication);
37
+ if (options.additionalTypeKeywords) {
38
+ solutionTemplateBase.typeKeywords = [].concat(solutionTemplateBase.typeKeywords, options.additionalTypeKeywords);
39
+ }
40
+ // Get the thumbnail file
41
+ let thumbFilename = "thumbnail";
42
+ let thumbDef = Promise.resolve(null);
43
+ if (!options.thumbnail && options.thumbnailurl) {
44
+ // Figure out the thumbnail's filename
45
+ thumbFilename =
46
+ common.getFilenameFromUrl(options.thumbnailurl) || thumbFilename;
47
+ const thumbnailurl = common.appendQueryParam(options.thumbnailurl, "w=400");
48
+ delete options.thumbnailurl;
49
+ // Fetch the thumbnail
50
+ thumbDef = common.getBlobAsFile(thumbnailurl, thumbFilename, storageAuthentication, [400]);
51
+ }
52
+ _replaceParamVariables(solutionTemplateData, templateDictionary);
53
+ // Get information about deployment environment
54
+ Promise.all([
55
+ common.getPortal("", authentication),
56
+ common.getUser(authentication),
57
+ common.getFoldersAndGroups(authentication),
58
+ thumbDef
59
+ ])
60
+ .then(responses => {
61
+ const [portalResponse, userResponse, foldersAndGroupsResponse, thumbnailFile] = responses;
62
+ if (!options.thumbnail && thumbnailFile) {
63
+ options.thumbnail = thumbnailFile;
64
+ }
65
+ // update template items with source-itemId type keyword
66
+ solutionTemplateData.templates = _addSourceId(solutionTemplateData.templates);
67
+ templateDictionary.isPortal = portalResponse.isPortal;
68
+ templateDictionary.organization = Object.assign(templateDictionary.organization || {}, portalResponse);
69
+ // TODO: Add more computed properties here
70
+ // portal: portalResponse
71
+ // orgextent as bbox for assignment onto items
72
+ // more info in #266 https://github.com/Esri/solution.js/issues/266
73
+ templateDictionary.portalBaseUrl = _getPortalBaseUrl(portalResponse, authentication);
74
+ templateDictionary.user = userResponse;
75
+ templateDictionary.user.folders = foldersAndGroupsResponse.folders;
76
+ templateDictionary.user.groups = foldersAndGroupsResponse.groups.filter((group) => group.owner === templateDictionary.user.username);
77
+ // if we have tracking views and the user is not admin or the org doesn't support tracking an error is thrown
78
+ common.setLocationTrackingEnabled(portalResponse, userResponse, templateDictionary, solutionTemplateData.templates);
79
+ const trackingOwnerPromise = common.getTackingServiceOwner(templateDictionary, authentication);
80
+ // Create a folder to hold the deployed solution. We use the solution name, appending a sequential
81
+ // suffix if the folder exists, e.g.,
82
+ // * Manage Right of Way Activities
83
+ // * Manage Right of Way Activities 1
84
+ // * Manage Right of Way Activities 2
85
+ const folderPromise = common.createUniqueFolder(solutionTemplateBase.title, templateDictionary, authentication);
86
+ // Apply the portal extents to the solution
87
+ const portalExtent = portalResponse.defaultExtent;
88
+ const extentsPromise = common.convertExtentWithFallback(portalExtent, undefined, { wkid: 4326 }, portalResponse.helperServices.geometry.url, authentication);
89
+ // Await completion of async actions: folder creation & extents conversion
90
+ return Promise.all([folderPromise, extentsPromise, trackingOwnerPromise]);
91
+ })
92
+ .then(responses => {
93
+ const [folderResponse, wgs84Extent, trackingOwnerResponse] = responses;
94
+ deployedFolderId = folderResponse.folder.id;
95
+ templateDictionary.folderId = deployedFolderId;
96
+ templateDictionary.solutionItemExtent =
97
+ wgs84Extent.xmin +
98
+ "," +
99
+ wgs84Extent.ymin +
100
+ "," +
101
+ wgs84Extent.xmax +
102
+ "," +
103
+ wgs84Extent.ymax;
104
+ // Hub Solutions depend on organization defaultExtentBBox as a nested array not a string
105
+ templateDictionary.organization.defaultExtentBBox = [
106
+ [wgs84Extent.xmin, wgs84Extent.ymin],
107
+ [wgs84Extent.xmax, wgs84Extent.ymax]
108
+ ];
109
+ // update templateDictionary to indicate if the user owns the tracking service
110
+ // this will affect how we handle group sharing
111
+ /* istanbul ignore else */
112
+ if (templateDictionary.locationTrackingEnabled) {
113
+ setCreateProp(templateDictionary, "locationTracking.userIsOwner", trackingOwnerResponse);
114
+ }
115
+ // Create a deployed Solution item
116
+ const createSolutionItemBase = {
117
+ ...common.sanitizeJSON(solutionTemplateBase),
118
+ type: "Solution",
119
+ typeKeywords: ["Solution"]
120
+ };
121
+ if (options.additionalTypeKeywords) {
122
+ createSolutionItemBase.typeKeywords = ["Solution"].concat(options.additionalTypeKeywords);
123
+ }
124
+ // Create deployed solution item
125
+ createSolutionItemBase.thumbnail = options.thumbnail;
126
+ return common.createItemWithData(createSolutionItemBase, {}, authentication, deployedFolderId);
127
+ })
128
+ .then(createSolutionResponse => {
129
+ deployedSolutionId = createSolutionResponse.id;
130
+ // Protect the solution item
131
+ const protectOptions = {
132
+ id: deployedSolutionId,
133
+ authentication
134
+ };
135
+ return portal.protectItem(protectOptions);
136
+ })
137
+ .then(() => {
138
+ // TODO: Attach the whole solution model so we can
139
+ // have stuff like `{{solution.item.title}}
140
+ templateDictionary.solutionItemId = deployedSolutionId;
141
+ solutionTemplateBase.id = deployedSolutionId;
142
+ solutionTemplateBase.tryitUrl = _checkedReplaceAll(solutionTemplateBase.tryitUrl, templateSolutionId, deployedSolutionId);
143
+ solutionTemplateBase.url = _checkedReplaceAll(solutionTemplateBase.url, templateSolutionId, deployedSolutionId);
144
+ // Handle the contained item templates
145
+ return deployItems.deploySolutionItems(storageAuthentication.portal, templateSolutionId, solutionTemplateData.templates, storageAuthentication, templateDictionary, deployedSolutionId, authentication, options);
146
+ })
147
+ .then((clonedSolutionsResponse) => {
148
+ solutionTemplateData.templates = solutionTemplateData.templates.map((itemTemplate) => {
149
+ // Update ids present in template dictionary
150
+ itemTemplate.itemId = common.getProp(templateDictionary, `${itemTemplate.itemId}.itemId`);
151
+ // Update the dependencies hash to point to the new item ids
152
+ itemTemplate.dependencies = itemTemplate.dependencies.map((id) => getWithDefault(templateDictionary, `${id}.itemId`, id));
153
+ return itemTemplate;
154
+ });
155
+ // Sort the templates into build order, which is provided by clonedSolutionsResponse
156
+ sortTemplates(solutionTemplateData.templates, clonedSolutionsResponse.map(response => response.id));
157
+ // Wrap up with post-processing, in which we deal with groups and cycle remnants
158
+ return postProcess(deployedSolutionId, solutionTemplateData.templates, clonedSolutionsResponse, authentication, templateDictionary);
159
+ })
160
+ .then(() => {
161
+ // Update solution item using internal representation & and the updated data JSON
162
+ solutionTemplateBase.typeKeywords = [].concat(solutionTemplateBase.typeKeywords, ["Deployed"]);
163
+ const iTemplateKeyword = solutionTemplateBase.typeKeywords.indexOf("Template");
164
+ /* istanbul ignore else */
165
+ if (iTemplateKeyword >= 0) {
166
+ solutionTemplateBase.typeKeywords.splice(iTemplateKeyword, 1);
167
+ }
168
+ solutionTemplateData.templates = solutionTemplateData.templates.map((itemTemplate) => _purgeTemplateProperties(itemTemplate));
169
+ solutionTemplateData.templates = _updateGroupReferences(solutionTemplateData.templates, templateDictionary);
170
+ // Update solution items data using template dictionary, and then update the
171
+ // itemId & dependencies in each item template
172
+ solutionTemplateBase.data = common.replaceInTemplate(solutionTemplateData, templateDictionary);
173
+ // Write any user defined params to the solution
174
+ /* istanbul ignore else */
175
+ if (templateDictionary.params) {
176
+ solutionTemplateBase.data.params = templateDictionary.params;
177
+ }
178
+ return common.updateItem(solutionTemplateBase, authentication, deployedFolderId);
179
+ })
180
+ .then(() => resolve(solutionTemplateBase.id), reject);
181
+ });
182
+ }
183
+ /**
184
+ * Add source-id to items/groups typeKeywords
185
+ *
186
+ * @param template the array of solution data templates
187
+ * @private
188
+ */
189
+ export function _addSourceId(templates) {
190
+ return templates.map((template) => {
191
+ /* istanbul ignore else */
192
+ if (template.item) {
193
+ const typeKeywords = template.item.typeKeywords || [];
194
+ typeKeywords.push("source-" + template.itemId);
195
+ template.item.typeKeywords = typeKeywords;
196
+ }
197
+ return template;
198
+ });
199
+ }
200
+ /**
201
+ * Update the deployOptions with the group properties
202
+ *
203
+ * @param deployOptions
204
+ * @param sourceInfo
205
+ * @param authentication
206
+ * @param isGroup Boolean to indicate if the files are associated with a group or item
207
+ * @private
208
+ */
209
+ export function _applySourceToDeployOptions(deployOptions, solutionTemplateBase, templateDictionary, authentication) {
210
+ // Deploy a solution from the template's contents,
211
+ // using the template's information as defaults for the deployed solution item
212
+ ["title", "snippet", "description", "tags"].forEach(prop => {
213
+ deployOptions[prop] = deployOptions[prop] ?? solutionTemplateBase[prop];
214
+ if (deployOptions[prop]) {
215
+ solutionTemplateBase[prop] = deployOptions[prop];
216
+ // carry these options forward on the templateDict
217
+ templateDictionary[prop] = deployOptions[prop];
218
+ }
219
+ });
220
+ if (!deployOptions.thumbnailurl && solutionTemplateBase.thumbnail) {
221
+ // Get the full path to the thumbnail
222
+ deployOptions.thumbnailurl = common.generateSourceThumbnailUrl(authentication.portal, solutionTemplateBase.id, solutionTemplateBase.thumbnail);
223
+ delete solutionTemplateBase.thumbnail;
224
+ }
225
+ return deployOptions;
226
+ }
227
+ //TODO: function doc
228
+ export function _replaceParamVariables(solutionTemplateData, templateDictionary) {
229
+ // a custom params object can be passed in with the options to deploy a solution
230
+ // in most cases we can defer to the item type handlers to use these values
231
+ // for variable replacement
232
+ // for spatial reference specifically we need to replace up front so the default extent
233
+ // logic can execute as expected
234
+ solutionTemplateData.templates = solutionTemplateData.templates.map((template) => {
235
+ // can't do this as it causes other values that don't exist in the dict yet to revert to defaults they may have defined
236
+ // return common.replaceInTemplate(template, templateDictionary);
237
+ /* istanbul ignore else */
238
+ if (template.type === "Feature Service") {
239
+ const paramsLookup = "params.";
240
+ const wkidItemPath = "item.spatialReference.wkid";
241
+ template = _updateProp(template, wkidItemPath, paramsLookup, templateDictionary);
242
+ const wkidServicePath = "properties.service.spatialReference.wkid";
243
+ template = _updateProp(template, wkidServicePath, paramsLookup, templateDictionary);
244
+ }
245
+ return template;
246
+ });
247
+ }
248
+ //TODO: function doc
249
+ export function _updateProp(template, path, lookup, templateDictionary) {
250
+ const wkid = common.getProp(template, path);
251
+ /* istanbul ignore else */
252
+ if (wkid && typeof wkid === "string" && wkid.indexOf(lookup) > -1) {
253
+ common.setProp(template, path, common.replaceInTemplate(wkid, templateDictionary));
254
+ }
255
+ return template;
256
+ }
257
+ //TODO: function doc
258
+ export function _checkedReplaceAll(template, oldValue, newValue) {
259
+ let newTemplate;
260
+ if (template && template.indexOf(oldValue) > -1) {
261
+ const re = new RegExp(oldValue, "g");
262
+ newTemplate = template.replace(re, newValue);
263
+ }
264
+ else {
265
+ newTemplate = template;
266
+ }
267
+ return newTemplate;
268
+ }
269
+ //TODO: function doc
270
+ export function _getPortalBaseUrl(portalResponse, authentication) {
271
+ // As of Spring 2020, only HTTPS (see
272
+ // https://www.esri.com/arcgis-blog/products/product/administration/2019-arcgis-transport-security-improvements/)
273
+ const scheme = "https"; // portalResponse.allSSL ? "https" : "http";
274
+ const urlKey = common.getProp(portalResponse, "urlKey");
275
+ const customBaseUrl = common.getProp(portalResponse, "customBaseUrl");
276
+ const enterpriseBaseUrl = common.getProp(portalResponse, "portalHostname");
277
+ return urlKey && customBaseUrl
278
+ ? `${scheme}://${urlKey}.${customBaseUrl}`
279
+ : enterpriseBaseUrl
280
+ ? `${scheme}://${enterpriseBaseUrl}`
281
+ : authentication.portal.replace("/sharing/rest", "");
282
+ }
283
+ //TODO: function doc
284
+ export function _updateGroupReferences(itemTemplates, templateDictionary) {
285
+ const groupIds = itemTemplates.reduce((result, t) => {
286
+ if (t.type === "Group") {
287
+ result.push(t.itemId);
288
+ }
289
+ return result;
290
+ }, []);
291
+ Object.keys(templateDictionary).forEach(k => {
292
+ const newId = templateDictionary[k].itemId;
293
+ if (groupIds.indexOf(newId) > -1) {
294
+ itemTemplates.forEach(t => {
295
+ t.groups = t.groups.map((id) => (id === k ? newId : id));
296
+ });
297
+ }
298
+ });
299
+ return itemTemplates;
300
+ }
301
+ //TODO: function doc
302
+ export function _purgeTemplateProperties(itemTemplate) {
303
+ const retainProps = ["itemId", "type", "dependencies", "groups"];
304
+ const deleteProps = Object.keys(itemTemplate).filter(k => retainProps.indexOf(k) < 0);
305
+ common.deleteProps(itemTemplate, deleteProps);
306
+ return itemTemplate;
307
+ }
308
+ /**
309
+ * Returns a match of a supplied id with the suffix ".itemId" in the template dictionary.
310
+ *
311
+ * @param id Id to look for
312
+ * @param templateDictionary Hash mapping property names to replacement values
313
+ * @returns Match in template dictionary or original id
314
+ */
315
+ export function _getNewItemId(id, templateDictionary) {
316
+ return common.getProp(templateDictionary, id + ".itemId") ?? id;
317
+ }
318
318
  //# sourceMappingURL=deploySolutionFromTemplate.js.map