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");
|