@esri/solution-common 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 (261) hide show
  1. package/dist/cjs/completeItem.d.ts +29 -29
  2. package/dist/cjs/completeItem.js +81 -81
  3. package/dist/cjs/create-hub-request-options.d.ts +29 -29
  4. package/dist/cjs/create-hub-request-options.js +63 -63
  5. package/dist/cjs/deleteHelpers/deleteEmptyGroups.d.ts +24 -24
  6. package/dist/cjs/deleteHelpers/deleteEmptyGroups.js +41 -41
  7. package/dist/cjs/deleteHelpers/deleteGroupIfEmpty.d.ts +27 -27
  8. package/dist/cjs/deleteHelpers/deleteGroupIfEmpty.js +96 -96
  9. package/dist/cjs/deleteHelpers/deleteSolutionContents.d.ts +38 -38
  10. package/dist/cjs/deleteHelpers/deleteSolutionContents.js +129 -129
  11. package/dist/cjs/deleteHelpers/deleteSolutionFolder.d.ts +29 -29
  12. package/dist/cjs/deleteHelpers/deleteSolutionFolder.js +78 -78
  13. package/dist/cjs/deleteHelpers/deleteSolutionItem.d.ts +30 -30
  14. package/dist/cjs/deleteHelpers/deleteSolutionItem.js +53 -53
  15. package/dist/cjs/deleteHelpers/index.d.ts +22 -22
  16. package/dist/cjs/deleteHelpers/index.js +25 -25
  17. package/dist/cjs/deleteHelpers/reconstructBuildOrderIds.d.ts +27 -27
  18. package/dist/cjs/deleteHelpers/reconstructBuildOrderIds.js +33 -33
  19. package/dist/cjs/deleteHelpers/removeItems.d.ts +34 -34
  20. package/dist/cjs/deleteHelpers/removeItems.js +111 -111
  21. package/dist/cjs/deleteHelpers/reportProgress.d.ts +27 -27
  22. package/dist/cjs/deleteHelpers/reportProgress.js +45 -45
  23. package/dist/cjs/deleteSolution.d.ts +55 -55
  24. package/dist/cjs/deleteSolution.js +106 -106
  25. package/dist/cjs/dependencies.d.ts +26 -26
  26. package/dist/cjs/dependencies.js +170 -170
  27. package/dist/cjs/featureServiceHelpers.d.ts +791 -791
  28. package/dist/cjs/featureServiceHelpers.js +2420 -2420
  29. package/dist/cjs/generalHelpers.d.ts +392 -385
  30. package/dist/cjs/generalHelpers.js +857 -854
  31. package/dist/cjs/generalHelpers.js.map +1 -1
  32. package/dist/cjs/get-subscription-info.d.ts +27 -27
  33. package/dist/cjs/get-subscription-info.js +38 -38
  34. package/dist/cjs/getDeletableSolutionInfo.d.ts +29 -29
  35. package/dist/cjs/getDeletableSolutionInfo.js +52 -52
  36. package/dist/cjs/getItemTypeAbbrev.d.ts +19 -19
  37. package/dist/cjs/getItemTypeAbbrev.js +184 -184
  38. package/dist/cjs/getSolutionSummary.d.ts +27 -27
  39. package/dist/cjs/getSolutionSummary.js +100 -100
  40. package/dist/cjs/index.d.ts +43 -44
  41. package/dist/cjs/index.js +46 -47
  42. package/dist/cjs/index.js.map +1 -1
  43. package/dist/cjs/interfaces.d.ts +1334 -1334
  44. package/dist/cjs/interfaces.js +74 -74
  45. package/dist/cjs/interfaces.js.map +1 -1
  46. package/dist/cjs/libConnectors.d.ts +73 -73
  47. package/dist/cjs/libConnectors.js +114 -114
  48. package/dist/cjs/migrations/apply-schema.d.ts +24 -24
  49. package/dist/cjs/migrations/apply-schema.js +35 -35
  50. package/dist/cjs/migrations/is-legacy-solution.d.ts +24 -24
  51. package/dist/cjs/migrations/is-legacy-solution.js +39 -39
  52. package/dist/cjs/migrations/upgrade-three-dot-one.d.ts +27 -27
  53. package/dist/cjs/migrations/upgrade-three-dot-one.js +48 -48
  54. package/dist/cjs/migrations/upgrade-three-dot-zero.d.ts +27 -27
  55. package/dist/cjs/migrations/upgrade-three-dot-zero.js +42 -42
  56. package/dist/cjs/migrations/upgrade-two-dot-five.d.ts +24 -24
  57. package/dist/cjs/migrations/upgrade-two-dot-five.js +72 -72
  58. package/dist/cjs/migrations/upgrade-two-dot-four.d.ts +24 -24
  59. package/dist/cjs/migrations/upgrade-two-dot-four.js +71 -71
  60. package/dist/cjs/migrations/upgrade-two-dot-one.d.ts +7 -7
  61. package/dist/cjs/migrations/upgrade-two-dot-one.js +38 -38
  62. package/dist/cjs/migrations/upgrade-two-dot-seven.d.ts +23 -23
  63. package/dist/cjs/migrations/upgrade-two-dot-seven.js +57 -57
  64. package/dist/cjs/migrations/upgrade-two-dot-six.d.ts +27 -27
  65. package/dist/cjs/migrations/upgrade-two-dot-six.js +60 -60
  66. package/dist/cjs/migrations/upgrade-two-dot-three.d.ts +23 -23
  67. package/dist/cjs/migrations/upgrade-two-dot-three.js +54 -54
  68. package/dist/cjs/migrations/upgrade-two-dot-two.d.ts +23 -23
  69. package/dist/cjs/migrations/upgrade-two-dot-two.js +57 -57
  70. package/dist/cjs/migrations/upgrade-two-dot-zero.d.ts +44 -44
  71. package/dist/cjs/migrations/upgrade-two-dot-zero.js +94 -94
  72. package/dist/cjs/migrator.d.ts +25 -25
  73. package/dist/cjs/migrator.js +76 -76
  74. package/dist/cjs/resourceHelpers.d.ts +191 -191
  75. package/dist/cjs/resourceHelpers.js +383 -390
  76. package/dist/cjs/resourceHelpers.js.map +1 -1
  77. package/dist/cjs/resources/add-resource-from-blob.d.ts +26 -26
  78. package/dist/cjs/resources/add-resource-from-blob.js +51 -51
  79. package/dist/cjs/resources/addMetadataFromBlob.d.ts +25 -25
  80. package/dist/cjs/resources/addMetadataFromBlob.js +42 -42
  81. package/dist/cjs/resources/convert-item-resource-to-storage-resource.d.ts +32 -32
  82. package/dist/cjs/resources/convert-item-resource-to-storage-resource.js +69 -69
  83. package/dist/cjs/resources/convert-storage-resource-to-item-resource.d.ts +29 -29
  84. package/dist/cjs/resources/convert-storage-resource-to-item-resource.js +69 -69
  85. package/dist/cjs/resources/copyAssociatedFiles.d.ts +67 -67
  86. package/dist/cjs/resources/copyAssociatedFiles.js +301 -301
  87. package/dist/cjs/resources/copyDataIntoItem.d.ts +33 -33
  88. package/dist/cjs/resources/copyDataIntoItem.js +61 -62
  89. package/dist/cjs/resources/copyDataIntoItem.js.map +1 -1
  90. package/dist/cjs/resources/copyMetadataIntoItem.d.ts +26 -26
  91. package/dist/cjs/resources/copyMetadataIntoItem.js +45 -45
  92. package/dist/cjs/resources/copyResourceIntoZip.d.ts +33 -33
  93. package/dist/cjs/resources/copyResourceIntoZip.js +77 -77
  94. package/dist/cjs/resources/copyZipIntoItem.d.ts +25 -25
  95. package/dist/cjs/resources/copyZipIntoItem.js +53 -53
  96. package/dist/cjs/resources/createCopyResults.d.ts +25 -25
  97. package/dist/cjs/resources/createCopyResults.js +35 -35
  98. package/dist/cjs/resources/get-blob.d.ts +26 -26
  99. package/dist/cjs/resources/get-blob.js +26 -26
  100. package/dist/cjs/resources/getItemResourcesFilesFromPaths.d.ts +24 -24
  101. package/dist/cjs/resources/getItemResourcesFilesFromPaths.js +48 -48
  102. package/dist/cjs/resources/getItemResourcesPaths.d.ts +26 -26
  103. package/dist/cjs/resources/getItemResourcesPaths.js +72 -72
  104. package/dist/cjs/resources/index.d.ts +29 -29
  105. package/dist/cjs/resources/index.js +32 -32
  106. package/dist/cjs/resources/solution-resource.d.ts +35 -35
  107. package/dist/cjs/resources/solution-resource.js +30 -30
  108. package/dist/cjs/resources/solution-resource.js.map +1 -1
  109. package/dist/cjs/resources/transform-resource-paths-to-solution-resources.d.ts +56 -56
  110. package/dist/cjs/resources/transform-resource-paths-to-solution-resources.js +145 -145
  111. package/dist/cjs/restHelpers.d.ts +586 -585
  112. package/dist/cjs/restHelpers.js +1888 -1883
  113. package/dist/cjs/restHelpers.js.map +1 -1
  114. package/dist/cjs/restHelpersGet.d.ts +288 -288
  115. package/dist/cjs/restHelpersGet.js +803 -803
  116. package/dist/cjs/sharing/index.d.ts +16 -16
  117. package/dist/cjs/sharing/index.js +19 -19
  118. package/dist/cjs/sharing/share-item-to-groups.d.ts +26 -26
  119. package/dist/cjs/sharing/share-item-to-groups.js +43 -43
  120. package/dist/cjs/templatization.d.ts +139 -139
  121. package/dist/cjs/templatization.js +313 -313
  122. package/dist/cjs/trackingHelpers.d.ts +116 -116
  123. package/dist/cjs/trackingHelpers.js +216 -216
  124. package/dist/cjs/velocityHelpers.d.ts +57 -57
  125. package/dist/cjs/velocityHelpers.js +134 -134
  126. package/dist/cjs/workforceHelpers.d.ts +115 -115
  127. package/dist/cjs/workforceHelpers.js +746 -746
  128. package/dist/cjs/workforceHelpers.js.map +1 -1
  129. package/dist/esm/completeItem.d.ts +29 -29
  130. package/dist/esm/completeItem.js +76 -76
  131. package/dist/esm/create-hub-request-options.d.ts +29 -29
  132. package/dist/esm/create-hub-request-options.js +59 -59
  133. package/dist/esm/deleteHelpers/deleteEmptyGroups.d.ts +24 -24
  134. package/dist/esm/deleteHelpers/deleteEmptyGroups.js +37 -37
  135. package/dist/esm/deleteHelpers/deleteGroupIfEmpty.d.ts +27 -27
  136. package/dist/esm/deleteHelpers/deleteGroupIfEmpty.js +91 -91
  137. package/dist/esm/deleteHelpers/deleteSolutionContents.d.ts +38 -38
  138. package/dist/esm/deleteHelpers/deleteSolutionContents.js +124 -124
  139. package/dist/esm/deleteHelpers/deleteSolutionFolder.d.ts +29 -29
  140. package/dist/esm/deleteHelpers/deleteSolutionFolder.js +73 -73
  141. package/dist/esm/deleteHelpers/deleteSolutionItem.d.ts +30 -30
  142. package/dist/esm/deleteHelpers/deleteSolutionItem.js +48 -48
  143. package/dist/esm/deleteHelpers/index.d.ts +22 -22
  144. package/dist/esm/deleteHelpers/index.js +22 -22
  145. package/dist/esm/deleteHelpers/reconstructBuildOrderIds.d.ts +27 -27
  146. package/dist/esm/deleteHelpers/reconstructBuildOrderIds.js +28 -28
  147. package/dist/esm/deleteHelpers/removeItems.d.ts +34 -34
  148. package/dist/esm/deleteHelpers/removeItems.js +106 -106
  149. package/dist/esm/deleteHelpers/reportProgress.d.ts +27 -27
  150. package/dist/esm/deleteHelpers/reportProgress.js +41 -41
  151. package/dist/esm/deleteSolution.d.ts +55 -55
  152. package/dist/esm/deleteSolution.js +100 -100
  153. package/dist/esm/dependencies.d.ts +26 -26
  154. package/dist/esm/dependencies.js +166 -166
  155. package/dist/esm/featureServiceHelpers.d.ts +791 -791
  156. package/dist/esm/featureServiceHelpers.js +2336 -2336
  157. package/dist/esm/generalHelpers.d.ts +392 -385
  158. package/dist/esm/generalHelpers.js +810 -808
  159. package/dist/esm/generalHelpers.js.map +1 -1
  160. package/dist/esm/get-subscription-info.d.ts +27 -27
  161. package/dist/esm/get-subscription-info.js +34 -34
  162. package/dist/esm/getDeletableSolutionInfo.d.ts +29 -29
  163. package/dist/esm/getDeletableSolutionInfo.js +47 -47
  164. package/dist/esm/getItemTypeAbbrev.d.ts +19 -19
  165. package/dist/esm/getItemTypeAbbrev.js +180 -180
  166. package/dist/esm/getSolutionSummary.d.ts +27 -27
  167. package/dist/esm/getSolutionSummary.js +95 -95
  168. package/dist/esm/index.d.ts +43 -44
  169. package/dist/esm/index.js +43 -44
  170. package/dist/esm/index.js.map +1 -1
  171. package/dist/esm/interfaces.d.ts +1334 -1334
  172. package/dist/esm/interfaces.js +70 -70
  173. package/dist/esm/libConnectors.d.ts +73 -73
  174. package/dist/esm/libConnectors.js +104 -104
  175. package/dist/esm/migrations/apply-schema.d.ts +24 -24
  176. package/dist/esm/migrations/apply-schema.js +31 -31
  177. package/dist/esm/migrations/is-legacy-solution.d.ts +24 -24
  178. package/dist/esm/migrations/is-legacy-solution.js +35 -35
  179. package/dist/esm/migrations/upgrade-three-dot-one.d.ts +27 -27
  180. package/dist/esm/migrations/upgrade-three-dot-one.js +44 -44
  181. package/dist/esm/migrations/upgrade-three-dot-zero.d.ts +27 -27
  182. package/dist/esm/migrations/upgrade-three-dot-zero.js +38 -38
  183. package/dist/esm/migrations/upgrade-two-dot-five.d.ts +24 -24
  184. package/dist/esm/migrations/upgrade-two-dot-five.js +68 -68
  185. package/dist/esm/migrations/upgrade-two-dot-four.d.ts +24 -24
  186. package/dist/esm/migrations/upgrade-two-dot-four.js +67 -67
  187. package/dist/esm/migrations/upgrade-two-dot-one.d.ts +7 -7
  188. package/dist/esm/migrations/upgrade-two-dot-one.js +34 -34
  189. package/dist/esm/migrations/upgrade-two-dot-seven.d.ts +23 -23
  190. package/dist/esm/migrations/upgrade-two-dot-seven.js +53 -53
  191. package/dist/esm/migrations/upgrade-two-dot-six.d.ts +27 -27
  192. package/dist/esm/migrations/upgrade-two-dot-six.js +56 -56
  193. package/dist/esm/migrations/upgrade-two-dot-three.d.ts +23 -23
  194. package/dist/esm/migrations/upgrade-two-dot-three.js +50 -50
  195. package/dist/esm/migrations/upgrade-two-dot-two.d.ts +23 -23
  196. package/dist/esm/migrations/upgrade-two-dot-two.js +53 -53
  197. package/dist/esm/migrations/upgrade-two-dot-zero.d.ts +44 -44
  198. package/dist/esm/migrations/upgrade-two-dot-zero.js +87 -87
  199. package/dist/esm/migrator.d.ts +25 -25
  200. package/dist/esm/migrator.js +72 -72
  201. package/dist/esm/resourceHelpers.d.ts +191 -191
  202. package/dist/esm/resourceHelpers.js +364 -371
  203. package/dist/esm/resourceHelpers.js.map +1 -1
  204. package/dist/esm/resources/add-resource-from-blob.d.ts +26 -26
  205. package/dist/esm/resources/add-resource-from-blob.js +47 -47
  206. package/dist/esm/resources/addMetadataFromBlob.d.ts +25 -25
  207. package/dist/esm/resources/addMetadataFromBlob.js +38 -38
  208. package/dist/esm/resources/convert-item-resource-to-storage-resource.d.ts +32 -32
  209. package/dist/esm/resources/convert-item-resource-to-storage-resource.js +65 -65
  210. package/dist/esm/resources/convert-storage-resource-to-item-resource.d.ts +29 -29
  211. package/dist/esm/resources/convert-storage-resource-to-item-resource.js +65 -65
  212. package/dist/esm/resources/copyAssociatedFiles.d.ts +67 -67
  213. package/dist/esm/resources/copyAssociatedFiles.js +293 -293
  214. package/dist/esm/resources/copyDataIntoItem.d.ts +33 -33
  215. package/dist/esm/resources/copyDataIntoItem.js +56 -57
  216. package/dist/esm/resources/copyDataIntoItem.js.map +1 -1
  217. package/dist/esm/resources/copyMetadataIntoItem.d.ts +26 -26
  218. package/dist/esm/resources/copyMetadataIntoItem.js +41 -41
  219. package/dist/esm/resources/copyResourceIntoZip.d.ts +33 -33
  220. package/dist/esm/resources/copyResourceIntoZip.js +72 -72
  221. package/dist/esm/resources/copyZipIntoItem.d.ts +25 -25
  222. package/dist/esm/resources/copyZipIntoItem.js +49 -49
  223. package/dist/esm/resources/createCopyResults.d.ts +25 -25
  224. package/dist/esm/resources/createCopyResults.js +31 -31
  225. package/dist/esm/resources/get-blob.d.ts +26 -26
  226. package/dist/esm/resources/get-blob.js +22 -22
  227. package/dist/esm/resources/getItemResourcesFilesFromPaths.d.ts +24 -24
  228. package/dist/esm/resources/getItemResourcesFilesFromPaths.js +44 -44
  229. package/dist/esm/resources/getItemResourcesPaths.d.ts +26 -26
  230. package/dist/esm/resources/getItemResourcesPaths.js +68 -68
  231. package/dist/esm/resources/index.d.ts +29 -29
  232. package/dist/esm/resources/index.js +29 -29
  233. package/dist/esm/resources/solution-resource.d.ts +35 -35
  234. package/dist/esm/resources/solution-resource.js +27 -27
  235. package/dist/esm/resources/transform-resource-paths-to-solution-resources.d.ts +56 -56
  236. package/dist/esm/resources/transform-resource-paths-to-solution-resources.js +137 -137
  237. package/dist/esm/restHelpers.d.ts +586 -585
  238. package/dist/esm/restHelpers.js +1827 -1823
  239. package/dist/esm/restHelpers.js.map +1 -1
  240. package/dist/esm/restHelpersGet.d.ts +288 -288
  241. package/dist/esm/restHelpersGet.js +763 -763
  242. package/dist/esm/sharing/index.d.ts +16 -16
  243. package/dist/esm/sharing/index.js +16 -16
  244. package/dist/esm/sharing/share-item-to-groups.d.ts +26 -26
  245. package/dist/esm/sharing/share-item-to-groups.js +39 -39
  246. package/dist/esm/templatization.d.ts +139 -139
  247. package/dist/esm/templatization.js +293 -293
  248. package/dist/esm/trackingHelpers.d.ts +116 -116
  249. package/dist/esm/trackingHelpers.js +204 -204
  250. package/dist/esm/velocityHelpers.d.ts +57 -57
  251. package/dist/esm/velocityHelpers.js +128 -128
  252. package/dist/esm/workforceHelpers.d.ts +115 -115
  253. package/dist/esm/workforceHelpers.js +717 -717
  254. package/dist/esm/workforceHelpers.js.map +1 -1
  255. package/package.json +2 -2
  256. package/dist/cjs/polyfills.d.ts +0 -40
  257. package/dist/cjs/polyfills.js +0 -98
  258. package/dist/cjs/polyfills.js.map +0 -1
  259. package/dist/esm/polyfills.d.ts +0 -40
  260. package/dist/esm/polyfills.js +0 -93
  261. package/dist/esm/polyfills.js.map +0 -1
