@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
|
|
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` |
|
|
32
|
-
| `CORE_APP_ID` |
|
|
33
|
-
| `CORE_APP_SECRET` |
|
|
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
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
|
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.
|
|
4
|
-
"description": "Internal NestJS client for service-to-service access to
|
|
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",
|