dt-common-device 13.0.8 → 13.0.10

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.
@@ -9,7 +9,14 @@ export declare class AdminRepository {
9
9
  getZonesByAccessGroups(accessGroupIds: string[], type?: string[]): Promise<any[]>;
10
10
  getAccessGroup(accessGroupId: string, propertyId?: string): Promise<IAccessGroup | null>;
11
11
  getZoneAccessGroupByZoneId(zoneId: string): Promise<IZoneAccessGroup[] | null>;
12
+ getZoneIdsByAccessGroupIds(accessGroupIds: string[]): Promise<string[]>;
13
+ getAllParentZones(zoneId: string): Promise<string[]>;
14
+ getDirectChildZones(zoneId: string, propertyId?: string): Promise<string[]>;
15
+ getAllChildZones(zoneId: string, propertyId?: string): Promise<string[]>;
16
+ getAccessGroupsByZoneId(zoneId: string): Promise<IAccessGroup[]>;
17
+ getAccessGroupsByZoneIds(zoneIds: string[]): Promise<IAccessGroup[]>;
12
18
  getZone(zoneId: string, propertyId?: string): Promise<IZone | null>;
19
+ getZonesByIds(zoneIds: string[]): Promise<IZone[]>;
13
20
  getUser(userId: string): Promise<IUser | null>;
14
21
  getZoneByDeviceId(deviceId: string): Promise<IZone | null>;
15
22
  getAccessGroups(propertyId: string, accessibleBy?: string): Promise<IAccessGroup[]>;
@@ -278,6 +278,128 @@ let AdminRepository = (() => {
278
278
  }
279
279
  return null;
280
280
  }
281
+ async getZoneIdsByAccessGroupIds(accessGroupIds) {
282
+ if (accessGroupIds.length === 0) {
283
+ return [];
284
+ }
285
+ const query = `
286
+ SELECT DISTINCT "zoneId"
287
+ FROM dt_zones_collection_map
288
+ WHERE "collectionId" = ANY($1)
289
+ `;
290
+ const result = await this.postgres.query(query, [accessGroupIds]);
291
+ return result.rows.map((row) => row.zoneId);
292
+ }
293
+ async getAllParentZones(zoneId) {
294
+ const allParentZoneIds = [];
295
+ let currentZoneId = zoneId;
296
+ while (currentZoneId) {
297
+ const zone = await this.getZone(currentZoneId);
298
+ if (!zone || !zone.parentId) {
299
+ break;
300
+ }
301
+ allParentZoneIds.push(zone.parentId);
302
+ currentZoneId = zone.parentId;
303
+ }
304
+ return allParentZoneIds;
305
+ }
306
+ async getDirectChildZones(zoneId, propertyId) {
307
+ const redisKey = propertyId
308
+ ? `${propertyId}:childZones:${zoneId}`
309
+ : `childZones:${zoneId}`;
310
+ // Try to get from cache first
311
+ const zonesCache = await this.redisUtils.get(redisKey);
312
+ let zones = null;
313
+ if (zonesCache !== null && zonesCache !== undefined) {
314
+ (0, config_1.getLogger)().info(`Got child zones from redis for zone ${zoneId}`);
315
+ zones = JSON.parse(zonesCache);
316
+ }
317
+ else {
318
+ const response = await (0, utils_1.getAdminServiceAxiosInstance)().get(`/zones/child?zoneId=${zoneId}`);
319
+ zones = response?.data?.data;
320
+ if (zones) {
321
+ await this.redisUtils.set(redisKey, JSON.stringify(zones), 86400);
322
+ }
323
+ }
324
+ // Get only direct children (first level), not all descendants
325
+ if (zones && zones.childZones) {
326
+ if (Array.isArray(zones.childZones)) {
327
+ return zones.childZones
328
+ .map((z) => z.id)
329
+ .filter((id) => id);
330
+ }
331
+ else if (zones.childZones.id) {
332
+ return [zones.childZones.id];
333
+ }
334
+ }
335
+ return [];
336
+ }
337
+ async getAllChildZones(zoneId, propertyId) {
338
+ const redisKey = propertyId
339
+ ? `${propertyId}:childZones:${zoneId}`
340
+ : `childZones:${zoneId}`;
341
+ // Try to get from cache first
342
+ const zonesCache = await this.redisUtils.get(redisKey);
343
+ let zones = null;
344
+ if (zonesCache !== null && zonesCache !== undefined) {
345
+ (0, config_1.getLogger)().info(`Got child zones from redis for zone ${zoneId}`);
346
+ zones = JSON.parse(zonesCache);
347
+ }
348
+ else {
349
+ const response = await (0, utils_1.getAdminServiceAxiosInstance)().get(`/zones/child?zoneId=${zoneId}`);
350
+ zones = response?.data?.data;
351
+ if (zones) {
352
+ await this.redisUtils.set(redisKey, JSON.stringify(zones), 86400);
353
+ }
354
+ }
355
+ // Recursively collect all child zone IDs (using the same pattern as getZonesByAccessGroupIds)
356
+ const _zones = (nestedZones, allZones = []) => {
357
+ nestedZones.forEach((z) => {
358
+ const onlyZone = { ...z };
359
+ delete onlyZone.childZones;
360
+ allZones.push(onlyZone);
361
+ if (!Array.isArray(z.childZones)) {
362
+ if (z.childZones && z.childZones.id)
363
+ z.childZones = [z.childZones];
364
+ }
365
+ if (z.childZones && z.childZones.length) {
366
+ _zones(z.childZones, allZones);
367
+ }
368
+ });
369
+ return allZones.map((e) => e.id);
370
+ };
371
+ if (zones && zones.childZones && zones.childZones.length > 0) {
372
+ const childZoneIds = _zones(zones.childZones, []);
373
+ return childZoneIds;
374
+ }
375
+ return [];
376
+ }
377
+ async getAccessGroupsByZoneId(zoneId) {
378
+ const query = `
379
+ SELECT DISTINCT c.*
380
+ FROM dt_collections c
381
+ INNER JOIN dt_zones_collection_map zcm ON c.id = zcm."collectionId"
382
+ WHERE zcm."zoneId" = $1
383
+ AND c."isDeleted" = false
384
+ `;
385
+ const result = await this.postgres.query(query, [zoneId]);
386
+ return result.rows;
387
+ }
388
+ async getAccessGroupsByZoneIds(zoneIds) {
389
+ if (zoneIds.length === 0) {
390
+ return [];
391
+ }
392
+ // Get all access groups associated with any of these zones
393
+ const query = `
394
+ SELECT DISTINCT c.*
395
+ FROM dt_collections c
396
+ INNER JOIN dt_zones_collection_map zcm ON c.id = zcm."collectionId"
397
+ WHERE zcm."zoneId" = ANY($1)
398
+ AND c."isDeleted" = false
399
+ `;
400
+ const result = await this.postgres.query(query, [zoneIds]);
401
+ return result.rows;
402
+ }
281
403
  async getZone(zoneId, propertyId) {
282
404
  try {
283
405
  let query;
@@ -306,6 +428,40 @@ let AdminRepository = (() => {
306
428
  throw new Error("Failed to get zone");
307
429
  }
308
430
  }
431
+ async getZonesByIds(zoneIds) {
432
+ if (zoneIds.length === 0) {
433
+ return [];
434
+ }
435
+ try {
436
+ // Batch query to get all zones at once
437
+ const query = `SELECT * FROM dt_zones WHERE "id" = ANY($1)`;
438
+ const result = await this.postgres.query(query, [zoneIds]);
439
+ // Get all unique zone type IDs
440
+ const zoneTypeIds = [
441
+ ...new Set(result.rows.map((row) => row.zoneTypeId)),
442
+ ];
443
+ // Batch query for zone types
444
+ let zoneTypesMap = new Map();
445
+ if (zoneTypeIds.length > 0) {
446
+ const zoneTypesQuery = `SELECT * FROM "dt_zoneTypes" WHERE "id" = ANY($1)`;
447
+ const zoneTypesResult = await this.postgres.query(zoneTypesQuery, [
448
+ zoneTypeIds,
449
+ ]);
450
+ zoneTypesResult.rows.forEach((zt) => {
451
+ zoneTypesMap.set(zt.id, { id: zt.id, name: zt.name });
452
+ });
453
+ }
454
+ // Map zone types to zones
455
+ return result.rows.map((zoneData) => {
456
+ zoneData.zoneType = zoneTypesMap.get(zoneData.zoneTypeId) || {};
457
+ return zoneData;
458
+ });
459
+ }
460
+ catch (error) {
461
+ console.error("Error in getZonesByIds:", error);
462
+ return [];
463
+ }
464
+ }
309
465
  async getUser(userId) {
310
466
  const user = await this.postgres.query(`SELECT * FROM dt_users WHERE "id" = $1`, [userId]);
311
467
  if (user.rows.length > 0) {
@@ -7,6 +7,8 @@ export declare class AdminService {
7
7
  getZonesByAccessGroups(accessGroupIds: string[], type?: string[]): Promise<any[]>;
8
8
  getAccessGroup(accessGroupId: string, propertyId?: string): Promise<IAccessGroup | null>;
9
9
  getAccessGroupByZoneId(zoneId: string): Promise<IAccessGroup[] | []>;
10
+ getAccessgroupBySubParentZoneId(zoneId: string): Promise<IAccessGroup[]>;
11
+ getAllParentSubZonesByAccessGroupIds(accessGroupIds: string[]): Promise<string[]>;
10
12
  getZone(zoneId: string, propertyId?: string): Promise<IZone | null>;
11
13
  getUser(userId: string): Promise<IUser | null>;
12
14
  getZoneByDeviceId(deviceId: string): Promise<IZone | null>;
@@ -133,6 +133,130 @@ let AdminService = (() => {
133
133
  }
134
134
  return accessGroups;
135
135
  }
136
+ //--------------------------USED FOR QUERY RESERVATION-----------------------
137
+ async getAccessgroupBySubParentZoneId(zoneId) {
138
+ if (!zoneId) {
139
+ throw new Error("Zone ID is required");
140
+ }
141
+ try {
142
+ // Get the zone to retrieve propertyId if needed
143
+ const zone = await this.adminRepository.getZone(zoneId);
144
+ if (!zone) {
145
+ return [];
146
+ }
147
+ // Use Map to collect unique access groups (by ID)
148
+ const accessGroupsMap = new Map();
149
+ // Track visited zones to avoid cycles and duplicate checks
150
+ const visitedZoneIds = new Set();
151
+ // OPTIMIZATION: Set to true if you only need to check if ANY access group exists
152
+ // Set to false if you need ALL access groups from entire hierarchy
153
+ const stopOnFirstFound = true;
154
+ // Recursive function to traverse hierarchy and collect access groups
155
+ const traverseZoneHierarchy = async (currentZoneId, propertyId) => {
156
+ // Base case 1: if zone already visited, return (prevents cycles)
157
+ if (visitedZoneIds.has(currentZoneId)) {
158
+ return false;
159
+ }
160
+ // Base case 2: early exit if we found access group and only need existence check
161
+ if (stopOnFirstFound && accessGroupsMap.size > 0) {
162
+ return true;
163
+ }
164
+ // Mark as visited
165
+ visitedZoneIds.add(currentZoneId);
166
+ // Check access groups for current zone (query as we traverse)
167
+ const zoneAccessGroups = await this.adminRepository.getAccessGroupsByZoneId(currentZoneId);
168
+ // If we found access groups, add to map
169
+ if (zoneAccessGroups.length > 0) {
170
+ zoneAccessGroups.forEach((ag) => accessGroupsMap.set(ag.id, ag));
171
+ // Early exit optimization: if we only need to know if access group exists
172
+ if (stopOnFirstFound) {
173
+ return true; // Stop traversal - we found what we need
174
+ }
175
+ }
176
+ // Get current zone details
177
+ const currentZone = await this.adminRepository.getZone(currentZoneId);
178
+ if (!currentZone) {
179
+ return false;
180
+ }
181
+ // Recursive case 1: Traverse up to parent zone (if exists and not visited)
182
+ if (currentZone.parentId && !visitedZoneIds.has(currentZone.parentId)) {
183
+ const found = await traverseZoneHierarchy(currentZone.parentId, propertyId);
184
+ // Early exit: if we found access group in parent, stop checking children
185
+ if (found && stopOnFirstFound) {
186
+ return true;
187
+ }
188
+ }
189
+ // Recursive case 2: Traverse down to direct child zones only
190
+ // Only check children if we haven't found an access group yet (optimization)
191
+ if (!stopOnFirstFound || accessGroupsMap.size === 0) {
192
+ const directChildZoneIds = await this.adminRepository.getDirectChildZones(currentZoneId, propertyId);
193
+ // Recursively process each direct child zone
194
+ for (const childZoneId of directChildZoneIds) {
195
+ if (!visitedZoneIds.has(childZoneId)) {
196
+ const found = await traverseZoneHierarchy(childZoneId, propertyId);
197
+ // Early exit: if we found access group in any child, we can stop
198
+ if (found && stopOnFirstFound) {
199
+ return true;
200
+ }
201
+ }
202
+ }
203
+ }
204
+ return accessGroupsMap.size > 0;
205
+ };
206
+ // Start recursive traversal from the given zone
207
+ await traverseZoneHierarchy(zoneId, zone.propertyId);
208
+ return Array.from(accessGroupsMap.values());
209
+ }
210
+ catch (error) {
211
+ console.error("Error in getAccessgroupBySubParentZoneId:", error);
212
+ return [];
213
+ }
214
+ }
215
+ // used for query reservation to find all the zones mapped with the access groups
216
+ async getAllParentSubZonesByAccessGroupIds(accessGroupIds) {
217
+ if (!accessGroupIds || accessGroupIds.length === 0) {
218
+ throw new Error("Access Group IDs are required");
219
+ }
220
+ try {
221
+ // Step 1: Find all zones mapped with these access groups
222
+ const mappedZoneIds = await this.adminRepository.getZoneIdsByAccessGroupIds(accessGroupIds);
223
+ if (mappedZoneIds.length === 0) {
224
+ return [];
225
+ }
226
+ // OPTIMIZATION 1: Batch get all zones at once (reduces DB calls from O(n) to O(1))
227
+ const zones = await this.adminRepository.getZonesByIds(mappedZoneIds);
228
+ const zoneMap = new Map();
229
+ zones.forEach((zone) => zoneMap.set(zone.id, zone));
230
+ // Use Set to collect unique zone IDs from the start
231
+ const allZoneIds = new Set(mappedZoneIds);
232
+ // OPTIMIZATION 2: Process all zones in parallel (reduces time from O(n) sequential to O(1) parallel)
233
+ const zoneProcessingPromises = mappedZoneIds.map(async (zoneId) => {
234
+ const zone = zoneMap.get(zoneId);
235
+ if (!zone) {
236
+ return { parentIds: [], childIds: [] };
237
+ }
238
+ // OPTIMIZATION 3: Get parents and children in parallel for each zone
239
+ const [parentZoneIds, childZoneIds] = await Promise.all([
240
+ this.adminRepository.getAllParentZones(zoneId),
241
+ this.adminRepository.getAllChildZones(zoneId, zone.propertyId),
242
+ ]);
243
+ return { parentIds: parentZoneIds, childIds: childZoneIds };
244
+ });
245
+ // Wait for all zone processing to complete
246
+ const results = await Promise.all(zoneProcessingPromises);
247
+ // OPTIMIZATION 4: Batch add all IDs to Set (O(n) operation)
248
+ results.forEach(({ parentIds, childIds }) => {
249
+ parentIds.forEach((id) => allZoneIds.add(id));
250
+ childIds.forEach((id) => allZoneIds.add(id));
251
+ });
252
+ return Array.from(allZoneIds);
253
+ }
254
+ catch (error) {
255
+ console.error("Error in getAllParentSubZonesByAccessGroupIds:", error);
256
+ return [];
257
+ }
258
+ }
259
+ //----------------------------------------------------------------------------------
136
260
  async getZone(zoneId, propertyId) {
137
261
  if (!zoneId) {
138
262
  throw new Error("Zone ID is required");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dt-common-device",
3
- "version": "13.0.8",
3
+ "version": "13.0.10",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [