Skip to content

Commit 8e0d470

Browse files
Merge pull request #725 from Tusharmahajan12/new_aspmar25
Pass courseId in array for shortlisting users
2 parents 1710b2a + c59903b commit 8e0d470

2 files changed

Lines changed: 104 additions & 105 deletions

File tree

src/adapters/postgres/cohortMembers-adapter.ts

Lines changed: 62 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6162,70 +6162,38 @@ export class PostgresCohortMembersService {
61626162
}
61636163

61646164
/**
6165-
* Enrolls a user to multiple courses
6166-
* Uses for...of loop for proper async handling
6165+
* Enrolls a user to multiple courses in bulk
6166+
* Makes API call to LMS service to create enrollments
61676167
*
61686168
* @param userId - The user ID to enroll
61696169
* @param courses - Array of courses to enroll the user in
6170-
* @returns Promise with enrollment results for each course
6170+
* @returns Promise with enrollment results
61716171
*/
61726172
private async enrollUserToCourses(
61736173
userId: string,
61746174
courses: any[],
61756175
cohortId: string
6176-
) {
6177-
const enrollmentResults = [];
6178-
6179-
// Use for...of loop for proper async handling
6180-
for (let i = 0; i < courses.length; i++) {
6181-
const course = courses[i];
6182-
6183-
try {
6184-
const enrollmentResult = await this.enrollUserToSingleCourse(
6185-
userId,
6186-
course.courseId,
6187-
cohortId
6188-
);
6189-
6190-
enrollmentResults.push({
6191-
courseId: course.courseId,
6192-
status: 'success',
6193-
result: enrollmentResult,
6194-
});
6195-
} catch (error) {
6196-
enrollmentResults.push({
6197-
courseId: course.courseId,
6198-
status: 'failed',
6199-
error: error.message,
6200-
});
6201-
}
6202-
}
6203-
6204-
return enrollmentResults;
6205-
}
6206-
6207-
/**
6208-
* Enrolls a user to a single course
6209-
* Makes API call to LMS service to create enrollment
6210-
*
6211-
* @param userId - The user ID to enroll
6212-
* @param courseId - The course ID to enroll in
6213-
* @returns Promise with enrollment result
6214-
*/
6215-
private async enrollUserToSingleCourse(
6216-
userId: string,
6217-
courseId: string,
6218-
cohortId: string
6219-
) {
6176+
): Promise<
6177+
Array<{
6178+
courseId: string;
6179+
status: 'success' | 'failed';
6180+
result?: any;
6181+
error?: any;
6182+
}>
6183+
> {
62206184
const lmsBaseUrl = process.env.LMS_SERVICE_URL;
62216185
const tenantId = process.env.DEFAULT_TENANT_ID;
62226186
const organisationId = process.env.DEFAULT_ORGANISATION_ID;
62236187

6188+
const courseIds = courses
6189+
.map((c) => c.courseId || c.id || c.course_id)
6190+
.filter((id) => !!id);
6191+
62246192
try {
62256193
const requestUrl = `${lmsBaseUrl}/lms-service/v1/enrollments`;
62266194

62276195
const requestBody = {
6228-
courseId: courseId,
6196+
courseId: courseIds,
62296197
learnerId: userId,
62306198
status: 'published',
62316199
};
@@ -6242,28 +6210,62 @@ export class PostgresCohortMembersService {
62426210
},
62436211
});
62446212

6245-
// Log successful enrollment
6213+
// Log successful enrollment for all courses in bulk
62466214
ShortlistingLogger.logLMSEnrollmentSuccess({
62476215
dateTime: new Date().toISOString(),
62486216
userId: userId,
62496217
cohortId: cohortId,
6250-
courseId: courseId,
6251-
enrollmentId: response.data?.enrollmentId || response.data?.id,
6218+
courseId: courseIds.join(','),
6219+
enrollmentId: 'bulk', // response.data is an array now
62526220
});
62536221

6254-
return response.data;
6222+
// Map to the expected return format extracting individual course results
6223+
const successfullyEnrolled = response.data?.successfullyEnrolled || [];
6224+
const alreadyEnrolledCourseIds = response.data?.alreadyEnrolledCourseIds || [];
6225+
const failedCourseIds = response.data?.failedCourseIds || [];
6226+
6227+
return courseIds.map((id) => {
6228+
if (failedCourseIds.includes(id)) {
6229+
return {
6230+
courseId: id,
6231+
status: 'failed',
6232+
error: 'LMS API reported failure for this course',
6233+
};
6234+
}
6235+
if (alreadyEnrolledCourseIds.includes(id)) {
6236+
// Previously, a 409 threw an error and was recorded as 'failed' in the loop
6237+
return {
6238+
courseId: id,
6239+
status: 'failed',
6240+
error: 'User already enrolled (409 Conflict)',
6241+
};
6242+
}
6243+
6244+
const successMatch = successfullyEnrolled.find(
6245+
(e: any) => e.courseId === id
6246+
);
6247+
return {
6248+
courseId: id,
6249+
status: 'success',
6250+
result: successMatch || { status: 'PUBLISHED' },
6251+
};
6252+
});
62556253
} catch (error) {
62566254
// Log failed enrollment
62576255
ShortlistingLogger.logLMSEnrollmentFailure({
62586256
dateTime: new Date().toISOString(),
62596257
userId: userId,
62606258
cohortId: cohortId,
6261-
courseId: courseId,
6259+
courseId: courseIds.join(','),
62626260
failureReason: error.message,
62636261
errorCode: error.response?.status?.toString() || 'UNKNOWN',
62646262
});
62656263

6266-
throw error;
6264+
return courseIds.map(id => ({
6265+
courseId: id,
6266+
status: 'failed',
6267+
error: error.message,
6268+
}));
62676269
}
62686270
}
62696271

