@lumina-study/courses-sdk
Version:
Type-safe TypeScript SDK for the Courses Platform API, auto-generated from OpenAPI specification
516 lines (508 loc) • 15.7 kB
JavaScript
import createClient from 'openapi-fetch';
// src/client/factory.ts
// src/errors/index.ts
var ApiError = class extends Error {
constructor(message, statusCode) {
super(message);
this.name = "ApiError";
this.statusCode = statusCode;
this.isNetworkError = false;
}
};
var ValidationError = class extends ApiError {
constructor(message, fields) {
super(message, 400);
this.name = "ValidationError";
this.fields = fields;
}
};
var AuthenticationError = class extends ApiError {
constructor(message = "Authentication required") {
super(message, 401);
this.name = "AuthenticationError";
}
};
var AuthorizationError = class extends ApiError {
constructor(message = "Insufficient permissions") {
super(message, 403);
this.name = "AuthorizationError";
}
};
var NotFoundError = class extends ApiError {
constructor(resourceType, resourceId) {
super(`${resourceType} with ID ${resourceId} not found`, 404);
this.name = "NotFoundError";
this.resourceType = resourceType;
this.resourceId = resourceId;
}
};
var ServerError = class extends ApiError {
constructor(message, statusCode = 500) {
super(message, statusCode);
this.name = "ServerError";
}
};
var NetworkError = class extends Error {
constructor(message = "Network connection failed") {
super(message);
this.isNetworkError = true;
this.name = "NetworkError";
}
};
// src/client/error-handler.ts
function handleApiError(error, response, resourceType, resourceId) {
if (error instanceof TypeError && error.message.includes("Failed to fetch")) {
throw new NetworkError("Unable to connect to server. Please check your network connection.");
}
const status = response?.status ?? 500;
switch (status) {
case 400:
if (typeof error === "object" && error !== null && "message" in error) {
const errorObj = error;
if (Array.isArray(errorObj.message)) {
const fields = {};
errorObj.message.forEach((msg) => {
const match = msg.match(/^(\w+)\.(.+)$/);
if (match && match[1] && match[2]) {
const field = match[1];
const message = match[2];
if (!fields[field]) fields[field] = [];
fields[field].push(message);
} else {
if (!fields.general) fields.general = [];
fields.general.push(msg);
}
});
throw new ValidationError("Validation failed", fields);
}
}
throw new ValidationError("Invalid request", {});
case 401:
throw new AuthenticationError();
case 403:
throw new AuthorizationError();
case 404:
if (resourceType && resourceId) {
throw new NotFoundError(resourceType, resourceId);
}
throw new ApiError("Resource not found", 404);
case 500:
case 502:
case 503:
case 504:
throw new ServerError("Server error occurred", status);
default:
if (status >= 500) {
throw new ServerError(`Server error occurred (${status})`, status);
}
throw new ApiError(`Request failed with status ${status}`, status);
}
}
// src/namespaces/courses.ts
function createCoursesNamespace(client) {
return {
/**
* Get all courses (public endpoint)
* @returns Array of all published courses
*/
async findAll() {
const { data, error, response } = await client.GET("/api/courses");
if (error || !data) {
handleApiError(error, response);
}
return data;
},
/**
* Get all published courses
* @returns Array of published courses
*/
async findAllPublished() {
const { data, error, response } = await client.GET("/api/courses/published");
if (error || !data) {
handleApiError(error, response);
}
return data;
},
/**
* Get a single course by ID
* @param id - Course ID
* @returns Course details
* @throws NotFoundError if course doesn't exist
*/
async findOne(id) {
const { data, error, response } = await client.GET("/api/courses/{id}", {
params: { path: { id } }
});
if (error || !data) {
handleApiError(error, response, "Course", id);
}
return data;
},
/**
* Search for courses (authenticated endpoint)
* @returns Array of matching courses
*/
async search() {
const { data, error, response } = await client.GET("/api/courses/search");
if (error || !data) {
handleApiError(error, response);
}
return data;
},
/**
* Publish a new course from local draft (authenticated endpoint)
* @param dto - Course data to publish
* @returns Published course details
* @throws ValidationError if course data is invalid
* @throws AuthenticationError if not authenticated
*/
async publishCourse(dto) {
const { data, error, response } = await client.POST("/api/courses/publish", {
body: dto
});
if (error || !data) {
handleApiError(error, response);
}
return data;
},
/**
* Get courses owned by authenticated user
* @returns Array of user's courses (published and unpublished)
* @throws AuthenticationError if not authenticated
*/
async getMyCourses() {
const { data, error, response } = await client.GET("/api/courses/my-courses");
if (error || !data) {
handleApiError(error, response);
}
return data;
},
/**
* Update an existing course
* @param id - Course ID
* @param dto - Updated course data
* @returns Updated course details
* @throws NotFoundError if course doesn't exist
* @throws AuthorizationError if user doesn't own the course
*/
async update(id, dto) {
const { data, error, response } = await client.PATCH("/api/courses/{id}", {
params: { path: { id } },
body: dto
});
if (error || !data) {
handleApiError(error, response, "Course", id);
}
return data;
},
/**
* Delete a course
* @param id - Course ID
* @throws NotFoundError if course doesn't exist
* @throws AuthorizationError if user doesn't own the course
*/
async remove(id) {
const { error, response } = await client.DELETE("/api/courses/{id}", {
params: { path: { id } }
});
if (error) {
handleApiError(error, response, "Course", id);
}
},
/**
* Publish a course (change state to published)
* @param id - Course ID
* @returns Updated course details
*/
async publish(id) {
const { data, error, response } = await client.PATCH("/api/courses/{id}/publish", {
params: { path: { id } }
});
if (error || !data) {
handleApiError(error, response, "Course", id);
}
return data;
},
/**
* Unpublish a course (change state to unpublished)
* @param id - Course ID
* @returns Updated course details
*/
async unpublish(id) {
const { data, error, response } = await client.PATCH("/api/courses/{id}/unpublish", {
params: { path: { id } }
});
if (error || !data) {
handleApiError(error, response, "Course", id);
}
return data;
},
/**
* Get version history for a course
* @param id - Course ID
* @param params - Optional pagination parameters
* @returns Paginated version history
*/
async getVersionHistory(id, params) {
const { data, error, response } = await client.GET("/api/courses/{id}/versions", {
params: {
path: { id },
query: params
}
});
if (error || !data) {
handleApiError(error, response, "Course", id);
}
return data;
},
/**
* Get the latest version of a course
* @param id - Course ID
* @returns Latest course version
*/
async getLatestVersion(id) {
const { data, error, response } = await client.GET("/api/courses/{id}/versions/latest", {
params: { path: { id } }
});
if (error || !data) {
handleApiError(error, response, "CourseVersion", id);
}
return data;
},
/**
* Get a specific version of a course
* @param id - Course ID
* @param versionNumber - Version number
* @returns Course version details
*/
async getVersion(id, versionNumber) {
const { data, error, response } = await client.GET("/api/courses/{id}/versions/{versionNumber}", {
params: { path: { id, versionNumber } }
});
if (error || !data) {
handleApiError(error, response, "CourseVersion", `${id}/v${versionNumber}`);
}
return data;
},
/**
* Compare two versions of a course
* @param id - Course ID
* @param v1 - First version number
* @param v2 - Second version number
* @returns Comparison data for both versions
*/
async compareVersions(id, v1, v2) {
const { data, error, response } = await client.GET("/api/courses/{id}/versions/compare", {
params: {
path: { id },
query: { v1, v2 }
}
});
if (error || !data) {
handleApiError(error, response, "Course", id);
}
return data;
}
};
}
// src/namespaces/enrollments.ts
function createEnrollmentsNamespace(client) {
return {
/**
* Enroll in a course (idempotent - returns existing enrollment if already enrolled)
* @param courseId - Course ID to enroll in
* @returns Enrollment details
* @throws AuthenticationError if not authenticated
* @throws NotFoundError if course doesn't exist
*/
async enroll(courseId) {
const { data, error, response } = await client.POST("/api/enrollments", {
body: { courseId }
});
if (error || !data) {
handleApiError(error, response);
}
return data;
},
/**
* Get all enrollments for authenticated user
* @returns Array of active enrollments
* @throws AuthenticationError if not authenticated
*/
async getUserEnrollments() {
const { data, error, response } = await client.GET("/api/enrollments");
if (error || !data) {
handleApiError(error, response);
}
return data;
},
/**
* Check if user is enrolled in a specific course
* @param courseId - Course ID to check
* @returns Enrollment details or null if not enrolled
* @throws AuthenticationError if not authenticated
*/
async getEnrollment(courseId) {
const { data, error, response } = await client.GET("/api/enrollments/{courseId}", {
params: { path: { courseId } }
});
if (response?.status === 404) {
return null;
}
if (error || !data) {
handleApiError(error, response);
}
return data;
},
/**
* Withdraw from a course
* @param courseId - Course ID to withdraw from
* @returns Updated enrollment with withdrawn status
* @throws AuthenticationError if not authenticated
* @throws NotFoundError if not enrolled in course
*/
async withdraw(courseId) {
const { data, error, response } = await client.DELETE("/api/enrollments/{courseId}", {
params: { path: { courseId } }
});
if (error || !data) {
handleApiError(error, response);
}
return data;
},
/**
* Check if user is enrolled in a course (boolean helper)
* @param courseId - Course ID to check
* @returns true if enrolled and active, false otherwise
*/
async isEnrolled(courseId) {
try {
const enrollment = await this.getEnrollment(courseId);
return enrollment !== null && enrollment.status === "active";
} catch {
return false;
}
}
};
}
// src/namespaces/admin.ts
function createAdminNamespace(client) {
return {
courses: {
/**
* Get all courses (admin endpoint)
* @returns Array of all courses
* @throws AuthenticationError if not authenticated
* @throws AuthorizationError if user is not admin
*/
async findAll() {
const { data, error, response } = await client.GET("/api/admin/courses");
if (error || !data) {
handleApiError(error, response);
}
return data;
},
/**
* Get a single course by ID (admin endpoint)
* @param id - Course ID
* @returns Course details
* @throws AuthorizationError if user is not admin
*/
async findOne(id) {
const { data, error, response } = await client.GET("/api/admin/courses/{id}", {
params: { path: { id } }
});
if (error || !data) {
handleApiError(error, response, "Course", id);
}
return data;
},
/**
* Update a course (admin endpoint)
* @param id - Course ID
* @param dto - Updated course data
* @returns Updated course details
* @throws AuthorizationError if user is not admin
*/
async update(id, dto) {
const { data, error, response } = await client.PATCH("/api/admin/courses/{id}", {
params: { path: { id } },
body: dto
});
if (error || !data) {
handleApiError(error, response, "Course", id);
}
return data;
},
/**
* Delete a course (admin endpoint)
* @param id - Course ID
* @throws AuthorizationError if user is not admin
*/
async remove(id) {
const { error, response } = await client.DELETE("/api/admin/courses/{id}", {
params: { path: { id } }
});
if (error) {
handleApiError(error, response, "Course", id);
}
},
/**
* Publish a course (admin endpoint)
* @param id - Course ID
* @returns Updated course details
* @throws AuthorizationError if user is not admin
*/
async publish(id) {
const { data, error, response } = await client.PATCH("/api/admin/courses/{id}/publish", {
params: { path: { id } }
});
if (error || !data) {
handleApiError(error, response, "Course", id);
}
return data;
},
/**
* Unpublish a course (admin endpoint)
* @param id - Course ID
* @returns Updated course details
* @throws AuthorizationError if user is not admin
*/
async unpublish(id) {
const { data, error, response } = await client.PATCH("/api/admin/courses/{id}/unpublish", {
params: { path: { id } }
});
if (error || !data) {
handleApiError(error, response, "Course", id);
}
return data;
}
}
};
}
// src/client/factory.ts
function createCoursesSDK(config) {
const client = createClient({
baseUrl: config.baseUrl,
fetch: config.fetch
});
if (config.getToken) {
client.use({
async onRequest({ request }) {
const token = await Promise.resolve(config.getToken?.());
if (token) {
request.headers.set("Authorization", `Bearer ${token}`);
}
return request;
}
});
}
return {
courses: createCoursesNamespace(client),
enrollments: createEnrollmentsNamespace(client),
admin: createAdminNamespace(client)
};
}
export { ApiError, AuthenticationError, AuthorizationError, NetworkError, NotFoundError, ServerError, ValidationError, createCoursesSDK };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map