rez_core 5.0.207 → 5.0.209

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "5.0.207",
3
+ "version": "5.0.209",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -4,8 +4,7 @@ import { ReflectionHelper } from '../../../utils/service/reflection-helper.servi
4
4
 
5
5
  @Injectable()
6
6
  export class SchoolRepository {
7
- constructor(private readonly reflectionHelper: ReflectionHelper) {
8
- }
7
+ constructor(private readonly reflectionHelper: ReflectionHelper) {}
9
8
 
10
9
  async findAllByOrgId(orgId: number): Promise<any[]> {
11
10
  const schoolRepo = this.reflectionHelper.getRepoService('SSOSchool');
@@ -26,11 +25,12 @@ export class SchoolRepository {
26
25
  }
27
26
 
28
27
  async getUserContextDropdown(userId: number, appCode: string, org_id) {
29
- const userRoleRepo = this.reflectionHelper.getRepoService('UserRoleMapping');
28
+ const userRoleRepo =
29
+ this.reflectionHelper.getRepoService('UserRoleMapping');
30
30
 
31
31
  const hasOrgAccess = await userRoleRepo
32
32
  .createQueryBuilder('urm')
33
- .where('urm.user_id = :userId', { userId })
33
+ .where('urm.user_id::text = :userId', { userId: String(userId) })
34
34
  .andWhere('urm.appcode = :appCode', { appCode })
35
35
  .andWhere('urm.level_type = :levelType', { levelType: 'ORG' })
36
36
  .getRawMany();
@@ -40,7 +40,8 @@ export class SchoolRepository {
40
40
  currentORGLevel_id = org_id;
41
41
  }
42
42
 
43
- const listMasterItemsRepo = this.reflectionHelper.getRepoService('ListMasterItems');
43
+ const listMasterItemsRepo =
44
+ this.reflectionHelper.getRepoService('ListMasterItems');
44
45
  const resolvedInactiveStatus = await listMasterItemsRepo.findOne({
45
46
  where: {
46
47
  code: STATUS_INACTIVE,
@@ -49,7 +50,8 @@ export class SchoolRepository {
49
50
  });
50
51
 
51
52
  if (hasOrgAccess.length > 0) {
52
- const organizationRepo = this.reflectionHelper.getRepoService('OrganizationData');
53
+ const organizationRepo =
54
+ this.reflectionHelper.getRepoService('OrganizationData');
53
55
 
54
56
  const schools = await organizationRepo
55
57
  .createQueryBuilder('org')
@@ -72,7 +74,7 @@ export class SchoolRepository {
72
74
  .innerJoin(
73
75
  'sso_organization',
74
76
  'brn',
75
- 'brn.type = \'BRN\' AND brn.parent_id::varchar = org.id::varchar',
77
+ "brn.type = 'BRN' AND brn.parent_id::varchar = org.id::varchar",
76
78
  )
77
79
  // JOIN school table
78
80
  .innerJoin(
@@ -161,7 +163,6 @@ export class SchoolRepository {
161
163
  brands: Object.values(org.brands),
162
164
  }));
163
165
  } else {
164
-
165
166
  const orgResult = await organizationRepo.findOne({
166
167
  where: {
167
168
  id: currentORGLevel_id,
@@ -180,8 +181,8 @@ export class SchoolRepository {
180
181
  ];
181
182
  }
182
183
  } else {
183
-
184
- const userRoleMappingRepo = this.reflectionHelper.getRepoService('UserRoleMapping');
184
+ const userRoleMappingRepo =
185
+ this.reflectionHelper.getRepoService('UserRoleMapping');
185
186
 
186
187
  const brnMappings = await userRoleMappingRepo
187
188
  .createQueryBuilder('urm')
@@ -195,9 +196,17 @@ export class SchoolRepository {
195
196
  'school.location AS school_location',
196
197
  'school.status AS school_status',
197
198
  ])
198
- .innerJoin('sso_organization', 'brand', 'brand.id = urm.level_id')
199
- .innerJoin('sso_school', 'school', 'school.brand_id = brand.id')
200
- .where('urm.user_id = :userId', { userId })
199
+ .innerJoin(
200
+ 'sso_organization',
201
+ 'brand',
202
+ 'brand.id::text = urm.level_id::text',
203
+ )
204
+ .innerJoin(
205
+ 'sso_school',
206
+ 'school',
207
+ 'school.brand_id::text = brand.id::text',
208
+ )
209
+ .where('urm.user_id::text = :userId', { userId: String(userId) })
201
210
  .andWhere('urm.appcode = :appCode', { appCode })
202
211
  .andWhere('urm.level_type = :levelType', { levelType: 'BRN' })
203
212
  .distinct(true)
@@ -215,9 +224,17 @@ export class SchoolRepository {
215
224
  'school.location AS school_location',
216
225
  'school.status AS school_status',
217
226
  ])
218
- .innerJoin('sso_school', 'school', 'school.id = urm.level_id')
219
- .innerJoin('sso_organization', 'brand', 'brand.id = school.brand_id')
220
- .where('urm.user_id = :userId', { userId })
227
+ .innerJoin(
228
+ 'sso_school',
229
+ 'school',
230
+ 'school.id::text = urm.level_id::text',
231
+ )
232
+ .innerJoin(
233
+ 'sso_organization',
234
+ 'brand',
235
+ 'brand.id::text = school.brand_id::text',
236
+ )
237
+ .where('urm.user_id::text = :userId', { userId: String(userId) })
221
238
  .andWhere('urm.appcode = :appCode', { appCode })
222
239
  .andWhere('urm.level_type = :levelType', { levelType: 'SCH' })
223
240
  .distinct(true)
@@ -111,9 +111,9 @@ export class ResolverService {
111
111
  attr.element_type === 'date' ||
112
112
  attr.element_type === 'datetime'
113
113
  ) {
114
- // 👇 Explicitly define input format to avoid deprecation warning
115
- const dateValue = moment(codeValue, "DD-MM-YYYY", true).utcOffset('+05:30');
116
-
114
+ const dateFormat =
115
+ attr.element_type === 'date' ? 'DD-MM-YYYY' : 'DD-MM-YYYY HH:mm:ss';
116
+ const dateValue = moment(codeValue, dateFormat).utcOffset('+05:30'); // IST
117
117
  if (dateValue.isValid()) {
118
118
  resolvedEntityData[field] =
119
119
  attr.element_type === 'date'
@@ -159,70 +159,82 @@ export class ResolverService {
159
159
  attrKey,
160
160
  loggedInUser.enterprise_id,
161
161
  );
162
- if (!attr) return rawValue;
163
162
 
164
- // -------- ENTITY data_source_type --------
163
+ if (!attr) return rawValue;
164
+ // ----------- ENTITY TYPE RESOLUTION -------------------
165
165
  if (attr.data_source_type === 'entity') {
166
166
  const entityDef = await this.entityMasterRepo.getEntityByMappedEntityType(
167
167
  attr.datasource_list,
168
168
  loggedInUser.enterprise_id,
169
169
  );
170
-
171
170
  if (!entityDef) return rawValue;
172
171
 
173
172
  const tableName = entityDef.db_table_name;
174
173
 
174
+ // --- If array (multi-select) ---
175
175
  if (Array.isArray(rawValue)) {
176
176
  const resolvedValues: string[] = [];
177
+
177
178
  for (const value of rawValue) {
178
179
  const query =
179
180
  tableName === 'sso_organization'
180
- ? `SELECT *
181
- FROM ${tableName}
182
- WHERE code = $1`
183
- : `SELECT *
184
- FROM ${tableName}
185
- WHERE id = $1`;
186
- const params = tableName === 'sso_organization' ? [value] : [value];
187
-
188
- const [item] = await this.entityManger.query(query, params);
181
+ ? `SELECT * FROM ${tableName} WHERE code = $1`
182
+ : `SELECT * FROM ${tableName} WHERE id = $1`;
183
+
184
+ const [item] = await this.entityManger.query(query, [value]);
189
185
  resolvedValues.push(item?.[attr.data_source_attribute] ?? value);
190
186
  }
191
- return resolvedValues;
192
- } else {
193
- const query = `SELECT *
194
- FROM ${tableName}
195
- WHERE id = $1`;
196
- const params = [rawValue];
197
187
 
198
- const [item] = await this.entityManger.query(query, params);
199
- return item?.[attr.data_source_attribute] ?? rawValue;
188
+ return resolvedValues;
200
189
  }
190
+
191
+ // --- Single value ---
192
+ const query = `SELECT * FROM ${tableName} WHERE id = $1`;
193
+ const [item] = await this.entityManger.query(query, [rawValue]);
194
+
195
+ return item?.[attr.data_source_attribute] ?? rawValue;
201
196
  }
202
197
 
203
- // -------- MASTER data_source_type --------
204
- else if (attr.data_source_type === 'master') {
198
+ // ----------- MASTER TYPE RESOLUTION -------------------
199
+ if (attr.data_source_type === 'master') {
205
200
  const repo = this.reflectionHelper.getRepoService('ListMasterItems');
206
- if (Array.isArray(rawValue)) {
201
+
202
+ let value = rawValue;
203
+
204
+ // 🟦 If rawValue is a JSON string representing array → parse it
205
+ if (typeof rawValue === 'string') {
206
+ try {
207
+ const parsed = JSON.parse(rawValue);
208
+ if (Array.isArray(parsed)) value = parsed;
209
+ } catch (e) {
210
+ // keep as string
211
+ }
212
+ }
213
+
214
+ // 🟩 If value is array → resolve each
215
+ if (Array.isArray(value)) {
207
216
  const resolvedValues: string[] = [];
208
- for (const code of rawValue) {
209
- let item = repo.findOne({
210
- where: {
211
- id: code,
212
- },
217
+
218
+ for (const id of value) {
219
+ const item = await repo.findOne({
220
+ where: { id: Number(id) },
213
221
  });
214
- resolvedValues.push(item?.[attr.data_source_attribute] ?? code);
222
+ resolvedValues.push(item?.[attr.data_source_attribute] ?? id);
215
223
  }
224
+
216
225
  return resolvedValues;
217
- } else {
218
- if (typeof rawValue !== 'number') return rawValue;
219
- let item = await repo.findOne({
220
- where: {
221
- id: Number(rawValue),
222
- },
226
+ }
227
+
228
+ // 🟨 Single value (must be number)
229
+ if (!isNaN(rawValue)) {
230
+ const item = await repo.findOne({
231
+ where: { id: Number(rawValue) },
223
232
  });
233
+
224
234
  return item?.[attr.data_source_attribute] ?? rawValue;
225
235
  }
236
+
237
+ return rawValue;
226
238
  }
227
239
 
228
240
  return rawValue;
@@ -17,6 +17,7 @@ import { NotificationsController } from './controller/notification.controller';
17
17
  import { NotificationsService } from './service/notification.service';
18
18
  import { EntityModule } from '../meta/entity.module';
19
19
  import { FirebaseAdminProvider } from './firebase-admin.config';
20
+ import { NotificationRepository } from './repository/notification.repository';
20
21
 
21
22
  @Module({
22
23
  imports: [
@@ -57,6 +58,7 @@ import { FirebaseAdminProvider } from './firebase-admin.config';
57
58
  EmailService,
58
59
  NotificationsService,
59
60
  FirebaseAdminProvider,
61
+ NotificationRepository,
60
62
  ],
61
63
  exports: [
62
64
  OtpService,
@@ -0,0 +1,33 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { InjectRepository } from '@nestjs/typeorm';
3
+ import { Repository, EntityManager } from 'typeorm';
4
+ import { NotificationData } from '../entity/notification.entity';
5
+
6
+ @Injectable()
7
+ export class NotificationRepository {
8
+ constructor(
9
+ @InjectRepository(NotificationData)
10
+ private readonly notificationRepo: Repository<NotificationData>,
11
+ ) {}
12
+
13
+ async getAllNotifications(
14
+ userId: number,
15
+ levelId: number,
16
+ levelType: string,
17
+ isReadFilter?,
18
+ ) {
19
+ const qb = this.notificationRepo
20
+ .createQueryBuilder('n')
21
+ .where('n.user_id = :userId', { userId })
22
+ .andWhere('n.level_id = :levelId', { levelId })
23
+ .andWhere('n.level_type = :levelType', { levelType });
24
+
25
+ if (isReadFilter !== undefined) {
26
+ qb.andWhere('n.is_read = :isRead', { isRead: isReadFilter });
27
+ }
28
+
29
+ qb.orderBy('n.created_date', 'DESC');
30
+
31
+ return await qb.getMany();
32
+ }
33
+ }
@@ -5,6 +5,7 @@ import * as admin from 'firebase-admin';
5
5
  import axios from 'axios';
6
6
  import { ConfigService } from '@nestjs/config';
7
7
  import { ReflectionHelper } from 'src/utils/service/reflection-helper.service';
8
+ import { NotificationRepository } from '../repository/notification.repository';
8
9
 
9
10
  @Injectable()
10
11
  export class NotificationsService {
@@ -14,6 +15,7 @@ export class NotificationsService {
14
15
  private readonly configService: ConfigService,
15
16
  private readonly reflectionHelper: ReflectionHelper,
16
17
  @Inject('FIREBASE_ADMIN') private readonly firebaseAdmin: typeof admin,
18
+ private readonly notificationRepository: NotificationRepository,
17
19
  ) {}
18
20
 
19
21
  private tokens: Map<string, string> = new Map(); // store in memory for now
@@ -69,73 +71,53 @@ export class NotificationsService {
69
71
  loggedInUser: any,
70
72
  filterQuery?: { is_read?: string },
71
73
  ) {
72
- const { id, level_id, level_type } = loggedInUser;
73
-
74
- //
75
- // Convert "true"/"false" → "1"/"0" as strings
76
- //
77
- let isReadFilter: string | undefined = undefined;
78
- if (filterQuery && filterQuery.is_read !== undefined) {
79
- const isRead = String(filterQuery.is_read).toLowerCase() === 'true';
80
- isReadFilter = isRead ? '1' : '0'; // <-- always string
81
- }
82
-
83
- //
84
- // Build base SQL
85
- //
86
- let paramIndex = 1;
87
- let sql = `
88
- SELECT n.*
89
- FROM frm_notification n
90
- WHERE n.user_id = $${paramIndex}
91
- AND n.level_id = $${paramIndex + 1}
92
- AND n.level_type = $${paramIndex + 2}
93
- `;
94
-
95
- const params: string[] = [String(id), String(level_id), String(level_type)];
96
- paramIndex += 3;
97
-
98
- //
99
- // Apply optional filter
100
- //
101
- if (isReadFilter !== undefined) {
102
- sql += ` AND n.is_read = $${paramIndex}`;
103
- params.push(String(isReadFilter));
104
- paramIndex++;
105
- }
106
-
107
- sql += ` ORDER BY n.created_date DESC`;
74
+ const { id: userId, level_id, level_type } = loggedInUser;
75
+
76
+ // Fetch notifications from repository
77
+ const notifications: any =
78
+ await this.notificationRepository.getAllNotifications(
79
+ userId,
80
+ level_id,
81
+ level_type,
82
+ filterQuery?.is_read,
83
+ );
108
84
 
109
85
  //
110
- // Execute query
86
+ // Avoid duplicate API calls for each user_id
111
87
  //
112
- const notifications = await this.entityManager.query(sql, params);
88
+ const mediaCache = new Map<
89
+ number,
90
+ { name: string; profile_image: string }
91
+ >();
113
92
 
114
- //
115
- // Enrich profile image + user name
116
- //
117
- const mediaCache = new Map();
93
+ const baseUrl = this.configService.get<string>('REDIRECT_BE_URL');
118
94
 
119
95
  for (const notification of notifications) {
120
- notification.is_read =
121
- notification.is_read === 1 || notification.is_read === '1';
122
-
123
- try {
124
- const baseUrl = this.configService.get<string>('REDIRECT_BE_URL');
125
- const queryParams = new URLSearchParams({
126
- loggedInUser: JSON.stringify(loggedInUser),
127
- }).toString();
128
-
129
- const response = await axios.get(
130
- `${baseUrl}/users/profile-image-url/${notification.user_id}?entity_type=USR&${queryParams}`,
131
- { headers: { 'Content-Type': 'application/json' } },
132
- );
133
-
134
- notification.user_name = response.data.name;
135
- notification.user_profile = response.data.profile_image;
136
- } catch (error) {
137
- console.error('⚠️ Internal Entity API call failed:', error.message);
96
+ const uid = notification?.user_id;
97
+
98
+ if (uid && !mediaCache.has(uid)) {
99
+ try {
100
+ const queryParams = new URLSearchParams({
101
+ loggedInUser: JSON.stringify(loggedInUser),
102
+ }).toString();
103
+
104
+ const response = await axios.get(
105
+ `${baseUrl}/users/profile-image-url/${uid}?entity_type=USR&${queryParams}`,
106
+ { headers: { 'Content-Type': 'application/json' } },
107
+ );
108
+
109
+ mediaCache.set(uid, {
110
+ name: response.data.name,
111
+ profile_image: response.data.profile_image,
112
+ });
113
+ } catch (err) {
114
+ console.error('⚠️ Internal Entity API call failed:', err.message);
115
+ }
138
116
  }
117
+
118
+ const cachedData = mediaCache.get(uid);
119
+ notification.user_name = cachedData?.name;
120
+ notification.user_profile = cachedData?.profile_image;
139
121
  }
140
122
 
141
123
  return notifications;