@@ -1,2337 +1,2337 @@
1
- /** @license
2
- * Copyright 2019 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
- /**
17
- * Provides general helper functions.
18
- *
19
- * @module featureServiceHelpers
20
- */
21
- // ------------------------------------------------------------------------------------------------------------------ //
22
- export { queryFeatures as rest_queryFeatures, addFeatures as rest_addFeatures } from "@esri/arcgis-rest-feature-layer";
23
- //#region Imports -------------------------------------------------------------------------------------------------------//
24
- import { UNREACHABLE } from "./interfaces";
25
- import { checkUrlPathTermination, deleteProp, deleteProps, fail, getProp, setCreateProp, setProp } from "./generalHelpers";
26
- import { replaceInTemplate, templatizeTerm, templatizeIds } from "./templatization";
27
- import { addToServiceDefinition, getLayerUpdates, getRequest, rest_request } from "./restHelpers";
28
- import { isTrackingViewTemplate, templatizeTracker } from "./trackingHelpers";
29
- //#endregion ------------------------------------------------------------------------------------------------------------//
30
- //#region Public functions ----------------------------------------------------------------------------------------------//
31
- /**
32
- * Templatize the ID, url, field references ect
33
- *
34
- * @param itemTemplate Template for feature service item
35
- * @param dependencies Array of IDependency for name mapping
36
- * @param templatizeFieldReferences Templatize all field references within a layer
37
- * @param templateDictionary Hash mapping property names to replacement values
38
- * @returns A promise that will resolve when template has been updated
39
- * @private
40
- */
41
- export function templatize(itemTemplate, dependencies, templatizeFieldReferences, templateDictionary) {
42
- templateDictionary = templateDictionary || {};
43
- // Common templatizations
44
- const id = itemTemplate.item.id;
45
- const fsUrl = itemTemplate.item.url;
46
- itemTemplate.item = {
47
- ...itemTemplate.item,
48
- id: templatizeTerm(id, id, ".itemId"),
49
- url: _templatize(id, "url"),
50
- typeKeywords: templatizeIds(itemTemplate.item.typeKeywords)
51
- };
52
- // special handeling if we are dealing with a tracker view
53
- templatizeTracker(itemTemplate);
54
- // added for issue #928
55
- deleteProp(itemTemplate, "properties.service.size");
56
- const jsonLayers = itemTemplate.properties.layers || [];
57
- const jsonTables = itemTemplate.properties.tables || [];
58
- const jsonItems = jsonLayers.concat(jsonTables);
59
- const data = itemTemplate.data || {};
60
- const layers = data.layers || [];
61
- const tables = data.tables || [];
62
- const _items = layers.concat(tables);
63
- // Set up symbols for the URL of the feature service and its layers and tables
64
- templateDictionary[fsUrl] = itemTemplate.item.url; // map FS URL to its templatized form
65
- jsonItems.concat(_items).forEach(layer => {
66
- templateDictionary[fsUrl + "/" + layer.id] = _templatize(id, "layer" + layer.id + ".url");
67
- });
68
- // templatize the service references serviceItemId
69
- itemTemplate.properties.service.serviceItemId = templatizeTerm(itemTemplate.properties.service.serviceItemId, itemTemplate.properties.service.serviceItemId, ".itemId");
70
- const initialExtent = getProp(itemTemplate, "properties.service.initialExtent");
71
- /* istanbul ignore else */
72
- if (initialExtent) {
73
- itemTemplate.properties.service.initialExtent = templatizeTerm(id, id, ".solutionExtent");
74
- }
75
- const fullExtent = getProp(itemTemplate, "properties.service.fullExtent");
76
- /* istanbul ignore else */
77
- if (fullExtent) {
78
- itemTemplate.properties.service.fullExtent = templatizeTerm(id, id, ".solutionExtent");
79
- }
80
- // this default extent will be used in cases where it does not make sense to apply the orgs
81
- // extent to a service with a local spatial reference
82
- itemTemplate.properties.defaultExtent = initialExtent || fullExtent;
83
- // in some cases a service does not have a spatial reference defined
84
- // added for issue #699
85
- if (!getProp(itemTemplate, "properties.service.spatialReference") &&
86
- getProp(itemTemplate, "properties.defaultExtent.spatialReference")) {
87
- setCreateProp(itemTemplate, "properties.service.spatialReference", itemTemplate.properties.defaultExtent.spatialReference);
88
- }
89
- // if any layer hasZ enabled then we need to set
90
- // enableZDefaults and zDefault to deploy to enterprise
91
- let hasZ = false;
92
- jsonItems.forEach((jsonItem) => {
93
- // get the source service json for the given data item
94
- const matchingItems = _items.filter(item => {
95
- return jsonItem.id === item.id;
96
- });
97
- // templatize the source service json
98
- const _item = matchingItems.length === 1 ? matchingItems[0] : undefined;
99
- _templatizeLayer(_item, jsonItem, itemTemplate, dependencies, templatizeFieldReferences, templateDictionary);
100
- hasZ = jsonItem.hasZ || (_item && _item.hasZ) ? true : hasZ;
101
- });
102
- if (hasZ) {
103
- itemTemplate.properties.service.enableZDefaults = true;
104
- itemTemplate.properties.service.zDefault = 0;
105
- }
106
- return itemTemplate;
107
- }
108
- /**
109
- * Delete key properties that are system managed
110
- *
111
- * @param layer The data layer instance with field name references within
112
- */
113
- export function deleteViewProps(layer) {
114
- const props = ["definitionQuery"];
115
- props.forEach(prop => {
116
- deleteProp(layer, prop);
117
- });
118
- }
119
- /**
120
- * Cache properties that contain field references
121
- *
122
- * removeProp added for issue #644
123
- * setting all props on add for online now
124
- * investigating if we can also just allow them to be set during add for portal
125
- *
126
- * @param layer The data layer instance with field name references within
127
- * @param fieldInfos the object that stores the cached field infos
128
- * @returns An updated instance of the fieldInfos
129
- */
130
- export function cacheFieldInfos(layer, fieldInfos) {
131
- // cache the source fields as they are in the original source
132
- if (layer && layer.fields) {
133
- fieldInfos[layer.id] = {
134
- sourceFields: JSON.parse(JSON.stringify(layer.fields)),
135
- type: layer.type,
136
- id: layer.id
137
- };
138
- }
139
- // cache each of these properties as they each can contain field references
140
- // and will have associated updateDefinition calls when deploying to portal
141
- // as well as online for relationships...as relationships added with addToDef will cause failure
142
- const props = {
143
- editFieldsInfo: false,
144
- types: false,
145
- templates: false,
146
- relationships: true,
147
- drawingInfo: false,
148
- timeInfo: false,
149
- viewDefinitionQuery: false
150
- };
151
- Object.keys(props).forEach(k => {
152
- _cacheFieldInfo(layer, k, fieldInfos, props[k]);
153
- });
154
- return fieldInfos;
155
- }
156
- /**
157
- * Cache the stored contingent values so we can add them in subsequent addToDef calls
158
- *
159
- * @param id The layer id for the associated values to be stored with
160
- * @param fieldInfos The object that stores the cached field infos
161
- * @param itemTemplate The current itemTemplate being processed
162
- * @returns An updated instance of the fieldInfos
163
- */
164
- export function cacheContingentValues(id, fieldInfos, itemTemplate) {
165
- const contingentValues = getProp(itemTemplate, 'properties.contingentValues');
166
- if (contingentValues && contingentValues[id]) {
167
- fieldInfos[id]['contingentValues'] = contingentValues[id];
168
- }
169
- return fieldInfos;
170
- }
171
- /**
172
- * Helper function to cache a single property into the fieldInfos object
173
- * This property will be removed from the layer instance.
174
- *
175
- * @param layer the data layer being cloned
176
- * @param prop the property name used to cache
177
- * @param fieldInfos the object that will store the cached property
178
- * @private
179
- */
180
- export function _cacheFieldInfo(layer, prop, fieldInfos, removeProp) {
181
- /* istanbul ignore else */
182
- if (layer &&
183
- layer.hasOwnProperty(prop) &&
184
- fieldInfos &&
185
- fieldInfos.hasOwnProperty(layer.id)) {
186
- fieldInfos[layer.id][prop] = layer[prop];
187
- // editFieldsInfo does not come through unless its with the layer
188
- // when it's being added
189
- /* istanbul ignore else */
190
- if (removeProp) {
191
- layer[prop] = null;
192
- }
193
- }
194
- }
195
- /**
196
- * Cache popup info that can contain field references
197
- *
198
- * @param data The items data property
199
- * @returns An updated instance of the popupInfos
200
- */
201
- export function cachePopupInfos(data) {
202
- // store any popupInfo so we can update after any potential name changes
203
- const popupInfos = {
204
- layers: {},
205
- tables: {}
206
- };
207
- if (data && data.layers && data.layers.length > 0) {
208
- _cachePopupInfo(popupInfos, "layers", data.layers);
209
- }
210
- if (data && data.tables && data.tables.length > 0) {
211
- _cachePopupInfo(popupInfos, "tables", data.tables);
212
- }
213
- return popupInfos;
214
- }
215
- /**
216
- * Helper function to cache a single popupInfo
217
- * This property will be reset on the layer
218
- *
219
- * @param popupInfos object to store the cahced popupInfo
220
- * @param type is it a layer or table
221
- * @param _items list or either layers or tables
222
- * @private
223
- */
224
- export function _cachePopupInfo(popupInfos, type, _items) {
225
- _items.forEach((item) => {
226
- if (item && item.hasOwnProperty("popupInfo")) {
227
- popupInfos[type][item.id] = item.popupInfo;
228
- item.popupInfo = {};
229
- }
230
- });
231
- }
232
- /**
233
- * Store basic layer information for potential replacement if we are unable to access a given service
234
- * added for issue #859
235
- *
236
- * @param layerId the id for the layer
237
- * @param itemId the id for the item
238
- * @param url the url for the layer
239
- * @param templateDictionary Hash of key details used for variable replacement
240
- * @returns templatized itemTemplate
241
- */
242
- export function cacheLayerInfo(layerId, itemId, url, templateDictionary) {
243
- if (layerId) {
244
- const layerIdVar = `layer${layerId}`;
245
- // need to structure these differently so they are not used for standard replacement calls
246
- // this now adds additional vars that are not needing replacement unless we fail to fetch the service
247
- const newVars = getProp(templateDictionary, `${UNREACHABLE}.${itemId}`) || {
248
- itemId
249
- };
250
- newVars[layerIdVar] = getProp(newVars, layerIdVar) || {
251
- layerId,
252
- itemId
253
- };
254
- if (url !== "") {
255
- newVars[layerIdVar]["url"] = url;
256
- }
257
- const unreachableVars = {};
258
- unreachableVars[itemId] = newVars;
259
- templateDictionary[UNREACHABLE] = {
260
- ...templateDictionary[UNREACHABLE],
261
- ...unreachableVars
262
- };
263
- }
264
- }
265
- /**
266
- * Creates an item in a specified folder (except for Group item type).
267
- *
268
- * @param itemTemplate Item to be created; n.b.: this item is modified
269
- * @param templateDictionary Hash mapping property names to replacement values
270
- * @param createResponse Response from create service
271
- * @returns An updated instance of the template
272
- * @private
273
- */
274
- export function updateTemplate(itemTemplate, templateDictionary, createResponse) {
275
- // Update the item with any typeKeywords that were added on create
276
- _updateTypeKeywords(itemTemplate, createResponse);
277
- // Add the new item to the template dictionary
278
- templateDictionary[itemTemplate.itemId] = Object.assign(templateDictionary[itemTemplate.itemId] || {}, {
279
- itemId: createResponse.serviceItemId,
280
- url: checkUrlPathTermination(createResponse.serviceurl),
281
- name: createResponse.name
282
- });
283
- // Update the item template now that the new service has been created
284
- itemTemplate.itemId = createResponse.serviceItemId;
285
- return replaceInTemplate(itemTemplate, templateDictionary);
286
- }
287
- /**
288
- * Updates the items typeKeywords to include any typeKeywords that
289
- * were added by the create service request
290
- *
291
- * @param itemTemplate Item to be created; n.b.: this item is modified
292
- * @param createResponse Response from create service
293
- * @returns An updated instance of the template
294
- * @private
295
- */
296
- export function _updateTypeKeywords(itemTemplate, createResponse) {
297
- // https://github.com/Esri/solution.js/issues/589
298
- const iKwords = getProp(itemTemplate, "item.typeKeywords");
299
- const cKwords = getProp(createResponse, "typeKeywords");
300
- if (iKwords && cKwords) {
301
- setProp(itemTemplate, "item.typeKeywords", iKwords.concat(cKwords.filter(k => iKwords.indexOf(k) < 0)));
302
- }
303
- return itemTemplate;
304
- }
305
- /**
306
- * Create the name mapping object that will allow for all templatized field
307
- * references to be de-templatized.
308
- * This also removes the stored sourceFields and newFields arrays from fieldInfos.
309
- *
310
- * @example
311
- * \{ layer0: \{ fields: \{ lowerCaseSourceFieldName: newFieldNameAfterDeployment \} \} \}
312
- *
313
- * @param layerInfos The object that stores the cached layer properties and name mapping
314
- * @returns The settings object that will be used to de-templatize the field references.
315
- */
316
- export function getLayerSettings(layerInfos, url, itemId, enterpriseIDMapping) {
317
- const settings = {};
318
- const ids = Object.keys(layerInfos);
319
- ids.forEach((id) => {
320
- const _layerId = getProp(layerInfos[id], "item.id");
321
- const isNum = parseInt(_layerId, 10) > -1;
322
- const layerId = isNum && enterpriseIDMapping
323
- ? enterpriseIDMapping[_layerId]
324
- : isNum
325
- ? _layerId
326
- : id;
327
- settings[`layer${isNum ? _layerId : id}`] = {
328
- fields: _getNameMapping(layerInfos, id),
329
- url: checkUrlPathTermination(url) + layerId,
330
- layerId,
331
- itemId
332
- };
333
- deleteProp(layerInfos[id], "newFields");
334
- deleteProp(layerInfos[id], "sourceFields");
335
- });
336
- return settings;
337
- }
338
- /**
339
- * Set the names and titles for all feature services.
340
- *
341
- * This function will ensure that we have unique feature service names.
342
- * The feature service name will have the solution item id appended.
343
- *
344
- * @param templates A collection of AGO item templates.
345
- * @param solutionItemId The item id for the deployed solution item.
346
- * @returns An updated collection of AGO templates with unique feature service names.
347
- */
348
- export function setNamesAndTitles(templates, solutionItemId) {
349
- const names = [];
350
- return templates.map(t => {
351
- /* istanbul ignore else */
352
- if (t.item.type === "Feature Service") {
353
- // Retain the existing title but swap with name if it's missing
354
- t.item.title = t.item.title || t.item.name;
355
- /* istanbul ignore else */
356
- if (!isTrackingViewTemplate(t)) {
357
- // Need to set the service name: name + "_" + newItemId
358
- let baseName = t.item.name || t.item.title;
359
- // If the name already contains a GUID remove it
360
- baseName = baseName.replace(/_[0-9A-F]{32}/gi, "");
361
- // The name length limit is 98
362
- // Limit the baseName to 50 characters before the _<guid>
363
- const name = baseName.substring(0, 50) + "_" + solutionItemId;
364
- // If the name + GUID already exists then append "_occurrenceCount"
365
- t.item.name =
366
- names.indexOf(name) === -1
367
- ? name
368
- : `${name}_${names.filter(n => n === name).length}`;
369
- names.push(name);
370
- }
371
- }
372
- return t;
373
- });
374
- }
375
- /**
376
- * This is used when deploying views.
377
- * We need to update fields referenced in adminLayerInfo for relationships prior to deploying the view.
378
- * This moves the fieldInfos for the views source layers from the item settings for the source layer
379
- * to the item settings for the view.
380
- *
381
- * @param itemTemplate The current itemTemplate being processed.
382
- * @param settings The settings object used to de-templatize the various templates within the item.
383
- */
384
- export function updateSettingsFieldInfos(itemTemplate, settings) {
385
- const dependencies = itemTemplate.dependencies;
386
- const id = itemTemplate.itemId;
387
- const settingsKeys = Object.keys(settings);
388
- settingsKeys.forEach((k) => {
389
- if (id === settings[k].itemId) {
390
- dependencies.forEach((d) => {
391
- settingsKeys.forEach((_k) => {
392
- /* istanbul ignore else */
393
- if (d === _k) {
394
- // combine for multi-source views
395
- const fieldInfos = {};
396
- fieldInfos[d] = getProp(settings[_k], "fieldInfos");
397
- settings[k]["sourceServiceFields"] = settings[k]["sourceServiceFields"]
398
- ? { ...settings[k]["sourceServiceFields"], ...fieldInfos }
399
- : fieldInfos;
400
- const layerKeys = Object.keys(settings[_k]);
401
- layerKeys.forEach(layerKey => {
402
- /* istanbul ignore else */
403
- if (layerKey.startsWith("layer")) {
404
- settings[k][layerKey] = settings[_k][layerKey];
405
- }
406
- });
407
- }
408
- });
409
- });
410
- }
411
- });
412
- }
413
- /**
414
- * Add flag to indicate item should be ignored.
415
- * Construct template dictionary to detemplatize any references to this item by other items.
416
- *
417
- * @param template Template for feature service item
418
- * @param authentication Credentials for the request
419
- * @returns A promise that will resolve when template has been updated
420
- * @private
421
- */
422
- export function updateTemplateForInvalidDesignations(template, authentication) {
423
- return new Promise((resolve, reject) => {
424
- template.properties.hasInvalidDesignations = true;
425
- if (template.item.url) {
426
- // get the admin URL
427
- const url = template.item.url;
428
- rest_request(url + "?f=json", {
429
- authentication: authentication
430
- }).then(serviceData => {
431
- const layerInfos = {};
432
- const layersAndTables = (serviceData.layers || []).concat(serviceData.tables || []);
433
- layersAndTables.forEach((l) => {
434
- /* istanbul ignore else */
435
- if (l && l.hasOwnProperty("id")) {
436
- layerInfos[l.id] = l;
437
- }
438
- });
439
- template.data[template.itemId] = Object.assign({
440
- itemId: template.itemId
441
- }, getLayerSettings(layerInfos, url, template.itemId));
442
- resolve(template);
443
- }, e => reject(fail(e)));
444
- }
445
- else {
446
- resolve(template);
447
- }
448
- });
449
- }
450
- /**
451
- * Get the contingent values for each layer in the service.
452
- * Remove key props that cannot be included with the addToDef call on deploy.
453
- * Store the values alongside other key feature service properties in the template
454
- *
455
- * @param properties the current feature services properties
456
- * @param adminUrl the current feature service url
457
- * @param authentication Credentials for the request to AGOL
458
- * @returns A promise that will resolve when the contingent values have been fetched.
459
- * This function will update the provided properties argument when contingent values are found.
460
- */
461
- export function processContingentValues(properties, adminUrl, authentication) {
462
- return new Promise((resolve, reject) => {
463
- if (getProp(properties, 'service.isView')) {
464
- // views will inherit from the source service
465
- resolve();
466
- }
467
- else {
468
- const layersAndTables = (properties.layers || []).concat(properties.tables || []);
469
- const layerIds = [];
470
- const contingentValuePromises = layersAndTables.reduce((prev, cur) => {
471
- /* istanbul ignore else */
472
- if (cur.hasContingentValuesDefinition) {
473
- prev.push(rest_request(`${adminUrl}/${cur['id']}/contingentValues?f=json`, { authentication }));
474
- layerIds.push(cur['id']);
475
- }
476
- return prev;
477
- }, []);
478
- if (contingentValuePromises.length > 0) {
479
- Promise.all(contingentValuePromises).then((results) => {
480
- const contingentValues = {};
481
- results.forEach((r, i) => {
482
- deleteProp(r, 'typeCodes');
483
- /* istanbul ignore else */
484
- if (getProp(r, 'stringDicts') && getProp(r, 'contingentValuesDefinition')) {
485
- r.contingentValuesDefinition['stringDicts'] = r.stringDicts;
486
- deleteProp(r, 'stringDicts');
487
- }
488
- deleteProps(getProp(r, 'contingentValuesDefinition'), ['layerID', 'layerName', 'geometryType', 'hasSubType']);
489
- contingentValues[layerIds[i]] = r;
490
- });
491
- properties.contingentValues = contingentValues;
492
- resolve();
493
- }, reject);
494
- }
495
- else {
496
- resolve();
497
- }
498
- }
499
- });
500
- }
501
- /**
502
- * Replace the field name reference templates with the new field names after deployment.
503
- *
504
- * @param fieldInfos The object that stores the cached layer properties and name mapping
505
- * @param popupInfos The object from the popupInfo property for the layer
506
- * @param adminLayerInfos The object from the adminLayerInfo property for the layer
507
- * @param settings The settings object that has all of the mappings for de-templatizing.
508
- * @returns An object that contains updated instances of popupInfos, fieldInfos, and adminLayerInfos
509
- */
510
- export function deTemplatizeFieldInfos(fieldInfos, popupInfos, adminLayerInfos, settings) {
511
- const fieldInfoKeys = Object.keys(fieldInfos);
512
- fieldInfoKeys.forEach(id => {
513
- if (fieldInfos[id].hasOwnProperty("templates")) {
514
- fieldInfos[id].templates = JSON.parse(replaceInTemplate(JSON.stringify(fieldInfos[id].templates), settings));
515
- }
516
- if (fieldInfos[id].hasOwnProperty("adminLayerInfo")) {
517
- adminLayerInfos[id].viewLayerDefinition.table.relatedTables =
518
- fieldInfos[id].adminLayerInfo;
519
- deleteProp(fieldInfos[id], "adminLayerInfo");
520
- }
521
- if (fieldInfos[id].hasOwnProperty("types")) {
522
- fieldInfos[id].types = JSON.parse(replaceInTemplate(JSON.stringify(fieldInfos[id].types), settings));
523
- }
524
- });
525
- return {
526
- popupInfos: replaceInTemplate(popupInfos, settings),
527
- fieldInfos: replaceInTemplate(fieldInfos, settings),
528
- adminLayerInfos: replaceInTemplate(adminLayerInfos, settings)
529
- };
530
- }
531
- /**
532
- * This is used when deploying views.
533
- * We need to update fields referenced in adminLayerInfo for relationships prior to deploying the view.
534
- * This moves the fieldInfos for the views source layers from the item settings for the source layer
535
- * to the item settings for the view.
536
- *
537
- * @param itemTemplate The current itemTemplate being processed.
538
- * @returns array of layers and tables
539
- */
540
- export function getLayersAndTables(itemTemplate) {
541
- const properties = itemTemplate.properties;
542
- const layersAndTables = [];
543
- (properties.layers || []).forEach(function (layer) {
544
- layersAndTables.push({
545
- item: layer,
546
- type: "layer"
547
- });
548
- });
549
- (properties.tables || []).forEach(function (table) {
550
- layersAndTables.push({
551
- item: table,
552
- type: "table"
553
- });
554
- });
555
- return layersAndTables;
556
- }
557
- /**
558
- * Fetch each layer and table from service so we can determine what fields they have.
559
- * This is leveraged when we are using existing services so we can determine if we need to
560
- * remove any fields from views that depend on these layers and tables.
561
- *
562
- * @param url Feature service endpoint
563
- * @param ids layer and table ids
564
- * @param authentication Credentials for the request
565
- * @returns A promise that will resolve an array of promises with either a failure or the data
566
- * @private
567
- */
568
- export function getExistingLayersAndTables(url, ids, authentication) {
569
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
570
- return new Promise(resolve => {
571
- const defs = ids.map(id => {
572
- return rest_request(checkUrlPathTermination(url) + id, {
573
- authentication
574
- });
575
- });
576
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
577
- Promise.all(defs.map(p => p.catch(e => e))).then(resolve);
578
- });
579
- }
580
- /**
581
- * Adds the layers and tables of a feature service to it and restores their relationships.
582
- *
583
- * @param itemTemplate Feature service
584
- * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature
585
- * service)
586
- * @param popupInfos the cached popup info from the layers
587
- * @param authentication Credentials for the request
588
- * @returns A promise that will resolve when all layers and tables have been added
589
- * @private
590
- */
591
- export function addFeatureServiceLayersAndTables(itemTemplate, templateDictionary, popupInfos, authentication) {
592
- return new Promise((resolve, reject) => {
593
- if (isTrackingViewTemplate(itemTemplate)) {
594
- resolve(null);
595
- }
596
- else {
597
- // Create a hash of various properties that contain field references
598
- const fieldInfos = {};
599
- const adminLayerInfos = {};
600
- // Add the service's layers and tables to it
601
- const layersAndTables = getLayersAndTables(itemTemplate);
602
- if (layersAndTables.length > 0) {
603
- addFeatureServiceDefinition(itemTemplate.item.url || "", layersAndTables, templateDictionary, authentication, itemTemplate.key, adminLayerInfos, fieldInfos, itemTemplate).then(() => {
604
- // Detemplatize field references and update the layer properties
605
- // Only failure path is handled by addFeatureServiceDefinition
606
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
607
- updateLayerFieldReferences(itemTemplate, fieldInfos, popupInfos, adminLayerInfos, templateDictionary).then(r => {
608
- // Update relationships and layer definitions
609
- const updates = getLayerUpdates({
610
- message: "updated layer definition",
611
- objects: r.layerInfos.fieldInfos,
612
- itemTemplate: r.itemTemplate,
613
- authentication
614
- }, templateDictionary.isPortal);
615
- // Process the updates sequentially
616
- updates
617
- .reduce((prev, update) => {
618
- return prev.then(() => {
619
- return getRequest(update);
620
- });
621
- }, Promise.resolve(null))
622
- .then(() => resolve(null), (e) => reject(fail(e)) // getRequest
623
- );
624
- });
625
- }, e => reject(fail(e)) // addFeatureServiceDefinition
626
- );
627
- }
628
- else {
629
- resolve(null);
630
- }
631
- }
632
- });
633
- }
634
- /**
635
- * Updates a feature service with a list of layers and/or tables.
636
- *
637
- * @param serviceUrl URL of feature service
638
- * @param listToAdd List of layers and/or tables to add
639
- * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature
640
- * service)
641
- * @param authentication Credentials for the request
642
- * @param key
643
- * @param adminLayerInfos Hash map of a layers adminLayerInfo
644
- * @param fieldInfos Hash map of properties that contain field references
645
- * @param itemTemplate
646
- * @returns A promise that will resolve when the feature service has been updated
647
- * @private
648
- */
649
- export function addFeatureServiceDefinition(serviceUrl, listToAdd, templateDictionary, authentication, key, adminLayerInfos, fieldInfos, itemTemplate) {
650
- return new Promise((resolve, reject) => {
651
- if (isTrackingViewTemplate(itemTemplate)) {
652
- resolve(null);
653
- }
654
- else {
655
- let options = {
656
- layers: [],
657
- tables: [],
658
- authentication
659
- };
660
- // if the service has veiws keep track of the fields so we can use them to
661
- // compare with the view fields
662
- /* istanbul ignore else */
663
- if (getProp(itemTemplate, "properties.service.hasViews")) {
664
- _updateTemplateDictionaryFields(itemTemplate, templateDictionary);
665
- }
666
- const isSelfReferential = _isSelfReferential(listToAdd);
667
- listToAdd = _updateOrder(listToAdd, isSelfReferential);
668
- const chunkSize = _getLayerChunkSize();
669
- const layerChunks = [];
670
- listToAdd.forEach((toAdd, i) => {
671
- let item = toAdd.item;
672
- const originalId = item.id;
673
- fieldInfos = cacheFieldInfos(item, fieldInfos);
674
- // cache the values to be added in seperate addToDef calls
675
- fieldInfos = cacheContingentValues(item.id, fieldInfos, itemTemplate);
676
- /* istanbul ignore else */
677
- if (item.isView) {
678
- deleteViewProps(item);
679
- }
680
- // when the item is a view we need to grab the supporting fieldInfos
681
- /* istanbul ignore else */
682
- if (itemTemplate.properties.service.isView) {
683
- _updateGeomFieldName(item.adminLayerInfo, templateDictionary);
684
- adminLayerInfos[originalId] = item.adminLayerInfo;
685
- // need to update adminLayerInfo before adding to the service def
686
- // bring over the fieldInfos from the source layer
687
- updateSettingsFieldInfos(itemTemplate, templateDictionary);
688
- // update adminLayerInfo before add to definition with view source fieldInfo settings
689
- item.adminLayerInfo = replaceInTemplate(item.adminLayerInfo, templateDictionary);
690
- /* istanbul ignore else */
691
- if (fieldInfos && fieldInfos.hasOwnProperty(item.id)) {
692
- Object.keys(templateDictionary).some(k => {
693
- if (templateDictionary[k].itemId === itemTemplate.itemId) {
694
- fieldInfos[item.id]["sourceServiceFields"] =
695
- templateDictionary[k].sourceServiceFields;
696
- return true;
697
- }
698
- else {
699
- return false;
700
- }
701
- });
702
- }
703
- }
704
- /* istanbul ignore else */
705
- if (templateDictionary.isPortal) {
706
- item = _updateForPortal(item, itemTemplate, templateDictionary);
707
- }
708
- removeLayerOptimization(item);
709
- // this can still chunk layers
710
- options = _updateAddOptions(itemTemplate, options, layerChunks, isSelfReferential, authentication);
711
- if (item.type === "Feature Layer") {
712
- options.layers.push(item);
713
- }
714
- else {
715
- options.tables.push(item);
716
- }
717
- // In general we are switching to not use chunking. Rather if we exceed the defined chunk size
718
- // we will use an async request.
719
- // Currently the only case that should chunk the requests is when we have a multisource view
720
- // handled in _updateAddOptions above
721
- /* istanbul ignore else */
722
- if (i + 1 === listToAdd.length) {
723
- layerChunks.push(Object.assign({}, options));
724
- options = {
725
- layers: [],
726
- tables: [],
727
- authentication
728
- };
729
- }
730
- });
731
- // will use async by default rather than chunk the layer requests when we have more layers
732
- // than the defined chunk size
733
- const useAsync = listToAdd.length > chunkSize;
734
- layerChunks
735
- .reduce((prev, curr) => prev.then(() => addToServiceDefinition(serviceUrl, curr, false, useAsync)), Promise.resolve(null))
736
- .then(() => resolve(null), (e) => reject(fail(e)));
737
- }
738
- });
739
- }
740
- /**
741
- * When a view is a multi service view sort based on the id
742
- * https://github.com/Esri/solution.js/issues/1048
743
- *
744
- * @param layersAndTables The list of layers and tables for the current template
745
- * @param isSelfReferential Indicates if any layers or tables have relationships with other layers or tables in the same service
746
- *
747
- * @returns Sorted list of layers and tables when using a multi-service view
748
- * @private
749
- */
750
- export function _updateOrder(layersAndTables, isSelfReferential) {
751
- return isSelfReferential ? layersAndTables.sort((a, b) => a.item.id - b.item.id) : layersAndTables;
752
- }
753
- /**
754
- * When a view is a multi service view add each layer separately
755
- * https://github.com/Esri/solution.js/issues/871
756
- *
757
- * @param itemTemplate The current itemTemplate being processed
758
- * @param options Add to service definition options
759
- * @param layerChunks Groups of layers or tables to add to the service
760
- * @param isSelfReferential Indicates if any layers or tables have relationships with other layers or tables in the same service
761
- * @param authentication Credentials for the request
762
- *
763
- * @returns Add to service definition options
764
- * @private
765
- */
766
- export function _updateAddOptions(itemTemplate, options, layerChunks, isSelfReferential, authentication) {
767
- const isMsView = getProp(itemTemplate, "properties.service.isMultiServicesView") || false;
768
- /* istanbul ignore else */
769
- if (isMsView || isSelfReferential) {
770
- // if we already have some layers or tables add them first
771
- /* istanbul ignore else */
772
- if (options.layers.length > 0 || options.tables.length > 0) {
773
- layerChunks.push(Object.assign({}, options));
774
- options = {
775
- layers: [],
776
- tables: [],
777
- authentication
778
- };
779
- }
780
- }
781
- return options;
782
- }
783
- /**
784
- * Determine if any layer or table within the service references
785
- * other layers or tables within the same service
786
- *
787
- * @param layersAndTables the list of layers and tables from the service
788
- *
789
- * @returns true when valid internal references are found
790
- * @private
791
- */
792
- export function _isSelfReferential(layersAndTables) {
793
- const names = layersAndTables.map(l => l.item.name);
794
- const srcTables = {};
795
- return layersAndTables.some(l => {
796
- const table = l.item.adminLayerInfo?.viewLayerDefinition?.table;
797
- if (table) {
798
- const name = table.sourceServiceName;
799
- const id = table.sourceLayerId;
800
- if (name && id > -1) {
801
- if (Object.keys(srcTables).indexOf(name) > -1) {
802
- if (srcTables[name].indexOf(id) > -1) {
803
- return true;
804
- }
805
- else {
806
- srcTables[name].push(id);
807
- }
808
- }
809
- else {
810
- srcTables[name] = [id];
811
- }
812
- }
813
- return (table.relatedTables || []).some(r => names.indexOf(r.name) > -1);
814
- }
815
- });
816
- }
817
- /**
818
- * Remove "multiScaleGeometryInfo" for issue #526 to prevent invalid enablement of layer optimization
819
- *
820
- * @param layer the layer to evaluate
821
- * @private
822
- */
823
- export function removeLayerOptimization(layer) {
824
- /* istanbul ignore else */
825
- if (layer.multiScaleGeometryInfo) {
826
- deleteProp(layer, "multiScaleGeometryInfo");
827
- }
828
- }
829
- /**
830
- * Handle portal specific updates to the item
831
- *
832
- * @param item the item to update
833
- * @param itemTemplate the item template
834
- * @param templateDictionary Hash mapping Solution source id to id of its clone
835
- *
836
- * @returns the updated item
837
- * @private
838
- */
839
- export function _updateForPortal(item, itemTemplate, templateDictionary) {
840
- // When deploying to portal we need to adjust the uniquie ID field up front
841
- /* istanbul ignore else */
842
- if (item.uniqueIdField && item.uniqueIdField.name) {
843
- item.uniqueIdField.name = String(item.uniqueIdField.name).toLocaleLowerCase();
844
- }
845
- // Portal will fail if the geometryField is null
846
- if (item.type === "Table" && item.adminLayerInfo) {
847
- deleteProp(item.adminLayerInfo, "geometryField");
848
- }
849
- // Portal will fail if the sourceFields in the viewLayerDef contain fields that are not in the source service
850
- /* istanbul ignore else */
851
- if (item.isView) {
852
- const viewLayerDefTable = getProp(item, "adminLayerInfo.viewLayerDefinition.table");
853
- let fieldNames = [];
854
- if (viewLayerDefTable) {
855
- const tableFieldNames = _getFieldNames(viewLayerDefTable, itemTemplate, templateDictionary);
856
- fieldNames = fieldNames.concat(tableFieldNames);
857
- const dynamicFieldNames = _getDynamicFieldNames(viewLayerDefTable);
858
- fieldNames = fieldNames.concat(dynamicFieldNames);
859
- setProp(item, "adminLayerInfo.viewLayerDefinition.table", _updateSourceLayerFields(viewLayerDefTable, fieldNames));
860
- // Handle related also
861
- /* istanbul ignore else */
862
- if (Array.isArray(viewLayerDefTable.relatedTables)) {
863
- viewLayerDefTable.relatedTables.map((relatedTable) => {
864
- const relatedTableFieldNames = _getFieldNames(relatedTable, itemTemplate, templateDictionary);
865
- fieldNames = fieldNames.concat(relatedTableFieldNames);
866
- const dynamicRelatedFieldNames = _getDynamicFieldNames(relatedTable);
867
- fieldNames = fieldNames.concat(dynamicRelatedFieldNames);
868
- return _updateSourceLayerFields(relatedTable, [...relatedTableFieldNames, ...dynamicRelatedFieldNames]);
869
- });
870
- }
871
- }
872
- else {
873
- Object.keys(templateDictionary).some(k => {
874
- /* istanbul ignore else */
875
- if (templateDictionary[k].itemId === item.serviceItemId) {
876
- const layerInfo = templateDictionary[k][`layer${item.id}`];
877
- /* istanbul ignore else */
878
- if (layerInfo && layerInfo.fields) {
879
- if (Array.isArray(layerInfo.fields)) {
880
- fieldNames = layerInfo.fields.map((f) => f.name);
881
- }
882
- else {
883
- fieldNames = Object.keys(layerInfo.fields);
884
- }
885
- }
886
- return true;
887
- }
888
- });
889
- }
890
- item = _updateItemFields(item, fieldNames);
891
- }
892
- // not allowed to set sourceSchemaChangesAllowed or isView for portal
893
- // these are set when you create the service
894
- deleteProp(item, "isView");
895
- return item;
896
- }
897
- /**
898
- * Get a list of the source layer field names
899
- *
900
- * @param table the table instance to compare
901
- * @param itemTemplate the item template
902
- * @param templateDictionary Hash mapping Solution source id to id of its clone
903
- *
904
- * @returns an array of the source layers fields
905
- * @private
906
- */
907
- export function _getFieldNames(table, itemTemplate, templateDictionary) {
908
- let sourceLayerFields = [];
909
- const viewSourceLayerId = table.sourceLayerId;
910
- /* istanbul ignore else */
911
- if (typeof viewSourceLayerId === "number") {
912
- // need to make sure these actually exist in the source..
913
- itemTemplate.dependencies.forEach(d => {
914
- const layerInfo = templateDictionary[d][`layer${viewSourceLayerId}`];
915
- /* istanbul ignore else */
916
- if (layerInfo &&
917
- layerInfo.fields &&
918
- templateDictionary[d].name === table.sourceServiceName) {
919
- if (Array.isArray(layerInfo.fields)) {
920
- sourceLayerFields = sourceLayerFields.concat(layerInfo.fields.map((f) => f.name));
921
- }
922
- else {
923
- sourceLayerFields = sourceLayerFields.concat(Object.keys(layerInfo.fields));
924
- }
925
- }
926
- });
927
- return sourceLayerFields;
928
- }
929
- }
930
- /**
931
- * Get a list of any dynamically calculated fields
932
- * These fields are still valid but will not exist in the source service
933
- *
934
- * @param table the table instance to compare
935
- *
936
- * @returns an array of field names
937
- * @private
938
- */
939
- export function _getDynamicFieldNames(table) {
940
- const fieldNames = table.sourceLayerFields.reduce((prev, cur) => {
941
- if (cur.statisticType) {
942
- prev.push(cur.name);
943
- }
944
- return prev;
945
- }, []);
946
- return [...new Set(fieldNames)];
947
- }
948
- /**
949
- * Remove fields references from fields and indexes that do not exist in the source service
950
- *
951
- * @param item Layer or table
952
- * @param templateDictionary Hash mapping Solution source id to id of its clone
953
- *
954
- * @returns updated layer or table
955
- * @private
956
- */
957
- export function _updateItemFields(item, fieldNames) {
958
- /* istanbul ignore else */
959
- if (fieldNames.length > 0) {
960
- /* istanbul ignore else */
961
- if (item.fields) {
962
- item.fields = item.fields.filter((f) => fieldNames.indexOf(f.name) > -1);
963
- }
964
- /* istanbul ignore else */
965
- if (item.indexes) {
966
- item.indexes = item.indexes.filter((f) => fieldNames.indexOf(f.fields) > -1);
967
- }
968
- }
969
- return item;
970
- }
971
- /**
972
- * Filter the sourceLayerFields for the table
973
- *
974
- * @param table the table instance to evaluate
975
- * @param sourceLayerFields array of fields from the source service
976
- * @returns Updated instance of the table
977
- * @private
978
- */
979
- export function _updateSourceLayerFields(table, sourceLayerFields) {
980
- /* istanbul ignore else */
981
- if (Array.isArray(table.sourceLayerFields) &&
982
- table.sourceLayerFields.length > 0) {
983
- // need to make sure these actually exist in the source..
984
- /* istanbul ignore else */
985
- if (sourceLayerFields.length > 0) {
986
- setProp(table, "sourceLayerFields", table.sourceLayerFields.filter((f) => sourceLayerFields.indexOf(f.source.toLowerCase()) > -1));
987
- }
988
- }
989
- return table;
990
- }
991
- /**
992
- * When the itemm is a view with a geometry field update the value to
993
- * use the table name from the view layer def
994
- *
995
- * @param item the item details from the current template
996
- * @param templateDictionary Hash mapping property names to replacement values
997
- * @private
998
- */
999
- export function _updateGeomFieldName(adminLayerInfo, templateDictionary) {
1000
- // issue #471
1001
- const tableName = getProp(adminLayerInfo, "viewLayerDefinition.table.name");
1002
- const fieldName = getProp(adminLayerInfo, "geometryField.name");
1003
- /* istanbul ignore else */
1004
- if (fieldName && tableName) {
1005
- const geomName = templateDictionary.isPortal
1006
- ? `${tableName}.shape`
1007
- : `${tableName}.Shape`;
1008
- setProp(adminLayerInfo, "geometryField.name", geomName);
1009
- }
1010
- else if (!fieldName && getProp(adminLayerInfo, "geometryField")) {
1011
- // null geom field will cause failure to deploy in portal
1012
- // this is also checked and removed on deploy for older solutions
1013
- deleteProp(adminLayerInfo, "geometryField");
1014
- }
1015
- }
1016
- /**
1017
- * Add the fields to the templateDictionary when a service has views
1018
- * these are used to compare with fields from the view when domains are involved
1019
- * when a view field has a domain that differs from that of the source service
1020
- * the definition needs to be modified in an update call rather than when it is first added.
1021
- * This should only happen when the domain differs.
1022
- *
1023
- * @param itemTemplate
1024
- * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature service)
1025
- * @private
1026
- */
1027
- export function _updateTemplateDictionaryFields(itemTemplate, templateDictionary, compareItemId = true) {
1028
- const layers = itemTemplate.properties.layers;
1029
- const tables = itemTemplate.properties.tables;
1030
- const layersAndTables = layers.concat(tables);
1031
- const fieldInfos = {};
1032
- layersAndTables.forEach(layerOrTable => {
1033
- fieldInfos[layerOrTable.id] = layerOrTable.fields;
1034
- });
1035
- Object.keys(templateDictionary).some(k => {
1036
- if (compareItemId
1037
- ? templateDictionary[k].itemId === itemTemplate.itemId
1038
- : k === itemTemplate.itemId) {
1039
- templateDictionary[k].fieldInfos = fieldInfos;
1040
- return true;
1041
- }
1042
- else {
1043
- return false;
1044
- }
1045
- });
1046
- }
1047
- /**
1048
- * Set the defaultSpatialReference variable with the services spatial reference.
1049
- * If this item is a Feature Service that has child views then we will use this value
1050
- * if one or more of the child views spatial reference differs from that of its parent.
1051
- *
1052
- * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature service)
1053
- * @param itemId The source id for the item
1054
- * @param spatialReference \{ wkid: 102100 \} for example
1055
- * @private
1056
- */
1057
- export function setDefaultSpatialReference(templateDictionary, itemId, spatialReference) {
1058
- /* istanbul ignore else */
1059
- if (spatialReference) {
1060
- setCreateProp(templateDictionary, `${itemId}.defaultSpatialReference`, spatialReference);
1061
- }
1062
- }
1063
- /**
1064
- * Compare the spatial reference of the current item against its dependencies.
1065
- * The spatial reference of a view cannot differ from its source service.
1066
- * If the view has a different spatial reference from its source use the source spatial reference.
1067
- *
1068
- * @param serviceInfo Basic service information
1069
- * @param itemTemplate The current template to process
1070
- * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature service)
1071
- * @private
1072
- */
1073
- export function validateSpatialReferenceAndExtent(serviceInfo, itemTemplate, templateDictionary) {
1074
- /* istanbul ignore else */
1075
- if (getProp(serviceInfo, "service.isView")) {
1076
- let sourceSR;
1077
- let sourceExt;
1078
- itemTemplate.dependencies.some(id => {
1079
- const source = templateDictionary[id];
1080
- const sr = getProp(source, "defaultSpatialReference");
1081
- /* istanbul ignore else */
1082
- if (!sourceSR && sr) {
1083
- sourceSR = sr;
1084
- }
1085
- const ext = getProp(source, "defaultExtent");
1086
- /* istanbul ignore else */
1087
- if (!sourceExt && ext) {
1088
- sourceExt = ext;
1089
- }
1090
- return sourceSR && sourceExt;
1091
- });
1092
- const sourceWkid = getProp(sourceSR, "wkid");
1093
- const viewWkid = getProp(serviceInfo, "service.spatialReference.wkid");
1094
- /* istanbul ignore else */
1095
- if (sourceWkid && viewWkid && sourceWkid !== viewWkid) {
1096
- setCreateProp(serviceInfo, "service.spatialReference", sourceSR);
1097
- }
1098
- const viewExt = getProp(serviceInfo, "service.fullExtent");
1099
- /* istanbul ignore else */
1100
- if (sourceExt &&
1101
- viewExt &&
1102
- JSON.stringify(sourceExt) !== JSON.stringify(viewExt)) {
1103
- setCreateProp(serviceInfo, "defaultExtent", sourceExt);
1104
- }
1105
- }
1106
- }
1107
- /**
1108
- * Updates a feature service with a list of layers and/or tables.
1109
- *
1110
- * @param itemTemplate
1111
- * @param fieldInfos Hash map of properties that contain field references
1112
- * @param popupInfos Hash map of a layers popupInfo
1113
- * @param adminLayerInfos Hash map of a layers adminLayerInfo
1114
- * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature service)
1115
- * @param authentication Credentials for the request
1116
- * @returns A promise that will resolve when the feature service has been updated
1117
- * @private
1118
- */
1119
- export function updateLayerFieldReferences(itemTemplate, fieldInfos, popupInfos, adminLayerInfos, templateDictionary) {
1120
- return new Promise((resolveFn, rejectFn) => {
1121
- // Will need to do some post processing for fields
1122
- // to handle any potential field name changes when deploying to portal
1123
- postProcessFields(itemTemplate, fieldInfos, popupInfos, adminLayerInfos, templateDictionary).then((layerInfos) => {
1124
- // Update the items text with detemplatized popupInfo
1125
- updatePopupInfo(itemTemplate, layerInfos.popupInfos);
1126
- resolveFn({
1127
- itemTemplate,
1128
- layerInfos
1129
- });
1130
- }, e => rejectFn(fail(e)));
1131
- });
1132
- }
1133
- /**
1134
- * Update the names of fields for each layer or table after it has been
1135
- * added to the definition
1136
- *
1137
- * @param itemTemplate Item to be created
1138
- * @param layerInfos Hash map of properties that contain field references and various layer info
1139
- * @param popupInfos Hash map of a layers popupInfo
1140
- * @param adminLayerInfos Hash map of a layers adminLayerInfo
1141
- * @param templateDictionary
1142
- * @param authentication Credentials for the request
1143
- * @returns An object with detemplatized field references
1144
- * @private
1145
- */
1146
- export function postProcessFields(itemTemplate, layerInfos, popupInfos, adminLayerInfos, templateDictionary) {
1147
- return new Promise((resolveFn, rejectFn) => {
1148
- if (!itemTemplate.item.url) {
1149
- rejectFn(fail("Feature layer " + itemTemplate.itemId + " does not have a URL"));
1150
- }
1151
- else {
1152
- const id = itemTemplate.itemId;
1153
- const settingsKeys = Object.keys(templateDictionary);
1154
- let templateInfo;
1155
- settingsKeys.some(k => {
1156
- if (templateDictionary[k].itemId === id) {
1157
- templateInfo = templateDictionary[k];
1158
- return true;
1159
- }
1160
- else {
1161
- return false;
1162
- }
1163
- });
1164
- // concat any layers and tables to process
1165
- const layers = itemTemplate.properties.layers;
1166
- const tables = itemTemplate.properties.tables;
1167
- const layersAndTables = layers.concat(tables);
1168
- // Set the newFields property for the layerInfos...this will contain all fields
1169
- // as they are after being added to the definition.
1170
- // This allows us to handle any potential field name changes after deploy to portal
1171
- layersAndTables.forEach((item) => {
1172
- // when deploying to portal "isView" is only set for create service and will fail when
1173
- // present on addToDef so this property is removed from item and we should check the templates service info
1174
- const isView = item.isView || itemTemplate.properties.service.isView;
1175
- /* istanbul ignore else */
1176
- if (layerInfos && layerInfos.hasOwnProperty(item.id)) {
1177
- const layerInfo = layerInfos[item.id];
1178
- layerInfo["isView"] = item.isView;
1179
- layerInfo["newFields"] = item.fields;
1180
- layerInfo["sourceSchemaChangesAllowed"] =
1181
- item.sourceSchemaChangesAllowed;
1182
- /* istanbul ignore else */
1183
- if (item.editFieldsInfo) {
1184
- // more than case change when deployed to protal so keep track of the new names
1185
- layerInfo["newEditFieldsInfo"] = JSON.parse(JSON.stringify(item.editFieldsInfo));
1186
- }
1187
- /* istanbul ignore else */
1188
- if (isView && templateInfo && templateDictionary.isPortal) {
1189
- // when the item is a view bring over the source service fields so we can compare the domains
1190
- layerInfo["sourceServiceFields"] = templateInfo.sourceServiceFields;
1191
- }
1192
- }
1193
- });
1194
- // Add the layerInfos to the settings object to be used while detemplatizing
1195
- settingsKeys.forEach((k) => {
1196
- if (id === templateDictionary[k].itemId) {
1197
- templateDictionary[k] = Object.assign(templateDictionary[k], getLayerSettings(layerInfos, templateDictionary[k].url, id));
1198
- }
1199
- });
1200
- // update the layerInfos object with current field names
1201
- resolveFn(deTemplatizeFieldInfos(layerInfos, popupInfos, adminLayerInfos, templateDictionary));
1202
- }
1203
- });
1204
- }
1205
- /**
1206
- * Add popup info back to the layer item
1207
- *
1208
- * @param itemTemplate
1209
- * @param popupInfos popup info to be added back to the layer
1210
- * @private
1211
- */
1212
- export function updatePopupInfo(itemTemplate, popupInfos) {
1213
- ["layers", "tables"].forEach(type => {
1214
- const _items = getProp(itemTemplate, "data." + type);
1215
- /* istanbul ignore else */
1216
- if (_items && Array.isArray(_items)) {
1217
- _items.forEach((item) => {
1218
- item.popupInfo = getProp(popupInfos, type + "." + item.id) || {};
1219
- });
1220
- }
1221
- });
1222
- }
1223
- //#endregion
1224
- //#region Private helper functions --------------------------------------------------//
1225
- /**
1226
- * Helper function to templatize value and make sure its converted to lowercase
1227
- *
1228
- * @param basePath path used to de-templatize while deploying
1229
- * @param value to be converted to lower case for lookup while deploying
1230
- * @private
1231
- */
1232
- export function _templatize(basePath, value, suffix) {
1233
- if (value.startsWith("{{")) {
1234
- return value;
1235
- }
1236
- else {
1237
- return String(templatizeTerm(basePath, basePath, "." + String(value).toLowerCase() + (suffix ? "." + suffix : "")));
1238
- }
1239
- }
1240
- /**
1241
- * templatize an objects property
1242
- *
1243
- * @param object the object with the property to templatize
1244
- * @param property the property of the object to templatize
1245
- * @param basePath path used to de-templatize while deploying
1246
- * @private
1247
- */
1248
- export function _templatizeProperty(object, property, basePath, suffix) {
1249
- if (object && object.hasOwnProperty(property) && object[property]) {
1250
- object[property] = _templatize(basePath, object[property], suffix);
1251
- }
1252
- }
1253
- /**
1254
- * Templatize field references, serviceItemId, and adminLayerInfo for a layer
1255
- *
1256
- * @param dataItem from the items data property
1257
- * @param adminItem from the services admin api
1258
- * @param itemTemplate Template for feature service item
1259
- * @param dependencies Array of IDependency for name mapping
1260
- * @param templatizeFieldReferences Templatize all field references within a layer
1261
- * @returns A promise that will resolve when template has been updated
1262
- * @private
1263
- */
1264
- export function _templatizeLayer(dataItem, adminItem, itemTemplate, dependencies, templatizeFieldReferences, templateDictionary) {
1265
- // check for and repair common field issues
1266
- _validateFields(adminItem);
1267
- // Templatize all properties that contain field references
1268
- /* istanbul ignore else */
1269
- if (templatizeFieldReferences) {
1270
- _templatizeLayerFieldReferences(dataItem, itemTemplate.itemId, adminItem, dependencies);
1271
- }
1272
- const updates = [adminItem];
1273
- if (dataItem) {
1274
- updates.push(dataItem);
1275
- }
1276
- updates.forEach(update => {
1277
- if (update.hasOwnProperty("name")) {
1278
- // templatize the name but leave the current name as the optional default
1279
- update.name = templatizeTerm(update["serviceItemId"] + ".layer" + update.id, update["serviceItemId"] + ".layer" + update.id, ".name||" + update.name);
1280
- }
1281
- if (update.hasOwnProperty("extent")) {
1282
- update.extent = templatizeTerm(update["serviceItemId"], update["serviceItemId"], ".solutionExtent");
1283
- }
1284
- if (update.hasOwnProperty("serviceItemId")) {
1285
- update["serviceItemId"] = templatizeTerm(update["serviceItemId"], update["serviceItemId"], ".itemId");
1286
- }
1287
- if (update.hasOwnProperty("adminLayerInfo")) {
1288
- update.adminLayerInfo = _templatizeAdminLayerInfo(update, dependencies, templateDictionary);
1289
- }
1290
- });
1291
- }
1292
- /**
1293
- * Repair common issues that can occur with feature service field references.
1294
- * This function will mutate the input item if any of the common issues have occured.
1295
- *
1296
- * @param adminItem layer or table from the service
1297
- */
1298
- export function _validateFields(adminItem) {
1299
- const fieldNames = (adminItem.fields || []).map((f) => f.name);
1300
- // Update primary display field if field isn't in the layer.
1301
- _validateDisplayField(adminItem, fieldNames);
1302
- // Remove indexes on fields that don't exist in the layer.
1303
- // Remove duplicate indexes on the same field.
1304
- _validateIndexes(adminItem, fieldNames);
1305
- // Remove field references in templates when field doesn't exist in the layer.
1306
- _validateTemplatesFields(adminItem, fieldNames);
1307
- _validateTypesTemplates(adminItem, fieldNames);
1308
- // Repair editFieldsInfo if field referenced doesn't exist in the layer
1309
- _validateEditFieldsInfo(adminItem, fieldNames);
1310
- }
1311
- /**
1312
- * Update primary display field if casing doesn't match.
1313
- * Update primary display field to the first non OID or GlobalId if the field isn't in the layer.
1314
- *
1315
- * @param adminItem layer or table from the service
1316
- * @param fieldNames string list of fields names
1317
- * @private
1318
- */
1319
- export function _validateDisplayField(adminItem, fieldNames) {
1320
- const displayField = adminItem.displayField || "";
1321
- let i = -1;
1322
- if (fieldNames.some(name => {
1323
- i += 1;
1324
- return name === displayField || name === displayField.toLowerCase();
1325
- })) {
1326
- adminItem.displayField = fieldNames[i];
1327
- }
1328
- else {
1329
- // use the first non-OID non-globalId field we find
1330
- const skipFields = [];
1331
- const oidField = getProp(adminItem, "uniqueIdField.name");
1332
- /* istanbul ignore else */
1333
- if (oidField) {
1334
- skipFields.push(oidField);
1335
- }
1336
- const globalIdField = getProp(adminItem, "globalIdField");
1337
- /* istanbul ignore else */
1338
- if (globalIdField) {
1339
- skipFields.push(globalIdField);
1340
- }
1341
- fieldNames.some(name => {
1342
- if (skipFields.indexOf(name) === -1) {
1343
- adminItem.displayField = name;
1344
- return true;
1345
- }
1346
- else {
1347
- return false;
1348
- }
1349
- });
1350
- }
1351
- }
1352
- /**
1353
- * Remove indexes on fields that don't exist in the layer.
1354
- * Remove duplicate indexes on the same field.
1355
- *
1356
- * @param adminItem layer or table from the service
1357
- * @param fieldNames string list of fields names
1358
- * @private
1359
- */
1360
- export function _validateIndexes(adminItem, fieldNames) {
1361
- const indexes = adminItem.indexes;
1362
- /* istanbul ignore else */
1363
- if (indexes) {
1364
- const indexedFields = [];
1365
- adminItem.indexes = indexes.reduce((filtered, index) => {
1366
- const indexFields = index.fields.split(",");
1367
- const verifiedFields = [];
1368
- indexFields.forEach(indexField => {
1369
- /* istanbul ignore else */
1370
- if (indexedFields.indexOf(indexField) === -1) {
1371
- indexedFields.push(indexField);
1372
- // this is the first index with this field and it should be added if the field exists
1373
- /* istanbul ignore else */
1374
- if (fieldNames.indexOf(indexField) > -1) {
1375
- verifiedFields.push(indexField);
1376
- }
1377
- }
1378
- // else the field has more than one index associated and should not be returned
1379
- });
1380
- /* istanbul ignore else */
1381
- if (verifiedFields.length > 0) {
1382
- index.fields = verifiedFields.join(",");
1383
- filtered.push(index);
1384
- }
1385
- return filtered;
1386
- }, []);
1387
- }
1388
- }
1389
- /**
1390
- * Remove field references from templates that no longer exist.
1391
- *
1392
- * @param adminItem layer or table from the service
1393
- * @param fieldNames string list of fields names
1394
- * @private
1395
- */
1396
- export function _validateTemplatesFields(adminItem, fieldNames) {
1397
- const templates = adminItem.templates;
1398
- /* istanbul ignore else */
1399
- if (templates) {
1400
- adminItem.templates = templates.map(template => {
1401
- const attributes = getProp(template, "prototype.attributes");
1402
- /* istanbul ignore else */
1403
- if (attributes) {
1404
- Object.keys(attributes).forEach(k => {
1405
- /* istanbul ignore else */
1406
- if (fieldNames.indexOf(k) === -1) {
1407
- delete attributes[k];
1408
- }
1409
- });
1410
- setProp(template, "prototype.attributes", attributes);
1411
- }
1412
- return template;
1413
- });
1414
- }
1415
- }
1416
- /**
1417
- * Remove field references from templates that no longer exist.
1418
- *
1419
- * @param adminItem layer or table from the service
1420
- * @param fieldNames string list of fields names
1421
- * @private
1422
- */
1423
- export function _validateTypesTemplates(adminItem, fieldNames) {
1424
- const types = adminItem.types;
1425
- /* istanbul ignore else */
1426
- if (types) {
1427
- adminItem.types = types.map(t => {
1428
- _validateTemplatesFields(t, fieldNames);
1429
- return t;
1430
- });
1431
- }
1432
- }
1433
- /**
1434
- * Check if edit feilds exist but with lower case
1435
- *
1436
- * @param adminItem layer or table from the service
1437
- * @param fieldNames string list of fields names
1438
- * @private
1439
- */
1440
- export function _validateEditFieldsInfo(adminItem, fieldNames) {
1441
- const editFieldsInfo = adminItem.editFieldsInfo;
1442
- /* istanbul ignore else */
1443
- if (editFieldsInfo) {
1444
- const editFieldsInfoKeys = Object.keys(editFieldsInfo);
1445
- editFieldsInfoKeys.forEach(k => {
1446
- const editFieldName = editFieldsInfo[k];
1447
- /* istanbul ignore else */
1448
- if (editFieldName) {
1449
- fieldNames.some(name => {
1450
- if (name === editFieldName) {
1451
- return true;
1452
- }
1453
- else if (name === editFieldName.toLowerCase()) {
1454
- editFieldsInfo[k] = name;
1455
- return true;
1456
- }
1457
- else {
1458
- return false;
1459
- }
1460
- });
1461
- }
1462
- });
1463
- }
1464
- }
1465
- /**
1466
- *
1467
- * Templatize all field references within a layer
1468
- * This is necessary to support potential field name changes when deploying to portal
1469
- * Portal will force all field names to be lower case
1470
- *
1471
- * @param dataItem The data layer instance with field name references within
1472
- * @param itemID The id for the item that contains this layer.
1473
- * @param layer JSON return from the layer being templatized.
1474
- * @param dependencies
1475
- * @returns An updated instance of the layer
1476
- * @private
1477
- */
1478
- export function _templatizeLayerFieldReferences(dataItem, itemID, layer, dependencies) {
1479
- // This is the value that will be used as the template for adlib replacement
1480
- const path = itemID + ".layer" + layer.id + ".fields";
1481
- // Get the field names for various tests
1482
- const fieldNames = layer.fields.map((f) => f.name);
1483
- // Update the layer from the items data property
1484
- if (dataItem) {
1485
- _templatizeAdminLayerInfoFields(dataItem, dependencies);
1486
- _templatizePopupInfo(dataItem, layer, path, itemID, fieldNames);
1487
- }
1488
- // Update the layer
1489
- _templatizeAdminLayerInfoFields(layer, dependencies);
1490
- _templatizeRelationshipFields(layer, itemID);
1491
- _templatizeDefinitionEditor(layer, path, fieldNames);
1492
- _templatizeDefinitionExpression(layer, path, fieldNames);
1493
- _templatizeDrawingInfo(layer, path, fieldNames);
1494
- _templatizeTemplates(layer, path);
1495
- _templatizeTypeTemplates(layer, path);
1496
- _templatizeTimeInfo(layer, path);
1497
- _templatizeDefinitionQuery(layer, path, fieldNames);
1498
- }
1499
- /**
1500
- * Templatize a layers adminLayerInfo by removing properties that will case issues with clone.
1501
- * Also templatizes the source service name when we are dealing with a view.
1502
- *
1503
- * @param layer The layer to be modified
1504
- * @param dependencies Array of service dependencies
1505
- * @returns A new copy of the modified adminLayerInfo for the given layer
1506
- * @private
1507
- */
1508
- export function _templatizeAdminLayerInfo(layer, dependencies, templateDictionary) {
1509
- // Create new instance of adminLayerInfo to update for clone
1510
- const adminLayerInfo = Object.assign({}, layer.adminLayerInfo);
1511
- _updateGeomFieldName(adminLayerInfo, templateDictionary);
1512
- deleteProp(adminLayerInfo, "xssTrustedFields");
1513
- deleteProp(adminLayerInfo, "tableName");
1514
- // Remove unnecessary properties and templatize key properties from viewLayerDefinition
1515
- /* istanbul ignore else */
1516
- if (adminLayerInfo.viewLayerDefinition) {
1517
- const viewDef = Object.assign({}, adminLayerInfo.viewLayerDefinition);
1518
- _processAdminObject(viewDef, dependencies);
1519
- // Remove unnecessary properties and templatize key properties from viewLayerDefinition.table
1520
- /* istanbul ignore else */
1521
- if (viewDef.table) {
1522
- _processAdminObject(viewDef.table, dependencies);
1523
- /* istanbul ignore else */
1524
- if (viewDef.table.relatedTables) {
1525
- viewDef.table.relatedTables.forEach((table) => {
1526
- _processAdminObject(table, dependencies);
1527
- });
1528
- }
1529
- }
1530
- adminLayerInfo.viewLayerDefinition = viewDef;
1531
- }
1532
- return adminLayerInfo;
1533
- }
1534
- /**
1535
- * Remove sourceId and templatize the sourceServiceName
1536
- *
1537
- * @param object The layer to be modified
1538
- * @param dependencies Array of service dependencies
1539
- * @private
1540
- */
1541
- export function _processAdminObject(object, dependencies) {
1542
- deleteProp(object, "sourceId");
1543
- if (object.hasOwnProperty("sourceServiceName")) {
1544
- object.sourceServiceName = _templatizeSourceServiceName(object.sourceServiceName, dependencies);
1545
- }
1546
- }
1547
- /**
1548
- * Templatize the name based on the given dependencies
1549
- *
1550
- * @param lookupName The current name from the source service
1551
- * @param dependencies Array of IDependency for name mapping
1552
- * @returns The templatized name || undefined when no matching dependency is found
1553
- * @private
1554
- */
1555
- export function _templatizeSourceServiceName(lookupName, dependencies) {
1556
- const deps = dependencies.filter(dependency => dependency.name === lookupName);
1557
- return deps.length === 1 ? _templatize(deps[0].id, "name") : undefined;
1558
- }
1559
- /**
1560
- * templatize the fields referenced in adminLayerInfo
1561
- *
1562
- * @param layer the layer object with the adminLayerInfo property to templatize
1563
- * @param basePath path used to de-templatize while deploying
1564
- * @param itemID the id for the item that contains this layer
1565
- * @private
1566
- */
1567
- export function _templatizeAdminLayerInfoFields(layer, dependencies) {
1568
- // templatize the source layer fields
1569
- const table = getProp(layer, "adminLayerInfo.viewLayerDefinition.table");
1570
- if (table) {
1571
- let id = _getDependantItemId(table.sourceServiceName, dependencies);
1572
- const path = id + ".layer" + table.sourceLayerId + ".fields";
1573
- _templatizeAdminSourceLayerFields(table.sourceLayerFields || [], path);
1574
- // templatize the releated table fields
1575
- const relatedTables = getProp(layer, "adminLayerInfo.viewLayerDefinition.table.relatedTables") || [];
1576
- if (relatedTables.length > 0) {
1577
- relatedTables.forEach((t) => {
1578
- id = _getDependantItemId(t.sourceServiceName, dependencies);
1579
- const relatedPath = id + ".layer" + t.sourceLayerId + ".fields";
1580
- _templatizeTopFilter(t.topFilter || {}, relatedPath);
1581
- _templatizeAdminSourceLayerFields(t.sourceLayerFields || [], relatedPath);
1582
- const parentKeyFields = t.parentKeyFields || [];
1583
- t.parentKeyFields = parentKeyFields.map((f) => {
1584
- return _templatize(path, f, "name");
1585
- });
1586
- const keyFields = t.keyFields || [];
1587
- t.keyFields = keyFields.map((f) => {
1588
- return _templatize(relatedPath, f, "name");
1589
- });
1590
- });
1591
- }
1592
- }
1593
- }
1594
- /**
1595
- * find id based on dependency name
1596
- *
1597
- * @param lookupName name of dependency we want to find the id of
1598
- * @param dependencies array of item dependencies
1599
- * @private
1600
- */
1601
- export function _getDependantItemId(lookupName, dependencies) {
1602
- const deps = dependencies.filter(dependency => dependency.name === lookupName);
1603
- return deps.length === 1 ? deps[0].id : "";
1604
- }
1605
- /**
1606
- * templatize the sourceLayerFields referenced in adminLayerInfo
1607
- *
1608
- * @param fields array of sourceLayerFields to templatize
1609
- * @param basePath path used to de-templatize while deploying
1610
- * @private
1611
- */
1612
- export function _templatizeAdminSourceLayerFields(fields, basePath) {
1613
- fields.forEach(f => _templatizeProperty(f, "source", basePath, "name"));
1614
- }
1615
- /**
1616
- * templatize the topFilter property from adminLayerInfo related tables
1617
- *
1618
- * @param topFilter the topFilter object to templatize
1619
- * @param basePath path used to de-templatize while deploying
1620
- * @private
1621
- */
1622
- export function _templatizeTopFilter(topFilter, basePath) {
1623
- /* istanbul ignore else */
1624
- if (topFilter) {
1625
- // templatize the orderByFields prop
1626
- const orderByFields = topFilter["orderByFields"] || "";
1627
- /* istanbul ignore else */
1628
- if (orderByFields !== "") {
1629
- const orderByField = orderByFields.split(" ")[0];
1630
- topFilter.orderByFields = topFilter.orderByFields.replace(orderByField, _templatize(basePath, orderByField, "name"));
1631
- }
1632
- const groupByFields = topFilter["groupByFields"] || "";
1633
- /* istanbul ignore else */
1634
- if (groupByFields !== "") {
1635
- const _groupByFields = groupByFields.split(",");
1636
- /* istanbul ignore else */
1637
- if (_groupByFields.length > 0) {
1638
- const mappedFields = _groupByFields.map((f) => {
1639
- return _templatize(basePath, f, "name");
1640
- });
1641
- topFilter.groupByFields = mappedFields.join(",");
1642
- }
1643
- }
1644
- }
1645
- }
1646
- /**
1647
- * templatize the relationships key fields using the related table id in the basePath
1648
- *
1649
- * @param layer the layer that has the relationships to templatize
1650
- * @param itemID the id of the item that contains the related table
1651
- * @private
1652
- */
1653
- export function _templatizeRelationshipFields(layer, itemID) {
1654
- if (layer && layer.relationships) {
1655
- const relationships = layer.relationships;
1656
- relationships.forEach(r => {
1657
- /* istanbul ignore else */
1658
- if (r.keyField) {
1659
- const basePath = itemID + ".layer" + layer.id + ".fields";
1660
- _templatizeProperty(r, "keyField", basePath, "name");
1661
- }
1662
- });
1663
- }
1664
- }
1665
- /**
1666
- * templatize the popupInfo
1667
- *
1668
- * @param layerDefinition the layerDefinition that has the popupInfo to templatize
1669
- * @param layer the JSON for the layer being templatized
1670
- * @param basePath path used to de-templatize while deploying
1671
- * @param itemID the id for the item that contains this layer
1672
- * @param fieldNames array of fieldNames
1673
- * @private
1674
- */
1675
- export function _templatizePopupInfo(layerDefinition, layer, basePath, itemID, fieldNames) {
1676
- // the data layer does not have the fields...will need to get those
1677
- // from the associated layer json
1678
- if (fieldNames && layerDefinition.popupInfo) {
1679
- const popupInfo = layerDefinition.popupInfo;
1680
- _templatizeName(popupInfo, "title", fieldNames, basePath);
1681
- _templatizeName(popupInfo, "description", fieldNames, basePath);
1682
- const fieldInfos = popupInfo.fieldInfos || [];
1683
- _templatizePopupInfoFieldInfos(fieldInfos, layer, itemID, basePath);
1684
- const expressionInfos = popupInfo.expressionInfos || [];
1685
- _templatizeExpressionInfos(expressionInfos, fieldNames, basePath);
1686
- const popupElements = popupInfo.popupElements || [];
1687
- _templatizePopupElements(popupElements, basePath, layer, itemID, fieldNames);
1688
- const mediaInfos = popupInfo.mediaInfos || [];
1689
- _templatizeMediaInfos(mediaInfos, fieldNames, basePath, layer, itemID);
1690
- }
1691
- }
1692
- /**
1693
- * templatize field name when referenced like this: \{\{fieldName\}\}
1694
- * checks each field name from the layer
1695
- *
1696
- * @param object with the property to test for a field name
1697
- * @param property that could have a field name referenced
1698
- * @param fieldNames array for field names for the layer
1699
- * @param basePath path used to de-templatize while deploying
1700
- * @private
1701
- */
1702
- export function _templatizeName(object, property, fieldNames, basePath) {
1703
- if (object.hasOwnProperty(property)) {
1704
- fieldNames.forEach(name => {
1705
- // Only test and replace instance of the name so any enclosing characters
1706
- // will be retained
1707
- const regEx = new RegExp("(\\b" + name + "\\b(?![}]{2}))", "gm");
1708
- if (regEx.test(object[property])) {
1709
- object[property] = object[property].replace(regEx, _templatize(basePath, name, "name"));
1710
- }
1711
- });
1712
- }
1713
- }
1714
- /**
1715
- * templatize field name when referenced like this: \{\{fieldName\}\}
1716
- * checks each field name from the layer
1717
- *
1718
- * @param fieldInfos object that contains the popups fieldInfos
1719
- * @param layer json of layer being cloned
1720
- * @param itemID id of the item that contains the current layer
1721
- * @param basePath path used to de-templatize while deploying
1722
- * @private
1723
- */
1724
- export function _templatizePopupInfoFieldInfos(fieldInfos, layer, itemID, basePath) {
1725
- fieldInfos.forEach((f) => {
1726
- f.fieldName = _templatizeFieldName(f.fieldName, layer, itemID, basePath);
1727
- });
1728
- }
1729
- /**
1730
- * templatize field name when referenced like this: \{\{fieldName\}\}
1731
- * checks each field name from the layer
1732
- *
1733
- * @param name the field name to templatize
1734
- * @param layer json of layer being cloned
1735
- * @param itemID id of the item that contains the current layer
1736
- * @param basePath path used to de-templatize while deploying
1737
- * @private
1738
- */
1739
- export function _templatizeFieldName(name, layer, itemID, basePath) {
1740
- if (name.indexOf("relationships/") > -1) {
1741
- const rels = name.split("/");
1742
- const relationshipId = rels[1];
1743
- const adminRelatedTables = getProp(layer, "adminLayerInfo.viewLayerDefinition.table.relatedTables");
1744
- const relatedTables = layer.relationships || adminRelatedTables;
1745
- /* istanbul ignore else */
1746
- if (relatedTables && relatedTables.length > parseInt(relationshipId, 10)) {
1747
- const relatedTable = relatedTables[relationshipId];
1748
- // the layers relationships stores the property as relatedTableId
1749
- // the layers adminLayerInfo relatedTables stores the property as sourceLayerId
1750
- const prop = getProp(relatedTable, "relatedTableId")
1751
- ? "relatedTableId"
1752
- : "sourceLayerId";
1753
- const _basePath = itemID + ".layer" + relatedTable[prop] + ".fields";
1754
- rels[2] = _templatize(_basePath, rels[2], "name");
1755
- name = rels.join("/");
1756
- }
1757
- }
1758
- else {
1759
- // do not need to templatize expression references as the expression
1760
- // itself will be templatized
1761
- if (name.indexOf("expression/") === -1) {
1762
- name = _templatize(basePath, name, "name");
1763
- }
1764
- }
1765
- return name;
1766
- }
1767
- /**
1768
- * templatize field name when referenced in expressionInfos
1769
- *
1770
- * @param expressionInfos the popups expressionInfos to check
1771
- * @param fieldNames array of the layers field names
1772
- * @param basePath path used to de-templatize while deploying
1773
- * @private
1774
- */
1775
- export function _templatizeExpressionInfos(expressionInfos, fieldNames, basePath) {
1776
- return expressionInfos.map((i) => {
1777
- fieldNames.forEach(name => {
1778
- i.expression = _templatizeArcadeExpressions(i.expression, name, basePath);
1779
- });
1780
- return i;
1781
- });
1782
- }
1783
- /**
1784
- * templatize field name when referenced in popupElelments
1785
- *
1786
- * @param popupElelments the popups popupElelments to check
1787
- * @param basePath path used to de-templatize while deploying
1788
- * @param layer json of layer being cloned
1789
- * @param itemID id of the item that contains the current layer
1790
- * @param fieldNames array of field names
1791
- * @private
1792
- */
1793
- export function _templatizePopupElements(popupElelments, basePath, layer, itemID, fieldNames) {
1794
- popupElelments.forEach((pe) => {
1795
- if (pe.hasOwnProperty("fieldInfos")) {
1796
- _templatizePopupInfoFieldInfos(pe.fieldInfos, layer, itemID, basePath);
1797
- }
1798
- if (pe.hasOwnProperty("mediaInfos")) {
1799
- _templatizeMediaInfos(pe.mediaInfos, fieldNames, basePath, layer, itemID);
1800
- }
1801
- });
1802
- }
1803
- /**
1804
- * templatize field name when referenced in mediaInfos
1805
- *
1806
- * @param mediaInfos the popups mediaInfos to check
1807
- * @param fieldNames array of the layers field names
1808
- * @param basePath path used to de-templatize while deploying
1809
- * @param layer json of layer being cloned
1810
- * @param itemID id of the item that contains the current layer
1811
- * @private
1812
- */
1813
- export function _templatizeMediaInfos(mediaInfos, fieldNames, basePath, layer, itemId) {
1814
- // templatize various properties of mediaInfos
1815
- const props = ["title", "caption"];
1816
- props.forEach(p => _templatizeName(mediaInfos, p, fieldNames, basePath));
1817
- mediaInfos.forEach((mi) => {
1818
- /* istanbul ignore else */
1819
- if (mi.hasOwnProperty("value")) {
1820
- const v = mi.value;
1821
- const vfields = v.fields || [];
1822
- v.fields = vfields.map(f => _templatizeFieldName(f, layer, itemId, basePath));
1823
- if (v.hasOwnProperty("normalizeField")) {
1824
- _templatizeProperty(v, "normalizeField", basePath, "name");
1825
- }
1826
- /* istanbul ignore else */
1827
- if (v.hasOwnProperty("tooltipField")) {
1828
- v.tooltipField = _templatizeFieldName(v.tooltipField, layer, itemId, basePath);
1829
- }
1830
- }
1831
- });
1832
- }
1833
- /**
1834
- * templatize field names when referenced in definitionEditor
1835
- *
1836
- * @param layer the layer with the definition editor
1837
- * @param basePath path used to de-templatize while deploying
1838
- * @param fieldNames json of layer being cloned
1839
- * @private
1840
- */
1841
- export function _templatizeDefinitionEditor(layer, basePath, fieldNames) {
1842
- if (layer) {
1843
- const defEditor = layer.definitionEditor || {};
1844
- /* istanbul ignore else */
1845
- if (defEditor) {
1846
- const inputs = defEditor.inputs;
1847
- if (inputs) {
1848
- inputs.forEach(i => {
1849
- /* istanbul ignore else */
1850
- if (i.parameters) {
1851
- i.parameters.forEach((p) => {
1852
- _templatizeProperty(p, "fieldName", basePath, "name");
1853
- });
1854
- }
1855
- });
1856
- }
1857
- if (defEditor.hasOwnProperty("parameterizedExpression")) {
1858
- defEditor.parameterizedExpression = _templatizeSimpleName(defEditor.parameterizedExpression || "", basePath, fieldNames, "name");
1859
- }
1860
- }
1861
- }
1862
- }
1863
- /**
1864
- * templatize field names when referenced in definitionExpression
1865
- *
1866
- * @param layer the layer with the definition editor
1867
- * @param basePath path used to de-templatize while deploying
1868
- * @param fieldNames array of field names
1869
- * @private
1870
- */
1871
- export function _templatizeDefinitionExpression(layer, basePath, fieldNames) {
1872
- if (layer && layer.hasOwnProperty("definitionExpression")) {
1873
- layer.definitionExpression = _templatizeSimpleName(layer.definitionExpression || "", basePath, fieldNames, "name");
1874
- }
1875
- }
1876
- /**
1877
- * Case sensitive test for field names that appear anywhere within a string
1878
- *
1879
- * @param expression the expression to test for field name references
1880
- * @param basePath path used to de-templatize while deploying
1881
- * @param fieldNames array of the layers field names
1882
- * @private
1883
- */
1884
- export function _templatizeSimpleName(expression, basePath, fieldNames, suffix) {
1885
- fieldNames.forEach(name => {
1886
- // look for the name but not if its followed by }}
1887
- const regEx = new RegExp("\\b" + name + "\\b(?![}]{2})", "gm");
1888
- if (expression && regEx.test(expression)) {
1889
- expression = expression.replace(regEx, _templatize(basePath, name, suffix));
1890
- }
1891
- });
1892
- return expression;
1893
- }
1894
- /**
1895
- * Templatize field references within a layers drawingInfo
1896
- *
1897
- * @param layer the data layer
1898
- * @param basePath path used to de-templatize while deploying
1899
- * @param fieldNames array of the layers field names
1900
- * @private
1901
- */
1902
- export function _templatizeDrawingInfo(layer, basePath, fieldNames) {
1903
- if (layer) {
1904
- const drawingInfo = layer.drawingInfo;
1905
- if (drawingInfo) {
1906
- // templatize the renderer fields
1907
- const renderer = drawingInfo.renderer || {};
1908
- _templatizeRenderer(renderer, basePath, fieldNames);
1909
- // templatize the labelingInfo
1910
- const labelingInfo = drawingInfo.labelingInfo || [];
1911
- _templatizeLabelingInfo(labelingInfo, basePath, fieldNames);
1912
- }
1913
- }
1914
- }
1915
- /**
1916
- * Templatize field references within a layers drawingInfo
1917
- *
1918
- * @param renderer the layers renderer
1919
- * @param basePath path used to de-templatize while deploying
1920
- * @param fieldNames array of the layers field names
1921
- * @private
1922
- */
1923
- export function _templatizeRenderer(renderer, basePath, fieldNames) {
1924
- switch (renderer.type) {
1925
- case "classBreaks":
1926
- case "uniqueValue":
1927
- case "predominance":
1928
- case "simple":
1929
- case "heatmap":
1930
- _templatizeGenRenderer(renderer, basePath, fieldNames);
1931
- break;
1932
- case "temporal":
1933
- _templatizeTemporalRenderer(renderer, basePath, fieldNames);
1934
- break;
1935
- default:
1936
- break;
1937
- }
1938
- }
1939
- /**
1940
- * Templatize field references within a layers renderer
1941
- *
1942
- * @param renderer the renderer object to check for field references
1943
- * @param basePath path used to de-templatize while deploying
1944
- * @param fieldNames array of field names that will be used to search expressions
1945
- * @private
1946
- */
1947
- export function _templatizeGenRenderer(renderer, basePath, fieldNames) {
1948
- /* istanbul ignore else */
1949
- if (renderer) {
1950
- // update authoringInfo
1951
- const authoringInfo = renderer.authoringInfo;
1952
- if (authoringInfo) {
1953
- _templatizeAuthoringInfo(authoringInfo, basePath, fieldNames);
1954
- }
1955
- const props = ["field", "normalizationField"];
1956
- props.forEach(p => _templatizeProperty(renderer, p, basePath, "name"));
1957
- const fieldNameProps = ["field1", "field2", "field3"];
1958
- fieldNameProps.forEach(fnP => _templatizeProperty(renderer, fnP, basePath, "name"));
1959
- // When an attribute name is specified, it's enclosed in square brackets
1960
- const rExp = renderer.rotationExpression;
1961
- if (rExp) {
1962
- fieldNames.forEach(name => {
1963
- const regEx = new RegExp("(\\[" + name + "\\])", "gm");
1964
- if (regEx.test(rExp)) {
1965
- renderer.rotationExpression = rExp.replace(regEx, "[" + _templatize(basePath, name, "name") + "]");
1966
- }
1967
- });
1968
- }
1969
- // update valueExpression
1970
- if (renderer.valueExpression) {
1971
- fieldNames.forEach(name => {
1972
- renderer.valueExpression = _templatizeArcadeExpressions(renderer.valueExpression, name, basePath);
1973
- });
1974
- }
1975
- // update visualVariables
1976
- const visualVariables = renderer.visualVariables;
1977
- if (visualVariables) {
1978
- visualVariables.forEach(v => {
1979
- props.forEach(p => _templatizeProperty(v, p, basePath, "name"));
1980
- if (v.valueExpression) {
1981
- fieldNames.forEach(name => {
1982
- v.valueExpression = _templatizeArcadeExpressions(v.valueExpression, name, basePath);
1983
- });
1984
- }
1985
- });
1986
- }
1987
- }
1988
- }
1989
- /**
1990
- * Templatize field references within a layers renderer
1991
- *
1992
- * @param renderer the renderer object to check for field references
1993
- * @param basePath path used to de-templatize while deploying
1994
- * @param fieldNames array of field names that will be used to search expressions
1995
- * @private
1996
- */
1997
- export function _templatizeTemporalRenderer(renderer, basePath, fieldNames) {
1998
- const renderers = [
1999
- renderer.latestObservationRenderer,
2000
- renderer.observationRenderer,
2001
- renderer.trackRenderer
2002
- ];
2003
- renderers.forEach(r => {
2004
- _templatizeRenderer(r, basePath, fieldNames);
2005
- });
2006
- }
2007
- /**
2008
- * Templatize renderers authoringInfo
2009
- *
2010
- * @param authoringInfo object containing metadata about the authoring process
2011
- * @param basePath path used to de-templatize while deploying
2012
- * @param fieldNames the name of fields from the layer
2013
- * @private
2014
- */
2015
- export function _templatizeAuthoringInfo(authoringInfo, basePath, fieldNames) {
2016
- /* istanbul ignore else */
2017
- if (authoringInfo) {
2018
- const props = ["field", "normalizationField"];
2019
- const field1 = authoringInfo.field1;
2020
- props.forEach(p => _templatizeProperty(field1, p, basePath, "name"));
2021
- const field2 = authoringInfo.field2;
2022
- props.forEach(p => _templatizeProperty(field2, p, basePath, "name"));
2023
- const fields = authoringInfo.fields;
2024
- if (fields) {
2025
- authoringInfo.fields = fields.map(f => _templatize(basePath, f, "name"));
2026
- }
2027
- const vProps = ["endTime", "field", "startTime"];
2028
- const vVars = authoringInfo.visualVariables;
2029
- if (vVars) {
2030
- vProps.forEach(p => {
2031
- // endTime and startTime may or may not be a field name
2032
- if (fieldNames.indexOf(vVars[p]) > -1) {
2033
- _templatizeProperty(vVars, p, basePath, "name");
2034
- }
2035
- });
2036
- }
2037
- }
2038
- }
2039
- /**
2040
- * Templatize field references within an arcade expression
2041
- *
2042
- * @param text the text that contains the expression
2043
- * @param fieldName name of the field to test for
2044
- * @param basePath path used to de-templatize while deploying
2045
- * @private
2046
- */
2047
- export function _templatizeArcadeExpressions(text, fieldName, basePath) {
2048
- const t = _templatize(basePath, fieldName, "name");
2049
- if (text) {
2050
- // test for $feature. notation
2051
- // captures VOTED_DEM_2012 from $feature.VOTED_DEM_2012
2052
- let exp = "(?:\\$feature\\.)(" + fieldName + ")\\b";
2053
- let regEx = new RegExp(exp, "gm");
2054
- text = regEx.test(text) ? text.replace(regEx, "$feature." + t) : text;
2055
- // test for $feature[] notation
2056
- // captures VOTED_DEM_2012 from $feature["VOTED_DEM_2012"]
2057
- // captures VOTED_DEM_2012 from $feature['VOTED_DEM_2012']
2058
- // captures VOTED_DEM_2012 from $feature[VOTED_DEM_2012]
2059
- exp = "(?:[$]feature)(\\[\\\"?\\'?)" + fieldName + "(\\\"?\\'?\\])";
2060
- regEx = new RegExp(exp, "gm");
2061
- let result = regEx.exec(text);
2062
- if (result) {
2063
- text = text.replace(regEx, "$feature" + result[1] + t + result[2]);
2064
- }
2065
- // test for $feature[] with join case
2066
- // captures VOTED_DEM_2016 from $feature["COUNTY_ID.VOTED_DEM_2016"]
2067
- exp =
2068
- "(?:[$]feature)(\\[\\\"?\\'?)(\\w+)[.]" + fieldName + "(\\\"?\\'?\\])";
2069
- regEx = new RegExp(exp, "gm");
2070
- result = regEx.exec(text);
2071
- if (result && result.length > 3) {
2072
- // TODO result[2] is the table name...this needs to be templatized as well
2073
- text = text.replace(regEx, "$feature" + result[1] + result[2] + "." + t + result[3]);
2074
- }
2075
- // test for "fieldName"
2076
- // captures fieldName from "var names = ["fieldName", "fieldName2"]..."
2077
- // captures fieldName from "var names = ['fieldName', 'fieldName2']..."
2078
- exp = "(\\\"|\\')+" + fieldName + "(\\\"|\\')+";
2079
- regEx = new RegExp(exp, "gm");
2080
- result = regEx.exec(text);
2081
- if (result) {
2082
- text = text.replace(regEx, result[1] + t + result[2]);
2083
- }
2084
- }
2085
- return text;
2086
- }
2087
- /**
2088
- * templatize field names when referenced in the layers labelingInfo
2089
- *
2090
- * @param labelingInfo the object that contains the labelingInfo
2091
- * @param basePath path used to de-templatize while deploying
2092
- * @param fieldNames array of the layers field names
2093
- * @private
2094
- */
2095
- export function _templatizeLabelingInfo(labelingInfo, basePath, fieldNames) {
2096
- labelingInfo.forEach((li) => {
2097
- /* istanbul ignore else */
2098
- if (li.hasOwnProperty("fieldInfos")) {
2099
- const fieldInfos = li.fieldInfos || [];
2100
- fieldInfos.forEach(fi => _templatizeProperty(fi, "fieldName", basePath, "name"));
2101
- }
2102
- const labelExp = li.labelExpression || "";
2103
- const labelExpInfo = li.labelExpressionInfo || {};
2104
- fieldNames.forEach(n => {
2105
- const t = _templatize(basePath, n, "name");
2106
- // check for [fieldName] or ["fieldName"]
2107
- const regExBracket = new RegExp('(\\[\\"*)+(' + n + ')(\\"*\\])+', "gm");
2108
- let result = regExBracket.exec(labelExp);
2109
- if (result) {
2110
- li.labelExpression = labelExp.replace(regExBracket, result[1] + t + result[3]);
2111
- }
2112
- /* istanbul ignore else */
2113
- if (labelExpInfo.value) {
2114
- let v = labelExpInfo.value;
2115
- // check for {fieldName}
2116
- const regExCurly = new RegExp("(\\{" + n + "\\})", "gm");
2117
- v = regExCurly.test(v) ? v.replace(regExCurly, "{" + t + "}") : v;
2118
- // check for [fieldName] or ["fieldName"]
2119
- result = regExBracket.exec(v);
2120
- v = result ? v.replace(regExBracket, result[1] + t + result[3]) : v;
2121
- li.labelExpressionInfo.value = v;
2122
- }
2123
- /* istanbul ignore else */
2124
- if (labelExpInfo.expression) {
2125
- li.labelExpressionInfo.expression = _templatizeArcadeExpressions(labelExpInfo.expression, n, basePath);
2126
- }
2127
- });
2128
- });
2129
- }
2130
- /**
2131
- * templatize the layers editing templates
2132
- *
2133
- * @param layer the data layer being cloned
2134
- * @param basePath path used to de-templatize while deploying
2135
- * @private
2136
- */
2137
- export function _templatizeTemplates(layer, basePath) {
2138
- const templates = layer.templates || [];
2139
- templates.forEach(t => {
2140
- const attributes = getProp(t, "prototype.attributes");
2141
- const _attributes = _templatizeKeys(attributes, basePath, "name");
2142
- /* istanbul ignore else */
2143
- if (_attributes) {
2144
- t.prototype.attributes = _attributes;
2145
- }
2146
- });
2147
- }
2148
- /**
2149
- * templatize the layer types and templates
2150
- *
2151
- * @param layer the data layer being cloned
2152
- * @param basePath path used to de-templatize while deploying
2153
- * @private
2154
- */
2155
- export function _templatizeTypeTemplates(layer, basePath) {
2156
- const types = layer.types;
2157
- if (types && Array.isArray(types) && types.length > 0) {
2158
- types.forEach((type) => {
2159
- const domains = _templatizeKeys(type.domains, basePath, "name");
2160
- /* istanbul ignore else */
2161
- if (domains) {
2162
- type.domains = domains;
2163
- }
2164
- const templates = type.templates;
2165
- /* istanbul ignore else */
2166
- if (templates && templates.length > 0) {
2167
- templates.forEach((t) => {
2168
- const attributes = getProp(t, "prototype.attributes");
2169
- const _attributes = _templatizeKeys(attributes, basePath, "name");
2170
- /* istanbul ignore else */
2171
- if (_attributes) {
2172
- t.prototype.attributes = _attributes;
2173
- }
2174
- });
2175
- }
2176
- });
2177
- }
2178
- }
2179
- /**
2180
- * templatize object keys
2181
- *
2182
- * @param obj the object to templatize
2183
- * @param basePath path used to de-templatize while deploying
2184
- * @param suffix expected suffix for template variable
2185
- * @private
2186
- */
2187
- export function _templatizeKeys(obj, basePath, suffix) {
2188
- let _obj;
2189
- /* istanbul ignore else */
2190
- if (obj) {
2191
- _obj = {};
2192
- const objKeys = Object.keys(obj);
2193
- /* istanbul ignore else */
2194
- if (objKeys && objKeys.length > 0) {
2195
- objKeys.forEach(k => {
2196
- _obj[_templatize(basePath, k, suffix)] = obj[k];
2197
- });
2198
- }
2199
- }
2200
- return _obj;
2201
- }
2202
- /**
2203
- * templatize fields referenced in the layers time info
2204
- *
2205
- * @param layer the data layer being cloned
2206
- * @param basePath path used to de-templatize while deploying
2207
- * @private
2208
- */
2209
- export function _templatizeTimeInfo(layer, basePath) {
2210
- if (layer.timeInfo) {
2211
- const timeInfo = layer.timeInfo;
2212
- const timeProps = [
2213
- "endTimeField",
2214
- "startTimeField",
2215
- "trackIdField"
2216
- ];
2217
- timeProps.forEach(t => {
2218
- if (timeInfo[t] !== "") {
2219
- _templatizeProperty(timeInfo, t, basePath, "name");
2220
- }
2221
- else {
2222
- timeInfo[t] = null;
2223
- }
2224
- });
2225
- }
2226
- }
2227
- /**
2228
- * templatize the layers definition query
2229
- *
2230
- * @param layer the data layer being cloned
2231
- * @param basePath path used to de-templatize while deploying
2232
- * @param fieldNames array of the layers field names
2233
- * @private
2234
- */
2235
- export function _templatizeDefinitionQuery(layer, basePath, fieldNames) {
2236
- // templatize view definition query
2237
- if (layer && layer.hasOwnProperty("viewDefinitionQuery")) {
2238
- layer.viewDefinitionQuery = _templatizeSimpleName(layer.viewDefinitionQuery || "", basePath, fieldNames, "name");
2239
- }
2240
- if (layer && layer.hasOwnProperty("definitionQuery")) {
2241
- layer.definitionQuery = _templatizeSimpleName(layer.definitionQuery || "", basePath, fieldNames, "name");
2242
- }
2243
- }
2244
- /**
2245
- * Helper function to create the name mapping used to
2246
- * de-templatize the field reference
2247
- *
2248
- * @param fieldInfos the object that stores the cached information
2249
- * @param id the id for the current layer being processed
2250
- * @private
2251
- */
2252
- export function _getNameMapping(fieldInfos, id) {
2253
- // create name mapping
2254
- const fInfo = fieldInfos[id];
2255
- const nameMapping = {};
2256
- const newFields = fInfo.newFields;
2257
- const newFieldNames = newFields
2258
- ? newFields.map((f) => f.name)
2259
- : [];
2260
- const sourceFields = fInfo.sourceFields || [];
2261
- sourceFields.forEach((field) => {
2262
- const lName = String(field.name).toLowerCase();
2263
- newFields.forEach((f) => {
2264
- // Names can change more than case
2265
- if (newFieldNames.indexOf(field.name) === -1 &&
2266
- newFieldNames.indexOf(lName) === -1) {
2267
- // If both new (f) and source (field) aliases are defined and are equal, map the source name to the new name
2268
- if (f.alias && f.alias === field.alias) {
2269
- nameMapping[lName] = {
2270
- name: f.name,
2271
- alias: f.alias,
2272
- type: f.type ? f.type : ""
2273
- };
2274
- }
2275
- }
2276
- if (String(f.name).toLowerCase() === lName) {
2277
- nameMapping[lName] = {
2278
- name: f.name,
2279
- alias: f.alias ? f.alias : "",
2280
- type: f.type ? f.type : ""
2281
- };
2282
- }
2283
- });
2284
- });
2285
- // update for editFieldsInfo
2286
- if (fInfo.editFieldsInfo && fInfo.newEditFieldsInfo) {
2287
- const efi = JSON.parse(JSON.stringify(fInfo.editFieldsInfo));
2288
- const newEfi = JSON.parse(JSON.stringify(fInfo.newEditFieldsInfo));
2289
- const nameMappingKeys = Object.keys(nameMapping);
2290
- Object.keys(efi).forEach(k => {
2291
- const lowerEfi = String(efi[k]).toLowerCase();
2292
- if ((nameMappingKeys.indexOf(lowerEfi) === -1 ||
2293
- nameMapping[lowerEfi].name !== newEfi[k]) &&
2294
- newFieldNames.indexOf(lowerEfi) > -1) {
2295
- // Only add delete fields if source schema changes allowed
2296
- /* istanbul ignore else */
2297
- if (fInfo.sourceSchemaChangesAllowed && !fInfo.isView) {
2298
- /* istanbul ignore else */
2299
- if (!fInfo.hasOwnProperty("deleteFields")) {
2300
- fInfo.deleteFields = [];
2301
- }
2302
- // This issue only occurs on portal so we
2303
- // need to delete the lcase version of the field
2304
- fInfo.deleteFields.push(lowerEfi);
2305
- }
2306
- // editFieldsInfo only has the name and not the alias and type
2307
- let sourceEfiField;
2308
- fInfo.sourceFields.some((sf) => {
2309
- if (sf.name === efi[k]) {
2310
- sourceEfiField = sf;
2311
- }
2312
- return sf.name === efi[k];
2313
- });
2314
- nameMapping[lowerEfi] = {
2315
- name: newEfi[k],
2316
- alias: sourceEfiField && sourceEfiField.alias ? sourceEfiField.alias : "",
2317
- type: sourceEfiField && sourceEfiField.type ? sourceEfiField.type : ""
2318
- };
2319
- }
2320
- });
2321
- deleteProp(fInfo, "sourceSchemaChangesAllowed");
2322
- deleteProp(fInfo, "editFieldsInfo");
2323
- deleteProp(fInfo, "newEditFieldsInfo");
2324
- deleteProp(fInfo, "isView");
2325
- }
2326
- return nameMapping;
2327
- }
2328
- /**
2329
- * Helper function to ensure same chunk size value is used in multiple locations
2330
- *
2331
- * @returns a number that represents how many layers should be included per addToDef call
2332
- * @private
2333
- */
2334
- export function _getLayerChunkSize() {
2335
- return 20;
2336
- }
1
+ /** @license
2
+ * Copyright 2019 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
+ /**
17
+ * Provides general helper functions.
18
+ *
19
+ * @module featureServiceHelpers
20
+ */
21
+ // ------------------------------------------------------------------------------------------------------------------ //
22
+ export { queryFeatures as rest_queryFeatures, addFeatures as rest_addFeatures } from "@esri/arcgis-rest-feature-layer";
23
+ //#region Imports -------------------------------------------------------------------------------------------------------//
24
+ import { UNREACHABLE } from "./interfaces";
25
+ import { checkUrlPathTermination, deleteProp, deleteProps, fail, getProp, setCreateProp, setProp } from "./generalHelpers";
26
+ import { replaceInTemplate, templatizeTerm, templatizeIds } from "./templatization";
27
+ import { addToServiceDefinition, getLayerUpdates, getRequest, rest_request } from "./restHelpers";
28
+ import { isTrackingViewTemplate, templatizeTracker } from "./trackingHelpers";
29
+ //#endregion ------------------------------------------------------------------------------------------------------------//
30
+ //#region Public functions ----------------------------------------------------------------------------------------------//
31
+ /**
32
+ * Templatize the ID, url, field references ect
33
+ *
34
+ * @param itemTemplate Template for feature service item
35
+ * @param dependencies Array of IDependency for name mapping
36
+ * @param templatizeFieldReferences Templatize all field references within a layer
37
+ * @param templateDictionary Hash mapping property names to replacement values
38
+ * @returns A promise that will resolve when template has been updated
39
+ * @private
40
+ */
41
+ export function templatize(itemTemplate, dependencies, templatizeFieldReferences, templateDictionary) {
42
+ templateDictionary = templateDictionary || {};
43
+ // Common templatizations
44
+ const id = itemTemplate.item.id;
45
+ const fsUrl = itemTemplate.item.url;
46
+ itemTemplate.item = {
47
+ ...itemTemplate.item,
48
+ id: templatizeTerm(id, id, ".itemId"),
49
+ url: _templatize(id, "url"),
50
+ typeKeywords: templatizeIds(itemTemplate.item.typeKeywords)
51
+ };
52
+ // special handeling if we are dealing with a tracker view
53
+ templatizeTracker(itemTemplate);
54
+ // added for issue #928
55
+ deleteProp(itemTemplate, "properties.service.size");
56
+ const jsonLayers = itemTemplate.properties.layers || [];
57
+ const jsonTables = itemTemplate.properties.tables || [];
58
+ const jsonItems = jsonLayers.concat(jsonTables);
59
+ const data = itemTemplate.data || {};
60
+ const layers = data.layers || [];
61
+ const tables = data.tables || [];
62
+ const _items = layers.concat(tables);
63
+ // Set up symbols for the URL of the feature service and its layers and tables
64
+ templateDictionary[fsUrl] = itemTemplate.item.url; // map FS URL to its templatized form
65
+ jsonItems.concat(_items).forEach(layer => {
66
+ templateDictionary[fsUrl + "/" + layer.id] = _templatize(id, "layer" + layer.id + ".url");
67
+ });
68
+ // templatize the service references serviceItemId
69
+ itemTemplate.properties.service.serviceItemId = templatizeTerm(itemTemplate.properties.service.serviceItemId, itemTemplate.properties.service.serviceItemId, ".itemId");
70
+ const initialExtent = getProp(itemTemplate, "properties.service.initialExtent");
71
+ /* istanbul ignore else */
72
+ if (initialExtent) {
73
+ itemTemplate.properties.service.initialExtent = templatizeTerm(id, id, ".solutionExtent");
74
+ }
75
+ const fullExtent = getProp(itemTemplate, "properties.service.fullExtent");
76
+ /* istanbul ignore else */
77
+ if (fullExtent) {
78
+ itemTemplate.properties.service.fullExtent = templatizeTerm(id, id, ".solutionExtent");
79
+ }
80
+ // this default extent will be used in cases where it does not make sense to apply the orgs
81
+ // extent to a service with a local spatial reference
82
+ itemTemplate.properties.defaultExtent = initialExtent || fullExtent;
83
+ // in some cases a service does not have a spatial reference defined
84
+ // added for issue #699
85
+ if (!getProp(itemTemplate, "properties.service.spatialReference") &&
86
+ getProp(itemTemplate, "properties.defaultExtent.spatialReference")) {
87
+ setCreateProp(itemTemplate, "properties.service.spatialReference", itemTemplate.properties.defaultExtent.spatialReference);
88
+ }
89
+ // if any layer hasZ enabled then we need to set
90
+ // enableZDefaults and zDefault to deploy to enterprise
91
+ let hasZ = false;
92
+ jsonItems.forEach((jsonItem) => {
93
+ // get the source service json for the given data item
94
+ const matchingItems = _items.filter(item => {
95
+ return jsonItem.id === item.id;
96
+ });
97
+ // templatize the source service json
98
+ const _item = matchingItems.length === 1 ? matchingItems[0] : undefined;
99
+ _templatizeLayer(_item, jsonItem, itemTemplate, dependencies, templatizeFieldReferences, templateDictionary);
100
+ hasZ = jsonItem.hasZ || (_item && _item.hasZ) ? true : hasZ;
101
+ });
102
+ if (hasZ) {
103
+ itemTemplate.properties.service.enableZDefaults = true;
104
+ itemTemplate.properties.service.zDefault = 0;
105
+ }
106
+ return itemTemplate;
107
+ }
108
+ /**
109
+ * Delete key properties that are system managed
110
+ *
111
+ * @param layer The data layer instance with field name references within
112
+ */
113
+ export function deleteViewProps(layer) {
114
+ const props = ["definitionQuery"];
115
+ props.forEach(prop => {
116
+ deleteProp(layer, prop);
117
+ });
118
+ }
119
+ /**
120
+ * Cache properties that contain field references
121
+ *
122
+ * removeProp added for issue #644
123
+ * setting all props on add for online now
124
+ * investigating if we can also just allow them to be set during add for portal
125
+ *
126
+ * @param layer The data layer instance with field name references within
127
+ * @param fieldInfos the object that stores the cached field infos
128
+ * @returns An updated instance of the fieldInfos
129
+ */
130
+ export function cacheFieldInfos(layer, fieldInfos) {
131
+ // cache the source fields as they are in the original source
132
+ if (layer && layer.fields) {
133
+ fieldInfos[layer.id] = {
134
+ sourceFields: JSON.parse(JSON.stringify(layer.fields)),
135
+ type: layer.type,
136
+ id: layer.id
137
+ };
138
+ }
139
+ // cache each of these properties as they each can contain field references
140
+ // and will have associated updateDefinition calls when deploying to portal
141
+ // as well as online for relationships...as relationships added with addToDef will cause failure
142
+ const props = {
143
+ editFieldsInfo: false,
144
+ types: false,
145
+ templates: false,
146
+ relationships: true,
147
+ drawingInfo: false,
148
+ timeInfo: false,
149
+ viewDefinitionQuery: false
150
+ };
151
+ Object.keys(props).forEach(k => {
152
+ _cacheFieldInfo(layer, k, fieldInfos, props[k]);
153
+ });
154
+ return fieldInfos;
155
+ }
156
+ /**
157
+ * Cache the stored contingent values so we can add them in subsequent addToDef calls
158
+ *
159
+ * @param id The layer id for the associated values to be stored with
160
+ * @param fieldInfos The object that stores the cached field infos
161
+ * @param itemTemplate The current itemTemplate being processed
162
+ * @returns An updated instance of the fieldInfos
163
+ */
164
+ export function cacheContingentValues(id, fieldInfos, itemTemplate) {
165
+ const contingentValues = getProp(itemTemplate, 'properties.contingentValues');
166
+ if (contingentValues && contingentValues[id]) {
167
+ fieldInfos[id]['contingentValues'] = contingentValues[id];
168
+ }
169
+ return fieldInfos;
170
+ }
171
+ /**
172
+ * Helper function to cache a single property into the fieldInfos object
173
+ * This property will be removed from the layer instance.
174
+ *
175
+ * @param layer the data layer being cloned
176
+ * @param prop the property name used to cache
177
+ * @param fieldInfos the object that will store the cached property
178
+ * @private
179
+ */
180
+ export function _cacheFieldInfo(layer, prop, fieldInfos, removeProp) {
181
+ /* istanbul ignore else */
182
+ if (layer &&
183
+ layer.hasOwnProperty(prop) &&
184
+ fieldInfos &&
185
+ fieldInfos.hasOwnProperty(layer.id)) {
186
+ fieldInfos[layer.id][prop] = layer[prop];
187
+ // editFieldsInfo does not come through unless its with the layer
188
+ // when it's being added
189
+ /* istanbul ignore else */
190
+ if (removeProp) {
191
+ layer[prop] = null;
192
+ }
193
+ }
194
+ }
195
+ /**
196
+ * Cache popup info that can contain field references
197
+ *
198
+ * @param data The items data property
199
+ * @returns An updated instance of the popupInfos
200
+ */
201
+ export function cachePopupInfos(data) {
202
+ // store any popupInfo so we can update after any potential name changes
203
+ const popupInfos = {
204
+ layers: {},
205
+ tables: {}
206
+ };
207
+ if (data && data.layers && data.layers.length > 0) {
208
+ _cachePopupInfo(popupInfos, "layers", data.layers);
209
+ }
210
+ if (data && data.tables && data.tables.length > 0) {
211
+ _cachePopupInfo(popupInfos, "tables", data.tables);
212
+ }
213
+ return popupInfos;
214
+ }
215
+ /**
216
+ * Helper function to cache a single popupInfo
217
+ * This property will be reset on the layer
218
+ *
219
+ * @param popupInfos object to store the cahced popupInfo
220
+ * @param type is it a layer or table
221
+ * @param _items list or either layers or tables
222
+ * @private
223
+ */
224
+ export function _cachePopupInfo(popupInfos, type, _items) {
225
+ _items.forEach((item) => {
226
+ if (item && item.hasOwnProperty("popupInfo")) {
227
+ popupInfos[type][item.id] = item.popupInfo;
228
+ item.popupInfo = {};
229
+ }
230
+ });
231
+ }
232
+ /**
233
+ * Store basic layer information for potential replacement if we are unable to access a given service
234
+ * added for issue #859
235
+ *
236
+ * @param layerId the id for the layer
237
+ * @param itemId the id for the item
238
+ * @param url the url for the layer
239
+ * @param templateDictionary Hash of key details used for variable replacement
240
+ * @returns templatized itemTemplate
241
+ */
242
+ export function cacheLayerInfo(layerId, itemId, url, templateDictionary) {
243
+ if (layerId) {
244
+ const layerIdVar = `layer${layerId}`;
245
+ // need to structure these differently so they are not used for standard replacement calls
246
+ // this now adds additional vars that are not needing replacement unless we fail to fetch the service
247
+ const newVars = getProp(templateDictionary, `${UNREACHABLE}.${itemId}`) || {
248
+ itemId
249
+ };
250
+ newVars[layerIdVar] = getProp(newVars, layerIdVar) || {
251
+ layerId,
252
+ itemId
253
+ };
254
+ if (url !== "") {
255
+ newVars[layerIdVar]["url"] = url;
256
+ }
257
+ const unreachableVars = {};
258
+ unreachableVars[itemId] = newVars;
259
+ templateDictionary[UNREACHABLE] = {
260
+ ...templateDictionary[UNREACHABLE],
261
+ ...unreachableVars
262
+ };
263
+ }
264
+ }
265
+ /**
266
+ * Creates an item in a specified folder (except for Group item type).
267
+ *
268
+ * @param itemTemplate Item to be created; n.b.: this item is modified
269
+ * @param templateDictionary Hash mapping property names to replacement values
270
+ * @param createResponse Response from create service
271
+ * @returns An updated instance of the template
272
+ * @private
273
+ */
274
+ export function updateTemplate(itemTemplate, templateDictionary, createResponse) {
275
+ // Update the item with any typeKeywords that were added on create
276
+ _updateTypeKeywords(itemTemplate, createResponse);
277
+ // Add the new item to the template dictionary
278
+ templateDictionary[itemTemplate.itemId] = Object.assign(templateDictionary[itemTemplate.itemId] || {}, {
279
+ itemId: createResponse.serviceItemId,
280
+ url: checkUrlPathTermination(createResponse.serviceurl),
281
+ name: createResponse.name
282
+ });
283
+ // Update the item template now that the new service has been created
284
+ itemTemplate.itemId = createResponse.serviceItemId;
285
+ return replaceInTemplate(itemTemplate, templateDictionary);
286
+ }
287
+ /**
288
+ * Updates the items typeKeywords to include any typeKeywords that
289
+ * were added by the create service request
290
+ *
291
+ * @param itemTemplate Item to be created; n.b.: this item is modified
292
+ * @param createResponse Response from create service
293
+ * @returns An updated instance of the template
294
+ * @private
295
+ */
296
+ export function _updateTypeKeywords(itemTemplate, createResponse) {
297
+ // https://github.com/Esri/solution.js/issues/589
298
+ const iKwords = getProp(itemTemplate, "item.typeKeywords");
299
+ const cKwords = getProp(createResponse, "typeKeywords");
300
+ if (iKwords && cKwords) {
301
+ setProp(itemTemplate, "item.typeKeywords", iKwords.concat(cKwords.filter(k => iKwords.indexOf(k) < 0)));
302
+ }
303
+ return itemTemplate;
304
+ }
305
+ /**
306
+ * Create the name mapping object that will allow for all templatized field
307
+ * references to be de-templatized.
308
+ * This also removes the stored sourceFields and newFields arrays from fieldInfos.
309
+ *
310
+ * @example
311
+ * \{ layer0: \{ fields: \{ lowerCaseSourceFieldName: newFieldNameAfterDeployment \} \} \}
312
+ *
313
+ * @param layerInfos The object that stores the cached layer properties and name mapping
314
+ * @returns The settings object that will be used to de-templatize the field references.
315
+ */
316
+ export function getLayerSettings(layerInfos, url, itemId, enterpriseIDMapping) {
317
+ const settings = {};
318
+ const ids = Object.keys(layerInfos);
319
+ ids.forEach((id) => {
320
+ const _layerId = getProp(layerInfos[id], "item.id");
321
+ const isNum = parseInt(_layerId, 10) > -1;
322
+ const layerId = isNum && enterpriseIDMapping
323
+ ? enterpriseIDMapping[_layerId]
324
+ : isNum
325
+ ? _layerId
326
+ : id;
327
+ settings[`layer${isNum ? _layerId : id}`] = {
328
+ fields: _getNameMapping(layerInfos, id),
329
+ url: checkUrlPathTermination(url) + layerId,
330
+ layerId,
331
+ itemId
332
+ };
333
+ deleteProp(layerInfos[id], "newFields");
334
+ deleteProp(layerInfos[id], "sourceFields");
335
+ });
336
+ return settings;
337
+ }
338
+ /**
339
+ * Set the names and titles for all feature services.
340
+ *
341
+ * This function will ensure that we have unique feature service names.
342
+ * The feature service name will have the solution item id appended.
343
+ *
344
+ * @param templates A collection of AGO item templates.
345
+ * @param solutionItemId The item id for the deployed solution item.
346
+ * @returns An updated collection of AGO templates with unique feature service names.
347
+ */
348
+ export function setNamesAndTitles(templates, solutionItemId) {
349
+ const names = [];
350
+ return templates.map(t => {
351
+ /* istanbul ignore else */
352
+ if (t.item.type === "Feature Service") {
353
+ // Retain the existing title but swap with name if it's missing
354
+ t.item.title = t.item.title || t.item.name;
355
+ /* istanbul ignore else */
356
+ if (!isTrackingViewTemplate(t)) {
357
+ // Need to set the service name: name + "_" + newItemId
358
+ let baseName = t.item.name || t.item.title;
359
+ // If the name already contains a GUID remove it
360
+ baseName = baseName.replace(/_[0-9A-F]{32}/gi, "");
361
+ // The name length limit is 98
362
+ // Limit the baseName to 50 characters before the _<guid>
363
+ const name = baseName.substring(0, 50) + "_" + solutionItemId;
364
+ // If the name + GUID already exists then append "_occurrenceCount"
365
+ t.item.name =
366
+ names.indexOf(name) === -1
367
+ ? name
368
+ : `${name}_${names.filter(n => n === name).length}`;
369
+ names.push(name);
370
+ }
371
+ }
372
+ return t;
373
+ });
374
+ }
375
+ /**
376
+ * This is used when deploying views.
377
+ * We need to update fields referenced in adminLayerInfo for relationships prior to deploying the view.
378
+ * This moves the fieldInfos for the views source layers from the item settings for the source layer
379
+ * to the item settings for the view.
380
+ *
381
+ * @param itemTemplate The current itemTemplate being processed.
382
+ * @param settings The settings object used to de-templatize the various templates within the item.
383
+ */
384
+ export function updateSettingsFieldInfos(itemTemplate, settings) {
385
+ const dependencies = itemTemplate.dependencies;
386
+ const id = itemTemplate.itemId;
387
+ const settingsKeys = Object.keys(settings);
388
+ settingsKeys.forEach((k) => {
389
+ if (id === settings[k].itemId) {
390
+ dependencies.forEach((d) => {
391
+ settingsKeys.forEach((_k) => {
392
+ /* istanbul ignore else */
393
+ if (d === _k) {
394
+ // combine for multi-source views
395
+ const fieldInfos = {};
396
+ fieldInfos[d] = getProp(settings[_k], "fieldInfos");
397
+ settings[k]["sourceServiceFields"] = settings[k]["sourceServiceFields"]
398
+ ? { ...settings[k]["sourceServiceFields"], ...fieldInfos }
399
+ : fieldInfos;
400
+ const layerKeys = Object.keys(settings[_k]);
401
+ layerKeys.forEach(layerKey => {
402
+ /* istanbul ignore else */
403
+ if (layerKey.startsWith("layer")) {
404
+ settings[k][layerKey] = settings[_k][layerKey];
405
+ }
406
+ });
407
+ }
408
+ });
409
+ });
410
+ }
411
+ });
412
+ }
413
+ /**
414
+ * Add flag to indicate item should be ignored.
415
+ * Construct template dictionary to detemplatize any references to this item by other items.
416
+ *
417
+ * @param template Template for feature service item
418
+ * @param authentication Credentials for the request
419
+ * @returns A promise that will resolve when template has been updated
420
+ * @private
421
+ */
422
+ export function updateTemplateForInvalidDesignations(template, authentication) {
423
+ return new Promise((resolve, reject) => {
424
+ template.properties.hasInvalidDesignations = true;
425
+ if (template.item.url) {
426
+ // get the admin URL
427
+ const url = template.item.url;
428
+ rest_request(url + "?f=json", {
429
+ authentication: authentication
430
+ }).then(serviceData => {
431
+ const layerInfos = {};
432
+ const layersAndTables = (serviceData.layers || []).concat(serviceData.tables || []);
433
+ layersAndTables.forEach((l) => {
434
+ /* istanbul ignore else */
435
+ if (l && l.hasOwnProperty("id")) {
436
+ layerInfos[l.id] = l;
437
+ }
438
+ });
439
+ template.data[template.itemId] = Object.assign({
440
+ itemId: template.itemId
441
+ }, getLayerSettings(layerInfos, url, template.itemId));
442
+ resolve(template);
443
+ }, e => reject(fail(e)));
444
+ }
445
+ else {
446
+ resolve(template);
447
+ }
448
+ });
449
+ }
450
+ /**
451
+ * Get the contingent values for each layer in the service.
452
+ * Remove key props that cannot be included with the addToDef call on deploy.
453
+ * Store the values alongside other key feature service properties in the template
454
+ *
455
+ * @param properties the current feature services properties
456
+ * @param adminUrl the current feature service url
457
+ * @param authentication Credentials for the request to AGOL
458
+ * @returns A promise that will resolve when the contingent values have been fetched.
459
+ * This function will update the provided properties argument when contingent values are found.
460
+ */
461
+ export function processContingentValues(properties, adminUrl, authentication) {
462
+ return new Promise((resolve, reject) => {
463
+ if (getProp(properties, 'service.isView')) {
464
+ // views will inherit from the source service
465
+ resolve();
466
+ }
467
+ else {
468
+ const layersAndTables = (properties.layers || []).concat(properties.tables || []);
469
+ const layerIds = [];
470
+ const contingentValuePromises = layersAndTables.reduce((prev, cur) => {
471
+ /* istanbul ignore else */
472
+ if (cur.hasContingentValuesDefinition) {
473
+ prev.push(rest_request(`${adminUrl}/${cur['id']}/contingentValues?f=json`, { authentication }));
474
+ layerIds.push(cur['id']);
475
+ }
476
+ return prev;
477
+ }, []);
478
+ if (contingentValuePromises.length > 0) {
479
+ Promise.all(contingentValuePromises).then((results) => {
480
+ const contingentValues = {};
481
+ results.forEach((r, i) => {
482
+ deleteProp(r, 'typeCodes');
483
+ /* istanbul ignore else */
484
+ if (getProp(r, 'stringDicts') && getProp(r, 'contingentValuesDefinition')) {
485
+ r.contingentValuesDefinition['stringDicts'] = r.stringDicts;
486
+ deleteProp(r, 'stringDicts');
487
+ }
488
+ deleteProps(getProp(r, 'contingentValuesDefinition'), ['layerID', 'layerName', 'geometryType', 'hasSubType']);
489
+ contingentValues[layerIds[i]] = r;
490
+ });
491
+ properties.contingentValues = contingentValues;
492
+ resolve();
493
+ }, reject);
494
+ }
495
+ else {
496
+ resolve();
497
+ }
498
+ }
499
+ });
500
+ }
501
+ /**
502
+ * Replace the field name reference templates with the new field names after deployment.
503
+ *
504
+ * @param fieldInfos The object that stores the cached layer properties and name mapping
505
+ * @param popupInfos The object from the popupInfo property for the layer
506
+ * @param adminLayerInfos The object from the adminLayerInfo property for the layer
507
+ * @param settings The settings object that has all of the mappings for de-templatizing.
508
+ * @returns An object that contains updated instances of popupInfos, fieldInfos, and adminLayerInfos
509
+ */
510
+ export function deTemplatizeFieldInfos(fieldInfos, popupInfos, adminLayerInfos, settings) {
511
+ const fieldInfoKeys = Object.keys(fieldInfos);
512
+ fieldInfoKeys.forEach(id => {
513
+ if (fieldInfos[id].hasOwnProperty("templates")) {
514
+ fieldInfos[id].templates = JSON.parse(replaceInTemplate(JSON.stringify(fieldInfos[id].templates), settings));
515
+ }
516
+ if (fieldInfos[id].hasOwnProperty("adminLayerInfo")) {
517
+ adminLayerInfos[id].viewLayerDefinition.table.relatedTables =
518
+ fieldInfos[id].adminLayerInfo;
519
+ deleteProp(fieldInfos[id], "adminLayerInfo");
520
+ }
521
+ if (fieldInfos[id].hasOwnProperty("types")) {
522
+ fieldInfos[id].types = JSON.parse(replaceInTemplate(JSON.stringify(fieldInfos[id].types), settings));
523
+ }
524
+ });
525
+ return {
526
+ popupInfos: replaceInTemplate(popupInfos, settings),
527
+ fieldInfos: replaceInTemplate(fieldInfos, settings),
528
+ adminLayerInfos: replaceInTemplate(adminLayerInfos, settings)
529
+ };
530
+ }
531
+ /**
532
+ * This is used when deploying views.
533
+ * We need to update fields referenced in adminLayerInfo for relationships prior to deploying the view.
534
+ * This moves the fieldInfos for the views source layers from the item settings for the source layer
535
+ * to the item settings for the view.
536
+ *
537
+ * @param itemTemplate The current itemTemplate being processed.
538
+ * @returns array of layers and tables
539
+ */
540
+ export function getLayersAndTables(itemTemplate) {
541
+ const properties = itemTemplate.properties;
542
+ const layersAndTables = [];
543
+ (properties.layers || []).forEach(function (layer) {
544
+ layersAndTables.push({
545
+ item: layer,
546
+ type: "layer"
547
+ });
548
+ });
549
+ (properties.tables || []).forEach(function (table) {
550
+ layersAndTables.push({
551
+ item: table,
552
+ type: "table"
553
+ });
554
+ });
555
+ return layersAndTables;
556
+ }
557
+ /**
558
+ * Fetch each layer and table from service so we can determine what fields they have.
559
+ * This is leveraged when we are using existing services so we can determine if we need to
560
+ * remove any fields from views that depend on these layers and tables.
561
+ *
562
+ * @param url Feature service endpoint
563
+ * @param ids layer and table ids
564
+ * @param authentication Credentials for the request
565
+ * @returns A promise that will resolve an array of promises with either a failure or the data
566
+ * @private
567
+ */
568
+ export function getExistingLayersAndTables(url, ids, authentication) {
569
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
570
+ return new Promise(resolve => {
571
+ const defs = ids.map(id => {
572
+ return rest_request(checkUrlPathTermination(url) + id, {
573
+ authentication
574
+ });
575
+ });
576
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
577
+ Promise.all(defs.map(p => p.catch(e => e))).then(resolve);
578
+ });
579
+ }
580
+ /**
581
+ * Adds the layers and tables of a feature service to it and restores their relationships.
582
+ *
583
+ * @param itemTemplate Feature service
584
+ * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature
585
+ * service)
586
+ * @param popupInfos the cached popup info from the layers
587
+ * @param authentication Credentials for the request
588
+ * @returns A promise that will resolve when all layers and tables have been added
589
+ * @private
590
+ */
591
+ export function addFeatureServiceLayersAndTables(itemTemplate, templateDictionary, popupInfos, authentication) {
592
+ return new Promise((resolve, reject) => {
593
+ if (isTrackingViewTemplate(itemTemplate)) {
594
+ resolve(null);
595
+ }
596
+ else {
597
+ // Create a hash of various properties that contain field references
598
+ const fieldInfos = {};
599
+ const adminLayerInfos = {};
600
+ // Add the service's layers and tables to it
601
+ const layersAndTables = getLayersAndTables(itemTemplate);
602
+ if (layersAndTables.length > 0) {
603
+ addFeatureServiceDefinition(itemTemplate.item.url || "", layersAndTables, templateDictionary, authentication, itemTemplate.key, adminLayerInfos, fieldInfos, itemTemplate).then(() => {
604
+ // Detemplatize field references and update the layer properties
605
+ // Only failure path is handled by addFeatureServiceDefinition
606
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
607
+ updateLayerFieldReferences(itemTemplate, fieldInfos, popupInfos, adminLayerInfos, templateDictionary).then(r => {
608
+ // Update relationships and layer definitions
609
+ const updates = getLayerUpdates({
610
+ message: "updated layer definition",
611
+ objects: r.layerInfos.fieldInfos,
612
+ itemTemplate: r.itemTemplate,
613
+ authentication
614
+ }, templateDictionary.isPortal);
615
+ // Process the updates sequentially
616
+ updates
617
+ .reduce((prev, update) => {
618
+ return prev.then(() => {
619
+ return getRequest(update);
620
+ });
621
+ }, Promise.resolve(null))
622
+ .then(() => resolve(null), (e) => reject(fail(e)) // getRequest
623
+ );
624
+ });
625
+ }, e => reject(fail(e)) // addFeatureServiceDefinition
626
+ );
627
+ }
628
+ else {
629
+ resolve(null);
630
+ }
631
+ }
632
+ });
633
+ }
634
+ /**
635
+ * Updates a feature service with a list of layers and/or tables.
636
+ *
637
+ * @param serviceUrl URL of feature service
638
+ * @param listToAdd List of layers and/or tables to add
639
+ * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature
640
+ * service)
641
+ * @param authentication Credentials for the request
642
+ * @param key
643
+ * @param adminLayerInfos Hash map of a layers adminLayerInfo
644
+ * @param fieldInfos Hash map of properties that contain field references
645
+ * @param itemTemplate
646
+ * @returns A promise that will resolve when the feature service has been updated
647
+ * @private
648
+ */
649
+ export function addFeatureServiceDefinition(serviceUrl, listToAdd, templateDictionary, authentication, key, adminLayerInfos, fieldInfos, itemTemplate) {
650
+ return new Promise((resolve, reject) => {
651
+ if (isTrackingViewTemplate(itemTemplate)) {
652
+ resolve(null);
653
+ }
654
+ else {
655
+ let options = {
656
+ layers: [],
657
+ tables: [],
658
+ authentication
659
+ };
660
+ // if the service has veiws keep track of the fields so we can use them to
661
+ // compare with the view fields
662
+ /* istanbul ignore else */
663
+ if (getProp(itemTemplate, "properties.service.hasViews")) {
664
+ _updateTemplateDictionaryFields(itemTemplate, templateDictionary);
665
+ }
666
+ const isSelfReferential = _isSelfReferential(listToAdd);
667
+ listToAdd = _updateOrder(listToAdd, isSelfReferential);
668
+ const chunkSize = _getLayerChunkSize();
669
+ const layerChunks = [];
670
+ listToAdd.forEach((toAdd, i) => {
671
+ let item = toAdd.item;
672
+ const originalId = item.id;
673
+ fieldInfos = cacheFieldInfos(item, fieldInfos);
674
+ // cache the values to be added in seperate addToDef calls
675
+ fieldInfos = cacheContingentValues(item.id, fieldInfos, itemTemplate);
676
+ /* istanbul ignore else */
677
+ if (item.isView) {
678
+ deleteViewProps(item);
679
+ }
680
+ // when the item is a view we need to grab the supporting fieldInfos
681
+ /* istanbul ignore else */
682
+ if (itemTemplate.properties.service.isView) {
683
+ _updateGeomFieldName(item.adminLayerInfo, templateDictionary);
684
+ adminLayerInfos[originalId] = item.adminLayerInfo;
685
+ // need to update adminLayerInfo before adding to the service def
686
+ // bring over the fieldInfos from the source layer
687
+ updateSettingsFieldInfos(itemTemplate, templateDictionary);
688
+ // update adminLayerInfo before add to definition with view source fieldInfo settings
689
+ item.adminLayerInfo = replaceInTemplate(item.adminLayerInfo, templateDictionary);
690
+ /* istanbul ignore else */
691
+ if (fieldInfos && fieldInfos.hasOwnProperty(item.id)) {
692
+ Object.keys(templateDictionary).some(k => {
693
+ if (templateDictionary[k].itemId === itemTemplate.itemId) {
694
+ fieldInfos[item.id]["sourceServiceFields"] =
695
+ templateDictionary[k].sourceServiceFields;
696
+ return true;
697
+ }
698
+ else {
699
+ return false;
700
+ }
701
+ });
702
+ }
703
+ }
704
+ /* istanbul ignore else */
705
+ if (templateDictionary.isPortal) {
706
+ item = _updateForPortal(item, itemTemplate, templateDictionary);
707
+ }
708
+ removeLayerOptimization(item);
709
+ // this can still chunk layers
710
+ options = _updateAddOptions(itemTemplate, options, layerChunks, isSelfReferential, authentication);
711
+ if (item.type === "Feature Layer") {
712
+ options.layers.push(item);
713
+ }
714
+ else {
715
+ options.tables.push(item);
716
+ }
717
+ // In general we are switching to not use chunking. Rather if we exceed the defined chunk size
718
+ // we will use an async request.
719
+ // Currently the only case that should chunk the requests is when we have a multisource view
720
+ // handled in _updateAddOptions above
721
+ /* istanbul ignore else */
722
+ if (i + 1 === listToAdd.length) {
723
+ layerChunks.push(Object.assign({}, options));
724
+ options = {
725
+ layers: [],
726
+ tables: [],
727
+ authentication
728
+ };
729
+ }
730
+ });
731
+ // will use async by default rather than chunk the layer requests when we have more layers
732
+ // than the defined chunk size
733
+ const useAsync = listToAdd.length > chunkSize;
734
+ layerChunks
735
+ .reduce((prev, curr) => prev.then(() => addToServiceDefinition(serviceUrl, curr, false, useAsync)), Promise.resolve(null))
736
+ .then(() => resolve(null), (e) => reject(fail(e)));
737
+ }
738
+ });
739
+ }
740
+ /**
741
+ * When a view is a multi service view sort based on the id
742
+ * https://github.com/Esri/solution.js/issues/1048
743
+ *
744
+ * @param layersAndTables The list of layers and tables for the current template
745
+ * @param isSelfReferential Indicates if any layers or tables have relationships with other layers or tables in the same service
746
+ *
747
+ * @returns Sorted list of layers and tables when using a multi-service view
748
+ * @private
749
+ */
750
+ export function _updateOrder(layersAndTables, isSelfReferential) {
751
+ return isSelfReferential ? layersAndTables.sort((a, b) => a.item.id - b.item.id) : layersAndTables;
752
+ }
753
+ /**
754
+ * When a view is a multi service view add each layer separately
755
+ * https://github.com/Esri/solution.js/issues/871
756
+ *
757
+ * @param itemTemplate The current itemTemplate being processed
758
+ * @param options Add to service definition options
759
+ * @param layerChunks Groups of layers or tables to add to the service
760
+ * @param isSelfReferential Indicates if any layers or tables have relationships with other layers or tables in the same service
761
+ * @param authentication Credentials for the request
762
+ *
763
+ * @returns Add to service definition options
764
+ * @private
765
+ */
766
+ export function _updateAddOptions(itemTemplate, options, layerChunks, isSelfReferential, authentication) {
767
+ const isMsView = getProp(itemTemplate, "properties.service.isMultiServicesView") || false;
768
+ /* istanbul ignore else */
769
+ if (isMsView || isSelfReferential) {
770
+ // if we already have some layers or tables add them first
771
+ /* istanbul ignore else */
772
+ if (options.layers.length > 0 || options.tables.length > 0) {
773
+ layerChunks.push(Object.assign({}, options));
774
+ options = {
775
+ layers: [],
776
+ tables: [],
777
+ authentication
778
+ };
779
+ }
780
+ }
781
+ return options;
782
+ }
783
+ /**
784
+ * Determine if any layer or table within the service references
785
+ * other layers or tables within the same service
786
+ *
787
+ * @param layersAndTables the list of layers and tables from the service
788
+ *
789
+ * @returns true when valid internal references are found
790
+ * @private
791
+ */
792
+ export function _isSelfReferential(layersAndTables) {
793
+ const names = layersAndTables.map(l => l.item.name);
794
+ const srcTables = {};
795
+ return layersAndTables.some(l => {
796
+ const table = l.item.adminLayerInfo?.viewLayerDefinition?.table;
797
+ if (table) {
798
+ const name = table.sourceServiceName;
799
+ const id = table.sourceLayerId;
800
+ if (name && id > -1) {
801
+ if (Object.keys(srcTables).indexOf(name) > -1) {
802
+ if (srcTables[name].indexOf(id) > -1) {
803
+ return true;
804
+ }
805
+ else {
806
+ srcTables[name].push(id);
807
+ }
808
+ }
809
+ else {
810
+ srcTables[name] = [id];
811
+ }
812
+ }
813
+ return (table.relatedTables || []).some(r => names.indexOf(r.name) > -1);
814
+ }
815
+ });
816
+ }
817
+ /**
818
+ * Remove "multiScaleGeometryInfo" for issue #526 to prevent invalid enablement of layer optimization
819
+ *
820
+ * @param layer the layer to evaluate
821
+ * @private
822
+ */
823
+ export function removeLayerOptimization(layer) {
824
+ /* istanbul ignore else */
825
+ if (layer.multiScaleGeometryInfo) {
826
+ deleteProp(layer, "multiScaleGeometryInfo");
827
+ }
828
+ }
829
+ /**
830
+ * Handle portal specific updates to the item
831
+ *
832
+ * @param item the item to update
833
+ * @param itemTemplate the item template
834
+ * @param templateDictionary Hash mapping Solution source id to id of its clone
835
+ *
836
+ * @returns the updated item
837
+ * @private
838
+ */
839
+ export function _updateForPortal(item, itemTemplate, templateDictionary) {
840
+ // When deploying to portal we need to adjust the uniquie ID field up front
841
+ /* istanbul ignore else */
842
+ if (item.uniqueIdField && item.uniqueIdField.name) {
843
+ item.uniqueIdField.name = String(item.uniqueIdField.name).toLocaleLowerCase();
844
+ }
845
+ // Portal will fail if the geometryField is null
846
+ if (item.type === "Table" && item.adminLayerInfo) {
847
+ deleteProp(item.adminLayerInfo, "geometryField");
848
+ }
849
+ // Portal will fail if the sourceFields in the viewLayerDef contain fields that are not in the source service
850
+ /* istanbul ignore else */
851
+ if (item.isView) {
852
+ const viewLayerDefTable = getProp(item, "adminLayerInfo.viewLayerDefinition.table");
853
+ let fieldNames = [];
854
+ if (viewLayerDefTable) {
855
+ const tableFieldNames = _getFieldNames(viewLayerDefTable, itemTemplate, templateDictionary);
856
+ fieldNames = fieldNames.concat(tableFieldNames);
857
+ const dynamicFieldNames = _getDynamicFieldNames(viewLayerDefTable);
858
+ fieldNames = fieldNames.concat(dynamicFieldNames);
859
+ setProp(item, "adminLayerInfo.viewLayerDefinition.table", _updateSourceLayerFields(viewLayerDefTable, fieldNames));
860
+ // Handle related also
861
+ /* istanbul ignore else */
862
+ if (Array.isArray(viewLayerDefTable.relatedTables)) {
863
+ viewLayerDefTable.relatedTables.map((relatedTable) => {
864
+ const relatedTableFieldNames = _getFieldNames(relatedTable, itemTemplate, templateDictionary);
865
+ fieldNames = fieldNames.concat(relatedTableFieldNames);
866
+ const dynamicRelatedFieldNames = _getDynamicFieldNames(relatedTable);
867
+ fieldNames = fieldNames.concat(dynamicRelatedFieldNames);
868
+ return _updateSourceLayerFields(relatedTable, [...relatedTableFieldNames, ...dynamicRelatedFieldNames]);
869
+ });
870
+ }
871
+ }
872
+ else {
873
+ Object.keys(templateDictionary).some(k => {
874
+ /* istanbul ignore else */
875
+ if (templateDictionary[k].itemId === item.serviceItemId) {
876
+ const layerInfo = templateDictionary[k][`layer${item.id}`];
877
+ /* istanbul ignore else */
878
+ if (layerInfo && layerInfo.fields) {
879
+ if (Array.isArray(layerInfo.fields)) {
880
+ fieldNames = layerInfo.fields.map((f) => f.name);
881
+ }
882
+ else {
883
+ fieldNames = Object.keys(layerInfo.fields);
884
+ }
885
+ }
886
+ return true;
887
+ }
888
+ });
889
+ }
890
+ item = _updateItemFields(item, fieldNames);
891
+ }
892
+ // not allowed to set sourceSchemaChangesAllowed or isView for portal
893
+ // these are set when you create the service
894
+ deleteProp(item, "isView");
895
+ return item;
896
+ }
897
+ /**
898
+ * Get a list of the source layer field names
899
+ *
900
+ * @param table the table instance to compare
901
+ * @param itemTemplate the item template
902
+ * @param templateDictionary Hash mapping Solution source id to id of its clone
903
+ *
904
+ * @returns an array of the source layers fields
905
+ * @private
906
+ */
907
+ export function _getFieldNames(table, itemTemplate, templateDictionary) {
908
+ let sourceLayerFields = [];
909
+ const viewSourceLayerId = table.sourceLayerId;
910
+ /* istanbul ignore else */
911
+ if (typeof viewSourceLayerId === "number") {
912
+ // need to make sure these actually exist in the source..
913
+ itemTemplate.dependencies.forEach(d => {
914
+ const layerInfo = templateDictionary[d][`layer${viewSourceLayerId}`];
915
+ /* istanbul ignore else */
916
+ if (layerInfo &&
917
+ layerInfo.fields &&
918
+ templateDictionary[d].name === table.sourceServiceName) {
919
+ if (Array.isArray(layerInfo.fields)) {
920
+ sourceLayerFields = sourceLayerFields.concat(layerInfo.fields.map((f) => f.name));
921
+ }
922
+ else {
923
+ sourceLayerFields = sourceLayerFields.concat(Object.keys(layerInfo.fields));
924
+ }
925
+ }
926
+ });
927
+ return sourceLayerFields;
928
+ }
929
+ }
930
+ /**
931
+ * Get a list of any dynamically calculated fields
932
+ * These fields are still valid but will not exist in the source service
933
+ *
934
+ * @param table the table instance to compare
935
+ *
936
+ * @returns an array of field names
937
+ * @private
938
+ */
939
+ export function _getDynamicFieldNames(table) {
940
+ const fieldNames = table.sourceLayerFields.reduce((prev, cur) => {
941
+ if (cur.statisticType) {
942
+ prev.push(cur.name);
943
+ }
944
+ return prev;
945
+ }, []);
946
+ return [...new Set(fieldNames)];
947
+ }
948
+ /**
949
+ * Remove fields references from fields and indexes that do not exist in the source service
950
+ *
951
+ * @param item Layer or table
952
+ * @param templateDictionary Hash mapping Solution source id to id of its clone
953
+ *
954
+ * @returns updated layer or table
955
+ * @private
956
+ */
957
+ export function _updateItemFields(item, fieldNames) {
958
+ /* istanbul ignore else */
959
+ if (fieldNames.length > 0) {
960
+ /* istanbul ignore else */
961
+ if (item.fields) {
962
+ item.fields = item.fields.filter((f) => fieldNames.indexOf(f.name) > -1);
963
+ }
964
+ /* istanbul ignore else */
965
+ if (item.indexes) {
966
+ item.indexes = item.indexes.filter((f) => fieldNames.indexOf(f.fields) > -1);
967
+ }
968
+ }
969
+ return item;
970
+ }
971
+ /**
972
+ * Filter the sourceLayerFields for the table
973
+ *
974
+ * @param table the table instance to evaluate
975
+ * @param sourceLayerFields array of fields from the source service
976
+ * @returns Updated instance of the table
977
+ * @private
978
+ */
979
+ export function _updateSourceLayerFields(table, sourceLayerFields) {
980
+ /* istanbul ignore else */
981
+ if (Array.isArray(table.sourceLayerFields) &&
982
+ table.sourceLayerFields.length > 0) {
983
+ // need to make sure these actually exist in the source..
984
+ /* istanbul ignore else */
985
+ if (sourceLayerFields.length > 0) {
986
+ setProp(table, "sourceLayerFields", table.sourceLayerFields.filter((f) => sourceLayerFields.indexOf(f.source.toLowerCase()) > -1));
987
+ }
988
+ }
989
+ return table;
990
+ }
991
+ /**
992
+ * When the itemm is a view with a geometry field update the value to
993
+ * use the table name from the view layer def
994
+ *
995
+ * @param item the item details from the current template
996
+ * @param templateDictionary Hash mapping property names to replacement values
997
+ * @private
998
+ */
999
+ export function _updateGeomFieldName(adminLayerInfo, templateDictionary) {
1000
+ // issue #471
1001
+ const tableName = getProp(adminLayerInfo, "viewLayerDefinition.table.name");
1002
+ const fieldName = getProp(adminLayerInfo, "geometryField.name");
1003
+ /* istanbul ignore else */
1004
+ if (fieldName && tableName) {
1005
+ const geomName = templateDictionary.isPortal
1006
+ ? `${tableName}.shape`
1007
+ : `${tableName}.Shape`;
1008
+ setProp(adminLayerInfo, "geometryField.name", geomName);
1009
+ }
1010
+ else if (!fieldName && getProp(adminLayerInfo, "geometryField")) {
1011
+ // null geom field will cause failure to deploy in portal
1012
+ // this is also checked and removed on deploy for older solutions
1013
+ deleteProp(adminLayerInfo, "geometryField");
1014
+ }
1015
+ }
1016
+ /**
1017
+ * Add the fields to the templateDictionary when a service has views
1018
+ * these are used to compare with fields from the view when domains are involved
1019
+ * when a view field has a domain that differs from that of the source service
1020
+ * the definition needs to be modified in an update call rather than when it is first added.
1021
+ * This should only happen when the domain differs.
1022
+ *
1023
+ * @param itemTemplate
1024
+ * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature service)
1025
+ * @private
1026
+ */
1027
+ export function _updateTemplateDictionaryFields(itemTemplate, templateDictionary, compareItemId = true) {
1028
+ const layers = itemTemplate.properties.layers;
1029
+ const tables = itemTemplate.properties.tables;
1030
+ const layersAndTables = layers.concat(tables);
1031
+ const fieldInfos = {};
1032
+ layersAndTables.forEach(layerOrTable => {
1033
+ fieldInfos[layerOrTable.id] = layerOrTable.fields;
1034
+ });
1035
+ Object.keys(templateDictionary).some(k => {
1036
+ if (compareItemId
1037
+ ? templateDictionary[k].itemId === itemTemplate.itemId
1038
+ : k === itemTemplate.itemId) {
1039
+ templateDictionary[k].fieldInfos = fieldInfos;
1040
+ return true;
1041
+ }
1042
+ else {
1043
+ return false;
1044
+ }
1045
+ });
1046
+ }
1047
+ /**
1048
+ * Set the defaultSpatialReference variable with the services spatial reference.
1049
+ * If this item is a Feature Service that has child views then we will use this value
1050
+ * if one or more of the child views spatial reference differs from that of its parent.
1051
+ *
1052
+ * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature service)
1053
+ * @param itemId The source id for the item
1054
+ * @param spatialReference \{ wkid: 102100 \} for example
1055
+ * @private
1056
+ */
1057
+ export function setDefaultSpatialReference(templateDictionary, itemId, spatialReference) {
1058
+ /* istanbul ignore else */
1059
+ if (spatialReference) {
1060
+ setCreateProp(templateDictionary, `${itemId}.defaultSpatialReference`, spatialReference);
1061
+ }
1062
+ }
1063
+ /**
1064
+ * Compare the spatial reference of the current item against its dependencies.
1065
+ * The spatial reference of a view cannot differ from its source service.
1066
+ * If the view has a different spatial reference from its source use the source spatial reference.
1067
+ *
1068
+ * @param serviceInfo Basic service information
1069
+ * @param itemTemplate The current template to process
1070
+ * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature service)
1071
+ * @private
1072
+ */
1073
+ export function validateSpatialReferenceAndExtent(serviceInfo, itemTemplate, templateDictionary) {
1074
+ /* istanbul ignore else */
1075
+ if (getProp(serviceInfo, "service.isView")) {
1076
+ let sourceSR;
1077
+ let sourceExt;
1078
+ itemTemplate.dependencies.some(id => {
1079
+ const source = templateDictionary[id];
1080
+ const sr = getProp(source, "defaultSpatialReference");
1081
+ /* istanbul ignore else */
1082
+ if (!sourceSR && sr) {
1083
+ sourceSR = sr;
1084
+ }
1085
+ const ext = getProp(source, "defaultExtent");
1086
+ /* istanbul ignore else */
1087
+ if (!sourceExt && ext) {
1088
+ sourceExt = ext;
1089
+ }
1090
+ return sourceSR && sourceExt;
1091
+ });
1092
+ const sourceWkid = getProp(sourceSR, "wkid");
1093
+ const viewWkid = getProp(serviceInfo, "service.spatialReference.wkid");
1094
+ /* istanbul ignore else */
1095
+ if (sourceWkid && viewWkid && sourceWkid !== viewWkid) {
1096
+ setCreateProp(serviceInfo, "service.spatialReference", sourceSR);
1097
+ }
1098
+ const viewExt = getProp(serviceInfo, "service.fullExtent");
1099
+ /* istanbul ignore else */
1100
+ if (sourceExt &&
1101
+ viewExt &&
1102
+ JSON.stringify(sourceExt) !== JSON.stringify(viewExt)) {
1103
+ setCreateProp(serviceInfo, "defaultExtent", sourceExt);
1104
+ }
1105
+ }
1106
+ }
1107
+ /**
1108
+ * Updates a feature service with a list of layers and/or tables.
1109
+ *
1110
+ * @param itemTemplate
1111
+ * @param fieldInfos Hash map of properties that contain field references
1112
+ * @param popupInfos Hash map of a layers popupInfo
1113
+ * @param adminLayerInfos Hash map of a layers adminLayerInfo
1114
+ * @param templateDictionary Hash mapping Solution source id to id of its clone (and name & URL for feature service)
1115
+ * @param authentication Credentials for the request
1116
+ * @returns A promise that will resolve when the feature service has been updated
1117
+ * @private
1118
+ */
1119
+ export function updateLayerFieldReferences(itemTemplate, fieldInfos, popupInfos, adminLayerInfos, templateDictionary) {
1120
+ return new Promise((resolveFn, rejectFn) => {
1121
+ // Will need to do some post processing for fields
1122
+ // to handle any potential field name changes when deploying to portal
1123
+ postProcessFields(itemTemplate, fieldInfos, popupInfos, adminLayerInfos, templateDictionary).then((layerInfos) => {
1124
+ // Update the items text with detemplatized popupInfo
1125
+ updatePopupInfo(itemTemplate, layerInfos.popupInfos);
1126
+ resolveFn({
1127
+ itemTemplate,
1128
+ layerInfos
1129
+ });
1130
+ }, e => rejectFn(fail(e)));
1131
+ });
1132
+ }
1133
+ /**
1134
+ * Update the names of fields for each layer or table after it has been
1135
+ * added to the definition
1136
+ *
1137
+ * @param itemTemplate Item to be created
1138
+ * @param layerInfos Hash map of properties that contain field references and various layer info
1139
+ * @param popupInfos Hash map of a layers popupInfo
1140
+ * @param adminLayerInfos Hash map of a layers adminLayerInfo
1141
+ * @param templateDictionary
1142
+ * @param authentication Credentials for the request
1143
+ * @returns An object with detemplatized field references
1144
+ * @private
1145
+ */
1146
+ export function postProcessFields(itemTemplate, layerInfos, popupInfos, adminLayerInfos, templateDictionary) {
1147
+ return new Promise((resolveFn, rejectFn) => {
1148
+ if (!itemTemplate.item.url) {
1149
+ rejectFn(fail("Feature layer " + itemTemplate.itemId + " does not have a URL"));
1150
+ }
1151
+ else {
1152
+ const id = itemTemplate.itemId;
1153
+ const settingsKeys = Object.keys(templateDictionary);
1154
+ let templateInfo;
1155
+ settingsKeys.some(k => {
1156
+ if (templateDictionary[k].itemId === id) {
1157
+ templateInfo = templateDictionary[k];
1158
+ return true;
1159
+ }
1160
+ else {
1161
+ return false;
1162
+ }
1163
+ });
1164
+ // concat any layers and tables to process
1165
+ const layers = itemTemplate.properties.layers;
1166
+ const tables = itemTemplate.properties.tables;
1167
+ const layersAndTables = layers.concat(tables);
1168
+ // Set the newFields property for the layerInfos...this will contain all fields
1169
+ // as they are after being added to the definition.
1170
+ // This allows us to handle any potential field name changes after deploy to portal
1171
+ layersAndTables.forEach((item) => {
1172
+ // when deploying to portal "isView" is only set for create service and will fail when
1173
+ // present on addToDef so this property is removed from item and we should check the templates service info
1174
+ const isView = item.isView || itemTemplate.properties.service.isView;
1175
+ /* istanbul ignore else */
1176
+ if (layerInfos && layerInfos.hasOwnProperty(item.id)) {
1177
+ const layerInfo = layerInfos[item.id];
1178
+ layerInfo["isView"] = item.isView;
1179
+ layerInfo["newFields"] = item.fields;
1180
+ layerInfo["sourceSchemaChangesAllowed"] =
1181
+ item.sourceSchemaChangesAllowed;
1182
+ /* istanbul ignore else */
1183
+ if (item.editFieldsInfo) {
1184
+ // more than case change when deployed to protal so keep track of the new names
1185
+ layerInfo["newEditFieldsInfo"] = JSON.parse(JSON.stringify(item.editFieldsInfo));
1186
+ }
1187
+ /* istanbul ignore else */
1188
+ if (isView && templateInfo && templateDictionary.isPortal) {
1189
+ // when the item is a view bring over the source service fields so we can compare the domains
1190
+ layerInfo["sourceServiceFields"] = templateInfo.sourceServiceFields;
1191
+ }
1192
+ }
1193
+ });
1194
+ // Add the layerInfos to the settings object to be used while detemplatizing
1195
+ settingsKeys.forEach((k) => {
1196
+ if (id === templateDictionary[k].itemId) {
1197
+ templateDictionary[k] = Object.assign(templateDictionary[k], getLayerSettings(layerInfos, templateDictionary[k].url, id));
1198
+ }
1199
+ });
1200
+ // update the layerInfos object with current field names
1201
+ resolveFn(deTemplatizeFieldInfos(layerInfos, popupInfos, adminLayerInfos, templateDictionary));
1202
+ }
1203
+ });
1204
+ }
1205
+ /**
1206
+ * Add popup info back to the layer item
1207
+ *
1208
+ * @param itemTemplate
1209
+ * @param popupInfos popup info to be added back to the layer
1210
+ * @private
1211
+ */
1212
+ export function updatePopupInfo(itemTemplate, popupInfos) {
1213
+ ["layers", "tables"].forEach(type => {
1214
+ const _items = getProp(itemTemplate, "data." + type);
1215
+ /* istanbul ignore else */
1216
+ if (_items && Array.isArray(_items)) {
1217
+ _items.forEach((item) => {
1218
+ item.popupInfo = getProp(popupInfos, type + "." + item.id) || {};
1219
+ });
1220
+ }
1221
+ });
1222
+ }
1223
+ //#endregion
1224
+ //#region Private helper functions --------------------------------------------------//
1225
+ /**
1226
+ * Helper function to templatize value and make sure its converted to lowercase
1227
+ *
1228
+ * @param basePath path used to de-templatize while deploying
1229
+ * @param value to be converted to lower case for lookup while deploying
1230
+ * @private
1231
+ */
1232
+ export function _templatize(basePath, value, suffix) {
1233
+ if (value.startsWith("{{")) {
1234
+ return value;
1235
+ }
1236
+ else {
1237
+ return String(templatizeTerm(basePath, basePath, "." + String(value).toLowerCase() + (suffix ? "." + suffix : "")));
1238
+ }
1239
+ }
1240
+ /**
1241
+ * templatize an objects property
1242
+ *
1243
+ * @param object the object with the property to templatize
1244
+ * @param property the property of the object to templatize
1245
+ * @param basePath path used to de-templatize while deploying
1246
+ * @private
1247
+ */
1248
+ export function _templatizeProperty(object, property, basePath, suffix) {
1249
+ if (object && object.hasOwnProperty(property) && object[property]) {
1250
+ object[property] = _templatize(basePath, object[property], suffix);
1251
+ }
1252
+ }
1253
+ /**
1254
+ * Templatize field references, serviceItemId, and adminLayerInfo for a layer
1255
+ *
1256
+ * @param dataItem from the items data property
1257
+ * @param adminItem from the services admin api
1258
+ * @param itemTemplate Template for feature service item
1259
+ * @param dependencies Array of IDependency for name mapping
1260
+ * @param templatizeFieldReferences Templatize all field references within a layer
1261
+ * @returns A promise that will resolve when template has been updated
1262
+ * @private
1263
+ */
1264
+ export function _templatizeLayer(dataItem, adminItem, itemTemplate, dependencies, templatizeFieldReferences, templateDictionary) {
1265
+ // check for and repair common field issues
1266
+ _validateFields(adminItem);
1267
+ // Templatize all properties that contain field references
1268
+ /* istanbul ignore else */
1269
+ if (templatizeFieldReferences) {
1270
+ _templatizeLayerFieldReferences(dataItem, itemTemplate.itemId, adminItem, dependencies);
1271
+ }
1272
+ const updates = [adminItem];
1273
+ if (dataItem) {
1274
+ updates.push(dataItem);
1275
+ }
1276
+ updates.forEach(update => {
1277
+ if (update.hasOwnProperty("name")) {
1278
+ // templatize the name but leave the current name as the optional default
1279
+ update.name = templatizeTerm(update["serviceItemId"] + ".layer" + update.id, update["serviceItemId"] + ".layer" + update.id, ".name||" + update.name);
1280
+ }
1281
+ if (update.hasOwnProperty("extent")) {
1282
+ update.extent = templatizeTerm(update["serviceItemId"], update["serviceItemId"], ".solutionExtent");
1283
+ }
1284
+ if (update.hasOwnProperty("serviceItemId")) {
1285
+ update["serviceItemId"] = templatizeTerm(update["serviceItemId"], update["serviceItemId"], ".itemId");
1286
+ }
1287
+ if (update.hasOwnProperty("adminLayerInfo")) {
1288
+ update.adminLayerInfo = _templatizeAdminLayerInfo(update, dependencies, templateDictionary);
1289
+ }
1290
+ });
1291
+ }
1292
+ /**
1293
+ * Repair common issues that can occur with feature service field references.
1294
+ * This function will mutate the input item if any of the common issues have occured.
1295
+ *
1296
+ * @param adminItem layer or table from the service
1297
+ */
1298
+ export function _validateFields(adminItem) {
1299
+ const fieldNames = (adminItem.fields || []).map((f) => f.name);
1300
+ // Update primary display field if field isn't in the layer.
1301
+ _validateDisplayField(adminItem, fieldNames);
1302
+ // Remove indexes on fields that don't exist in the layer.
1303
+ // Remove duplicate indexes on the same field.
1304
+ _validateIndexes(adminItem, fieldNames);
1305
+ // Remove field references in templates when field doesn't exist in the layer.
1306
+ _validateTemplatesFields(adminItem, fieldNames);
1307
+ _validateTypesTemplates(adminItem, fieldNames);
1308
+ // Repair editFieldsInfo if field referenced doesn't exist in the layer
1309
+ _validateEditFieldsInfo(adminItem, fieldNames);
1310
+ }
1311
+ /**
1312
+ * Update primary display field if casing doesn't match.
1313
+ * Update primary display field to the first non OID or GlobalId if the field isn't in the layer.
1314
+ *
1315
+ * @param adminItem layer or table from the service
1316
+ * @param fieldNames string list of fields names
1317
+ * @private
1318
+ */
1319
+ export function _validateDisplayField(adminItem, fieldNames) {
1320
+ const displayField = adminItem.displayField || "";
1321
+ let i = -1;
1322
+ if (fieldNames.some(name => {
1323
+ i += 1;
1324
+ return name === displayField || name === displayField.toLowerCase();
1325
+ })) {
1326
+ adminItem.displayField = fieldNames[i];
1327
+ }
1328
+ else {
1329
+ // use the first non-OID non-globalId field we find
1330
+ const skipFields = [];
1331
+ const oidField = getProp(adminItem, "uniqueIdField.name");
1332
+ /* istanbul ignore else */
1333
+ if (oidField) {
1334
+ skipFields.push(oidField);
1335
+ }
1336
+ const globalIdField = getProp(adminItem, "globalIdField");
1337
+ /* istanbul ignore else */
1338
+ if (globalIdField) {
1339
+ skipFields.push(globalIdField);
1340
+ }
1341
+ fieldNames.some(name => {
1342
+ if (skipFields.indexOf(name) === -1) {
1343
+ adminItem.displayField = name;
1344
+ return true;
1345
+ }
1346
+ else {
1347
+ return false;
1348
+ }
1349
+ });
1350
+ }
1351
+ }
1352
+ /**
1353
+ * Remove indexes on fields that don't exist in the layer.
1354
+ * Remove duplicate indexes on the same field.
1355
+ *
1356
+ * @param adminItem layer or table from the service
1357
+ * @param fieldNames string list of fields names
1358
+ * @private
1359
+ */
1360
+ export function _validateIndexes(adminItem, fieldNames) {
1361
+ const indexes = adminItem.indexes;
1362
+ /* istanbul ignore else */
1363
+ if (indexes) {
1364
+ const indexedFields = [];
1365
+ adminItem.indexes = indexes.reduce((filtered, index) => {
1366
+ const indexFields = index.fields.split(",");
1367
+ const verifiedFields = [];
1368
+ indexFields.forEach(indexField => {
1369
+ /* istanbul ignore else */
1370
+ if (indexedFields.indexOf(indexField) === -1) {
1371
+ indexedFields.push(indexField);
1372
+ // this is the first index with this field and it should be added if the field exists
1373
+ /* istanbul ignore else */
1374
+ if (fieldNames.indexOf(indexField) > -1) {
1375
+ verifiedFields.push(indexField);
1376
+ }
1377
+ }
1378
+ // else the field has more than one index associated and should not be returned
1379
+ });
1380
+ /* istanbul ignore else */
1381
+ if (verifiedFields.length > 0) {
1382
+ index.fields = verifiedFields.join(",");
1383
+ filtered.push(index);
1384
+ }
1385
+ return filtered;
1386
+ }, []);
1387
+ }
1388
+ }
1389
+ /**
1390
+ * Remove field references from templates that no longer exist.
1391
+ *
1392
+ * @param adminItem layer or table from the service
1393
+ * @param fieldNames string list of fields names
1394
+ * @private
1395
+ */
1396
+ export function _validateTemplatesFields(adminItem, fieldNames) {
1397
+ const templates = adminItem.templates;
1398
+ /* istanbul ignore else */
1399
+ if (templates) {
1400
+ adminItem.templates = templates.map(template => {
1401
+ const attributes = getProp(template, "prototype.attributes");
1402
+ /* istanbul ignore else */
1403
+ if (attributes) {
1404
+ Object.keys(attributes).forEach(k => {
1405
+ /* istanbul ignore else */
1406
+ if (fieldNames.indexOf(k) === -1) {
1407
+ delete attributes[k];
1408
+ }
1409
+ });
1410
+ setProp(template, "prototype.attributes", attributes);
1411
+ }
1412
+ return template;
1413
+ });
1414
+ }
1415
+ }
1416
+ /**
1417
+ * Remove field references from templates that no longer exist.
1418
+ *
1419
+ * @param adminItem layer or table from the service
1420
+ * @param fieldNames string list of fields names
1421
+ * @private
1422
+ */
1423
+ export function _validateTypesTemplates(adminItem, fieldNames) {
1424
+ const types = adminItem.types;
1425
+ /* istanbul ignore else */
1426
+ if (types) {
1427
+ adminItem.types = types.map(t => {
1428
+ _validateTemplatesFields(t, fieldNames);
1429
+ return t;
1430
+ });
1431
+ }
1432
+ }
1433
+ /**
1434
+ * Check if edit feilds exist but with lower case
1435
+ *
1436
+ * @param adminItem layer or table from the service
1437
+ * @param fieldNames string list of fields names
1438
+ * @private
1439
+ */
1440
+ export function _validateEditFieldsInfo(adminItem, fieldNames) {
1441
+ const editFieldsInfo = adminItem.editFieldsInfo;
1442
+ /* istanbul ignore else */
1443
+ if (editFieldsInfo) {
1444
+ const editFieldsInfoKeys = Object.keys(editFieldsInfo);
1445
+ editFieldsInfoKeys.forEach(k => {
1446
+ const editFieldName = editFieldsInfo[k];
1447
+ /* istanbul ignore else */
1448
+ if (editFieldName) {
1449
+ fieldNames.some(name => {
1450
+ if (name === editFieldName) {
1451
+ return true;
1452
+ }
1453
+ else if (name === editFieldName.toLowerCase()) {
1454
+ editFieldsInfo[k] = name;
1455
+ return true;
1456
+ }
1457
+ else {
1458
+ return false;
1459
+ }
1460
+ });
1461
+ }
1462
+ });
1463
+ }
1464
+ }
1465
+ /**
1466
+ *
1467
+ * Templatize all field references within a layer
1468
+ * This is necessary to support potential field name changes when deploying to portal
1469
+ * Portal will force all field names to be lower case
1470
+ *
1471
+ * @param dataItem The data layer instance with field name references within
1472
+ * @param itemID The id for the item that contains this layer.
1473
+ * @param layer JSON return from the layer being templatized.
1474
+ * @param dependencies
1475
+ * @returns An updated instance of the layer
1476
+ * @private
1477
+ */
1478
+ export function _templatizeLayerFieldReferences(dataItem, itemID, layer, dependencies) {
1479
+ // This is the value that will be used as the template for adlib replacement
1480
+ const path = itemID + ".layer" + layer.id + ".fields";
1481
+ // Get the field names for various tests
1482
+ const fieldNames = layer.fields.map((f) => f.name);
1483
+ // Update the layer from the items data property
1484
+ if (dataItem) {
1485
+ _templatizeAdminLayerInfoFields(dataItem, dependencies);
1486
+ _templatizePopupInfo(dataItem, layer, path, itemID, fieldNames);
1487
+ }
1488
+ // Update the layer
1489
+ _templatizeAdminLayerInfoFields(layer, dependencies);
1490
+ _templatizeRelationshipFields(layer, itemID);
1491
+ _templatizeDefinitionEditor(layer, path, fieldNames);
1492
+ _templatizeDefinitionExpression(layer, path, fieldNames);
1493
+ _templatizeDrawingInfo(layer, path, fieldNames);
1494
+ _templatizeTemplates(layer, path);
1495
+ _templatizeTypeTemplates(layer, path);
1496
+ _templatizeTimeInfo(layer, path);
1497
+ _templatizeDefinitionQuery(layer, path, fieldNames);
1498
+ }
1499
+ /**
1500
+ * Templatize a layers adminLayerInfo by removing properties that will case issues with clone.
1501
+ * Also templatizes the source service name when we are dealing with a view.
1502
+ *
1503
+ * @param layer The layer to be modified
1504
+ * @param dependencies Array of service dependencies
1505
+ * @returns A new copy of the modified adminLayerInfo for the given layer
1506
+ * @private
1507
+ */
1508
+ export function _templatizeAdminLayerInfo(layer, dependencies, templateDictionary) {
1509
+ // Create new instance of adminLayerInfo to update for clone
1510
+ const adminLayerInfo = Object.assign({}, layer.adminLayerInfo);
1511
+ _updateGeomFieldName(adminLayerInfo, templateDictionary);
1512
+ deleteProp(adminLayerInfo, "xssTrustedFields");
1513
+ deleteProp(adminLayerInfo, "tableName");
1514
+ // Remove unnecessary properties and templatize key properties from viewLayerDefinition
1515
+ /* istanbul ignore else */
1516
+ if (adminLayerInfo.viewLayerDefinition) {
1517
+ const viewDef = Object.assign({}, adminLayerInfo.viewLayerDefinition);
1518
+ _processAdminObject(viewDef, dependencies);
1519
+ // Remove unnecessary properties and templatize key properties from viewLayerDefinition.table
1520
+ /* istanbul ignore else */
1521
+ if (viewDef.table) {
1522
+ _processAdminObject(viewDef.table, dependencies);
1523
+ /* istanbul ignore else */
1524
+ if (viewDef.table.relatedTables) {
1525
+ viewDef.table.relatedTables.forEach((table) => {
1526
+ _processAdminObject(table, dependencies);
1527
+ });
1528
+ }
1529
+ }
1530
+ adminLayerInfo.viewLayerDefinition = viewDef;
1531
+ }
1532
+ return adminLayerInfo;
1533
+ }
1534
+ /**
1535
+ * Remove sourceId and templatize the sourceServiceName
1536
+ *
1537
+ * @param object The layer to be modified
1538
+ * @param dependencies Array of service dependencies
1539
+ * @private
1540
+ */
1541
+ export function _processAdminObject(object, dependencies) {
1542
+ deleteProp(object, "sourceId");
1543
+ if (object.hasOwnProperty("sourceServiceName")) {
1544
+ object.sourceServiceName = _templatizeSourceServiceName(object.sourceServiceName, dependencies);
1545
+ }
1546
+ }
1547
+ /**
1548
+ * Templatize the name based on the given dependencies
1549
+ *
1550
+ * @param lookupName The current name from the source service
1551
+ * @param dependencies Array of IDependency for name mapping
1552
+ * @returns The templatized name || undefined when no matching dependency is found
1553
+ * @private
1554
+ */
1555
+ export function _templatizeSourceServiceName(lookupName, dependencies) {
1556
+ const deps = dependencies.filter(dependency => dependency.name === lookupName);
1557
+ return deps.length === 1 ? _templatize(deps[0].id, "name") : undefined;
1558
+ }
1559
+ /**
1560
+ * templatize the fields referenced in adminLayerInfo
1561
+ *
1562
+ * @param layer the layer object with the adminLayerInfo property to templatize
1563
+ * @param basePath path used to de-templatize while deploying
1564
+ * @param itemID the id for the item that contains this layer
1565
+ * @private
1566
+ */
1567
+ export function _templatizeAdminLayerInfoFields(layer, dependencies) {
1568
+ // templatize the source layer fields
1569
+ const table = getProp(layer, "adminLayerInfo.viewLayerDefinition.table");
1570
+ if (table) {
1571
+ let id = _getDependantItemId(table.sourceServiceName, dependencies);
1572
+ const path = id + ".layer" + table.sourceLayerId + ".fields";
1573
+ _templatizeAdminSourceLayerFields(table.sourceLayerFields || [], path);
1574
+ // templatize the releated table fields
1575
+ const relatedTables = getProp(layer, "adminLayerInfo.viewLayerDefinition.table.relatedTables") || [];
1576
+ if (relatedTables.length > 0) {
1577
+ relatedTables.forEach((t) => {
1578
+ id = _getDependantItemId(t.sourceServiceName, dependencies);
1579
+ const relatedPath = id + ".layer" + t.sourceLayerId + ".fields";
1580
+ _templatizeTopFilter(t.topFilter || {}, relatedPath);
1581
+ _templatizeAdminSourceLayerFields(t.sourceLayerFields || [], relatedPath);
1582
+ const parentKeyFields = t.parentKeyFields || [];
1583
+ t.parentKeyFields = parentKeyFields.map((f) => {
1584
+ return _templatize(path, f, "name");
1585
+ });
1586
+ const keyFields = t.keyFields || [];
1587
+ t.keyFields = keyFields.map((f) => {
1588
+ return _templatize(relatedPath, f, "name");
1589
+ });
1590
+ });
1591
+ }
1592
+ }
1593
+ }
1594
+ /**
1595
+ * find id based on dependency name
1596
+ *
1597
+ * @param lookupName name of dependency we want to find the id of
1598
+ * @param dependencies array of item dependencies
1599
+ * @private
1600
+ */
1601
+ export function _getDependantItemId(lookupName, dependencies) {
1602
+ const deps = dependencies.filter(dependency => dependency.name === lookupName);
1603
+ return deps.length === 1 ? deps[0].id : "";
1604
+ }
1605
+ /**
1606
+ * templatize the sourceLayerFields referenced in adminLayerInfo
1607
+ *
1608
+ * @param fields array of sourceLayerFields to templatize
1609
+ * @param basePath path used to de-templatize while deploying
1610
+ * @private
1611
+ */
1612
+ export function _templatizeAdminSourceLayerFields(fields, basePath) {
1613
+ fields.forEach(f => _templatizeProperty(f, "source", basePath, "name"));
1614
+ }
1615
+ /**
1616
+ * templatize the topFilter property from adminLayerInfo related tables
1617
+ *
1618
+ * @param topFilter the topFilter object to templatize
1619
+ * @param basePath path used to de-templatize while deploying
1620
+ * @private
1621
+ */
1622
+ export function _templatizeTopFilter(topFilter, basePath) {
1623
+ /* istanbul ignore else */
1624
+ if (topFilter) {
1625
+ // templatize the orderByFields prop
1626
+ const orderByFields = topFilter["orderByFields"] || "";
1627
+ /* istanbul ignore else */
1628
+ if (orderByFields !== "") {
1629
+ const orderByField = orderByFields.split(" ")[0];
1630
+ topFilter.orderByFields = topFilter.orderByFields.replace(orderByField, _templatize(basePath, orderByField, "name"));
1631
+ }
1632
+ const groupByFields = topFilter["groupByFields"] || "";
1633
+ /* istanbul ignore else */
1634
+ if (groupByFields !== "") {
1635
+ const _groupByFields = groupByFields.split(",");
1636
+ /* istanbul ignore else */
1637
+ if (_groupByFields.length > 0) {
1638
+ const mappedFields = _groupByFields.map((f) => {
1639
+ return _templatize(basePath, f, "name");
1640
+ });
1641
+ topFilter.groupByFields = mappedFields.join(",");
1642
+ }
1643
+ }
1644
+ }
1645
+ }
1646
+ /**
1647
+ * templatize the relationships key fields using the related table id in the basePath
1648
+ *
1649
+ * @param layer the layer that has the relationships to templatize
1650
+ * @param itemID the id of the item that contains the related table
1651
+ * @private
1652
+ */
1653
+ export function _templatizeRelationshipFields(layer, itemID) {
1654
+ if (layer && layer.relationships) {
1655
+ const relationships = layer.relationships;
1656
+ relationships.forEach(r => {
1657
+ /* istanbul ignore else */
1658
+ if (r.keyField) {
1659
+ const basePath = itemID + ".layer" + layer.id + ".fields";
1660
+ _templatizeProperty(r, "keyField", basePath, "name");
1661
+ }
1662
+ });
1663
+ }
1664
+ }
1665
+ /**
1666
+ * templatize the popupInfo
1667
+ *
1668
+ * @param layerDefinition the layerDefinition that has the popupInfo to templatize
1669
+ * @param layer the JSON for the layer being templatized
1670
+ * @param basePath path used to de-templatize while deploying
1671
+ * @param itemID the id for the item that contains this layer
1672
+ * @param fieldNames array of fieldNames
1673
+ * @private
1674
+ */
1675
+ export function _templatizePopupInfo(layerDefinition, layer, basePath, itemID, fieldNames) {
1676
+ // the data layer does not have the fields...will need to get those
1677
+ // from the associated layer json
1678
+ if (fieldNames && layerDefinition.popupInfo) {
1679
+ const popupInfo = layerDefinition.popupInfo;
1680
+ _templatizeName(popupInfo, "title", fieldNames, basePath);
1681
+ _templatizeName(popupInfo, "description", fieldNames, basePath);
1682
+ const fieldInfos = popupInfo.fieldInfos || [];
1683
+ _templatizePopupInfoFieldInfos(fieldInfos, layer, itemID, basePath);
1684
+ const expressionInfos = popupInfo.expressionInfos || [];
1685
+ _templatizeExpressionInfos(expressionInfos, fieldNames, basePath);
1686
+ const popupElements = popupInfo.popupElements || [];
1687
+ _templatizePopupElements(popupElements, basePath, layer, itemID, fieldNames);
1688
+ const mediaInfos = popupInfo.mediaInfos || [];
1689
+ _templatizeMediaInfos(mediaInfos, fieldNames, basePath, layer, itemID);
1690
+ }
1691
+ }
1692
+ /**
1693
+ * templatize field name when referenced like this: \{\{fieldName\}\}
1694
+ * checks each field name from the layer
1695
+ *
1696
+ * @param object with the property to test for a field name
1697
+ * @param property that could have a field name referenced
1698
+ * @param fieldNames array for field names for the layer
1699
+ * @param basePath path used to de-templatize while deploying
1700
+ * @private
1701
+ */
1702
+ export function _templatizeName(object, property, fieldNames, basePath) {
1703
+ if (object.hasOwnProperty(property)) {
1704
+ fieldNames.forEach(name => {
1705
+ // Only test and replace instance of the name so any enclosing characters
1706
+ // will be retained
1707
+ const regEx = new RegExp("(\\b" + name + "\\b(?![}]{2}))", "gm");
1708
+ if (regEx.test(object[property])) {
1709
+ object[property] = object[property].replace(regEx, _templatize(basePath, name, "name"));
1710
+ }
1711
+ });
1712
+ }
1713
+ }
1714
+ /**
1715
+ * templatize field name when referenced like this: \{\{fieldName\}\}
1716
+ * checks each field name from the layer
1717
+ *
1718
+ * @param fieldInfos object that contains the popups fieldInfos
1719
+ * @param layer json of layer being cloned
1720
+ * @param itemID id of the item that contains the current layer
1721
+ * @param basePath path used to de-templatize while deploying
1722
+ * @private
1723
+ */
1724
+ export function _templatizePopupInfoFieldInfos(fieldInfos, layer, itemID, basePath) {
1725
+ fieldInfos.forEach((f) => {
1726
+ f.fieldName = _templatizeFieldName(f.fieldName, layer, itemID, basePath);
1727
+ });
1728
+ }
1729
+ /**
1730
+ * templatize field name when referenced like this: \{\{fieldName\}\}
1731
+ * checks each field name from the layer
1732
+ *
1733
+ * @param name the field name to templatize
1734
+ * @param layer json of layer being cloned
1735
+ * @param itemID id of the item that contains the current layer
1736
+ * @param basePath path used to de-templatize while deploying
1737
+ * @private
1738
+ */
1739
+ export function _templatizeFieldName(name, layer, itemID, basePath) {
1740
+ if (name.indexOf("relationships/") > -1) {
1741
+ const rels = name.split("/");
1742
+ const relationshipId = rels[1];
1743
+ const adminRelatedTables = getProp(layer, "adminLayerInfo.viewLayerDefinition.table.relatedTables");
1744
+ const relatedTables = layer.relationships || adminRelatedTables;
1745
+ /* istanbul ignore else */
1746
+ if (relatedTables && relatedTables.length > parseInt(relationshipId, 10)) {
1747
+ const relatedTable = relatedTables[relationshipId];
1748
+ // the layers relationships stores the property as relatedTableId
1749
+ // the layers adminLayerInfo relatedTables stores the property as sourceLayerId
1750
+ const prop = getProp(relatedTable, "relatedTableId")
1751
+ ? "relatedTableId"
1752
+ : "sourceLayerId";
1753
+ const _basePath = itemID + ".layer" + relatedTable[prop] + ".fields";
1754
+ rels[2] = _templatize(_basePath, rels[2], "name");
1755
+ name = rels.join("/");
1756
+ }
1757
+ }
1758
+ else {
1759
+ // do not need to templatize expression references as the expression
1760
+ // itself will be templatized
1761
+ if (name.indexOf("expression/") === -1) {
1762
+ name = _templatize(basePath, name, "name");
1763
+ }
1764
+ }
1765
+ return name;
1766
+ }
1767
+ /**
1768
+ * templatize field name when referenced in expressionInfos
1769
+ *
1770
+ * @param expressionInfos the popups expressionInfos to check
1771
+ * @param fieldNames array of the layers field names
1772
+ * @param basePath path used to de-templatize while deploying
1773
+ * @private
1774
+ */
1775
+ export function _templatizeExpressionInfos(expressionInfos, fieldNames, basePath) {
1776
+ return expressionInfos.map((i) => {
1777
+ fieldNames.forEach(name => {
1778
+ i.expression = _templatizeArcadeExpressions(i.expression, name, basePath);
1779
+ });
1780
+ return i;
1781
+ });
1782
+ }
1783
+ /**
1784
+ * templatize field name when referenced in popupElelments
1785
+ *
1786
+ * @param popupElelments the popups popupElelments to check
1787
+ * @param basePath path used to de-templatize while deploying
1788
+ * @param layer json of layer being cloned
1789
+ * @param itemID id of the item that contains the current layer
1790
+ * @param fieldNames array of field names
1791
+ * @private
1792
+ */
1793
+ export function _templatizePopupElements(popupElelments, basePath, layer, itemID, fieldNames) {
1794
+ popupElelments.forEach((pe) => {
1795
+ if (pe.hasOwnProperty("fieldInfos")) {
1796
+ _templatizePopupInfoFieldInfos(pe.fieldInfos, layer, itemID, basePath);
1797
+ }
1798
+ if (pe.hasOwnProperty("mediaInfos")) {
1799
+ _templatizeMediaInfos(pe.mediaInfos, fieldNames, basePath, layer, itemID);
1800
+ }
1801
+ });
1802
+ }
1803
+ /**
1804
+ * templatize field name when referenced in mediaInfos
1805
+ *
1806
+ * @param mediaInfos the popups mediaInfos to check
1807
+ * @param fieldNames array of the layers field names
1808
+ * @param basePath path used to de-templatize while deploying
1809
+ * @param layer json of layer being cloned
1810
+ * @param itemID id of the item that contains the current layer
1811
+ * @private
1812
+ */
1813
+ export function _templatizeMediaInfos(mediaInfos, fieldNames, basePath, layer, itemId) {
1814
+ // templatize various properties of mediaInfos
1815
+ const props = ["title", "caption"];
1816
+ props.forEach(p => _templatizeName(mediaInfos, p, fieldNames, basePath));
1817
+ mediaInfos.forEach((mi) => {
1818
+ /* istanbul ignore else */
1819
+ if (mi.hasOwnProperty("value")) {
1820
+ const v = mi.value;
1821
+ const vfields = v.fields || [];
1822
+ v.fields = vfields.map(f => _templatizeFieldName(f, layer, itemId, basePath));
1823
+ if (v.hasOwnProperty("normalizeField")) {
1824
+ _templatizeProperty(v, "normalizeField", basePath, "name");
1825
+ }
1826
+ /* istanbul ignore else */
1827
+ if (v.hasOwnProperty("tooltipField")) {
1828
+ v.tooltipField = _templatizeFieldName(v.tooltipField, layer, itemId, basePath);
1829
+ }
1830
+ }
1831
+ });
1832
+ }
1833
+ /**
1834
+ * templatize field names when referenced in definitionEditor
1835
+ *
1836
+ * @param layer the layer with the definition editor
1837
+ * @param basePath path used to de-templatize while deploying
1838
+ * @param fieldNames json of layer being cloned
1839
+ * @private
1840
+ */
1841
+ export function _templatizeDefinitionEditor(layer, basePath, fieldNames) {
1842
+ if (layer) {
1843
+ const defEditor = layer.definitionEditor || {};
1844
+ /* istanbul ignore else */
1845
+ if (defEditor) {
1846
+ const inputs = defEditor.inputs;
1847
+ if (inputs) {
1848
+ inputs.forEach(i => {
1849
+ /* istanbul ignore else */
1850
+ if (i.parameters) {
1851
+ i.parameters.forEach((p) => {
1852
+ _templatizeProperty(p, "fieldName", basePath, "name");
1853
+ });
1854
+ }
1855
+ });
1856
+ }
1857
+ if (defEditor.hasOwnProperty("parameterizedExpression")) {
1858
+ defEditor.parameterizedExpression = _templatizeSimpleName(defEditor.parameterizedExpression || "", basePath, fieldNames, "name");
1859
+ }
1860
+ }
1861
+ }
1862
+ }
1863
+ /**
1864
+ * templatize field names when referenced in definitionExpression
1865
+ *
1866
+ * @param layer the layer with the definition editor
1867
+ * @param basePath path used to de-templatize while deploying
1868
+ * @param fieldNames array of field names
1869
+ * @private
1870
+ */
1871
+ export function _templatizeDefinitionExpression(layer, basePath, fieldNames) {
1872
+ if (layer && layer.hasOwnProperty("definitionExpression")) {
1873
+ layer.definitionExpression = _templatizeSimpleName(layer.definitionExpression || "", basePath, fieldNames, "name");
1874
+ }
1875
+ }
1876
+ /**
1877
+ * Case sensitive test for field names that appear anywhere within a string
1878
+ *
1879
+ * @param expression the expression to test for field name references
1880
+ * @param basePath path used to de-templatize while deploying
1881
+ * @param fieldNames array of the layers field names
1882
+ * @private
1883
+ */
1884
+ export function _templatizeSimpleName(expression, basePath, fieldNames, suffix) {
1885
+ fieldNames.forEach(name => {
1886
+ // look for the name but not if its followed by }}
1887
+ const regEx = new RegExp("\\b" + name + "\\b(?![}]{2})", "gm");
1888
+ if (expression && regEx.test(expression)) {
1889
+ expression = expression.replace(regEx, _templatize(basePath, name, suffix));
1890
+ }
1891
+ });
1892
+ return expression;
1893
+ }
1894
+ /**
1895
+ * Templatize field references within a layers drawingInfo
1896
+ *
1897
+ * @param layer the data layer
1898
+ * @param basePath path used to de-templatize while deploying
1899
+ * @param fieldNames array of the layers field names
1900
+ * @private
1901
+ */
1902
+ export function _templatizeDrawingInfo(layer, basePath, fieldNames) {
1903
+ if (layer) {
1904
+ const drawingInfo = layer.drawingInfo;
1905
+ if (drawingInfo) {
1906
+ // templatize the renderer fields
1907
+ const renderer = drawingInfo.renderer || {};
1908
+ _templatizeRenderer(renderer, basePath, fieldNames);
1909
+ // templatize the labelingInfo
1910
+ const labelingInfo = drawingInfo.labelingInfo || [];
1911
+ _templatizeLabelingInfo(labelingInfo, basePath, fieldNames);
1912
+ }
1913
+ }
1914
+ }
1915
+ /**
1916
+ * Templatize field references within a layers drawingInfo
1917
+ *
1918
+ * @param renderer the layers renderer
1919
+ * @param basePath path used to de-templatize while deploying
1920
+ * @param fieldNames array of the layers field names
1921
+ * @private
1922
+ */
1923
+ export function _templatizeRenderer(renderer, basePath, fieldNames) {
1924
+ switch (renderer.type) {
1925
+ case "classBreaks":
1926
+ case "uniqueValue":
1927
+ case "predominance":
1928
+ case "simple":
1929
+ case "heatmap":
1930
+ _templatizeGenRenderer(renderer, basePath, fieldNames);
1931
+ break;
1932
+ case "temporal":
1933
+ _templatizeTemporalRenderer(renderer, basePath, fieldNames);
1934
+ break;
1935
+ default:
1936
+ break;
1937
+ }
1938
+ }
1939
+ /**
1940
+ * Templatize field references within a layers renderer
1941
+ *
1942
+ * @param renderer the renderer object to check for field references
1943
+ * @param basePath path used to de-templatize while deploying
1944
+ * @param fieldNames array of field names that will be used to search expressions
1945
+ * @private
1946
+ */
1947
+ export function _templatizeGenRenderer(renderer, basePath, fieldNames) {
1948
+ /* istanbul ignore else */
1949
+ if (renderer) {
1950
+ // update authoringInfo
1951
+ const authoringInfo = renderer.authoringInfo;
1952
+ if (authoringInfo) {
1953
+ _templatizeAuthoringInfo(authoringInfo, basePath, fieldNames);
1954
+ }
1955
+ const props = ["field", "normalizationField"];
1956
+ props.forEach(p => _templatizeProperty(renderer, p, basePath, "name"));
1957
+ const fieldNameProps = ["field1", "field2", "field3"];
1958
+ fieldNameProps.forEach(fnP => _templatizeProperty(renderer, fnP, basePath, "name"));
1959
+ // When an attribute name is specified, it's enclosed in square brackets
1960
+ const rExp = renderer.rotationExpression;
1961
+ if (rExp) {
1962
+ fieldNames.forEach(name => {
1963
+ const regEx = new RegExp("(\\[" + name + "\\])", "gm");
1964
+ if (regEx.test(rExp)) {
1965
+ renderer.rotationExpression = rExp.replace(regEx, "[" + _templatize(basePath, name, "name") + "]");
1966
+ }
1967
+ });
1968
+ }
1969
+ // update valueExpression
1970
+ if (renderer.valueExpression) {
1971
+ fieldNames.forEach(name => {
1972
+ renderer.valueExpression = _templatizeArcadeExpressions(renderer.valueExpression, name, basePath);
1973
+ });
1974
+ }
1975
+ // update visualVariables
1976
+ const visualVariables = renderer.visualVariables;
1977
+ if (visualVariables) {
1978
+ visualVariables.forEach(v => {
1979
+ props.forEach(p => _templatizeProperty(v, p, basePath, "name"));
1980
+ if (v.valueExpression) {
1981
+ fieldNames.forEach(name => {
1982
+ v.valueExpression = _templatizeArcadeExpressions(v.valueExpression, name, basePath);
1983
+ });
1984
+ }
1985
+ });
1986
+ }
1987
+ }
1988
+ }
1989
+ /**
1990
+ * Templatize field references within a layers renderer
1991
+ *
1992
+ * @param renderer the renderer object to check for field references
1993
+ * @param basePath path used to de-templatize while deploying
1994
+ * @param fieldNames array of field names that will be used to search expressions
1995
+ * @private
1996
+ */
1997
+ export function _templatizeTemporalRenderer(renderer, basePath, fieldNames) {
1998
+ const renderers = [
1999
+ renderer.latestObservationRenderer,
2000
+ renderer.observationRenderer,
2001
+ renderer.trackRenderer
2002
+ ];
2003
+ renderers.forEach(r => {
2004
+ _templatizeRenderer(r, basePath, fieldNames);
2005
+ });
2006
+ }
2007
+ /**
2008
+ * Templatize renderers authoringInfo
2009
+ *
2010
+ * @param authoringInfo object containing metadata about the authoring process
2011
+ * @param basePath path used to de-templatize while deploying
2012
+ * @param fieldNames the name of fields from the layer
2013
+ * @private
2014
+ */
2015
+ export function _templatizeAuthoringInfo(authoringInfo, basePath, fieldNames) {
2016
+ /* istanbul ignore else */
2017
+ if (authoringInfo) {
2018
+ const props = ["field", "normalizationField"];
2019
+ const field1 = authoringInfo.field1;
2020
+ props.forEach(p => _templatizeProperty(field1, p, basePath, "name"));
2021
+ const field2 = authoringInfo.field2;
2022
+ props.forEach(p => _templatizeProperty(field2, p, basePath, "name"));
2023
+ const fields = authoringInfo.fields;
2024
+ if (fields) {
2025
+ authoringInfo.fields = fields.map(f => _templatize(basePath, f, "name"));
2026
+ }
2027
+ const vProps = ["endTime", "field", "startTime"];
2028
+ const vVars = authoringInfo.visualVariables;
2029
+ if (vVars) {
2030
+ vProps.forEach(p => {
2031
+ // endTime and startTime may or may not be a field name
2032
+ if (fieldNames.indexOf(vVars[p]) > -1) {
2033
+ _templatizeProperty(vVars, p, basePath, "name");
2034
+ }
2035
+ });
2036
+ }
2037
+ }
2038
+ }
2039
+ /**
2040
+ * Templatize field references within an arcade expression
2041
+ *
2042
+ * @param text the text that contains the expression
2043
+ * @param fieldName name of the field to test for
2044
+ * @param basePath path used to de-templatize while deploying
2045
+ * @private
2046
+ */
2047
+ export function _templatizeArcadeExpressions(text, fieldName, basePath) {
2048
+ const t = _templatize(basePath, fieldName, "name");
2049
+ if (text) {
2050
+ // test for $feature. notation
2051
+ // captures VOTED_DEM_2012 from $feature.VOTED_DEM_2012
2052
+ let exp = "(?:\\$feature\\.)(" + fieldName + ")\\b";
2053
+ let regEx = new RegExp(exp, "gm");
2054
+ text = regEx.test(text) ? text.replace(regEx, "$feature." + t) : text;
2055
+ // test for $feature[] notation
2056
+ // captures VOTED_DEM_2012 from $feature["VOTED_DEM_2012"]
2057
+ // captures VOTED_DEM_2012 from $feature['VOTED_DEM_2012']
2058
+ // captures VOTED_DEM_2012 from $feature[VOTED_DEM_2012]
2059
+ exp = "(?:[$]feature)(\\[\\\"?\\'?)" + fieldName + "(\\\"?\\'?\\])";
2060
+ regEx = new RegExp(exp, "gm");
2061
+ let result = regEx.exec(text);
2062
+ if (result) {
2063
+ text = text.replace(regEx, "$feature" + result[1] + t + result[2]);
2064
+ }
2065
+ // test for $feature[] with join case
2066
+ // captures VOTED_DEM_2016 from $feature["COUNTY_ID.VOTED_DEM_2016"]
2067
+ exp =
2068
+ "(?:[$]feature)(\\[\\\"?\\'?)(\\w+)[.]" + fieldName + "(\\\"?\\'?\\])";
2069
+ regEx = new RegExp(exp, "gm");
2070
+ result = regEx.exec(text);
2071
+ if (result && result.length > 3) {
2072
+ // TODO result[2] is the table name...this needs to be templatized as well
2073
+ text = text.replace(regEx, "$feature" + result[1] + result[2] + "." + t + result[3]);
2074
+ }
2075
+ // test for "fieldName"
2076
+ // captures fieldName from "var names = ["fieldName", "fieldName2"]..."
2077
+ // captures fieldName from "var names = ['fieldName', 'fieldName2']..."
2078
+ exp = "(\\\"|\\')+" + fieldName + "(\\\"|\\')+";
2079
+ regEx = new RegExp(exp, "gm");
2080
+ result = regEx.exec(text);
2081
+ if (result) {
2082
+ text = text.replace(regEx, result[1] + t + result[2]);
2083
+ }
2084
+ }
2085
+ return text;
2086
+ }
2087
+ /**
2088
+ * templatize field names when referenced in the layers labelingInfo
2089
+ *
2090
+ * @param labelingInfo the object that contains the labelingInfo
2091
+ * @param basePath path used to de-templatize while deploying
2092
+ * @param fieldNames array of the layers field names
2093
+ * @private
2094
+ */
2095
+ export function _templatizeLabelingInfo(labelingInfo, basePath, fieldNames) {
2096
+ labelingInfo.forEach((li) => {
2097
+ /* istanbul ignore else */
2098
+ if (li.hasOwnProperty("fieldInfos")) {
2099
+ const fieldInfos = li.fieldInfos || [];
2100
+ fieldInfos.forEach(fi => _templatizeProperty(fi, "fieldName", basePath, "name"));
2101
+ }
2102
+ const labelExp = li.labelExpression || "";
2103
+ const labelExpInfo = li.labelExpressionInfo || {};
2104
+ fieldNames.forEach(n => {
2105
+ const t = _templatize(basePath, n, "name");
2106
+ // check for [fieldName] or ["fieldName"]
2107
+ const regExBracket = new RegExp('(\\[\\"*)+(' + n + ')(\\"*\\])+', "gm");
2108
+ let result = regExBracket.exec(labelExp);
2109
+ if (result) {
2110
+ li.labelExpression = labelExp.replace(regExBracket, result[1] + t + result[3]);
2111
+ }
2112
+ /* istanbul ignore else */
2113
+ if (labelExpInfo.value) {
2114
+ let v = labelExpInfo.value;
2115
+ // check for {fieldName}
2116
+ const regExCurly = new RegExp("(\\{" + n + "\\})", "gm");
2117
+ v = regExCurly.test(v) ? v.replace(regExCurly, "{" + t + "}") : v;
2118
+ // check for [fieldName] or ["fieldName"]
2119
+ result = regExBracket.exec(v);
2120
+ v = result ? v.replace(regExBracket, result[1] + t + result[3]) : v;
2121
+ li.labelExpressionInfo.value = v;
2122
+ }
2123
+ /* istanbul ignore else */
2124
+ if (labelExpInfo.expression) {
2125
+ li.labelExpressionInfo.expression = _templatizeArcadeExpressions(labelExpInfo.expression, n, basePath);
2126
+ }
2127
+ });
2128
+ });
2129
+ }
2130
+ /**
2131
+ * templatize the layers editing templates
2132
+ *
2133
+ * @param layer the data layer being cloned
2134
+ * @param basePath path used to de-templatize while deploying
2135
+ * @private
2136
+ */
2137
+ export function _templatizeTemplates(layer, basePath) {
2138
+ const templates = layer.templates || [];
2139
+ templates.forEach(t => {
2140
+ const attributes = getProp(t, "prototype.attributes");
2141
+ const _attributes = _templatizeKeys(attributes, basePath, "name");
2142
+ /* istanbul ignore else */
2143
+ if (_attributes) {
2144
+ t.prototype.attributes = _attributes;
2145
+ }
2146
+ });
2147
+ }
2148
+ /**
2149
+ * templatize the layer types and templates
2150
+ *
2151
+ * @param layer the data layer being cloned
2152
+ * @param basePath path used to de-templatize while deploying
2153
+ * @private
2154
+ */
2155
+ export function _templatizeTypeTemplates(layer, basePath) {
2156
+ const types = layer.types;
2157
+ if (types && Array.isArray(types) && types.length > 0) {
2158
+ types.forEach((type) => {
2159
+ const domains = _templatizeKeys(type.domains, basePath, "name");
2160
+ /* istanbul ignore else */
2161
+ if (domains) {
2162
+ type.domains = domains;
2163
+ }
2164
+ const templates = type.templates;
2165
+ /* istanbul ignore else */
2166
+ if (templates && templates.length > 0) {
2167
+ templates.forEach((t) => {
2168
+ const attributes = getProp(t, "prototype.attributes");
2169
+ const _attributes = _templatizeKeys(attributes, basePath, "name");
2170
+ /* istanbul ignore else */
2171
+ if (_attributes) {
2172
+ t.prototype.attributes = _attributes;
2173
+ }
2174
+ });
2175
+ }
2176
+ });
2177
+ }
2178
+ }
2179
+ /**
2180
+ * templatize object keys
2181
+ *
2182
+ * @param obj the object to templatize
2183
+ * @param basePath path used to de-templatize while deploying
2184
+ * @param suffix expected suffix for template variable
2185
+ * @private
2186
+ */
2187
+ export function _templatizeKeys(obj, basePath, suffix) {
2188
+ let _obj;
2189
+ /* istanbul ignore else */
2190
+ if (obj) {
2191
+ _obj = {};
2192
+ const objKeys = Object.keys(obj);
2193
+ /* istanbul ignore else */
2194
+ if (objKeys && objKeys.length > 0) {
2195
+ objKeys.forEach(k => {
2196
+ _obj[_templatize(basePath, k, suffix)] = obj[k];
2197
+ });
2198
+ }
2199
+ }
2200
+ return _obj;
2201
+ }
2202
+ /**
2203
+ * templatize fields referenced in the layers time info
2204
+ *
2205
+ * @param layer the data layer being cloned
2206
+ * @param basePath path used to de-templatize while deploying
2207
+ * @private
2208
+ */
2209
+ export function _templatizeTimeInfo(layer, basePath) {
2210
+ if (layer.timeInfo) {
2211
+ const timeInfo = layer.timeInfo;
2212
+ const timeProps = [
2213
+ "endTimeField",
2214
+ "startTimeField",
2215
+ "trackIdField"
2216
+ ];
2217
+ timeProps.forEach(t => {
2218
+ if (timeInfo[t] !== "") {
2219
+ _templatizeProperty(timeInfo, t, basePath, "name");
2220
+ }
2221
+ else {
2222
+ timeInfo[t] = null;
2223
+ }
2224
+ });
2225
+ }
2226
+ }
2227
+ /**
2228
+ * templatize the layers definition query
2229
+ *
2230
+ * @param layer the data layer being cloned
2231
+ * @param basePath path used to de-templatize while deploying
2232
+ * @param fieldNames array of the layers field names
2233
+ * @private
2234
+ */
2235
+ export function _templatizeDefinitionQuery(layer, basePath, fieldNames) {
2236
+ // templatize view definition query
2237
+ if (layer && layer.hasOwnProperty("viewDefinitionQuery")) {
2238
+ layer.viewDefinitionQuery = _templatizeSimpleName(layer.viewDefinitionQuery || "", basePath, fieldNames, "name");
2239
+ }
2240
+ if (layer && layer.hasOwnProperty("definitionQuery")) {
2241
+ layer.definitionQuery = _templatizeSimpleName(layer.definitionQuery || "", basePath, fieldNames, "name");
2242
+ }
2243
+ }
2244
+ /**
2245
+ * Helper function to create the name mapping used to
2246
+ * de-templatize the field reference
2247
+ *
2248
+ * @param fieldInfos the object that stores the cached information
2249
+ * @param id the id for the current layer being processed
2250
+ * @private
2251
+ */
2252
+ export function _getNameMapping(fieldInfos, id) {
2253
+ // create name mapping
2254
+ const fInfo = fieldInfos[id];
2255
+ const nameMapping = {};
2256
+ const newFields = fInfo.newFields;
2257
+ const newFieldNames = newFields
2258
+ ? newFields.map((f) => f.name)
2259
+ : [];
2260
+ const sourceFields = fInfo.sourceFields || [];
2261
+ sourceFields.forEach((field) => {
2262
+ const lName = String(field.name).toLowerCase();
2263
+ newFields.forEach((f) => {
2264
+ // Names can change more than case
2265
+ if (newFieldNames.indexOf(field.name) === -1 &&
2266
+ newFieldNames.indexOf(lName) === -1) {
2267
+ // If both new (f) and source (field) aliases are defined and are equal, map the source name to the new name
2268
+ if (f.alias && f.alias === field.alias) {
2269
+ nameMapping[lName] = {
2270
+ name: f.name,
2271
+ alias: f.alias,
2272
+ type: f.type ? f.type : ""
2273
+ };
2274
+ }
2275
+ }
2276
+ if (String(f.name).toLowerCase() === lName) {
2277
+ nameMapping[lName] = {
2278
+ name: f.name,
2279
+ alias: f.alias ? f.alias : "",
2280
+ type: f.type ? f.type : ""
2281
+ };
2282
+ }
2283
+ });
2284
+ });
2285
+ // update for editFieldsInfo
2286
+ if (fInfo.editFieldsInfo && fInfo.newEditFieldsInfo) {
2287
+ const efi = JSON.parse(JSON.stringify(fInfo.editFieldsInfo));
2288
+ const newEfi = JSON.parse(JSON.stringify(fInfo.newEditFieldsInfo));
2289
+ const nameMappingKeys = Object.keys(nameMapping);
2290
+ Object.keys(efi).forEach(k => {
2291
+ const lowerEfi = String(efi[k]).toLowerCase();
2292
+ if ((nameMappingKeys.indexOf(lowerEfi) === -1 ||
2293
+ nameMapping[lowerEfi].name !== newEfi[k]) &&
2294
+ newFieldNames.indexOf(lowerEfi) > -1) {
2295
+ // Only add delete fields if source schema changes allowed
2296
+ /* istanbul ignore else */
2297
+ if (fInfo.sourceSchemaChangesAllowed && !fInfo.isView) {
2298
+ /* istanbul ignore else */
2299
+ if (!fInfo.hasOwnProperty("deleteFields")) {
2300
+ fInfo.deleteFields = [];
2301
+ }
2302
+ // This issue only occurs on portal so we
2303
+ // need to delete the lcase version of the field
2304
+ fInfo.deleteFields.push(lowerEfi);
2305
+ }
2306
+ // editFieldsInfo only has the name and not the alias and type
2307
+ let sourceEfiField;
2308
+ fInfo.sourceFields.some((sf) => {
2309
+ if (sf.name === efi[k]) {
2310
+ sourceEfiField = sf;
2311
+ }
2312
+ return sf.name === efi[k];
2313
+ });
2314
+ nameMapping[lowerEfi] = {
2315
+ name: newEfi[k],
2316
+ alias: sourceEfiField && sourceEfiField.alias ? sourceEfiField.alias : "",
2317
+ type: sourceEfiField && sourceEfiField.type ? sourceEfiField.type : ""
2318
+ };
2319
+ }
2320
+ });
2321
+ deleteProp(fInfo, "sourceSchemaChangesAllowed");
2322
+ deleteProp(fInfo, "editFieldsInfo");
2323
+ deleteProp(fInfo, "newEditFieldsInfo");
2324
+ deleteProp(fInfo, "isView");
2325
+ }
2326
+ return nameMapping;
2327
+ }
2328
+ /**
2329
+ * Helper function to ensure same chunk size value is used in multiple locations
2330
+ *
2331
+ * @returns a number that represents how many layers should be included per addToDef call
2332
+ * @private
2333
+ */
2334
+ export function _getLayerChunkSize() {
2335
+ return 20;
2336
+ }
2337
2337
  //# sourceMappingURL=featureServiceHelpers.js.map