@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 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
- getClasses(schoolId: string): Promise<any[]>;
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
- return data?.data || data?.students || data || [];
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 getClasses(schoolId) {
228
+ async getTeacherByUserId(userId, schoolId) {
181
229
  try {
182
- const data = await this.get('classes', schoolId);
183
- return data?.data || data || [];
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
- return data?.data || data || [];
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 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loka-sms/core-integration-be",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Internal NestJS client for service-to-service access to Loka SMS Core APIs",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",