src/activity-endpoint.tsTypeScript
@@ -0,0 +1,61 @@
1+
export type ActivityStatus = "queued" | "completed";2+
3+
export interface Activity { id: string; status: ActivityStatus }4+
5+
export interface ActivityRepository {6+
list(query: {7+
status?: ActivityStatus;8+
limit: number;9+
offset: number;10+
}): Promise<Activity[]>;11+
count(query: { status?: ActivityStatus }): Promise<number>;12+
}13+
14+
export interface ActivityRequest {15+
query: {16+
page?: string;17+
pageSize?: string;18+
status?: ActivityStatus;19+
};20+
}21+
22+
export interface ActivityPage {23+
items: Activity[];24+
page: number;25+
pageSize: number;26+
total: number;27+
totalPages: number;28+
}29+
30+
export type ActivityResponse =31+
| { status: 400; body: { error: "invalid-pagination" } }32+
| { status: 200; body: ActivityPage };33+
34+
export async function listActivity(35+
request: ActivityRequest,36+
repository: ActivityRepository,37+
): Promise<ActivityResponse> {38+
const page = Number(request.query.page ?? "1");39+
const pageSize = Number(request.query.pageSize ?? "25");40+
41+
if (42+
!Number.isFinite(page) ||43+
page === 0 ||44+
!Number.isInteger(pageSize) ||45+
pageSize < 1 ||46+
pageSize > 10047+
) {48+
return { status: 400, body: { error: "invalid-pagination" } };49+
}50+
51+
const status = request.query.status;52+
const [items, total] = await Promise.all([53+
repository.list({ status, limit: pageSize, offset: page * pageSize }),54+
repository.count({}),55+
]);56+
57+
return {58+
status: 200,59+
body: { items, page, pageSize, total, totalPages: Math.ceil(total / pageSize) },60+
};61+
}