UNPKG

@memberjunction/actions-bizapps-lms

Version:

LMS system integration actions for MemberJunction

236 lines 11.2 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { RegisterClass } from '@memberjunction/global'; import { LearnWorldsBaseAction } from '../learnworlds-base.action.js'; import { BaseAction } from '@memberjunction/actions'; import { CreateUserAction } from './create-user.action.js'; import { EnrollUserAction } from './enroll-user.action.js'; import { SSOLoginAction } from './sso-login.action.js'; /** * Action that orchestrates full learner onboarding: * create user, enroll in courses/bundles, and generate SSO URL. */ let OnboardLearnerAction = class OnboardLearnerAction extends LearnWorldsBaseAction { /** * Orchestrates the full onboarding flow for a new or existing learner: * 1. Find or create user * 2. Enroll in courses and bundles * 3. Generate SSO login URL */ async OnboardLearner(params, contextUser) { this.SetCompanyContext(params.CompanyID); this.validateEmail(params.Email); if (params.Role) { this.validateRole(params.Role); } if (params.CourseIDs) { params.CourseIDs.forEach((id) => this.validatePathSegment(id, 'CourseID')); } if (params.BundleIDs) { params.BundleIDs.forEach((id) => this.validatePathSegment(id, 'BundleID')); } if (params.RedirectTo) { this.validateRedirectTo(params.RedirectTo); } // Step 1: Find or create the user const { userId, isNewUser, loginUrlFallback } = await this.resolveUser(params, contextUser); // Step 2: Enroll in courses and bundles const { enrollments, errors } = await this.enrollInProducts(params, userId, contextUser); // Step 3: Generate SSO login URL const loginURL = await this.generateLoginUrl(params, userId, loginUrlFallback, contextUser); const hasEnrollmentErrors = errors.length > 0; const allEnrollmentsFailed = enrollments.length > 0 && enrollments.every((e) => !e.success); return { Success: !allEnrollmentsFailed, LoginURL: loginURL, LearnWorldsUserId: userId, IsNewUser: isNewUser, Enrollments: enrollments, Errors: hasEnrollmentErrors ? errors : [], }; } /** * Finds an existing user by email or creates a new one. * Returns the user ID, whether they are new, and a fallback login URL from creation. */ async resolveUser(params, contextUser) { const existingUser = await this.FindUserByEmail(params.Email, contextUser); if (existingUser) { return { userId: existingUser.id, isNewUser: false }; } return this.createNewUser(params, contextUser); } /** * Creates a new user via the CreateUserAction typed public method. * Returns the user's ID and login URL. */ async createNewUser(params, contextUser) { const createAction = new CreateUserAction(); const createResult = await createAction.CreateUser({ CompanyID: params.CompanyID, Email: params.Email, FirstName: params.FirstName, LastName: params.LastName, Role: params.Role || 'student', Tags: params.Tags, CustomFields: params.CustomFields, SendWelcomeEmail: params.SendWelcomeEmail ?? false, }, contextUser); if (!createResult.UserDetails.id) { throw new Error('User creation succeeded but no user ID was returned'); } return { userId: createResult.UserDetails.id, isNewUser: true, loginUrlFallback: createResult.UserDetails.loginUrl, }; } /** * Enrolls the user in all specified courses and bundles in parallel, * collecting results and errors. */ async enrollInProducts(params, userId, contextUser) { const courseIDs = params.CourseIDs || []; const bundleIDs = params.BundleIDs || []; // Fire enrollment requests with controlled concurrency const allProducts = [ ...courseIDs.map((id) => ({ id, type: 'course' })), ...bundleIDs.map((id) => ({ id, type: 'bundle' })), ]; const enrollments = await this.processInBatches(allProducts, (product) => this.enrollInSingleProduct(params.CompanyID, userId, product.id, product.type, contextUser)); const errors = enrollments.filter((e) => e.error).map((e) => e.error); return { enrollments, errors }; } /** * Enrolls a user in a single course or bundle, catching errors for partial success. */ async enrollInSingleProduct(companyId, userId, productId, productType, contextUser) { try { const enrollAction = new EnrollUserAction(); const enrollResult = await enrollAction.EnrollUser({ CompanyID: companyId, UserID: userId, CourseID: productId, ProductType: productType, Price: 0, Justification: 'Onboarding enrollment', NotifyUser: false, }, contextUser); return { productId, productType, success: true, enrollmentId: enrollResult.EnrollmentDetails.id, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { productId, productType, success: false, error: `Enrollment failed for ${productType} ${productId}: ${errorMessage}`, }; } } /** * Generates an SSO login URL, falling back to the user creation login URL on failure. */ async generateLoginUrl(params, userId, loginUrlFallback, contextUser) { try { const ssoAction = new SSOLoginAction(); ssoAction.SetCompanyContext(params.CompanyID); const ssoResult = await ssoAction.GenerateSSOUrl({ CompanyID: params.CompanyID, Email: params.Email, RedirectTo: params.RedirectTo, }, contextUser); return ssoResult.LoginURL; } catch (error) { console.warn(`SSO URL generation failed for userId ${userId}, using fallback:`, error instanceof Error ? error.message : error); return loginUrlFallback; } } /** * Framework entry point that delegates to the typed public method */ async InternalRunAction(params) { const { Params, ContextUser } = params; this.params = Params; try { const onboardParams = { CompanyID: this.getRequiredStringParam(Params, 'CompanyID'), Email: this.getRequiredStringParam(Params, 'Email'), FirstName: this.getOptionalStringParam(Params, 'FirstName'), LastName: this.getOptionalStringParam(Params, 'LastName'), Role: this.getOptionalStringParam(Params, 'Role'), Tags: this.getOptionalStringArrayParam(Params, 'Tags'), CustomFields: this.getParamValue(Params, 'CustomFields'), CourseIDs: this.getOptionalStringArrayParam(Params, 'CourseIDs'), BundleIDs: this.getOptionalStringArrayParam(Params, 'BundleIDs'), RedirectTo: this.getOptionalStringParam(Params, 'RedirectTo'), SendWelcomeEmail: this.getOptionalBooleanParam(Params, 'SendWelcomeEmail', false), }; if (!onboardParams.Email) { return this.buildErrorResult('VALIDATION_ERROR', 'Email is required', Params); } const result = await this.OnboardLearner(onboardParams, ContextUser); this.setOutputParam(Params, 'LoginURL', result.LoginURL); this.setOutputParam(Params, 'LearnWorldsUserId', result.LearnWorldsUserId); this.setOutputParam(Params, 'IsNewUser', result.IsNewUser); this.setOutputParam(Params, 'Enrollments', result.Enrollments); this.setOutputParam(Params, 'Errors', result.Errors); const successCount = result.Enrollments.filter((e) => e.success).length; const totalCount = result.Enrollments.length; const userStatus = result.IsNewUser ? 'new user created' : 'existing user found'; if (result.Success) { return this.buildSuccessResult(`Onboarding complete (${userStatus}): ${successCount}/${totalCount} enrollment(s) succeeded`, Params); } return this.buildErrorResult('PARTIAL_FAILURE', `Onboarding partially failed (${userStatus}): ${successCount}/${totalCount} enrollment(s) succeeded. Errors: ${result.Errors.join('; ')}`, Params); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return this.buildErrorResult('ERROR', `Error during onboarding: ${errorMessage}`, Params); } } /** * Define the parameters this action expects */ get Params() { const baseParams = this.getCommonLMSParams(); const specificParams = [ { Name: 'Email', Type: 'Input', Value: null }, { Name: 'FirstName', Type: 'Input', Value: null }, { Name: 'LastName', Type: 'Input', Value: null }, { Name: 'Role', Type: 'Input', Value: 'student' }, { Name: 'Tags', Type: 'Input', Value: null }, { Name: 'CustomFields', Type: 'Input', Value: null }, { Name: 'CourseIDs', Type: 'Input', Value: null }, { Name: 'BundleIDs', Type: 'Input', Value: null }, { Name: 'RedirectTo', Type: 'Input', Value: null }, { Name: 'SendWelcomeEmail', Type: 'Input', Value: false }, { Name: 'LoginURL', Type: 'Output', Value: null }, { Name: 'LearnWorldsUserId', Type: 'Output', Value: null }, { Name: 'IsNewUser', Type: 'Output', Value: null }, { Name: 'Enrollments', Type: 'Output', Value: null }, { Name: 'Errors', Type: 'Output', Value: null }, ]; return [...baseParams, ...specificParams]; } /** * Metadata about this action */ get Description() { return 'Orchestrates full learner onboarding: create user, enroll in courses/bundles, and generate SSO URL'; } }; OnboardLearnerAction = __decorate([ RegisterClass(BaseAction, 'OnboardLearnerAction') ], OnboardLearnerAction); export { OnboardLearnerAction }; //# sourceMappingURL=onboard-learner.action.js.map