UNPKG

@memberjunction/actions-bizapps-lms

Version:

LMS system integration actions for MemberJunction

204 lines 8.69 kB
import { BaseLMSAction } from '../../base/base-lms.action.js'; import { UserInfo } from '@memberjunction/core'; import { ActionParam } from '@memberjunction/actions-base'; import { LWApiEnrollmentStatus, LWApiProgressData, LearnWorldsUser } from './interfaces/index.js'; /** * Base class for all LearnWorlds LMS actions. * Handles LearnWorlds-specific authentication and API interaction patterns. */ export declare abstract class LearnWorldsBaseAction extends BaseLMSAction { protected lmsProvider: string; protected integrationName: string; /** * LearnWorlds API version */ protected apiVersion: string; /** * Concurrency limit for parallel API calls to avoid overwhelming the API. */ protected static readonly CONCURRENCY_LIMIT = 5; /** * Allowed user roles in LearnWorlds. */ protected static readonly ALLOWED_ROLES: readonly string[]; /** * Maximum number of items per page supported by the LearnWorlds API */ protected static readonly LW_MAX_PAGE_SIZE = 100; private static readonly MAX_RETRIES; private static readonly BASE_DELAY_MS; private static readonly MAX_DELAY_MS; private static readonly RATE_LIMIT_WINDOW_MS; private static readonly RATE_LIMIT_MAX_REQUESTS; private static readonly INTER_BATCH_DELAY_MS; /** * Safety limit for pagination to prevent infinite loops if the API misbehaves. */ protected static readonly MAX_PAGES = 100; /** * Sliding-window timestamps shared across all instances so concurrent * actions against the same LearnWorlds school stay within the limit. */ private static requestTimestamps; /** * Reset the global rate-limiter state. Intended for test teardown only. */ static ResetRateLimiter(): void; /** * Current action parameters (set by the framework or by SetCompanyContext) */ protected params: ActionParam[]; /** * Tracks the number of actual HTTP requests made since last reset. * Useful for callers that need accurate API call counts (e.g., bulk data). */ protected apiCallCount: number; /** * Public accessor for the running API call count. */ get ApiCallCount(): number; /** * Set the company context for direct (non-framework) calls. * This populates `this.params` so that `makeLearnWorldsRequest` can find the CompanyID. */ SetCompanyContext(companyId: string): void; /** * Makes an authenticated request to LearnWorlds API. * The body parameter accepts any object that will be JSON-serialized. */ protected makeLearnWorldsRequest<T = Record<string, unknown>>(endpoint: string, method?: 'GET' | 'POST' | 'PUT' | 'DELETE', body?: object | null, contextUser?: UserInfo): Promise<T>; /** * Resolves integration credentials and builds the base URL and headers. */ private buildRequestConfig; /** * Makes an authenticated request to a non-versioned LearnWorlds API endpoint (e.g. /admin/api/sso). */ protected makeLearnWorldsNonVersionedRequest<T = Record<string, unknown>>(endpoint: string, method?: 'GET' | 'POST' | 'PUT' | 'DELETE', body?: object | null, contextUser?: UserInfo): Promise<T>; /** * Parses error details from a non-OK API response. */ private buildErrorMessage; /** * Makes a paginated request to LearnWorlds API */ protected makeLearnWorldsPaginatedRequest<T = Record<string, unknown>>(endpoint: string, queryParams?: Record<string, string | number | boolean>, contextUser?: UserInfo, maxResults?: number): Promise<T[]>; /** * Convert LearnWorlds date format to Date object */ protected parseLearnWorldsDate(dateString: string | number): Date; /** * Format date for LearnWorlds API (ISO 8601) */ protected formatLearnWorldsDate(date: Date): string; /** * Map LearnWorlds user status to standard status */ protected mapUserStatus(status: string): 'active' | 'inactive' | 'suspended'; /** * Map LearnWorlds enrollment status */ protected mapLearnWorldsEnrollmentStatus(enrollment: LWApiEnrollmentStatus): 'active' | 'completed' | 'expired' | 'suspended'; /** * Calculate progress from LearnWorlds data */ protected calculateProgress(progressData: LWApiProgressData): { percentage: number; completedUnits: number; totalUnits: number; timeSpent: number; }; /** * Safely parses a date string to ISO format. * Returns undefined if the input is falsy or produces an invalid date. */ protected safeParseDateToISO(dateString: string | undefined): string | undefined; /** * Validates that a value is safe to use as a URL path segment. * Rejects values containing path traversal or URL manipulation characters. */ protected validatePathSegment(value: string, paramName: string): string; /** * Validates that a school domain is safe to use in URL construction. * Must look like a hostname (alphanumeric + hyphens + dots). */ private validateSchoolDomain; /** * Validates that a role is in the allowed set. */ protected validateRole(role: string): string; /** * Validates that a string is a plausible email address. */ protected validateEmail(email: string, paramName?: string): string; /** * Validates that a redirect URL is either a relative path or an absolute URL * that belongs to the LearnWorlds school domain and uses http(s). */ protected validateRedirectTo(redirectTo: string, schoolDomain?: string): string; /** * Returns a promise that resolves after `ms` milliseconds. */ private waitForRetryDelay; /** * Pure calculation — determines how long to wait before the next retry. * Prefers the Retry-After header (seconds → ms, capped); falls back to * exponential backoff with random jitter. */ private calculateRetryDelay; /** * Proactive throttle: if the sliding window is at capacity, sleep until * the oldest request falls outside the window. */ private waitForRateLimitCapacity; /** * Stamps the current time into the sliding window. */ private recordRequest; /** * Wraps `fetch` with 429-aware retry + proactive rate-limit throttling. * Non-429 responses are returned immediately without retry. * If all retries are exhausted the final 429 response is returned (not thrown). */ private sendRequestWithRetry; /** * Processes items in batches with controlled concurrency. * Prevents unbounded parallel API calls from overwhelming the target API. */ protected processInBatches<TItem, TResult>(items: TItem[], processFn: (item: TItem) => Promise<TResult>, batchSize?: number): Promise<TResult[]>; /** * Gets a required string parameter, throwing if missing or empty. */ protected getRequiredStringParam(params: ActionParam[], name: string): string; /** * Gets an optional string parameter. */ protected getOptionalStringParam(params: ActionParam[], name: string): string | undefined; /** * Gets an optional boolean parameter with a default value. * When defaultValue is undefined, returns undefined if the parameter is missing. */ protected getOptionalBooleanParam(params: ActionParam[], name: string, defaultValue: boolean): boolean; protected getOptionalBooleanParam(params: ActionParam[], name: string, defaultValue: boolean | undefined): boolean | undefined; /** * Gets an optional number parameter with a default value. * When defaultValue is undefined, returns undefined if the parameter is missing. */ protected getOptionalNumberParam(params: ActionParam[], name: string, defaultValue: number): number; protected getOptionalNumberParam(params: ActionParam[], name: string, defaultValue: number | undefined): number | undefined; /** * Gets an optional string array parameter. */ protected getOptionalStringArrayParam(params: ActionParam[], name: string): string[] | undefined; /** * Shared utility: find a LearnWorlds user by email. * Returns the user if found, null if not found (empty results). * Re-throws errors for network failures, auth errors, rate limiting, etc. */ FindUserByEmail(email: string, contextUser: UserInfo): Promise<LearnWorldsUser | null>; /** * Maps a raw LW API user to the normalized LearnWorldsUser shape. */ private mapLWApiUserToLearnWorldsUser; } //# sourceMappingURL=learnworlds-base.action.d.ts.map