@esri/solutions-components 0.5.3 → 0.5.4

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 (116) hide show
  1. package/dist/cjs/calcite-input-text_5.cjs.entry.js +36 -36
  2. package/dist/cjs/calcite-shell-panel_14.cjs.entry.js +1 -1
  3. package/dist/cjs/solution-configuration.cjs.entry.js +1 -1
  4. package/dist/cjs/solution-contents_3.cjs.entry.js +1 -1
  5. package/dist/cjs/{solution-store-70002b57.js → solution-store-ca4639d5.js} +3 -3
  6. package/dist/collection/components/add-record-modal/test/add-record-modal.e2e.js +24 -0
  7. package/dist/collection/components/add-record-modal/test/add-record-modal.spec.js +32 -0
  8. package/dist/collection/components/buffer-tools/test/buffer-tools.e2e.js +24 -0
  9. package/dist/collection/components/buffer-tools/test/buffer-tools.spec.js +157 -0
  10. package/dist/collection/components/card-manager/test/card-manager.e2e.js +24 -0
  11. package/dist/collection/components/card-manager/test/card-manager.spec.js +32 -0
  12. package/dist/collection/components/comment-card/test/comment-card.e2e.js +24 -0
  13. package/dist/collection/components/comment-card/test/comment-card.spec.js +32 -0
  14. package/dist/collection/components/crowdsource-manager/test/crowdsource-manager.e2e.js +24 -0
  15. package/dist/collection/components/crowdsource-manager/test/crowdsource-manager.spec.js +32 -0
  16. package/dist/collection/components/crowdsource-reporter/test/crowdsource-reporter.e2e.js +24 -0
  17. package/dist/collection/components/crowdsource-reporter/test/crowdsource-reporter.spec.js +32 -0
  18. package/dist/collection/components/deduct-calculator/test/deduct-calculator.e2e.js +24 -0
  19. package/dist/collection/components/deduct-calculator/test/deduct-calculator.spec.js +32 -0
  20. package/dist/collection/components/edit-record-modal/test/edit-record-modal.e2e.js +24 -0
  21. package/dist/collection/components/edit-record-modal/test/edit-record-modal.spec.js +32 -0
  22. package/dist/collection/components/info-card/test/info-card.e2e.js +24 -0
  23. package/dist/collection/components/info-card/test/info-card.spec.js +32 -0
  24. package/dist/collection/components/json-editor/test/json-editor.e2e.js +31 -0
  25. package/dist/collection/components/json-editor/test/json-editor.spec.js +60 -0
  26. package/dist/collection/components/layer-table/test/layer-table.e2e.js +24 -0
  27. package/dist/collection/components/layer-table/test/layer-table.spec.js +32 -0
  28. package/dist/collection/components/list-item/test/list-item.e2e.js +24 -0
  29. package/dist/collection/components/list-item/test/list-item.spec.js +32 -0
  30. package/dist/collection/components/map-card/test/map-card.e2e.js +24 -0
  31. package/dist/collection/components/map-card/test/map-card.spec.js +32 -0
  32. package/dist/collection/components/map-draw-tools/test/map-draw-tools.e2e.js +24 -0
  33. package/dist/collection/components/map-draw-tools/test/map-draw-tools.spec.js +32 -0
  34. package/dist/collection/components/map-layer-picker/test/map-layer-picker.e2e.js +24 -0
  35. package/dist/collection/components/map-layer-picker/test/map-layer-picker.spec.js +109 -0
  36. package/dist/collection/components/map-search/test/map-search.e2e.js +24 -0
  37. package/dist/collection/components/map-search/test/map-search.spec.js +53 -0
  38. package/dist/collection/components/map-select-tools/map-select-tools.js +36 -36
  39. package/dist/collection/components/map-select-tools/test/map-select-tools.e2e.js +24 -0
  40. package/dist/collection/components/map-select-tools/test/map-select-tools.spec.js +349 -0
  41. package/dist/collection/components/media-card/test/media-card.e2e.js +24 -0
  42. package/dist/collection/components/media-card/test/media-card.spec.js +32 -0
  43. package/dist/collection/components/pci-calculator/test/pci-calculator.e2e.js +24 -0
  44. package/dist/collection/components/pci-calculator/test/pci-calculator.spec.js +32 -0
  45. package/dist/collection/components/pdf-download/test/pdf-download.e2e.js +71 -0
  46. package/dist/collection/components/pdf-download/test/pdf-download.spec.js +104 -0
  47. package/dist/collection/components/public-notification/test/public-notification.e2e.js +95 -0
  48. package/dist/collection/components/public-notification/test/public-notification.spec.js +149 -0
  49. package/dist/collection/components/refine-selection/refine-selection.css +85 -85
  50. package/dist/collection/components/solution-configuration/test/solution-configuration.e2e.js +31 -0
  51. package/dist/collection/components/solution-configuration/test/solution-configuration.spec.js +114 -0
  52. package/dist/collection/components/solution-contents/test/solution-contents.e2e.js +89 -0
  53. package/dist/collection/components/solution-contents/test/solution-contents.spec.js +138 -0
  54. package/dist/collection/components/solution-item/test/solution-item.e2e.js +31 -0
  55. package/dist/collection/components/solution-item/test/solution-item.spec.js +72 -0
  56. package/dist/collection/components/solution-item-details/test/solution-item-details.e2e.js +31 -0
  57. package/dist/collection/components/solution-item-details/test/solution-item-details.spec.js +137 -0
  58. package/dist/collection/components/solution-item-icon/test/solution-item-icon.e2e.js +24 -0
  59. package/dist/collection/components/solution-item-icon/test/solution-item-icon.spec.js +34 -0
  60. package/dist/collection/components/solution-item-sharing/test/solution-item-sharing.e2e.js +31 -0
  61. package/dist/collection/components/solution-item-sharing/test/solution-item-sharing.spec.js +49 -0
  62. package/dist/collection/components/solution-organization-variables/test/solution-organization-variables.e2e.js +31 -0
  63. package/dist/collection/components/solution-organization-variables/test/solution-organization-variables.spec.js +60 -0
  64. package/dist/collection/components/solution-resource-item/test/solution-resource-item.e2e.js +31 -0
  65. package/dist/collection/components/solution-resource-item/test/solution-resource-item.spec.js +50 -0
  66. package/dist/collection/components/solution-spatial-ref/test/solution-spatial-ref.e2e.js +31 -0
  67. package/dist/collection/components/solution-spatial-ref/test/solution-spatial-ref.spec.js +170 -0
  68. package/dist/collection/components/solution-template-data/test/solution-template-data.e2e.js +31 -0
  69. package/dist/collection/components/solution-template-data/test/solution-template-data.spec.js +55 -0
  70. package/dist/collection/components/solution-variables/test/solution-variables.e2e.js +31 -0
  71. package/dist/collection/components/solution-variables/test/solution-variables.spec.js +126 -0
  72. package/dist/collection/utils/publicNotificationUtils.js +45 -0
  73. package/dist/collection/utils/templates.e2e.js +25 -0
  74. package/dist/collection/utils/test/csvUtils.spec.js +46 -0
  75. package/dist/collection/utils/test/downloadUtils.spec.js +102 -0
  76. package/dist/collection/utils/test/pciUtils.spec.js +297 -0
  77. package/dist/collection/utils/test/solution-store.spec.js +439 -0
  78. package/dist/components/map-select-tools2.js +36 -36
  79. package/dist/components/solution-store.js +3 -3
  80. package/dist/esm/calcite-input-text_5.entry.js +36 -36
  81. package/dist/esm/calcite-shell-panel_14.entry.js +1 -1
  82. package/dist/esm/solution-configuration.entry.js +1 -1
  83. package/dist/esm/solution-contents_3.entry.js +1 -1
  84. package/dist/esm/{solution-store-5d068b07.js → solution-store-70f874f8.js} +3 -3
  85. package/dist/solutions-components/{p-41802f6b.entry.js → p-16dfb254.entry.js} +1 -1
  86. package/dist/solutions-components/{p-4769a2a5.entry.js → p-5ed755a2.entry.js} +1 -1
  87. package/dist/solutions-components/{p-826a814d.js → p-78719506.js} +2 -2
  88. package/dist/solutions-components/{p-9f620303.entry.js → p-b4b19fd3.entry.js} +1 -1
  89. package/dist/solutions-components/{p-24fe6e1c.entry.js → p-d5d5942d.entry.js} +1 -1
  90. package/dist/solutions-components/solutions-components.esm.js +1 -1
  91. package/dist/solutions-components/utils/common.js +291 -0
  92. package/dist/solutions-components/utils/csvDownload.js +36 -0
  93. package/dist/solutions-components/utils/csvUtils.js +32 -0
  94. package/dist/solutions-components/utils/downloadUtils.js +386 -0
  95. package/dist/solutions-components/utils/interfaces.js +56 -0
  96. package/dist/solutions-components/utils/languageUtil.js +85 -0
  97. package/dist/solutions-components/utils/loadModules.js +20 -0
  98. package/dist/solutions-components/utils/locale.js +56 -0
  99. package/dist/solutions-components/utils/mapViewUtils.js +140 -0
  100. package/dist/solutions-components/utils/pciUtils.js +837 -0
  101. package/dist/solutions-components/utils/pdfUtils.js +62 -0
  102. package/dist/solutions-components/utils/publicNotificationStore.js +38 -0
  103. package/dist/solutions-components/utils/publicNotificationUtils.js +45 -0
  104. package/dist/solutions-components/utils/queryUtils.js +148 -0
  105. package/dist/solutions-components/utils/solution-store.js +579 -0
  106. package/dist/solutions-components/utils/templates.e2e.js +25 -0
  107. package/dist/solutions-components/utils/templates.js +341 -0
  108. package/dist/solutions-components/utils/test/csvUtils.spec.js +46 -0
  109. package/dist/solutions-components/utils/test/downloadUtils.spec.js +102 -0
  110. package/dist/solutions-components/utils/test/mocks/jsApi.js +74 -0
  111. package/dist/solutions-components/utils/test/pciUtils.spec.js +297 -0
  112. package/dist/solutions-components/utils/test/solution-store.spec.js +439 -0
  113. package/dist/solutions-components/utils/test/testUtils.js +135 -0
  114. package/dist/solutions-components/utils/types.js +14 -0
  115. package/dist/solutions-components_commit.txt +7 -0
  116. package/package.json +7 -7
