@loka-sms/core-integration-be 0.0.1 → 0.0.2
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 +10 -2
- package/dist/core-integration.service.d.ts +20 -1
- package/dist/core-integration.service.js +161 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ export class RaporSyncService {
|
|
|
89
89
|
|
|
90
90
|
async generate(schoolId: string) {
|
|
91
91
|
const students = await this.core.getStudents(schoolId, { status: 'active' });
|
|
92
|
-
const classes = await this.core.getClasses(schoolId);
|
|
92
|
+
const classes = await this.core.getClasses(schoolId, { grade: 7 });
|
|
93
93
|
return { students, classes };
|
|
94
94
|
}
|
|
95
95
|
}
|
|
@@ -107,10 +107,12 @@ Package selalu mengirim `X-App-ID`, `X-App-Secret`, dan `X-School-ID`. Tidak per
|
|
|
107
107
|
| `findStudentByEmail(email, schoolId)` | `GET /api/students?q=...&limit=1` |
|
|
108
108
|
| `getParentChildrenByUserId(parentUserId, schoolId)` | `GET /api/parents/by-user/:id/children` |
|
|
109
109
|
| `getParentChildIdsByUserId(parentUserId, schoolId)` | `GET /api/parents/by-user/:id/children` |
|
|
110
|
+
| `getParentChildUserIdsByUserId(parentUserId, schoolId)` | `GET /api/parents/by-user/:id/children` |
|
|
110
111
|
| `resolveParentStudentScope(parentUserId, schoolId, requestedStudentId?)` | `GET /api/parents/by-user/:id/children` |
|
|
112
|
+
| `resolveParentStudentUserScope(parentUserId, schoolId, requestedStudentUserId?)` | `GET /api/parents/by-user/:id/children` |
|
|
111
113
|
| `getTeachers(schoolId)` | `GET /api/teachers` |
|
|
112
114
|
| `getTeacher(teacherId, schoolId)` | `GET /api/teachers/:id` |
|
|
113
|
-
| `getClasses(schoolId)` | `GET /api/classes` |
|
|
115
|
+
| `getClasses(schoolId, params)` | `GET /api/classes` |
|
|
114
116
|
| `getSubjects(schoolId, params)` | `GET /api/subjects` |
|
|
115
117
|
| `getCurriculums(schoolId, params)` | `GET /api/curriculum` |
|
|
116
118
|
| `getAcademicCalendar(schoolId, params)` | `GET /api/academic-calendar` |
|
|
@@ -138,6 +140,12 @@ await this.core.post('notifications', schoolId, {
|
|
|
138
140
|
|
|
139
141
|
Path tidak perlu diawali `/api` atau `/api/v1`.
|
|
140
142
|
|
|
143
|
+
## Catatan Identity Siswa
|
|
144
|
+
|
|
145
|
+
- `getParentChildIdsByUserId()` dan `resolveParentStudentScope()` mengembalikan **Core student profile id** (`students.id`).
|
|
146
|
+
- `getParentChildUserIdsByUserId()` dan `resolveParentStudentUserScope()` mengembalikan **Core user id siswa** (`students.user_id` / `userId`). Gunakan ini untuk modul yang menyimpan relasi berdasarkan user id, misalnya LMS enrollment `studentUserId`.
|
|
147
|
+
- `getClasses(schoolId, params)` mendukung filter Core seperti `{ grade: 7, academic_year: '2025/2026' }`.
|
|
148
|
+
|
|
141
149
|
Benar:
|
|
142
150
|
```ts
|
|
143
151
|
this.core.get('students', schoolId)
|
|
@@ -14,6 +14,8 @@ export declare class CoreIntegrationService {
|
|
|
14
14
|
private getHeaders;
|
|
15
15
|
private makeUrl;
|
|
16
16
|
private extractStudentId;
|
|
17
|
+
private extractStudentUserId;
|
|
18
|
+
private unwrapRecord;
|
|
17
19
|
get<T = any>(path: string, schoolId?: string, options?: CoreRequestOptions): Promise<T>;
|
|
18
20
|
post<T = any>(path: string, schoolId: string | undefined, body?: unknown, options?: CoreRequestOptions): Promise<T>;
|
|
19
21
|
put<T = any>(path: string, schoolId: string | undefined, body?: unknown, options?: CoreRequestOptions): Promise<T>;
|
|
@@ -21,15 +23,32 @@ export declare class CoreIntegrationService {
|
|
|
21
23
|
delete<T = any>(path: string, schoolId?: string, options?: CoreRequestOptions): Promise<T>;
|
|
22
24
|
getParentChildrenByUserId(parentUserId: string, schoolId: string): Promise<any[]>;
|
|
23
25
|
getParentChildIdsByUserId(parentUserId: string, schoolId: string): Promise<string[]>;
|
|
26
|
+
getParentChildUserIdsByUserId(parentUserId: string, schoolId: string): Promise<string[]>;
|
|
27
|
+
resolveParentStudentUserScope(parentUserId: string, schoolId: string, requestedStudentUserId?: string): Promise<string[]>;
|
|
24
28
|
resolveParentStudentScope(parentUserId: string, schoolId: string, requestedStudentId?: string): Promise<string[]>;
|
|
25
29
|
getStudents(schoolId: string, params?: Record<string, unknown>): Promise<any[]>;
|
|
26
30
|
getStudentsByClass(classId: string, schoolId: string): Promise<any[]>;
|
|
31
|
+
getUser(userId: string, schoolId: string): Promise<any>;
|
|
27
32
|
getStudent(studentId: string, schoolId: string): Promise<any>;
|
|
33
|
+
getStudentByUserId(userId: string, schoolId: string): Promise<any>;
|
|
34
|
+
resolveStudentUserIdToStudentId(userId: string, schoolId: string): Promise<string | null>;
|
|
28
35
|
findStudentByEmail(email: string, schoolId: string): Promise<any>;
|
|
29
36
|
getTeacher(teacherId: string, schoolId: string): Promise<any>;
|
|
30
|
-
|
|
37
|
+
getTeacherByUserId(userId: string, schoolId: string): Promise<any>;
|
|
38
|
+
resolveTeacherUserIdToTeacherId(userId: string, schoolId: string): Promise<string | null>;
|
|
39
|
+
getClass(classId: string, schoolId: string): Promise<any>;
|
|
40
|
+
getClasses(schoolId: string, params?: Record<string, unknown>): Promise<any[]>;
|
|
31
41
|
getTeachers(schoolId: string): Promise<any[]>;
|
|
32
42
|
getSubjects(schoolId: string, params?: Record<string, unknown>): Promise<any[]>;
|
|
43
|
+
getSubject(subjectId: string, schoolId: string): Promise<any>;
|
|
44
|
+
getRoom(roomId: string, schoolId: string): Promise<any>;
|
|
45
|
+
getTimeSlots(schoolId: string): Promise<any[]>;
|
|
46
|
+
getActiveAcademicYear(schoolId: string): Promise<any | null>;
|
|
47
|
+
getActiveSemester(schoolId: string): Promise<any | null>;
|
|
48
|
+
getAcademicYear(yearId: string, schoolId: string): Promise<any | null>;
|
|
49
|
+
getSemester(semesterId: string, schoolId: string): Promise<any | null>;
|
|
50
|
+
getTeachingSchedule(scheduleId: string, schoolId: string): Promise<any | null>;
|
|
51
|
+
findTeachingSchedules(schoolId: string, params?: Record<string, unknown>): Promise<any[]>;
|
|
33
52
|
getCurriculums(schoolId: string, params?: Record<string, unknown>): Promise<any[]>;
|
|
34
53
|
getAcademicCalendar(schoolId: string, params?: Record<string, unknown>): Promise<any[]>;
|
|
35
54
|
getRooms(schoolId: string, params?: Record<string, unknown>): Promise<any[]>;
|
|
@@ -59,6 +59,12 @@ let CoreIntegrationService = CoreIntegrationService_1 = class CoreIntegrationSer
|
|
|
59
59
|
extractStudentId(child) {
|
|
60
60
|
return child?.student_id || child?.student?.id || child?.studentUserId || child?.id || null;
|
|
61
61
|
}
|
|
62
|
+
extractStudentUserId(child) {
|
|
63
|
+
return child?.student_user_id || child?.studentUserId || child?.student?.userId || child?.student?.user_id || child?.student?.user?.id || child?.userId || child?.user_id || null;
|
|
64
|
+
}
|
|
65
|
+
unwrapRecord(data) {
|
|
66
|
+
return data?.data || data;
|
|
67
|
+
}
|
|
62
68
|
async get(path, schoolId, options = {}) {
|
|
63
69
|
const { headers, ...requestOptions } = options;
|
|
64
70
|
const res = await (0, rxjs_1.firstValueFrom)(this.httpService.get(this.makeUrl(path), {
|
|
@@ -118,6 +124,25 @@ let CoreIntegrationService = CoreIntegrationService_1 = class CoreIntegrationSer
|
|
|
118
124
|
.map((child) => this.extractStudentId(child))
|
|
119
125
|
.filter((studentId) => !!studentId);
|
|
120
126
|
}
|
|
127
|
+
async getParentChildUserIdsByUserId(parentUserId, schoolId) {
|
|
128
|
+
const children = await this.getParentChildrenByUserId(parentUserId, schoolId);
|
|
129
|
+
return children
|
|
130
|
+
.map((child) => this.extractStudentUserId(child))
|
|
131
|
+
.filter((studentUserId) => !!studentUserId);
|
|
132
|
+
}
|
|
133
|
+
async resolveParentStudentUserScope(parentUserId, schoolId, requestedStudentUserId) {
|
|
134
|
+
const childUserIds = await this.getParentChildUserIdsByUserId(parentUserId, schoolId);
|
|
135
|
+
if (childUserIds.length === 0) {
|
|
136
|
+
throw new common_1.ForbiddenException('Parent has no linked children with user accounts in this school');
|
|
137
|
+
}
|
|
138
|
+
if (!requestedStudentUserId) {
|
|
139
|
+
return childUserIds;
|
|
140
|
+
}
|
|
141
|
+
if (!childUserIds.includes(requestedStudentUserId)) {
|
|
142
|
+
throw new common_1.ForbiddenException('Child user is not linked to this parent');
|
|
143
|
+
}
|
|
144
|
+
return [requestedStudentUserId];
|
|
145
|
+
}
|
|
121
146
|
async resolveParentStudentScope(parentUserId, schoolId, requestedStudentId) {
|
|
122
147
|
const childIds = await this.getParentChildIdsByUserId(parentUserId, schoolId);
|
|
123
148
|
if (childIds.length === 0) {
|
|
@@ -134,7 +159,8 @@ let CoreIntegrationService = CoreIntegrationService_1 = class CoreIntegrationSer
|
|
|
134
159
|
async getStudents(schoolId, params = {}) {
|
|
135
160
|
try {
|
|
136
161
|
const data = await this.get('students', schoolId, { params });
|
|
137
|
-
|
|
162
|
+
const body = data?.data || data || {};
|
|
163
|
+
return body?.data || body?.students || body || [];
|
|
138
164
|
}
|
|
139
165
|
catch (error) {
|
|
140
166
|
if (error.response?.status === 404) {
|
|
@@ -147,15 +173,37 @@ let CoreIntegrationService = CoreIntegrationService_1 = class CoreIntegrationSer
|
|
|
147
173
|
async getStudentsByClass(classId, schoolId) {
|
|
148
174
|
return this.getStudents(schoolId, { class_id: classId, status: 'active' });
|
|
149
175
|
}
|
|
176
|
+
async getUser(userId, schoolId) {
|
|
177
|
+
try {
|
|
178
|
+
return this.unwrapRecord(await this.get(`users/${userId}`, schoolId));
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
this.logger.warn(`Failed to fetch user ${userId}: ${error.message}`);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
150
185
|
async getStudent(studentId, schoolId) {
|
|
151
186
|
try {
|
|
152
|
-
return await this.get(`students/${studentId}`, schoolId);
|
|
187
|
+
return this.unwrapRecord(await this.get(`students/${studentId}`, schoolId));
|
|
153
188
|
}
|
|
154
189
|
catch (error) {
|
|
155
190
|
this.logger.warn(`Failed to fetch student ${studentId}: ${error.message}`);
|
|
156
191
|
return null;
|
|
157
192
|
}
|
|
158
193
|
}
|
|
194
|
+
async getStudentByUserId(userId, schoolId) {
|
|
195
|
+
try {
|
|
196
|
+
return this.unwrapRecord(await this.get(`students/by-user/${userId}`, schoolId));
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
this.logger.warn(`Failed to fetch student by user ${userId}: ${error.message}`);
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async resolveStudentUserIdToStudentId(userId, schoolId) {
|
|
204
|
+
const student = await this.getStudentByUserId(userId, schoolId);
|
|
205
|
+
return student?.id || null;
|
|
206
|
+
}
|
|
159
207
|
async findStudentByEmail(email, schoolId) {
|
|
160
208
|
try {
|
|
161
209
|
const data = await this.get('students', schoolId, { params: { q: email, limit: 1 } });
|
|
@@ -177,10 +225,33 @@ let CoreIntegrationService = CoreIntegrationService_1 = class CoreIntegrationSer
|
|
|
177
225
|
return null;
|
|
178
226
|
}
|
|
179
227
|
}
|
|
180
|
-
async
|
|
228
|
+
async getTeacherByUserId(userId, schoolId) {
|
|
181
229
|
try {
|
|
182
|
-
|
|
183
|
-
|
|
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;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async resolveTeacherUserIdToTeacherId(userId, schoolId) {
|
|
238
|
+
const teacher = await this.getTeacherByUserId(userId, schoolId);
|
|
239
|
+
return teacher?.id || null;
|
|
240
|
+
}
|
|
241
|
+
async getClass(classId, schoolId) {
|
|
242
|
+
try {
|
|
243
|
+
return this.unwrapRecord(await this.get(`classes/${classId}`, schoolId));
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
this.logger.warn(`Failed to fetch class ${classId}: ${error.message}`);
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async getClasses(schoolId, params = {}) {
|
|
251
|
+
try {
|
|
252
|
+
const data = await this.get('classes', schoolId, { params });
|
|
253
|
+
const body = data?.data || data || {};
|
|
254
|
+
return body?.data || body || [];
|
|
184
255
|
}
|
|
185
256
|
catch (error) {
|
|
186
257
|
this.logger.error(`Failed to fetch classes: ${error.message}`);
|
|
@@ -200,7 +271,91 @@ let CoreIntegrationService = CoreIntegrationService_1 = class CoreIntegrationSer
|
|
|
200
271
|
}
|
|
201
272
|
async getSubjects(schoolId, params = {}) {
|
|
202
273
|
const data = await this.get('subjects', schoolId, { params });
|
|
203
|
-
|
|
274
|
+
const body = data?.data || data || {};
|
|
275
|
+
return body?.data || body || [];
|
|
276
|
+
}
|
|
277
|
+
async getSubject(subjectId, schoolId) {
|
|
278
|
+
try {
|
|
279
|
+
return this.unwrapRecord(await this.get(`subjects/${subjectId}`, schoolId));
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
this.logger.warn(`Failed to fetch subject ${subjectId}: ${error.message}`);
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async getRoom(roomId, schoolId) {
|
|
287
|
+
try {
|
|
288
|
+
return this.unwrapRecord(await this.get(`rooms/${roomId}`, schoolId));
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
this.logger.warn(`Failed to fetch room ${roomId}: ${error.message}`);
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async getTimeSlots(schoolId) {
|
|
296
|
+
try {
|
|
297
|
+
const data = await this.get('time-slots', schoolId);
|
|
298
|
+
return data?.data || data || [];
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
this.logger.warn(`Failed to fetch time slots: ${error.message}`);
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async getActiveAcademicYear(schoolId) {
|
|
306
|
+
try {
|
|
307
|
+
return this.unwrapRecord(await this.get('academic-years/active', schoolId));
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
this.logger.warn(`Failed to fetch active academic year: ${error.message}`);
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async getActiveSemester(schoolId) {
|
|
315
|
+
try {
|
|
316
|
+
return this.unwrapRecord(await this.get('academic-years/active-semester', schoolId));
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
this.logger.warn(`Failed to fetch active semester: ${error.message}`);
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async getAcademicYear(yearId, schoolId) {
|
|
324
|
+
try {
|
|
325
|
+
return this.unwrapRecord(await this.get(`academic-years/${yearId}`, schoolId));
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
this.logger.warn(`Failed to fetch academic year ${yearId}: ${error.message}`);
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async getSemester(semesterId, schoolId) {
|
|
333
|
+
try {
|
|
334
|
+
return this.unwrapRecord(await this.get(`academic-years/semesters/${semesterId}`, schoolId));
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
this.logger.warn(`Failed to fetch semester ${semesterId}: ${error.message}`);
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
async getTeachingSchedule(scheduleId, schoolId) {
|
|
342
|
+
try {
|
|
343
|
+
return this.unwrapRecord(await this.get(`teaching-schedules/${scheduleId}`, schoolId));
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
this.logger.warn(`Failed to fetch teaching schedule ${scheduleId}: ${error.message}`);
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async findTeachingSchedules(schoolId, params = {}) {
|
|
351
|
+
try {
|
|
352
|
+
const data = await this.get('teaching-schedules', schoolId, { params });
|
|
353
|
+
return data?.data || data || [];
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
this.logger.warn(`Failed to fetch teaching schedules: ${error.message}`);
|
|
357
|
+
return [];
|
|
358
|
+
}
|
|
204
359
|
}
|
|
205
360
|
async getCurriculums(schoolId, params = {}) {
|
|
206
361
|
const data = await this.get('curriculum', schoolId, { params });
|