kuzzle-device-manager 2.11.0 → 3.0.0-3-dev.2

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 (193) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +1 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/modules/asset/AssetModule.d.ts +1 -4
  5. package/dist/lib/modules/asset/AssetModule.js +0 -13
  6. package/dist/lib/modules/asset/AssetModule.js.map +1 -1
  7. package/dist/lib/modules/asset/AssetService.d.ts +7 -4
  8. package/dist/lib/modules/asset/AssetService.js +119 -50
  9. package/dist/lib/modules/asset/AssetService.js.map +1 -1
  10. package/dist/lib/modules/asset/AssetsController.d.ts +25 -7
  11. package/dist/lib/modules/asset/AssetsController.js +123 -36
  12. package/dist/lib/modules/asset/AssetsController.js.map +1 -1
  13. package/dist/lib/modules/asset/collections/assetsMappings.js +10 -15
  14. package/dist/lib/modules/asset/collections/assetsMappings.js.map +1 -1
  15. package/dist/lib/modules/asset/exports.d.ts +0 -4
  16. package/dist/lib/modules/asset/exports.js +0 -4
  17. package/dist/lib/modules/asset/exports.js.map +1 -1
  18. package/dist/lib/modules/asset/index.d.ts +1 -3
  19. package/dist/lib/modules/asset/index.js +1 -3
  20. package/dist/lib/modules/asset/index.js.map +1 -1
  21. package/dist/lib/modules/asset/model/AssetSerializer.js +0 -1
  22. package/dist/lib/modules/asset/model/AssetSerializer.js.map +1 -1
  23. package/dist/lib/modules/asset/roles/RoleAssetsAdmin.js +1 -1
  24. package/dist/lib/modules/asset/roles/RoleAssetsAdmin.js.map +1 -1
  25. package/dist/lib/modules/asset/types/AssetApi.d.ts +124 -9
  26. package/dist/lib/modules/asset/types/AssetContent.d.ts +9 -16
  27. package/dist/lib/modules/asset/types/AssetHistoryContent.d.ts +4 -11
  28. package/dist/lib/modules/decoder/DecodedPayload.d.ts +2 -0
  29. package/dist/lib/modules/decoder/DecodedPayload.js +13 -2
  30. package/dist/lib/modules/decoder/DecodedPayload.js.map +1 -1
  31. package/dist/lib/modules/decoder/Decoder.d.ts +1 -1
  32. package/dist/lib/modules/decoder/Decoder.js.map +1 -1
  33. package/dist/lib/modules/decoder/DecoderModule.js.map +1 -1
  34. package/dist/lib/modules/decoder/DecodersController.d.ts +4 -1
  35. package/dist/lib/modules/decoder/DecodersController.js +3 -0
  36. package/dist/lib/modules/decoder/DecodersController.js.map +1 -1
  37. package/dist/lib/modules/decoder/DecodersRegister.js +4 -1
  38. package/dist/lib/modules/decoder/DecodersRegister.js.map +1 -1
  39. package/dist/lib/modules/decoder/PayloadService.d.ts +1 -0
  40. package/dist/lib/modules/decoder/PayloadService.js +136 -26
  41. package/dist/lib/modules/decoder/PayloadService.js.map +1 -1
  42. package/dist/lib/modules/decoder/PayloadsController.js +1 -1
  43. package/dist/lib/modules/decoder/PayloadsController.js.map +1 -1
  44. package/dist/lib/modules/device/DeviceService.d.ts +6 -47
  45. package/dist/lib/modules/device/DeviceService.js +157 -291
  46. package/dist/lib/modules/device/DeviceService.js.map +1 -1
  47. package/dist/lib/modules/device/DevicesController.d.ts +5 -5
  48. package/dist/lib/modules/device/DevicesController.js +35 -23
  49. package/dist/lib/modules/device/DevicesController.js.map +1 -1
  50. package/dist/lib/modules/device/collections/deviceMappings.d.ts +6 -0
  51. package/dist/lib/modules/device/collections/deviceMappings.js +76 -6
  52. package/dist/lib/modules/device/collections/deviceMappings.js.map +1 -1
  53. package/dist/lib/modules/device/roles/RoleDevicesAdmin.d.ts +2 -2
  54. package/dist/lib/modules/device/roles/RoleDevicesAdmin.js +7 -4
  55. package/dist/lib/modules/device/roles/RoleDevicesAdmin.js.map +1 -1
  56. package/dist/lib/modules/device/roles/specificRoles/RoleDevicesAssetAssociation.js +4 -1
  57. package/dist/lib/modules/device/roles/specificRoles/RoleDevicesAssetAssociation.js.map +1 -1
  58. package/dist/lib/modules/device/types/DeviceApi.d.ts +69 -41
  59. package/dist/lib/modules/device/types/DeviceContent.d.ts +56 -5
  60. package/dist/lib/modules/device/types/DeviceEvents.d.ts +9 -24
  61. package/dist/lib/modules/group/GroupsController.d.ts +37 -0
  62. package/dist/lib/modules/group/GroupsController.js +472 -0
  63. package/dist/lib/modules/group/GroupsController.js.map +1 -0
  64. package/dist/lib/modules/group/GroupsModule.d.ts +10 -0
  65. package/dist/lib/modules/group/GroupsModule.js +25 -0
  66. package/dist/lib/modules/group/GroupsModule.js.map +1 -0
  67. package/dist/lib/modules/group/GroupsService.d.ts +118 -0
  68. package/dist/lib/modules/group/GroupsService.js +518 -0
  69. package/dist/lib/modules/group/GroupsService.js.map +1 -0
  70. package/dist/lib/modules/group/collections/groupsMappings.d.ts +2 -0
  71. package/dist/lib/modules/{asset/collections/assetsGroupsMapping.js → group/collections/groupsMappings.js} +4 -12
  72. package/dist/lib/modules/group/collections/groupsMappings.js.map +1 -0
  73. package/dist/lib/modules/group/exports.d.ts +5 -0
  74. package/dist/lib/modules/group/exports.js +22 -0
  75. package/dist/lib/modules/group/exports.js.map +1 -0
  76. package/dist/lib/modules/group/index.d.ts +4 -0
  77. package/dist/lib/modules/{measure/measures → group}/index.js +4 -5
  78. package/dist/lib/modules/group/index.js.map +1 -0
  79. package/dist/lib/modules/{asset/roles/RoleAssetsGroupsAdmin.d.ts → group/roles/RoleGroupsAdmin.d.ts} +3 -3
  80. package/dist/lib/modules/{asset/roles/RoleAssetsGroupsAdmin.js → group/roles/RoleGroupsAdmin.js} +7 -7
  81. package/dist/lib/modules/group/roles/RoleGroupsAdmin.js.map +1 -0
  82. package/dist/lib/modules/{asset/roles/RoleAssetsGroupsReader.d.ts → group/roles/RoleGroupsReader.d.ts} +3 -3
  83. package/dist/lib/modules/{asset/roles/RoleAssetsGroupsReader.js → group/roles/RoleGroupsReader.js} +7 -7
  84. package/dist/lib/modules/group/roles/RoleGroupsReader.js.map +1 -0
  85. package/dist/lib/modules/group/types/GroupContent.d.ts +10 -0
  86. package/dist/lib/modules/{asset/types/AssetGroupsApi.js → group/types/GroupContent.js} +1 -1
  87. package/dist/lib/modules/group/types/GroupContent.js.map +1 -0
  88. package/dist/lib/modules/group/types/GroupsApi.d.ts +141 -0
  89. package/dist/lib/modules/{asset/types/AssetGroupContent.js → group/types/GroupsApi.js} +1 -1
  90. package/dist/lib/modules/group/types/GroupsApi.js.map +1 -0
  91. package/dist/lib/modules/measure/MeasureExporter.js +14 -6
  92. package/dist/lib/modules/measure/MeasureExporter.js.map +1 -1
  93. package/dist/lib/modules/measure/MeasureService.d.ts +11 -43
  94. package/dist/lib/modules/measure/MeasureService.js +86 -375
  95. package/dist/lib/modules/measure/MeasureService.js.map +1 -1
  96. package/dist/lib/modules/measure/MeasureSourcesBuilder.d.ts +1 -2
  97. package/dist/lib/modules/measure/MeasureSourcesBuilder.js +5 -15
  98. package/dist/lib/modules/measure/MeasureSourcesBuilder.js.map +1 -1
  99. package/dist/lib/modules/measure/MeasureTargetBuilder.d.ts +1 -1
  100. package/dist/lib/modules/measure/MeasureTargetBuilder.js +2 -2
  101. package/dist/lib/modules/measure/MeasureTargetBuilder.js.map +1 -1
  102. package/dist/lib/modules/measure/collections/measuresMappings.d.ts +2 -91
  103. package/dist/lib/modules/measure/collections/measuresMappings.js +7 -11
  104. package/dist/lib/modules/measure/collections/measuresMappings.js.map +1 -1
  105. package/dist/lib/modules/measure/exports.d.ts +0 -1
  106. package/dist/lib/modules/measure/exports.js +0 -1
  107. package/dist/lib/modules/measure/exports.js.map +1 -1
  108. package/dist/lib/modules/measure/index.d.ts +0 -1
  109. package/dist/lib/modules/measure/index.js +0 -1
  110. package/dist/lib/modules/measure/index.js.map +1 -1
  111. package/dist/lib/modules/measure/types/MeasureContent.d.ts +9 -2
  112. package/dist/lib/modules/measure/types/MeasureEvents.d.ts +6 -149
  113. package/dist/lib/modules/measure/types/MeasureSources.d.ts +5 -0
  114. package/dist/lib/modules/measure/types/MeasureSources.js.map +1 -1
  115. package/dist/lib/modules/measure/types/MeasureTarget.d.ts +1 -1
  116. package/dist/lib/modules/measure/types/MeasureTarget.js +1 -1
  117. package/dist/lib/modules/measure/types/MeasureTarget.js.map +1 -1
  118. package/dist/lib/modules/model/ModelModule.js.map +1 -1
  119. package/dist/lib/modules/model/ModelSerializer.js +29 -0
  120. package/dist/lib/modules/model/ModelSerializer.js.map +1 -1
  121. package/dist/lib/modules/model/ModelService.d.ts +20 -14
  122. package/dist/lib/modules/model/ModelService.js +354 -117
  123. package/dist/lib/modules/model/ModelService.js.map +1 -1
  124. package/dist/lib/modules/model/ModelsController.d.ts +1 -1
  125. package/dist/lib/modules/model/ModelsController.js +33 -19
  126. package/dist/lib/modules/model/ModelsController.js.map +1 -1
  127. package/dist/lib/modules/model/ModelsRegister.d.ts +6 -5
  128. package/dist/lib/modules/model/ModelsRegister.js +10 -8
  129. package/dist/lib/modules/model/ModelsRegister.js.map +1 -1
  130. package/dist/lib/modules/model/collections/modelsMappings.js +20 -1
  131. package/dist/lib/modules/model/collections/modelsMappings.js.map +1 -1
  132. package/dist/lib/modules/model/types/ModelApi.d.ts +15 -9
  133. package/dist/lib/modules/model/types/ModelContent.d.ts +35 -3
  134. package/dist/lib/modules/model/types/ModelContent.js.map +1 -1
  135. package/dist/lib/modules/model/types/ModelDefinition.d.ts +6 -2
  136. package/dist/lib/modules/model/types/ModelEvents.d.ts +3 -1
  137. package/dist/lib/modules/plugin/DeviceManagerEngine.d.ts +15 -6
  138. package/dist/lib/modules/plugin/DeviceManagerEngine.js +151 -54
  139. package/dist/lib/modules/plugin/DeviceManagerEngine.js.map +1 -1
  140. package/dist/lib/modules/plugin/DeviceManagerPlugin.d.ts +10 -8
  141. package/dist/lib/modules/plugin/DeviceManagerPlugin.js +41 -44
  142. package/dist/lib/modules/plugin/DeviceManagerPlugin.js.map +1 -1
  143. package/dist/lib/modules/plugin/types/DeviceManagerConfiguration.d.ts +9 -14
  144. package/dist/lib/modules/plugin/types/InternalCollection.d.ts +1 -1
  145. package/dist/lib/modules/plugin/types/InternalCollection.js +1 -1
  146. package/dist/lib/modules/plugin/types/InternalCollection.js.map +1 -1
  147. package/dist/lib/modules/plugin/types/exports.d.ts +1 -0
  148. package/dist/lib/modules/plugin/types/exports.js +1 -0
  149. package/dist/lib/modules/plugin/types/exports.js.map +1 -1
  150. package/dist/lib/modules/shared/services/AbstractExporter.d.ts +3 -1
  151. package/dist/lib/modules/shared/services/AbstractExporter.js +7 -3
  152. package/dist/lib/modules/shared/services/AbstractExporter.js.map +1 -1
  153. package/dist/lib/modules/shared/services/BaseService.js.map +1 -1
  154. package/dist/lib/modules/shared/services/DigitalTwinExporter.d.ts +6 -1
  155. package/dist/lib/modules/shared/services/DigitalTwinExporter.js +49 -7
  156. package/dist/lib/modules/shared/services/DigitalTwinExporter.js.map +1 -1
  157. package/dist/lib/modules/shared/services/DigitalTwinService.d.ts +70 -2
  158. package/dist/lib/modules/shared/services/DigitalTwinService.js +309 -1
  159. package/dist/lib/modules/shared/services/DigitalTwinService.js.map +1 -1
  160. package/dist/lib/modules/shared/types/DigitalTwinContent.d.ts +4 -5
  161. package/dist/lib/modules/shared/types/DigitalTwinEvents.d.ts +30 -0
  162. package/package.json +15 -9
  163. package/dist/lib/modules/asset/AssetsGroupsController.d.ts +0 -24
  164. package/dist/lib/modules/asset/AssetsGroupsController.js +0 -239
  165. package/dist/lib/modules/asset/AssetsGroupsController.js.map +0 -1
  166. package/dist/lib/modules/asset/AssetsGroupsService.d.ts +0 -49
  167. package/dist/lib/modules/asset/AssetsGroupsService.js +0 -172
  168. package/dist/lib/modules/asset/AssetsGroupsService.js.map +0 -1
  169. package/dist/lib/modules/asset/collections/assetsGroupsMapping.d.ts +0 -2
  170. package/dist/lib/modules/asset/collections/assetsGroupsMapping.js.map +0 -1
  171. package/dist/lib/modules/asset/roles/RoleAssetsGroupsAdmin.js.map +0 -1
  172. package/dist/lib/modules/asset/roles/RoleAssetsGroupsReader.js.map +0 -1
  173. package/dist/lib/modules/asset/types/AssetGroupContent.d.ts +0 -11
  174. package/dist/lib/modules/asset/types/AssetGroupContent.js.map +0 -1
  175. package/dist/lib/modules/asset/types/AssetGroupsApi.d.ts +0 -59
  176. package/dist/lib/modules/asset/types/AssetGroupsApi.js.map +0 -1
  177. package/dist/lib/modules/measure/measures/BatteryMeasure.d.ts +0 -5
  178. package/dist/lib/modules/measure/measures/BatteryMeasure.js +0 -19
  179. package/dist/lib/modules/measure/measures/BatteryMeasure.js.map +0 -1
  180. package/dist/lib/modules/measure/measures/HumidityMeasure.d.ts +0 -5
  181. package/dist/lib/modules/measure/measures/HumidityMeasure.js +0 -19
  182. package/dist/lib/modules/measure/measures/HumidityMeasure.js.map +0 -1
  183. package/dist/lib/modules/measure/measures/MovementMeasure.d.ts +0 -5
  184. package/dist/lib/modules/measure/measures/MovementMeasure.js +0 -17
  185. package/dist/lib/modules/measure/measures/MovementMeasure.js.map +0 -1
  186. package/dist/lib/modules/measure/measures/PositionMeasure.d.ts +0 -10
  187. package/dist/lib/modules/measure/measures/PositionMeasure.js +0 -43
  188. package/dist/lib/modules/measure/measures/PositionMeasure.js.map +0 -1
  189. package/dist/lib/modules/measure/measures/TemperatureMeasure.d.ts +0 -5
  190. package/dist/lib/modules/measure/measures/TemperatureMeasure.js +0 -19
  191. package/dist/lib/modules/measure/measures/TemperatureMeasure.js.map +0 -1
  192. package/dist/lib/modules/measure/measures/index.d.ts +0 -5
  193. package/dist/lib/modules/measure/measures/index.js.map +0 -1