@@ -6351,22 +6353,25 @@ export class PostgresCohortMembersService {
63516353
// Use for...of loop for proper async handling
63526354
for (let i = 0; i < courses.length; i++) {
63536355
const course = courses[i];
6356+
const courseId = course.courseId || course.id || course.course_id;
6357+
6358+
if (!courseId) continue;
63546359

63556360
try {
63566361
const deenrollmentResult = await this.deenrollUserFromSingleCourse(
63576362
userId,
6358-
course.courseId,
6363+
courseId,
63596364
cohortId
63606365
);
63616366

63626367
deenrollmentResults.push({
6363-
courseId: course.courseId,
6368+
courseId: courseId,
63646369
status: 'success',
63656370
result: deenrollmentResult,
63666371
});
63676372
} catch (error) {
63686373
deenrollmentResults.push({
6369-
courseId: course.courseId,
6374+
courseId: courseId,
63706375
status: 'error',
63716376
error: error.message,
63726377
});

src/pathways/common/services/lms-client.service.ts

Lines changed: 42 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -485,60 +485,54 @@ export class LmsClientService {
485485
'Content-Type': 'application/json',
486486
};
487487

488-
const failedCourseIds: string[] = [];
489-
let alreadyEnrolledCount = 0;
490-
491-
const runChunk = async (chunk: string[]) => {
492-
const results = await Promise.all(
493-
chunk.map(async (courseId) => {
494-
try {
495-
const res = await axios.post(
496-
enrollUrl,
497-
{ learnerId: userId, courseId, status: 'published' },
498-
{ headers, params: { userId }, timeout: 15000, validateStatus: () => true }
499-
);
500-
if (res.status >= 200 && res.status < 300) return { courseId, ok: true, alreadyEnrolled: false };
501-
// 409 Conflict = already enrolled; treat as success so assignment can proceed
502-
if (res.status === 409) {
503-
this.logger.debug(
504-
`User ${userId} already enrolled in course ${courseId} (409); treating as success`
505-
);
506-
return { courseId, ok: true, alreadyEnrolled: true };
507-
}
508-
failedCourseIds.push(courseId);
509-
this.logger.warn(
510-
`LMS enrollment failed for user ${userId} course ${courseId}: status ${res.status}`
511-
);
512-
return { courseId, ok: false, alreadyEnrolled: false };
513-
} catch (err) {
514-
failedCourseIds.push(courseId);
515-
const msg = err instanceof Error ? err.message : 'Unknown error';
516-
this.logger.warn(
517-
`LMS enrollment error for user ${userId} course ${courseId}: ${msg}`
518-
);
519-
return { courseId, ok: false, alreadyEnrolled: false };
520-
}
521-
})
488+
try {
489+
this.logger.debug(
490+
`Enrolling user ${userId} to ${courseIds.length} courses in bulk`
491+
);
492+
const res = await axios.post(
493+
enrollUrl,
494+
{ learnerId: userId, courseId: courseIds, status: 'published' },
495+
{
496+
headers,
497+
params: { userId },
498+
timeout: 30000,
499+
validateStatus: () => true,
500+
}
522501
);
523-
return results;
524-
};
525502

526-
const concurrency = LmsClientService.ENROLL_CONCURRENCY;
527-
for (let i = 0; i < courseIds.length; i += concurrency) {
528-
const chunk = courseIds.slice(i, i + concurrency);
529-
const chunkResults = await runChunk(chunk);
530-
alreadyEnrolledCount += chunkResults.filter((r) => r.alreadyEnrolled).length;
531-
}
503+
if (res.status >= 200 && res.status < 300) {
504+
const alreadyEnrolledCount =
505+
res.data?.alreadyEnrolledCourseIds?.length || 0;
506+
return { success: true, alreadyEnrolledCount };
507+
}
508+
509+
// 409 Conflict = already enrolled; treat as success so assignment can proceed
510+
if (res.status === 409) {
511+
this.logger.debug(
512+
`User ${userId} already enrolled in courses (409); treating as success`
513+
);
514+
const alreadyEnrolledCount = courseIds.length;
515+
return { success: true, alreadyEnrolledCount };
516+
}
532517

533-
if (failedCourseIds.length > 0) {
518+
this.logger.warn(
519+
`LMS bulk enrollment failed for user ${userId}: status ${res.status}`
520+
);
521+
return {
522+
success: false,
523+
failedCourseIds: courseIds,
524+
message: `LMS bulk enrollment failed with status ${res.status}`,
525+
};
526+
} catch (err) {
527+
const msg = err instanceof Error ? err.message : 'Unknown error';
528+
this.logger.error(
529+
`LMS bulk enrollment error for user ${userId}: ${msg}`
530+
);
534531
return {
535532
success: false,
536-
failedCourseIds,
537-
message: `Enrollment failed for ${failedCourseIds.length} course(s)`,
533+
failedCourseIds: courseIds,
534+
message: `LMS bulk enrollment error: ${msg}`,
538535
};
539536
}
540-
return alreadyEnrolledCount > 0
541-
? { success: true, alreadyEnrolledCount }
542-
: { success: true };
543537
}
544538
}

0 commit comments

Comments
 (0)