UNPKG

@memberjunction/actions-bizapps-lms

Version:

LMS system integration actions for MemberJunction

246 lines 9.09 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; }; var BaseLMSAction_1; import { BaseAction } from '@memberjunction/actions'; import { RegisterClass, UUIDsEqual } from '@memberjunction/global'; import { RunView } from '@memberjunction/core'; /** * Base class for all LMS-related actions. * Provides common functionality and patterns for interacting with Learning Management Systems. */ let BaseLMSAction = class BaseLMSAction extends BaseAction { constructor() { super(...arguments); /** * Cached company integration for the current execution */ this._companyIntegration = null; } static { BaseLMSAction_1 = this; } /** * Common LMS parameters that many actions will need */ getCommonLMSParams() { return [ { Name: 'CompanyID', Type: 'Input', Value: null, }, ]; } /** * Validates that a string is a valid UUID format to prevent SQL injection. */ static { this.uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; } /** * Validates that a string is a valid UUID format to prevent SQL injection. * Exposed as a reusable helper for subclasses to validate CompanyID, UserID, etc. */ validateUUID(value, paramName) { if (!BaseLMSAction_1.uuidRegex.test(value)) { throw new Error(`${paramName} must be a valid UUID`); } return value; } /** * Validates that an integration name contains only safe characters (alphanumeric, spaces, hyphens, underscores). */ validateIntegrationName(name) { if (!/^[a-zA-Z0-9 _-]+$/.test(name)) { throw new Error('Invalid integration name: contains forbidden characters'); } return name; } /** * Gets the company integration record for the specified company and LMS */ async getCompanyIntegration(companyId, contextUser) { this.validateUUID(companyId, 'CompanyID'); // Validate integration name before SQL interpolation const safeIntegrationName = this.validateIntegrationName(this.integrationName); // Check cache first if (this._companyIntegration && UUIDsEqual(this._companyIntegration.CompanyID, companyId)) { return this._companyIntegration; } const rv = new RunView(); const result = await rv.RunView({ EntityName: 'MJ: Company Integrations', ExtraFilter: `CompanyID = '${companyId}' AND Integration = '${safeIntegrationName}'`, ResultType: 'entity_object', }, contextUser); if (!result.Success) { throw new Error(`Failed to retrieve company integration: ${result.ErrorMessage}`); } if (!result.Results || result.Results.length === 0) { throw new Error(`No ${this.integrationName} integration found for company ${companyId}. Please configure the integration first.`); } this._companyIntegration = result.Results[0]; return this._companyIntegration; } /** * Gets credentials from environment variables * Format: BIZAPPS_{PROVIDER}_{COMPANY_ID}_{CREDENTIAL_TYPE} * Example: BIZAPPS_LEARNWORLDS_12345_API_KEY */ getCredentialFromEnv(companyId, credentialType) { const provider = this.lmsProvider.toUpperCase().replace(/\s+/g, '_'); const credential = credentialType.toUpperCase(); const envKey = `BIZAPPS_${provider}_${companyId}_${credential}`; const value = process.env[envKey]; if (value !== undefined) return value; // Fallback: try lowercase companyId (SQL Server returns uppercase UUIDs) const lowerKey = `BIZAPPS_${provider}_${companyId.toLowerCase()}_${credential}`; return process.env[lowerKey]; } /** * Gets API credentials - first tries environment variables, then falls back to database */ async getAPICredentials(integration) { const companyId = integration.CompanyID; // Try environment variables first const envApiKey = this.getCredentialFromEnv(companyId, 'API_KEY'); const envApiSecret = this.getCredentialFromEnv(companyId, 'API_SECRET'); const envAccessToken = this.getCredentialFromEnv(companyId, 'ACCESS_TOKEN'); if (envApiKey || envAccessToken) { return { apiKey: envApiKey, apiSecret: envApiSecret, accessToken: envAccessToken, }; } // Fall back to database (for backwards compatibility) if (!integration.APIKey && !integration.AccessToken) { throw new Error(`No API credentials found for ${this.integrationName} integration. Please set environment variables or configure in database.`); } return { apiKey: integration.APIKey || undefined, accessToken: integration.AccessToken || undefined, }; } /** * Gets the base URL for API calls */ async getAPIBaseURL(integration) { // Check if custom URL is stored in the integration if (integration.CustomAttribute1) { return integration.CustomAttribute1; } // Return empty string - derived classes should override this return ''; } /** * Helper to get parameter value from an ActionParam array */ getParamValue(params, paramName) { const param = params.find((p) => p.Name === paramName); return param?.Value; } /** * Standard date format for LMS systems (ISO 8601) */ formatLMSDate(date) { return date.toISOString(); } /** * Parse date from LMS format */ parseLMSDate(dateString) { return new Date(dateString); } /** * Calculate progress percentage */ calculateProgressPercentage(completed, total) { if (total === 0) return 0; return Math.round((completed / total) * 100); } /** * Format duration in seconds to human readable format */ formatDuration(seconds) { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = seconds % 60; if (hours > 0) { return `${hours}h ${minutes}m ${secs}s`; } else if (minutes > 0) { return `${minutes}m ${secs}s`; } else { return `${secs}s`; } } /** * Helper to build consistent error messages for LMS operations */ buildLMSErrorMessage(operation, details, systemError) { let message = `LMS operation failed: ${operation}. ${details}`; if (systemError) { const errorMsg = typeof systemError === 'string' ? systemError : systemError.message || String(systemError); message += ` System error: ${errorMsg}`; } return message; } /** * Common enrollment status mapping */ mapEnrollmentStatus(status) { const statusMap = { active: 'active', completed: 'completed', finished: 'completed', expired: 'expired', suspended: 'suspended', paused: 'suspended', inactive: 'suspended', }; return statusMap[status.toLowerCase()] || 'unknown'; } /** * Helper to set an output parameter, creating it if it doesn't exist */ setOutputParam(params, name, value) { const existing = params.find((p) => p.Name === name); if (existing) { existing.Value = value; } else { params.push({ Name: name, Type: 'Output', Value: value }); } } /** * Build a standard success ActionResultSimple */ buildSuccessResult(message, params) { return { Success: true, ResultCode: 'SUCCESS', Message: message, ...(params ? { Params: params } : {}), }; } /** * Build a standard error ActionResultSimple */ buildErrorResult(code, message, params) { return { Success: false, ResultCode: code, Message: message, ...(params ? { Params: params } : {}), }; } }; BaseLMSAction = BaseLMSAction_1 = __decorate([ RegisterClass(BaseAction, 'BaseLMSAction') ], BaseLMSAction); export { BaseLMSAction }; //# sourceMappingURL=base-lms.action.js.map