@loka-sms/core-integration-be 0.0.2 → 0.0.3

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # @loka-sms/core-integration-be
1
+ # @loka-sms/core-integration-be
2
2
 
3
- NestJS helper package untuk backend modul Loka SMS yang perlu mengakses data Core via service-to-service auth (`X-App-ID` + `X-App-Secret`).
3
+ NestJS helper package untuk backend modul OMNISCHOOL yang perlu mengakses data Core via service-to-service auth (`X-App-ID` + `X-App-Secret`).
4
4
 
5
5
  Package ini cocok untuk **worker/background job/service sync**, bukan untuk forwarding token user.
6
6
 
@@ -28,9 +28,9 @@ npm install @loka-sms/core-integration-be
28
28
 
29
29
  | Variabel | Wajib | Fallback | Keterangan |
30
30
  |---|---|---|---|
31
- | `CORE_SERVICE_URL` | | | Base URL Gateway, contoh: `http://localhost:3000/api` |
32
- | `CORE_APP_ID` | | `APP_ID` | Application ID yang terdaftar di Core |
33
- | `CORE_APP_SECRET` | | `APP_SECRET` | Application secret (plain, bukan bcrypt) |
31
+ | `CORE_SERVICE_URL` | ✅ | — | Base URL Gateway, contoh: `http://localhost:3000/api` |
32
+ | `CORE_APP_ID` | ✅ | `APP_ID` | Application ID yang terdaftar di Core |
33
+ | `CORE_APP_SECRET` | ✅ | `APP_SECRET` | Application secret (plain, bukan bcrypt) |
34
34
 
35
35
  Contoh `.env`:
36
36
 
@@ -16,6 +16,10 @@ export declare class CoreIntegrationService {
16
16
  private extractStudentId;
17
17
  private extractStudentUserId;
18
18
  private unwrapRecord;
19
+ private extractUserId;
20
+ private extractEntityId;
21
+ private matchByUserId;
22
+ private assertTenantScope;
19
23
  get<T = any>(path: string, schoolId?: string, options?: CoreRequestOptions): Promise<T>;
20
24
  post<T = any>(path: string, schoolId: string | undefined, body?: unknown, options?: CoreRequestOptions): Promise<T>;
21
25
  put<T = any>(path: string, schoolId: string | undefined, body?: unknown, options?: CoreRequestOptions): Promise<T>;
@@ -36,6 +40,12 @@ export declare class CoreIntegrationService {
36
40
  getTeacher(teacherId: string, schoolId: string): Promise<any>;
37
41
  getTeacherByUserId(userId: string, schoolId: string): Promise<any>;
38
42
  resolveTeacherUserIdToTeacherId(userId: string, schoolId: string): Promise<string | null>;
43
+ reconcileAcademicData(schoolId: string, academicYear?: string): Promise<{
44
+ teachers: any[];
45
+ classes: any[];
46
+ assignments: any[];
47
+ }>;
48
+ getTeacherAssignments(teacherId: string, schoolId: string, academicYear?: string): Promise<any[]>;
39
49
  getClass(classId: string, schoolId: string): Promise<any>;
40
50
  getClasses(schoolId: string, params?: Record<string, unknown>): Promise<any[]>;
41
51
  getTeachers(schoolId: string): Promise<any[]>;
@@ -65,6 +65,28 @@ let CoreIntegrationService = CoreIntegrationService_1 = class CoreIntegrationSer
65
65
  unwrapRecord(data) {
66
66
  return data?.data || data;
67
67
  }
68
+ extractUserId(entity) {
69
+ return entity?.userId || entity?.user_id || entity?.user?.id || null;
70
+ }
71
+ extractEntityId(entity) {
72
+ return entity?.id || null;
73
+ }
74
+ matchByUserId(items, userId) {
75
+ for (const item of items) {
76
+ if (item?.userId === userId)
77
+ return item;
78
+ if (item?.user_id === userId)
79
+ return item;
80
+ if (item?.user?.id === userId)
81
+ return item;
82
+ }
83
+ return null;
84
+ }
85
+ assertTenantScope(schoolId, contextLabel) {
86
+ if (!schoolId) {
87
+ throw new Error(`${contextLabel}: schoolId (X-School-ID) is required for non-super_admin operations`);
88
+ }
89
+ }
68
90
  async get(path, schoolId, options = {}) {
69
91
  const { headers, ...requestOptions } = options;
70
92
  const res = await (0, rxjs_1.firstValueFrom)(this.httpService.get(this.makeUrl(path), {
@@ -226,17 +248,55 @@ let CoreIntegrationService = CoreIntegrationService_1 = class CoreIntegrationSer
226
248
  }
227
249
  }
228
250
  async getTeacherByUserId(userId, schoolId) {
251
+ // Fast path: try direct lookup endpoint (if Core provides it)
229
252
  try {
230
- return this.unwrapRecord(await this.get(`teachers/by-user/${userId}`, schoolId));
231
- }
232
- catch (error) {
233
- this.logger.warn(`Failed to fetch teacher by user ${userId}: ${error.message}`);
234
- return null;
253
+ const data = await this.get(`teachers/by-user/${userId}`, schoolId);
254
+ return this.unwrapRecord(data);
255
+ }
256
+ catch {
257
+ // Fallback: fetch all active teachers and find match by userId
258
+ try {
259
+ const teachers = await this.getTeachers(schoolId);
260
+ return this.matchByUserId(teachers, userId);
261
+ }
262
+ catch {
263
+ return null;
264
+ }
235
265
  }
236
266
  }
237
267
  async resolveTeacherUserIdToTeacherId(userId, schoolId) {
238
268
  const teacher = await this.getTeacherByUserId(userId, schoolId);
239
- return teacher?.id || null;
269
+ return this.extractEntityId(teacher);
270
+ }
271
+ async reconcileAcademicData(schoolId, academicYear) {
272
+ try {
273
+ const data = await this.post('integrations/lms/reconcile', schoolId, {
274
+ schoolId,
275
+ academicYear,
276
+ });
277
+ const body = data?.data || data;
278
+ return {
279
+ teachers: body?.teachers || [],
280
+ classes: body?.classes || [],
281
+ assignments: body?.assignments || [],
282
+ };
283
+ }
284
+ catch (error) {
285
+ this.logger.error(`Failed to reconcile academic data: ${error.message}`);
286
+ throw new Error('Core service unavailable: unable to reconcile academic data');
287
+ }
288
+ }
289
+ async getTeacherAssignments(teacherId, schoolId, academicYear) {
290
+ try {
291
+ const data = await this.get(`teachers/${teacherId}/assignments`, schoolId, {
292
+ params: academicYear ? { academicYear } : {},
293
+ });
294
+ const raw = data?.data || data;
295
+ return raw?.data || (Array.isArray(raw) ? raw : []);
296
+ }
297
+ catch {
298
+ return [];
299
+ }
240
300
  }
241
301
  async getClass(classId, schoolId) {
242
302
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
- {
1
+ {
2
2
  "name": "@loka-sms/core-integration-be",
3
- "version": "0.0.2",
4
- "description": "Internal NestJS client for service-to-service access to Loka SMS Core APIs",
3
+ "version": "0.0.3",
4
+ "description": "Internal NestJS client for service-to-service access to OMNISCHOOL Core APIs",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
7
7
  "main": "dist/index.js",