@memberjunction/actions-bizapps-lms
Version:
LMS system integration actions for MemberJunction
313 lines (280 loc) • 9.77 kB
text/typescript
import { RegisterClass } from '@memberjunction/global';
import { LearnWorldsBaseAction } from '../learnworlds-base.action';
import { ActionParam, ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
import { BaseAction } from '@memberjunction/actions';
import { UserInfo } from '@memberjunction/core';
import { CreateUserParams, CreateUserResult, CreateUserDetails, CreateUserEnrollmentResult, CreateUserSummary, LWApiUser } from '../interfaces';
/**
* Shape returned by the LearnWorlds POST /users endpoint
*/
interface LWCreateUserResponse {
id: string;
email: string;
username?: string;
first_name?: string;
last_name?: string;
role?: string;
is_active?: boolean;
tags?: string[];
custom_fields?: Record<string, unknown>;
created_at?: string;
login_url?: string;
reset_password_url?: string;
}
/**
* Shape returned by the LearnWorlds POST /courses/{id}/enrollments endpoint
*/
interface LWEnrollmentResponse {
id?: string;
}
/**
* Action to create a new user in LearnWorlds
*/
export class CreateUserAction extends LearnWorldsBaseAction {
/**
* Typed public method for direct (non-framework) callers.
* Throws on failure instead of returning ActionResultSimple.
*/
public async CreateUser(params: CreateUserParams, contextUser: UserInfo): Promise<CreateUserResult> {
this.SetCompanyContext(params.CompanyID);
const email = params.Email;
if (!email) {
throw new Error('Email is required');
}
this.validateEmail(email);
const role = this.validateRole(params.Role || 'student');
const isActive = params.IsActive !== false;
const sendWelcomeEmail = params.SendWelcomeEmail !== false;
// Prepare user data
const userData: Record<string, unknown> = {
email: email,
role: role,
is_active: isActive,
};
// Add optional fields
userData.username = params.Username || email;
if (params.Password) userData.password = params.Password;
if (params.FirstName) userData.first_name = params.FirstName;
if (params.LastName) userData.last_name = params.LastName;
userData.send_welcome_email = sendWelcomeEmail;
// Add tags if provided (expecting comma-separated string or array)
if (params.Tags) {
userData.tags = Array.isArray(params.Tags) ? params.Tags : params.Tags.split(',').map((t: string) => t.trim());
}
// Add custom fields if provided
if (params.CustomFields) {
userData.custom_fields = params.CustomFields;
}
// Create user
const newUser = await this.makeLearnWorldsRequest<LWCreateUserResponse>('users', 'POST', userData, contextUser);
if (!newUser.id) {
throw new Error('User creation response missing expected user ID');
}
// Format user details
const userDetails = this.buildUserDetails(newUser);
// Enroll in courses if requested
const enrollmentResults = await this.enrollInCourses(newUser.id, params.EnrollInCourses, contextUser);
// Create summary
const summary = this.buildSummary(userDetails, sendWelcomeEmail, enrollmentResults);
return {
UserDetails: userDetails,
EnrollmentResults: enrollmentResults,
Summary: summary,
};
}
/**
* Framework entry point -- delegates to the typed public method.
*/
protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
const { Params, ContextUser } = params;
this.params = Params;
try {
const typedParams = this.extractCreateUserParams(Params);
const result = await this.CreateUser(typedParams, ContextUser);
this.setOutputParam(Params, 'UserDetails', result.UserDetails);
this.setOutputParam(Params, 'EnrollmentResults', result.EnrollmentResults);
this.setOutputParam(Params, 'Summary', result.Summary);
return this.buildSuccessResult(`Successfully created user ${result.UserDetails.email}`, Params);
} catch (error) {
const msg = error instanceof Error ? error.message : 'Unknown error';
return this.buildErrorResult('ERROR', `Error creating user: ${msg}`, Params);
}
}
/**
* Extract typed params from the generic ActionParam array
*/
private extractCreateUserParams(params: ActionParam[]): CreateUserParams {
return {
CompanyID: this.getRequiredStringParam(params, 'CompanyID'),
Email: this.getRequiredStringParam(params, 'Email'),
Username: this.getOptionalStringParam(params, 'Username'),
Password: this.getOptionalStringParam(params, 'Password'),
FirstName: this.getOptionalStringParam(params, 'FirstName'),
LastName: this.getOptionalStringParam(params, 'LastName'),
Role: this.getOptionalStringParam(params, 'Role') || 'student',
IsActive: this.getOptionalBooleanParam(params, 'IsActive', undefined),
SendWelcomeEmail: this.getOptionalBooleanParam(params, 'SendWelcomeEmail', undefined),
Tags: this.getParamValue(params, 'Tags') as string | string[] | undefined,
CustomFields: this.getParamValue(params, 'CustomFields') as Record<string, unknown> | undefined,
EnrollInCourses: this.getParamValue(params, 'EnrollInCourses') as string | string[] | undefined,
};
}
/**
* Map the raw LW create-user response to our typed details shape.
*/
private buildUserDetails(newUser: LWCreateUserResponse): CreateUserDetails {
return {
id: newUser.id,
email: newUser.email,
username: newUser.username,
firstName: newUser.first_name,
lastName: newUser.last_name,
fullName: `${newUser.first_name || ''} ${newUser.last_name || ''}`.trim(),
role: newUser.role || 'student',
status: newUser.is_active ? 'active' : 'inactive',
tags: newUser.tags || [],
customFields: newUser.custom_fields || {},
createdAt: newUser.created_at,
loginUrl: newUser.login_url,
resetPasswordUrl: newUser.reset_password_url,
};
}
/**
* Enroll the newly created user in any requested courses.
*/
private async enrollInCourses(userId: string, enrollInCourses: string | string[] | undefined, contextUser: UserInfo): Promise<CreateUserEnrollmentResult[]> {
const enrollmentResults: CreateUserEnrollmentResult[] = [];
if (!enrollInCourses || (Array.isArray(enrollInCourses) && enrollInCourses.length === 0)) {
return enrollmentResults;
}
const courseIds = Array.isArray(enrollInCourses) ? enrollInCourses : [enrollInCourses];
for (const courseId of courseIds) {
try {
this.validatePathSegment(courseId, 'CourseID');
const enrollBody: Record<string, unknown> = {
productId: courseId,
productType: 'course',
justification: 'Enrolled during user creation',
price: 0,
send_enrollment_email: false,
};
const enrollData = await this.makeLearnWorldsRequest<LWEnrollmentResponse>(`users/${userId}/enrollment`, 'POST', enrollBody, contextUser);
enrollmentResults.push({
courseId: courseId,
success: true,
enrollmentId: enrollData.id,
});
} catch (enrollError) {
enrollmentResults.push({
courseId: courseId,
success: false,
error: enrollError instanceof Error ? enrollError.message : 'Enrollment failed',
});
}
}
return enrollmentResults;
}
/**
* Build the summary object from user details and enrollment results.
*/
private buildSummary(userDetails: CreateUserDetails, sendWelcomeEmail: boolean, enrollmentResults: CreateUserEnrollmentResult[]): CreateUserSummary {
return {
userId: userDetails.id,
email: userDetails.email,
username: userDetails.username,
fullName: userDetails.fullName,
role: userDetails.role,
status: userDetails.status,
welcomeEmailSent: sendWelcomeEmail,
coursesEnrolled: enrollmentResults.filter((r) => r.success).length,
totalCoursesRequested: enrollmentResults.length,
loginUrl: userDetails.loginUrl,
};
}
/**
* Define the parameters this action expects
*/
public get Params(): ActionParam[] {
const baseParams = this.getCommonLMSParams();
const specificParams: ActionParam[] = [
{
Name: 'Email',
Type: 'Input',
Value: null,
},
{
Name: 'Username',
Type: 'Input',
Value: null,
},
{
Name: 'Password',
Type: 'Input',
Value: null,
},
{
Name: 'FirstName',
Type: 'Input',
Value: null,
},
{
Name: 'LastName',
Type: 'Input',
Value: null,
},
{
Name: 'Role',
Type: 'Input',
Value: 'student',
},
{
Name: 'IsActive',
Type: 'Input',
Value: true,
},
{
Name: 'SendWelcomeEmail',
Type: 'Input',
Value: true,
},
{
Name: 'Tags',
Type: 'Input',
Value: null,
},
{
Name: 'CustomFields',
Type: 'Input',
Value: null,
},
{
Name: 'EnrollInCourses',
Type: 'Input',
Value: null,
},
{
Name: 'UserDetails',
Type: 'Output',
Value: null,
},
{
Name: 'EnrollmentResults',
Type: 'Output',
Value: null,
},
{
Name: 'Summary',
Type: 'Output',
Value: null,
},
];
return [...baseParams, ...specificParams];
}
/**
* Metadata about this action
*/
public get Description(): string {
return 'Creates a new user in LearnWorlds with optional course enrollments and welcome email';
}
}