@@ -0,0 +1,579 @@
1
+ /** @license
2
+ * Copyright 2022 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 { createStore } from "@stencil/store";
17
+ import { EUpdateType } from './interfaces';
18
+ import { EFileType, SolutionTemplateFormatVersion, copyFilesToStorageItem, generateStorageFilePaths, getItemDataAsJson, getProp, getThumbnailFromStorageItem, isSupportedFileType, removeItemResourceFile, setCreateProp, updateItem, updateItemResourceFile } from '@esri/solution-common';
19
+ const EmptySolutionStore = {
20
+ solutionItemId: "",
21
+ defaultWkid: undefined,
22
+ solutionData: { metadata: {}, templates: [] },
23
+ templateEdits: {},
24
+ featureServices: [],
25
+ spatialReferenceInfo: {
26
+ enabled: false,
27
+ services: {},
28
+ spatialReference: undefined
29
+ }
30
+ };
31
+ class SolutionStore {
32
+ /**
33
+ * Creates singleton instance when accessed; default export from module.
34
+ *
35
+ * @returns Static instance of the class
36
+ */
37
+ static get Store() {
38
+ return this._instance || (this._instance = new this());
39
+ }
40
+ /**
41
+ * Creates an empty store.
42
+ *
43
+ * @protected
44
+ */
45
+ constructor() {
46
+ this._hasChanges = false;
47
+ this._store = createStore(Object.assign({}, EmptySolutionStore));
48
+ }
49
+ /**
50
+ * Returns the stored information of an item.
51
+ *
52
+ * @param itemId Id of item to fetch
53
+ *
54
+ * @returns Item information or `undefined` if not found
55
+ */
56
+ getItemInfo(itemId) {
57
+ const templates = this._store.get("solutionData").templates;
58
+ let template;
59
+ templates.some((t) => {
60
+ if (itemId == t.itemId) {
61
+ template = t;
62
+ return true;
63
+ }
64
+ return false;
65
+ });
66
+ return template;
67
+ }
68
+ /**
69
+ * Returns a top-level store property: solutionItemId, defaultWkid, etc.
70
+ *
71
+ * @param propName Name of property
72
+ *
73
+ * @returns Value of property
74
+ */
75
+ getStoreInfo(propName) {
76
+ return this._store.get(propName);
77
+ }
78
+ /**
79
+ * Loads a Solution into the store from AGO.
80
+ *
81
+ * @param solutionItemId Id of the solution represented in the store
82
+ * @param authentication Credentials for fetching information to be loaded into the store
83
+ *
84
+ * @returns Promise that resolves when task is done
85
+ */
86
+ async loadSolution(solutionItemId, authentication) {
87
+ this._authentication = authentication;
88
+ const solutionData = await getItemDataAsJson(solutionItemId, authentication);
89
+ if (solutionData) {
90
+ const defaultWkid = getProp(solutionData, "params.wkid.default");
91
+ await this._prepareSolutionItemsForEditing(solutionItemId, solutionData.templates, authentication);
92
+ const featureServices = this._getFeatureServices(solutionData.templates);
93
+ const spatialReferenceInfo = this._getSpatialReferenceInfo(featureServices, defaultWkid);
94
+ this._store.set("solutionItemId", solutionItemId);
95
+ this._store.set("defaultWkid", defaultWkid);
96
+ this._store.set("solutionData", solutionData);
97
+ this._store.set("featureServices", featureServices);
98
+ this._store.set("spatialReferenceInfo", spatialReferenceInfo);
99
+ }
100
+ this._flagStoreHasChanges(false);
101
+ }
102
+ /**
103
+ * Queues the replacement of the thumbnail associated with a template item in the store.
104
+ *
105
+ * @param itemEdit Details of the template to modify, containing the new thumbnail in the `thumbnail`
106
+ * property
107
+ */
108
+ replaceItemThumbnail(itemEdit) {
109
+ // Flag the current thumbnail and any replacements for removal
110
+ itemEdit.resourceFilePaths.forEach((path) => {
111
+ if (path.type === EFileType.Thumbnail) {
112
+ if (path.updateType === EUpdateType.None) {
113
+ // Existing thumbnail not yet flagged for removal
114
+ path.updateType = EUpdateType.Remove;
115
+ }
116
+ else if (path.updateType === EUpdateType.Add || path.updateType === EUpdateType.Update) {
117
+ // An earlier replacement
118
+ path.updateType = EUpdateType.Obsolete;
119
+ }
120
+ }
121
+ });
122
+ // Remove any replacements already queued
123
+ itemEdit.resourceFilePaths =
124
+ itemEdit.resourceFilePaths.filter((path) => path.updateType != EUpdateType.Obsolete);
125
+ // Add the new thumbnail to the store item
126
+ itemEdit.resourceFilePaths.push({
127
+ blob: itemEdit.thumbnail,
128
+ filename: itemEdit.thumbnail.name,
129
+ type: EFileType.Thumbnail,
130
+ updateType: EUpdateType.Add
131
+ });
132
+ // Update the store
133
+ this.setItemInfo(itemEdit);
134
+ }
135
+ /**
136
+ * Writes a Solution into AGO from the store. Must use `loadSolution` to continue with solution.
137
+ *
138
+ * @returns Promise that resolves when task is done
139
+ */
140
+ async saveSolution() {
141
+ // Update the templates in the original solution item data
142
+ const solutionItemId = this._store.get("solutionItemId");
143
+ const solutionData = this._store.get("solutionData");
144
+ const spatialReferenceInfo = this._store.get("spatialReferenceInfo");
145
+ await this._prepareSolutionItemsForStorage(solutionItemId, solutionData.templates, this._authentication);
146
+ const updatedDefaultWkid = this._setSpatialReferenceInfo(spatialReferenceInfo, solutionData.templates);
147
+ if (updatedDefaultWkid) {
148
+ setCreateProp(solutionData, "params.wkid", {
149
+ "label": "Spatial Reference",
150
+ "default": updatedDefaultWkid,
151
+ "valueType": "spatialReference",
152
+ "attributes": {
153
+ "required": "true"
154
+ }
155
+ });
156
+ }
157
+ else {
158
+ setCreateProp(solutionData, "params.wkid", {});
159
+ }
160
+ const itemInfo = {
161
+ id: solutionItemId,
162
+ text: solutionData
163
+ };
164
+ await updateItem(itemInfo, this._authentication);
165
+ }
166
+ /**
167
+ * Stores information for item.
168
+ *
169
+ * @param itemEdit Item information
170
+ */
171
+ setItemInfo(itemEdit) {
172
+ const solutionData = this._store.get("solutionData");
173
+ const templates = solutionData.templates;
174
+ templates.some((t) => {
175
+ if (itemEdit.itemId == t.itemId) {
176
+ t = itemEdit;
177
+ this._store.set("solutionData", solutionData);
178
+ this._flagStoreHasChanges(true);
179
+ return true;
180
+ }
181
+ return false;
182
+ });
183
+ }
184
+ /**
185
+ * Sets a top-level store property: solutionItemId, defaultWkid, etc.
186
+ *
187
+ * @param propName Name of property
188
+ * @param value Value of property
189
+ */
190
+ setStoreInfo(propName, value) {
191
+ this._store.set(propName, value);
192
+ this._flagStoreHasChanges(true);
193
+ }
194
+ //------------------------------------------------------------------------------------------------------------------//
195
+ /** Provides access to protected methods for unit testing.
196
+ *
197
+ * @param methodName Name of protected method to run
198
+ * @param arg1 First argument to forward to method, e.g., for "_prepareSolutionItemsForEditing", `solutionItemId`
199
+ * @param arg2 Second argument to forward to method, e.g., for "_prepareSolutionItemsForEditing", `templates`
200
+ * @param arg3 Third argument to forward to method, e.g., for "_prepareSolutionItemsForEditing", `authentication`
201
+ *
202
+ * @returns
203
+ */
204
+ _testAccess(methodName, arg1, arg2, arg3) {
205
+ switch (methodName) {
206
+ case "_emptyTheStore":
207
+ this._emptyTheStore();
208
+ break;
209
+ case "_getFeatureServices":
210
+ return this._getFeatureServices(arg1);
211
+ case "_getItemsSharedWithThisGroup":
212
+ return this._getItemsSharedWithThisGroup(arg1, arg2);
213
+ case "_getResourceFilePaths":
214
+ return this._getResourceFilePaths(arg1, arg2, arg3);
215
+ case "_getResourceStorageName":
216
+ return this._getResourceStorageName(arg1, arg2);
217
+ case "_getSpatialReferenceInfo":
218
+ return this._getSpatialReferenceInfo(arg1, arg2);
219
+ case "_prepareSolutionItemsForEditing":
220
+ return this._prepareSolutionItemsForEditing(arg1, arg2, arg3);
221
+ case "_prepareSolutionItemsForStorage":
222
+ return this._prepareSolutionItemsForStorage(arg1, arg2, arg3);
223
+ case "_setSpatialReferenceInfo":
224
+ return this._setSpatialReferenceInfo(arg1, arg2);
225
+ case "_splitFilename":
226
+ return this._splitFilename(arg1);
227
+ }
228
+ return null;
229
+ }
230
+ /**
231
+ * Returns the store to the empty state.
232
+ *
233
+ * @protected
234
+ */
235
+ _emptyTheStore() {
236
+ this._store.set("solutionItemId", EmptySolutionStore.solutionItemId);
237
+ this._store.set("defaultWkid", EmptySolutionStore.defaultWkid);
238
+ this._store.set("solutionData", EmptySolutionStore.solutionData);
239
+ this._store.set("templateEdits", EmptySolutionStore.templateEdits);
240
+ this._store.set("featureServices", EmptySolutionStore.featureServices);
241
+ this._store.set("spatialReferenceInfo", EmptySolutionStore.spatialReferenceInfo);
242
+ }
243
+ /**
244
+ * Sets the store's flag indicating if it has changes and dispatches an event when
245
+ * the flag value changes.
246
+ *
247
+ * @param flagHasChanges Current state of change in the store; if it doesn't match the value saved in this
248
+ * object, an event is dispatched with the new value and the saved value is updated
249
+ *
250
+ * @protected
251
+ */
252
+ _flagStoreHasChanges(flagHasChanges) {
253
+ // Event for notifying if the store has changes or not
254
+ if (this._hasChanges !== flagHasChanges) {
255
+ window.dispatchEvent(new CustomEvent("solutionStoreHasChanges", {
256
+ detail: flagHasChanges,
257
+ bubbles: true,
258
+ cancelable: false,
259
+ composed: true
260
+ }));
261
+ }
262
+ this._hasChanges = flagHasChanges;
263
+ }
264
+ /**
265
+ * Gets a list of Feature Services that are not views.
266
+ *
267
+ * @param templates A list of item templates from the solution
268
+ *
269
+ * @returns a list of feature services
270
+ *
271
+ * @protected
272
+ */
273
+ _getCustomizableFeatureServices(templates) {
274
+ return templates.reduce((prev, cur) => {
275
+ if (cur.type === "Feature Service" && cur.item.typeKeywords.indexOf("View Service") < 0) {
276
+ prev.push(cur);
277
+ }
278
+ return prev;
279
+ }, []);
280
+ }
281
+ /**
282
+ * Gets a list of Feature Services that are not views along with an enabled property that indicates
283
+ * if the service currently uses a spatial reference variable.
284
+ *
285
+ * @param templates A list of item templates from the solution
286
+ *
287
+ * @returns a list of feature service names and an enabled property to indicate
288
+ * if they currently use a spatial reference variable.
289
+ *
290
+ * @protected
291
+ */
292
+ _getFeatureServices(templates) {
293
+ const customizeableFeatureServices = this._getCustomizableFeatureServices(templates);
294
+ return customizeableFeatureServices.map((fs) => {
295
+ const name = fs.item.title || fs.item.name;
296
+ const wkid = getProp(fs, "properties.service.spatialReference.wkid");
297
+ return { name, enabled: wkid.toString().startsWith("{{params.wkid||") };
298
+ });
299
+ }
300
+ /**
301
+ * Capture the key item details for a given group template
302
+ *
303
+ * @param template one of the templates from the current solution
304
+ * @param templates full list of templates
305
+ *
306
+ * @returns a list of IItemShare objects
307
+ *
308
+ * @protected
309
+ */
310
+ _getItemsSharedWithThisGroup(template, templates) {
311
+ return templates.reduce((prev, cur) => {
312
+ if (cur.itemId !== template.itemId && cur.type !== "Group") {
313
+ prev.push({
314
+ id: cur.itemId,
315
+ title: cur.item.name || cur.item.title,
316
+ isShared: (cur.groups || []).indexOf(template.itemId) > -1,
317
+ shareItem: (cur.groups || []).indexOf(template.itemId) > -1,
318
+ type: cur.type,
319
+ typeKeywords: cur.item.typeKeywords
320
+ });
321
+ }
322
+ return prev;
323
+ }, []);
324
+ }
325
+ /**
326
+ * Generate storage file paths from the solution template
327
+ *
328
+ * @param solutionId the id of the current solution
329
+ * @param template the current template from the solution
330
+ * @param portal Portal where file is to be found
331
+ *
332
+ * @returns a list of resource file infos
333
+ *
334
+ * @protected
335
+ */
336
+ _getResourceFilePaths(solutionId, template, portal) {
337
+ const resourceFilePaths = generateStorageFilePaths(portal, solutionId, template.resources, SolutionTemplateFormatVersion);
338
+ return resourceFilePaths.map((fp) => {
339
+ fp.updateType = EUpdateType.None;
340
+ return fp;
341
+ });
342
+ }
343
+ /**
344
+ * Generates a resource name from a storage file path.
345
+ *
346
+ * @param templateItemId The id of the template item whose resource this is; used as a prefix in the resource name
347
+ * @param resourcePath Resource file infos
348
+ *
349
+ * @returns The resource name to use when attaching a resource to the item.
350
+ *
351
+ * @protected
352
+ */
353
+ _getResourceStorageName(templateItemId, resourcePath) {
354
+ /* Converts
355
+ {
356
+ "url": "https://myorg.maps.arcgis.com/sharing/rest/content/items/ca924c6db7d247b9a31fa30532fb5913/resources/79036430a6274e17ae915d0278b8569c_info_metadata/metadata.xml",
357
+ "type": 2,
358
+ "folder": "",
359
+ "filename": "metadata.xml",
360
+ "updateType": 3
361
+ }
362
+ to
363
+ ca924c6db7d247b9a31fa30532fb5913_info_metadata/metadata.xml
364
+ */
365
+ let prefix = templateItemId;
366
+ switch (resourcePath.type) {
367
+ case EFileType.Data:
368
+ prefix = `${prefix}_info_data`;
369
+ break;
370
+ case EFileType.Info:
371
+ prefix = `${prefix}_info`;
372
+ break;
373
+ case EFileType.Metadata:
374
+ prefix = `${prefix}_info_metadata`;
375
+ break;
376
+ case EFileType.Resource:
377
+ break;
378
+ case EFileType.Thumbnail:
379
+ prefix = `${prefix}_info_thumbnail`;
380
+ break;
381
+ }
382
+ let filenameToUse = resourcePath.filename;
383
+ if (resourcePath.type == EFileType.Data && filenameToUse && !isSupportedFileType(filenameToUse)) {
384
+ filenameToUse = filenameToUse + ".zip";
385
+ prefix += "z";
386
+ }
387
+ const filename = resourcePath.folder ? resourcePath.folder + "/" + filenameToUse : filenameToUse;
388
+ return prefix + "/" + filename;
389
+ }
390
+ /**
391
+ * Extracts basic spatial reference information that is used to determine if a custom spatial reference parameter will
392
+ * be exposed while deploying this solution and if so what feature services will support it and what will the default wkid be
393
+ *
394
+ * @param services a list of objects with service name and enabled property (indicates if they currently use a spatial reference var)
395
+ * @param data the data object of a solution item
396
+ *
397
+ * @returns an object that stores if a custom spatial reference parameter is enabled/disabled,
398
+ * a list of services and if they are enabled/disabled, and the default wkid
399
+ *
400
+ * @protected
401
+ */
402
+ _getSpatialReferenceInfo(services, defaultWkid) {
403
+ const defaultServices = {};
404
+ services.forEach(service => {
405
+ defaultServices[service.name] = service.enabled;
406
+ });
407
+ return {
408
+ enabled: defaultWkid !== undefined,
409
+ services: defaultServices,
410
+ spatialReference: defaultWkid ? defaultWkid : undefined
411
+ };
412
+ }
413
+ /**
414
+ * Create and store template items for the editor.
415
+ *
416
+ * @param solutionItemId Id of the solution represented in the store
417
+ * @param templates A list of item templates from the solution
418
+ * @param authentication Credentials for fetching information to be loaded into the store
419
+ *
420
+ * @returns a promise that resolves when the templates are ready
421
+ *
422
+ * @protected
423
+ */
424
+ async _prepareSolutionItemsForEditing(solutionItemId, templates, authentication) {
425
+ const thumbnailPromises = [];
426
+ // Augment the template with paths to resources and group information, if relevant
427
+ templates.forEach((t) => {
428
+ t.resourceFilePaths = this._getResourceFilePaths(solutionItemId, t, authentication.portal);
429
+ thumbnailPromises.push(t.resourceFilePaths.length > 0 ?
430
+ getThumbnailFromStorageItem(authentication, t.resourceFilePaths) :
431
+ Promise.resolve());
432
+ t.groupDetails = t.type === "Group" ? this._getItemsSharedWithThisGroup(t, templates) : [];
433
+ });
434
+ // Augment the template with its thumbnail file
435
+ const thumbnails = await Promise.all(thumbnailPromises);
436
+ templates.forEach((t, i) => {
437
+ t.thumbnail = thumbnails[i] ? thumbnails[i] : undefined;
438
+ });
439
+ return Promise.resolve();
440
+ }
441
+ /**
442
+ * Prepares template items for sending to AGO by updating the resources held by the solution item.
443
+ *
444
+ * @param solutionItemId Id of the solution represented in the store
445
+ * @param templates A list of item templates from the solution
446
+ * @param authentication Credentials for fetching information to be loaded into the store
447
+ *
448
+ * @returns a promise that resolves when the templates are ready
449
+ *
450
+ * @protected
451
+ */
452
+ async _prepareSolutionItemsForStorage(solutionItemId, templates, authentication) {
453
+ const resourceAdds = [];
454
+ // Update the resources and remove the augmentation from a template
455
+ const pendingTasks = [];
456
+ templates.forEach((t) => {
457
+ // Run through the resourceFilePaths for the item seeking modifications to be made to the solution item's
458
+ // collection of resources; queue them for batching
459
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
460
+ t.resourceFilePaths.forEach(async (path) => {
461
+ const storageName = this._getResourceStorageName(t.itemId, path);
462
+ switch (path.updateType) {
463
+ case EUpdateType.Add:
464
+ const { prefix, suffix } = this._splitFilename(storageName);
465
+ t.resources.push(storageName);
466
+ resourceAdds.push({
467
+ itemId: t.itemId,
468
+ file: path.blob,
469
+ folder: prefix,
470
+ filename: suffix
471
+ });
472
+ break;
473
+ case EUpdateType.Update:
474
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
475
+ pendingTasks.push(new Promise(async (resolve) => {
476
+ try {
477
+ await updateItemResourceFile(solutionItemId, storageName, path.blob, authentication);
478
+ }
479
+ catch (err) {
480
+ console.log("Unable to update " + storageName + " for item " + t.itemId + ": " + JSON.stringify(err));
481
+ }
482
+ resolve();
483
+ }));
484
+ break;
485
+ case EUpdateType.Remove:
486
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
487
+ pendingTasks.push(new Promise(async (resolve) => {
488
+ try {
489
+ await removeItemResourceFile(solutionItemId, storageName, authentication);
490
+ t.resources = t.resources.filter((path) => path !== storageName);
491
+ }
492
+ catch (err) {
493
+ console.log("Unable to remove " + storageName + " for item " + t.itemId + ": " + JSON.stringify(err));
494
+ }
495
+ resolve();
496
+ }));
497
+ break;
498
+ }
499
+ return Promise.resolve();
500
+ });
501
+ delete t.resourceFilePaths;
502
+ delete t.thumbnail;
503
+ delete t.groupDetails;
504
+ });
505
+ // Update the resources
506
+ return Promise.all(pendingTasks)
507
+ .then(async () => {
508
+ if (resourceAdds.length > 0) {
509
+ await copyFilesToStorageItem(resourceAdds, solutionItemId, authentication);
510
+ }
511
+ return Promise.resolve();
512
+ });
513
+ }
514
+ /**
515
+ * Stores basic spatial reference information that is used to determine if a custom spatial reference parameter will
516
+ * be exposed while deploying this solution and if so what feature services will support it and what will the default wkid be
517
+ *
518
+ * @param spatialReferenceInfo The configuration settings for a custom spatial reference
519
+ * @param templates The templates in the current solution, which will be updated in place if
520
+ * `spatialReferenceInfo.enabled` is true
521
+ *
522
+ * @returns The new default wkid
523
+ *
524
+ * @protected
525
+ */
526
+ _setSpatialReferenceInfo(spatialReferenceInfo, templates) {
527
+ const customizingPrefix = "{{params.wkid||";
528
+ const customizeableFeatureServices = this._getCustomizableFeatureServices(templates);
529
+ if (spatialReferenceInfo.enabled) {
530
+ // Enable or disable this feature in each service
531
+ customizeableFeatureServices.forEach((fs) => {
532
+ const name = fs.item.title || fs.item.name;
533
+ let wkid;
534
+ if (spatialReferenceInfo.services[name]) { // enabled
535
+ wkid = `{{params.wkid||${spatialReferenceInfo.spatialReference}}}`;
536
+ setCreateProp(fs, "properties.service.spatialReference.wkid", wkid);
537
+ }
538
+ else { // disabled
539
+ wkid = getProp(fs, "properties.service.spatialReference.wkid");
540
+ // Remove customizing prefix if present
541
+ if (wkid.toString().startsWith(customizingPrefix)) {
542
+ wkid = wkid.toString().substring(customizingPrefix.length, wkid.length - 2);
543
+ setCreateProp(fs, "properties.service.spatialReference.wkid", wkid);
544
+ }
545
+ }
546
+ });
547
+ return spatialReferenceInfo.spatialReference;
548
+ }
549
+ else {
550
+ // Disable this feature in each service
551
+ customizeableFeatureServices.forEach((fs) => {
552
+ const wkid = getProp(fs, "properties.service.spatialReference.wkid");
553
+ // Remove customizing prefix if present
554
+ if (wkid.toString().startsWith(customizingPrefix)) {
555
+ setCreateProp(fs, "properties.service.spatialReference.wkid", wkid.toString().substring(customizingPrefix.length, wkid.length - 2));
556
+ }
557
+ });
558
+ return undefined;
559
+ }
560
+ }
561
+ /**
562
+ * Splits a pathed filename into a last term and a prefix; e.g., "a/b/c" returns "c" with a prefix of "a/b".
563
+ *
564
+ * @param filename Filename with optional path
565
+ *
566
+ * @returns An object consisting of a `prefix` (undefined if `filename` does not contain a path) and a `suffix`--the
567
+ * filename at the end of a path
568
+ *
569
+ * @protected
570
+ */
571
+ _splitFilename(filename) {
572
+ const filenameParts = filename.split("/");
573
+ return {
574
+ prefix: filenameParts.length > 1 ? filenameParts.slice(0, filenameParts.length - 1).join("/") : undefined,
575
+ suffix: filenameParts[filenameParts.length - 1]
576
+ };
577
+ }
578
+ }
579
+ export default SolutionStore.Store;
@@ -0,0 +1,25 @@
1
+ /** @license
2
+ * Copyright 2022 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 { getInventoryItems } from "./templates";
17
+ import * as electionOutreach from '../demos/data/election-outreach.json';
18
+ describe("getInventoryItems", () => {
19
+ it("can get inventory items", () => {
20
+ const templates = electionOutreach.templates;
21
+ expect(templates.length).toEqual(11);
22
+ const actual = getInventoryItems(templates);
23
+ expect(actual.length).toEqual(5);
24
+ });
25
+ });