@@ -18,8 +18,8 @@ class ModelService extends shared_1.BaseService {
18
18
  this.registerAskEvents();
19
19
  }
20
20
  registerAskEvents() {
21
- (0, kuzzle_plugin_commons_1.onAsk)("ask:device-manager:model:asset:get", async ({ engineGroup, model }) => {
22
- const assetModel = await this.getAsset(engineGroup, model);
21
+ (0, kuzzle_plugin_commons_1.onAsk)("ask:device-manager:model:asset:get", async ({ engineGroups, engineId, model }) => {
22
+ const assetModel = await this.getAsset(engineGroups, engineId, model);
23
23
  return assetModel._source;
24
24
  });
25
25
  (0, kuzzle_plugin_commons_1.onAsk)("ask:device-manager:model:device:get", async ({ model }) => {
@@ -30,13 +30,13 @@ class ModelService extends shared_1.BaseService {
30
30
  const groupModel = await this.getGroup(model);
31
31
  return groupModel._source;
32
32
  });
33
- (0, kuzzle_plugin_commons_1.onAsk)("ask:device-manager:model:measure:get", async ({ type }) => {
34
- const measureModel = await this.getMeasure(type);
33
+ (0, kuzzle_plugin_commons_1.onAsk)("ask:device-manager:model:measure:get", async ({ type, engineId }) => {
34
+ const measureModel = await this.getMeasure(type, engineId);
35
35
  return measureModel._source;
36
36
  });
37
37
  const genericModelsHandler = async (documents, request) => {
38
38
  const { index, collection } = request.input.args;
39
- if (index === this.config.adminIndex && collection === "models") {
39
+ if (index === this.config.platformIndex && collection === "models") {
40
40
  const models = documents.map((elt) => {
41
41
  return elt._source;
42
42
  });
@@ -48,7 +48,7 @@ class ModelService extends shared_1.BaseService {
48
48
  this.app.pipe.register("generic:document:beforeUpdate", genericModelsHandler);
49
49
  this.app.hook.register("generic:document:afterUpdate", async (documents, request) => {
50
50
  const { index, collection } = request.input.args;
51
- if (index === this.config.adminIndex && collection === "models") {
51
+ if (index === this.config.platformIndex && collection === "models") {
52
52
  await (0, kuzzle_plugin_commons_1.ask)("ask:device-manager:engine:updateAll");
53
53
  }
54
54
  });
@@ -105,7 +105,57 @@ class ModelService extends shared_1.BaseService {
105
105
  }
106
106
  }
107
107
  }
108
- async writeAsset(engineGroup, model, metadataMappings, defaultMetadata, metadataDetails, metadataGroups, measures, tooltipModels, locales) {
108
+ checkDefaultValues(metadataMappings, defaultMetadata) {
109
+ const flattenedMetadataMappings = (0, shared_1.flattenObject)(metadataMappings);
110
+ const metadata = Object.keys(JSON.parse(JSON.stringify(flattenedMetadataMappings)
111
+ .replace(/properties\./g, "")
112
+ .replace(/\.type/g, "")));
113
+ const values = Object.keys((0, shared_1.flattenObject)(defaultMetadata));
114
+ for (let i = 0; i < values.length; i++) {
115
+ const key = values[i];
116
+ // ? Check if the exact key exists in the metadata
117
+ if (!metadata.includes(key)) {
118
+ // ? Extract base key for complex types like geo_point or geo_shape
119
+ const baseKey = key.includes(".") ? key.split(".")[0] : key;
120
+ // ? Check if the base key is in the metadata
121
+ if (!metadata.includes(baseKey)) {
122
+ throw new kuzzle_1.BadRequestError(`The default value "${key}" is not in the metadata mappings.`);
123
+ }
124
+ // ? Accept nested properties for geo_point or geo_shape
125
+ const baseKeyMetadata = flattenedMetadataMappings[`${baseKey}.type`];
126
+ if (baseKeyMetadata === "geo_point" ||
127
+ baseKeyMetadata === "geo_shape") {
128
+ continue;
129
+ }
130
+ throw new kuzzle_1.BadRequestError(`The default value "${values[i]}" is not in the metadata mappings.`);
131
+ }
132
+ }
133
+ }
134
+ checkGroupAffinity(affinity) {
135
+ const { type, strict, models } = affinity;
136
+ if (!type || strict === undefined || !models) {
137
+ throw new kuzzle_1.BadRequestError(`The group affinity must specify a type array, models and strictness`);
138
+ }
139
+ if (!Array.isArray(type) ||
140
+ type.length > 2 ||
141
+ type.length < 1 ||
142
+ !type.every((t) => ["assets", "devices"].includes(t))) {
143
+ throw new kuzzle_1.BadRequestError(`The group type must be an array containing "assets" and/or "devices"`);
144
+ }
145
+ if (typeof strict !== "boolean") {
146
+ throw new kuzzle_1.BadRequestError(`The group affinity strict field must be a boolean`);
147
+ }
148
+ for (const t of type) {
149
+ if (!models[t] || !Array.isArray(models[t])) {
150
+ throw new kuzzle_1.BadRequestError(`The group affinity must contain an array for every present type`);
151
+ }
152
+ if (!models[t].every((model) => typeof model === "string")) {
153
+ throw new kuzzle_1.BadRequestError(`The group affinity models must be an array of models id`);
154
+ }
155
+ }
156
+ return { models, strict, type };
157
+ }
158
+ async writeAsset(engineGroups, model, metadataMappings, defaultMetadata, metadataDetails, metadataGroups, measures, tooltipModels, locales, engineIds) {
109
159
  if (kuzzle_1.Inflector.pascalCase(model) !== model) {
110
160
  throw new kuzzle_1.BadRequestError(`Asset model "${model}" must be PascalCase.`);
111
161
  }
@@ -113,6 +163,55 @@ class ModelService extends shared_1.BaseService {
113
163
  if (duplicates.length > 0) {
114
164
  throw new MeasuresNamesDuplicatesError_1.MeasuresNamesDuplicatesError("Asset model measures contain one or multiple duplicate measure name", duplicates);
115
165
  }
166
+ const normalizedEngineGroups = engineGroups.includes("commons")
167
+ ? ["commons"]
168
+ : engineGroups;
169
+ // Anti-shadowing: reject if a model with the same name exists at a different scope level
170
+ const isGlobal = normalizedEngineGroups.includes("commons") &&
171
+ (!engineIds || engineIds.length === 0);
172
+ const isTenantScoped = engineIds && engineIds.length > 0;
173
+ const isGroupScoped = !isGlobal && !isTenantScoped;
174
+ await this.sdk.collection.refresh(this.config.platformIndex, plugin_1.InternalCollection.MODELS);
175
+ const existing = await this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, {
176
+ query: {
177
+ bool: {
178
+ must: [
179
+ { term: { type: "asset" } },
180
+ { term: { "asset.model": model } },
181
+ ],
182
+ },
183
+ },
184
+ }, { size: 10 });
185
+ for (const hit of existing.hits) {
186
+ const doc = hit._source;
187
+ const docIsGlobal = doc.engineGroups?.includes("commons") &&
188
+ (!doc.engineIds || doc.engineIds.length === 0);
189
+ const docIsTenantScoped = doc.engineIds && doc.engineIds.length > 0;
190
+ const docIsGroupScoped = !docIsGlobal && !docIsTenantScoped;
191
+ // Same scope level: allow overwrite
192
+ if (isGlobal && docIsGlobal) {
193
+ continue;
194
+ }
195
+ if (isTenantScoped && docIsTenantScoped) {
196
+ continue;
197
+ }
198
+ if (isGroupScoped && docIsGroupScoped) {
199
+ continue;
200
+ }
201
+ // Different scope levels: reject
202
+ if (docIsGlobal) {
203
+ throw new kuzzle_1.BadRequestError(`Asset model "${model}" already exists as a global (commons) model. A model cannot exist at multiple scopes.`);
204
+ }
205
+ if (isGlobal) {
206
+ throw new kuzzle_1.BadRequestError(`Asset model "${model}" already exists at a non-global scope. A model cannot exist at multiple scopes.`);
207
+ }
208
+ if (isTenantScoped && docIsGroupScoped) {
209
+ throw new kuzzle_1.BadRequestError(`Asset model "${model}" already exists at group scope. A model cannot be both group-scoped and tenant-scoped.`);
210
+ }
211
+ if (isGroupScoped && docIsTenantScoped) {
212
+ throw new kuzzle_1.BadRequestError(`Asset model "${model}" already exists at tenant scope. A model cannot be both group-scoped and tenant-scoped.`);
213
+ }
214
+ }
116
215
  const modelContent = {
117
216
  asset: {
118
217
  defaultMetadata,
@@ -124,7 +223,8 @@ class ModelService extends shared_1.BaseService {
124
223
  model,
125
224
  tooltipModels,
126
225
  },
127
- engineGroup,
226
+ engineGroups: normalizedEngineGroups,
227
+ ...(engineIds?.length ? { engineIds } : {}),
128
228
  type: "asset",
129
229
  };
130
230
  this.checkDefaultValues(metadataMappings, defaultMetadata);
@@ -132,40 +232,14 @@ class ModelService extends shared_1.BaseService {
132
232
  if (conflicts.length > 0) {
133
233
  throw new MappingsConflictsError_1.MappingsConflictsError(`New assets mappings are causing conflicts`, conflicts);
134
234
  }
135
- const assetModel = await this.sdk.document.createOrReplace(this.config.adminIndex, plugin_1.InternalCollection.MODELS, ModelSerializer_1.ModelSerializer.id("asset", modelContent), modelContent);
136
- await this.sdk.collection.refresh(this.config.adminIndex, plugin_1.InternalCollection.MODELS);
235
+ const assetModel = await this.sdk.document.createOrReplace(this.config.platformIndex, plugin_1.InternalCollection.MODELS, ModelSerializer_1.ModelSerializer.id("asset", modelContent), modelContent);
236
+ await this.sdk.collection.refresh(this.config.platformIndex, plugin_1.InternalCollection.MODELS);
137
237
  await (0, kuzzle_plugin_commons_1.ask)("ask:device-manager:engine:updateAll");
138
238
  await (0, kuzzle_plugin_commons_1.ask)("ask:device-manager:asset:refresh-model", {
139
239
  assetModel: assetModel._source,
140
240
  });
141
241
  return assetModel;
142
242
  }
143
- checkDefaultValues(metadataMappings, defaultMetadata) {
144
- const flattenedMetadataMappings = (0, shared_1.flattenObject)(metadataMappings);
145
- const metadata = Object.keys(JSON.parse(JSON.stringify(flattenedMetadataMappings)
146
- .replace(/properties\./g, "")
147
- .replace(/\.type/g, "")));
148
- const values = Object.keys((0, shared_1.flattenObject)(defaultMetadata));
149
- for (let i = 0; i < values.length; i++) {
150
- const key = values[i];
151
- // ? Check if the exact key exists in the metadata
152
- if (!metadata.includes(key)) {
153
- // ? Extract base key for complex types like geo_point or geo_shape
154
- const baseKey = key.includes(".") ? key.split(".")[0] : key;
155
- // ? Check if the base key is in the metadata
156
- if (!metadata.includes(baseKey)) {
157
- throw new kuzzle_1.BadRequestError(`The default value "${key}" is not in the metadata mappings.`);
158
- }
159
- // ? Accept nested properties for geo_point or geo_shape
160
- const baseKeyMetadata = flattenedMetadataMappings[`${baseKey}.type`];
161
- if (baseKeyMetadata === "geo_point" ||
162
- baseKeyMetadata === "geo_shape") {
163
- continue;
164
- }
165
- throw new kuzzle_1.BadRequestError(`The default value "${values[i]}" is not in the metadata mappings.`);
166
- }
167
- }
168
- }
169
243
  async writeDevice(model, metadataMappings, defaultMetadata, metadataDetails, metadataGroups, measures) {
170
244
  if (kuzzle_1.Inflector.pascalCase(model) !== model) {
171
245
  throw new kuzzle_1.BadRequestError(`Device model "${model}" must be PascalCase.`);
@@ -189,18 +263,26 @@ class ModelService extends shared_1.BaseService {
189
263
  if (conflicts.length > 0) {
190
264
  throw new MappingsConflictsError_1.MappingsConflictsError(`New devices mappings are causing conflicts`, conflicts);
191
265
  }
192
- const deviceModel = await this.sdk.document.createOrReplace(this.config.adminIndex, plugin_1.InternalCollection.MODELS, ModelSerializer_1.ModelSerializer.id("device", modelContent), modelContent);
193
- await this.sdk.collection.refresh(this.config.adminIndex, plugin_1.InternalCollection.MODELS);
266
+ const deviceModel = await this.sdk.document.createOrReplace(this.config.platformIndex, plugin_1.InternalCollection.MODELS, ModelSerializer_1.ModelSerializer.id("device", modelContent), modelContent);
267
+ await this.sdk.collection.refresh(this.config.platformIndex, plugin_1.InternalCollection.MODELS);
194
268
  await (0, kuzzle_plugin_commons_1.ask)("ask:device-manager:engine:updateAll");
269
+ await (0, kuzzle_plugin_commons_1.ask)("ask:device-manager:device:refresh-model", {
270
+ deviceModel: modelContent,
271
+ });
195
272
  return deviceModel;
196
273
  }
197
- async writeGroup(engineGroup, model, metadataMappings, defaultMetadata, metadataDetails, metadataGroups) {
274
+ async writeGroup(engineGroups, model, affinity, metadataMappings, defaultMetadata, metadataDetails, metadataGroups) {
198
275
  if (kuzzle_1.Inflector.pascalCase(model) !== model) {
199
276
  throw new kuzzle_1.BadRequestError(`Group model "${model}" must be PascalCase.`);
200
277
  }
278
+ if (engineGroups.includes("commons")) {
279
+ engineGroups = ["commons"];
280
+ }
281
+ const groupAffinity = this.checkGroupAffinity(affinity);
201
282
  const modelContent = {
202
- engineGroup,
283
+ engineGroups,
203
284
  group: {
285
+ affinity: groupAffinity,
204
286
  defaultMetadata,
205
287
  metadataDetails,
206
288
  metadataGroups,
@@ -213,12 +295,12 @@ class ModelService extends shared_1.BaseService {
213
295
  if (conflicts.length > 0) {
214
296
  throw new MappingsConflictsError_1.MappingsConflictsError(`New group mappings are causing conflicts`, conflicts);
215
297
  }
216
- const groupModel = await this.sdk.document.createOrReplace(this.config.adminIndex, plugin_1.InternalCollection.MODELS, ModelSerializer_1.ModelSerializer.id("group", modelContent), modelContent);
217
- await this.sdk.collection.refresh(this.config.adminIndex, plugin_1.InternalCollection.MODELS);
298
+ const groupModel = await this.sdk.document.createOrReplace(this.config.platformIndex, plugin_1.InternalCollection.MODELS, ModelSerializer_1.ModelSerializer.id("group", modelContent), modelContent);
299
+ await this.sdk.collection.refresh(this.config.platformIndex, plugin_1.InternalCollection.MODELS);
218
300
  await (0, kuzzle_plugin_commons_1.ask)("ask:device-manager:engine:updateAll");
219
301
  return groupModel;
220
302
  }
221
- async writeMeasure(type, valuesMappings, validationSchema, valuesDetails, locales) {
303
+ async writeMeasure(type, valuesMappings, validationSchema, valuesDetails, locales, engineIds) {
222
304
  const modelContent = {
223
305
  measure: {
224
306
  locales,
@@ -228,6 +310,11 @@ class ModelService extends shared_1.BaseService {
228
310
  },
229
311
  type: "measure",
230
312
  };
313
+ if (engineIds?.length) {
314
+ modelContent.engineIds = engineIds;
315
+ }
316
+ // Anti-shadowing: a measure type must be either global or tenant-scoped, not both
317
+ await this.checkMeasureShadowing(type, engineIds);
231
318
  if (validationSchema) {
232
319
  try {
233
320
  (0, AJValidator_1.addSchemaToCache)(type, validationSchema);
@@ -241,25 +328,25 @@ class ModelService extends shared_1.BaseService {
241
328
  if (conflicts.length > 0) {
242
329
  throw new MappingsConflictsError_1.MappingsConflictsError(`New assets mappings are causing conflicts`, conflicts);
243
330
  }
244
- const measureModel = await this.sdk.document.createOrReplace(this.config.adminIndex, plugin_1.InternalCollection.MODELS, ModelSerializer_1.ModelSerializer.id("measure", modelContent), modelContent);
245
- await this.sdk.collection.refresh(this.config.adminIndex, plugin_1.InternalCollection.MODELS);
331
+ const measureModel = await this.sdk.document.createOrReplace(this.config.platformIndex, plugin_1.InternalCollection.MODELS, ModelSerializer_1.ModelSerializer.id("measure", modelContent), modelContent);
332
+ await this.sdk.collection.refresh(this.config.platformIndex, plugin_1.InternalCollection.MODELS);
246
333
  await (0, kuzzle_plugin_commons_1.ask)("ask:device-manager:engine:updateAll");
247
334
  return measureModel;
248
335
  }
249
336
  async deleteAsset(_id) {
250
- await this.sdk.document.delete(this.config.adminIndex, plugin_1.InternalCollection.MODELS, _id);
337
+ await this.sdk.document.delete(this.config.platformIndex, plugin_1.InternalCollection.MODELS, _id);
251
338
  }
252
339
  async deleteDevice(_id) {
253
- await this.sdk.document.delete(this.config.adminIndex, plugin_1.InternalCollection.MODELS, _id);
340
+ await this.sdk.document.delete(this.config.platformIndex, plugin_1.InternalCollection.MODELS, _id);
254
341
  }
255
342
  async deleteGroup(_id) {
256
- await this.sdk.document.delete(this.config.adminIndex, plugin_1.InternalCollection.MODELS, _id);
343
+ await this.sdk.document.delete(this.config.platformIndex, plugin_1.InternalCollection.MODELS, _id);
257
344
  }
258
345
  async deleteMeasure(_id) {
259
- await this.sdk.document.delete(this.config.adminIndex, plugin_1.InternalCollection.MODELS, _id);
346
+ await this.sdk.document.delete(this.config.platformIndex, plugin_1.InternalCollection.MODELS, _id);
260
347
  }
261
- async listAsset(engineGroup) {
262
- const result = await this.searchAssets(engineGroup, {
348
+ async listAsset(engineGroups, engineId) {
349
+ const result = await this.searchAssets(engineGroups, engineId, {
263
350
  searchBody: {
264
351
  sort: { "asset.model": "asc" },
265
352
  },
@@ -276,8 +363,8 @@ class ModelService extends shared_1.BaseService {
276
363
  });
277
364
  return result.hits;
278
365
  }
279
- async listGroups(engineGroup) {
280
- const result = await this.searchGroups(engineGroup, {
366
+ async listGroups(engineGroups) {
367
+ const result = await this.searchGroups(engineGroups, {
281
368
  searchBody: {
282
369
  sort: { "group.model": "asc" },
283
370
  },
@@ -285,8 +372,8 @@ class ModelService extends shared_1.BaseService {
285
372
  });
286
373
  return result.hits;
287
374
  }
288
- async listMeasures() {
289
- const result = await this.searchMeasures({
375
+ async listMeasures(engineId) {
376
+ const result = await this.searchMeasures(engineId, {
290
377
  searchBody: {
291
378
  sort: { "measure.type": "asc" },
292
379
  },
@@ -294,24 +381,62 @@ class ModelService extends shared_1.BaseService {
294
381
  });
295
382
  return result.hits;
296
383
  }
297
- async searchAssets(engineGroup, searchParams) {
384
+ async searchAssets(engineGroups, engineId, searchParams) {
385
+ const scopeFilter = engineId
386
+ ? {
387
+ bool: {
388
+ should: [
389
+ {
390
+ bool: {
391
+ must: [
392
+ { term: { engineIds: engineId } },
393
+ { terms: { engineGroups } },
394
+ ],
395
+ },
396
+ },
397
+ {
398
+ bool: {
399
+ must: [{ terms: { engineGroups } }],
400
+ must_not: [{ exists: { field: "engineIds" } }],
401
+ },
402
+ },
403
+ {
404
+ bool: {
405
+ must: [{ term: { engineGroups: "commons" } }],
406
+ must_not: [{ exists: { field: "engineIds" } }],
407
+ },
408
+ },
409
+ ],
410
+ },
411
+ }
412
+ : {
413
+ bool: {
414
+ should: [
415
+ {
416
+ bool: {
417
+ must: [{ terms: { engineGroups } }],
418
+ must_not: [{ exists: { field: "engineIds" } }],
419
+ },
420
+ },
421
+ {
422
+ bool: {
423
+ must: [{ term: { engineGroups: "commons" } }],
424
+ must_not: [{ exists: { field: "engineIds" } }],
425
+ },
426
+ },
427
+ ],
428
+ },
429
+ };
298
430
  const query = {
299
431
  bool: {
300
432
  must: [
301
433
  searchParams.searchBody.query,
302
- { match: { type: "asset" } },
303
- {
304
- bool: {
305
- should: [
306
- { match: { engineGroup } },
307
- { match: { engineGroup: "commons" } },
308
- ],
309
- },
310
- },
311
- ],
434
+ { term: { type: "asset" } },
435
+ scopeFilter,
436
+ ].filter(Boolean),
312
437
  },
313
438
  };
314
- return this.sdk.document.search(this.config.adminIndex, plugin_1.InternalCollection.MODELS, {
439
+ return this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, {
315
440
  ...searchParams.searchBody,
316
441
  query,
317
442
  }, {
@@ -324,10 +449,13 @@ class ModelService extends shared_1.BaseService {
324
449
  async searchDevices(searchParams) {
325
450
  const query = {
326
451
  bool: {
327
- must: [searchParams.searchBody.query, { match: { type: "device" } }],
452
+ must: [
453
+ searchParams.searchBody.query,
454
+ { term: { type: "device" } },
455
+ ].filter(Boolean),
328
456
  },
329
457
  };
330
- return this.sdk.document.search(this.config.adminIndex, plugin_1.InternalCollection.MODELS, {
458
+ return this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, {
331
459
  ...searchParams.searchBody,
332
460
  query,
333
461
  }, {
@@ -337,24 +465,24 @@ class ModelService extends shared_1.BaseService {
337
465
  size: searchParams.size,
338
466
  });
339
467
  }
340
- async searchGroups(engineGroup, searchParams) {
468
+ async searchGroups(engineGroups, searchParams) {
341
469
  const query = {
342
470
  bool: {
343
471
  must: [
344
472
  searchParams.searchBody.query,
345
- { match: { type: "group" } },
473
+ { term: { type: "group" } },
346
474
  {
347
475
  bool: {
348
476
  should: [
349
- { match: { engineGroup } },
350
- { match: { engineGroup: "commons" } },
477
+ { terms: { engineGroups } },
478
+ { term: { engineGroups: "commons" } },
351
479
  ],
352
480
  },
353
481
  },
354
- ],
482
+ ].filter(Boolean),
355
483
  },
356
484
  };
357
- return this.sdk.document.search(this.config.adminIndex, plugin_1.InternalCollection.MODELS, {
485
+ return this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, {
358
486
  ...searchParams.searchBody,
359
487
  query,
360
488
  }, {
@@ -364,13 +492,31 @@ class ModelService extends shared_1.BaseService {
364
492
  size: searchParams.size,
365
493
  });
366
494
  }
367
- async searchMeasures(searchParams) {
495
+ async searchMeasures(engineId, searchParams) {
496
+ const scopeFilter = engineId
497
+ ? {
498
+ bool: {
499
+ should: [
500
+ { term: { engineIds: engineId } },
501
+ { bool: { must_not: [{ exists: { field: "engineIds" } }] } },
502
+ ],
503
+ },
504
+ }
505
+ : {
506
+ bool: {
507
+ must_not: [{ exists: { field: "engineIds" } }],
508
+ },
509
+ };
368
510
  const query = {
369
511
  bool: {
370
- must: [searchParams.searchBody.query, { match: { type: "measure" } }],
512
+ must: [
513
+ searchParams.searchBody.query,
514
+ { term: { type: "measure" } },
515
+ scopeFilter,
516
+ ].filter(Boolean),
371
517
  },
372
518
  };
373
- return this.sdk.document.search(this.config.adminIndex, plugin_1.InternalCollection.MODELS, {
519
+ return this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, {
374
520
  ...searchParams.searchBody,
375
521
  query,
376
522
  }, {
@@ -387,7 +533,7 @@ class ModelService extends shared_1.BaseService {
387
533
  { equals: { "asset.model": model } },
388
534
  ],
389
535
  };
390
- const result = await this.sdk.document.search(this.config.adminIndex, plugin_1.InternalCollection.MODELS, { query }, { lang: "koncorde", size: 1 });
536
+ const result = await this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, { query }, { lang: "koncorde", size: 1 });
391
537
  return result.total > 0;
392
538
  }
393
539
  async deviceExists(model) {
@@ -397,27 +543,53 @@ class ModelService extends shared_1.BaseService {
397
543
  { equals: { "device.model": model } },
398
544
  ],
399
545
  };
400
- const result = await this.sdk.document.search(this.config.adminIndex, plugin_1.InternalCollection.MODELS, { query }, { lang: "koncorde", size: 1 });
546
+ const result = await this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, { query }, { lang: "koncorde", size: 1 });
401
547
  return result.total > 0;
402
548
  }
403
- async getAsset(engineGroup, model) {
404
- const query = {
405
- and: [
406
- {
407
- or: [
408
- { equals: { engineGroup } },
409
- { equals: { engineGroup: "commons" } },
549
+ async getAsset(engineGroups, engineId, model) {
550
+ const baseFilter = [
551
+ { term: { type: "asset" } },
552
+ { term: { "asset.model": model } },
553
+ ];
554
+ // Priority 1: tenant-scoped model for this specific engine
555
+ if (engineId) {
556
+ const tenantQuery = {
557
+ bool: {
558
+ must: [
559
+ ...baseFilter,
560
+ { terms: { engineGroups } },
561
+ { term: { engineIds: engineId } },
410
562
  ],
411
563
  },
412
- { equals: { type: "asset" } },
413
- { equals: { "asset.model": model } },
414
- ],
564
+ };
565
+ const tenantResult = await this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, { query: tenantQuery }, { lang: "elasticsearch", size: 1 });
566
+ if (tenantResult.total > 0) {
567
+ return tenantResult.hits[0];
568
+ }
569
+ }
570
+ // Priority 2: group-scoped model (no engineIds field)
571
+ const groupQuery = {
572
+ bool: {
573
+ must: [...baseFilter, { terms: { engineGroups } }],
574
+ must_not: [{ exists: { field: "engineIds" } }],
575
+ },
415
576
  };
416
- const result = await this.sdk.document.search(this.config.adminIndex, plugin_1.InternalCollection.MODELS, { query }, { lang: "koncorde", size: 1 });
417
- if (result.total === 0) {
418
- throw new kuzzle_1.NotFoundError(`Unknown Asset model "${model}" for engineGroup ${engineGroup}.`);
577
+ const groupResult = await this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, { query: groupQuery }, { lang: "elasticsearch", size: 1 });
578
+ if (groupResult.total > 0) {
579
+ return groupResult.hits[0];
419
580
  }
420
- return result.hits[0];
581
+ // Priority 3: commons model
582
+ const commonsQuery = {
583
+ bool: {
584
+ must: [...baseFilter, { term: { engineGroups: "commons" } }],
585
+ must_not: [{ exists: { field: "engineIds" } }],
586
+ },
587
+ };
588
+ const commonsResult = await this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, { query: commonsQuery }, { lang: "elasticsearch", size: 1 });
589
+ if (commonsResult.total > 0) {
590
+ return commonsResult.hits[0];
591
+ }
592
+ throw new kuzzle_1.NotFoundError(`Unknown Asset model "${model}" for engineGroups ${engineGroups.join(", ")}.`);
421
593
  }
422
594
  async getDevice(model) {
423
595
  const query = {
@@ -426,7 +598,7 @@ class ModelService extends shared_1.BaseService {
426
598
  { equals: { "device.model": model } },
427
599
  ],
428
600
  };
429
- const result = await this.sdk.document.search(this.config.adminIndex, plugin_1.InternalCollection.MODELS, { query }, { lang: "koncorde", size: 1 });
601
+ const result = await this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, { query }, { lang: "koncorde", size: 1 });
430
602
  if (result.total === 0) {
431
603
  throw new kuzzle_1.NotFoundError(`Unknown Device model "${model}".`);
432
604
  }
@@ -439,7 +611,7 @@ class ModelService extends shared_1.BaseService {
439
611
  { equals: { "group.model": model } },
440
612
  ],
441
613
  };
442
- const result = await this.sdk.document.search(this.config.adminIndex, plugin_1.InternalCollection.MODELS, { query }, { lang: "koncorde", size: 1 });
614
+ const result = await this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, { query }, { lang: "koncorde", size: 1 });
443
615
  if (result.total === 0) {
444
616
  throw new kuzzle_1.NotFoundError(`Unknown Group model "${model}".`);
445
617
  }
@@ -448,31 +620,92 @@ class ModelService extends shared_1.BaseService {
448
620
  }
449
621
  return result.hits[0];
450
622
  }
451
- async getMeasure(type) {
452
- const query = {
453
- and: [
454
- { equals: { type: "measure" } },
455
- { equals: { "measure.type": type } },
456
- ],
457
- };
458
- const result = await this.sdk.document.search(this.config.adminIndex, plugin_1.InternalCollection.MODELS, { query }, { lang: "koncorde", size: 1 });
459
- if (result.total === 0) {
460
- throw new kuzzle_1.NotFoundError(`Unknown Measure type "${type}".`);
623
+ async getMeasure(type, engineId) {
624
+ const baseFilter = [
625
+ { term: { type: "measure" } },
626
+ { term: { "measure.type": type } },
627
+ ];
628
+ const scopeFilter = engineId
629
+ ? {
630
+ bool: {
631
+ should: [
632
+ { term: { engineIds: engineId } },
633
+ { bool: { must_not: [{ exists: { field: "engineIds" } }] } },
634
+ ],
635
+ },
636
+ }
637
+ : {
638
+ bool: {
639
+ must_not: [{ exists: { field: "engineIds" } }],
640
+ },
641
+ };
642
+ const result = await this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, {
643
+ query: {
644
+ bool: {
645
+ must: [...baseFilter, scopeFilter],
646
+ },
647
+ },
648
+ }, { lang: "elasticsearch", size: 1 });
649
+ if (result.total > 0) {
650
+ return result.hits[0];
651
+ }
652
+ throw new kuzzle_1.NotFoundError(`Unknown Measure type "${type}".`);
653
+ }
654
+ /**
655
+ * Checks that creating/updating a measure model does not shadow an existing
656
+ * one at a different scope (global vs tenant-scoped).
657
+ */
658
+ async checkMeasureShadowing(type, engineIds) {
659
+ const isNewTenantScoped = engineIds && engineIds.length > 0;
660
+ // Check for existing measures of the same type at the opposite scope
661
+ const conflictQuery = isNewTenantScoped
662
+ ? {
663
+ // New is tenant-scoped → reject if a global measure exists
664
+ bool: {
665
+ must: [
666
+ { term: { type: "measure" } },
667
+ { term: { "measure.type": type } },
668
+ ],
669
+ must_not: [{ exists: { field: "engineIds" } }],
670
+ },
671
+ }
672
+ : {
673
+ // New is global → reject if any tenant-scoped measure exists
674
+ bool: {
675
+ must: [
676
+ { term: { type: "measure" } },
677
+ { term: { "measure.type": type } },
678
+ { exists: { field: "engineIds" } },
679
+ ],
680
+ },
681
+ };
682
+ const result = await this.sdk.document.search(this.config.platformIndex, plugin_1.InternalCollection.MODELS, { query: conflictQuery }, { lang: "elasticsearch", size: 1 });
683
+ if (result.total > 0) {
684
+ const scope = isNewTenantScoped ? "global" : "tenant-scoped";
685
+ throw new kuzzle_1.BadRequestError(`Measure type "${type}" already exists as a ${scope} measure. A measure type cannot be both global and tenant-scoped.`);
461
686
  }
462
- return result.hits[0];
463
687
  }
464
688
  /**
465
689
  * Update an asset model
466
690
  */
467
- async updateAsset(engineGroup, model, metadataMappings, defaultMetadata, metadataDetails, metadataGroups, measures, tooltipModels, locales, request) {
691
+ async updateAsset(engineGroups, engineId, model, metadataMappings, defaultMetadata, metadataDetails, metadataGroups, measures, tooltipModels, locales, request) {
468
692
  if (kuzzle_1.Inflector.pascalCase(model) !== model) {
469
693
  throw new kuzzle_1.BadRequestError(`Asset model "${model}" must be PascalCase.`);
470
694
  }
471
695
  this.checkDefaultValues(metadataMappings, defaultMetadata);
472
- const existingAsset = await this.getAsset(engineGroup, model);
696
+ const existingAsset = await this.getAsset(engineGroups, engineId, model);
473
697
  // The field must be deleted if an element of the table is to be deleted
474
- await this.sdk.document.deleteFields(this.config.adminIndex, plugin_1.InternalCollection.MODELS, existingAsset._id, ["asset.tooltipModels"], { source: true });
698
+ await this.sdk.document.deleteFields(this.config.platformIndex, plugin_1.InternalCollection.MODELS, existingAsset._id, [
699
+ "asset.tooltipModels",
700
+ "asset.metadataMappings",
701
+ "asset.defaultMetadata",
702
+ "asset.metadataDetails",
703
+ "asset.metadataGroups",
704
+ ], { source: true });
475
705
  const measuresUpdated = measures.length === 0 ? existingAsset._source.asset.measures : measures;
706
+ const normalizedEngineGroups = engineGroups.includes("commons")
707
+ ? ["commons"]
708
+ : engineGroups;
476
709
  const assetModelContent = {
477
710
  asset: {
478
711
  defaultMetadata,
@@ -484,7 +717,8 @@ class ModelService extends shared_1.BaseService {
484
717
  model,
485
718
  tooltipModels,
486
719
  },
487
- engineGroup,
720
+ engineGroups: normalizedEngineGroups,
721
+ engineIds: existingAsset._source.engineIds,
488
722
  type: "asset",
489
723
  };
490
724
  const assetModel = {
@@ -497,13 +731,16 @@ class ModelService extends shared_1.BaseService {
497
731
  }
498
732
  const endDocument = await this.updateDocument(request, assetModel, {
499
733
  collection: plugin_1.InternalCollection.MODELS,
500
- engineId: this.config.adminIndex,
734
+ engineId: this.config.platformIndex,
501
735
  }, { source: true });
502
- await this.sdk.collection.refresh(this.config.adminIndex, plugin_1.InternalCollection.MODELS);
503
- await (0, kuzzle_plugin_commons_1.ask)("ask:device-manager:engine:updateAll");
504
- await (0, kuzzle_plugin_commons_1.ask)("ask:device-manager:asset:refresh-model", {
505
- assetModel: endDocument._source,
506
- });
736
+ // ? Only update engineIds and refresh asset models when necessary
737
+ if (Object.keys(metadataMappings).length > 0 || measures.length > 0) {
738
+ await this.sdk.collection.refresh(this.config.platformIndex, plugin_1.InternalCollection.MODELS);
739
+ await (0, kuzzle_plugin_commons_1.ask)("ask:device-manager:engine:updateAll");
740
+ await (0, kuzzle_plugin_commons_1.ask)("ask:device-manager:asset:refresh-model", {
741
+ assetModel: endDocument._source,
742
+ });
743
+ }
507
744
  return endDocument;
508
745
  }
509
746
  }