From 94d2ed820e7536d8fa7070d11729cce697fbb0b3 Mon Sep 17 00:00:00 2001 From: notmd Date: Wed, 25 Jan 2023 15:04:39 +0700 Subject: [PATCH] Refactor `OasstApiClient` --- website/src/components/UserTable.tsx | 3 +- website/src/lib/oasst_api_client.ts | 215 +++++++++------------------ website/src/types/Users.ts | 16 ++ 3 files changed, 87 insertions(+), 147 deletions(-) diff --git a/website/src/components/UserTable.tsx b/website/src/components/UserTable.tsx index 71d4fe44..57a96c95 100644 --- a/website/src/components/UserTable.tsx +++ b/website/src/components/UserTable.tsx @@ -4,8 +4,7 @@ import { Pencil } from "lucide-react"; import Link from "next/link"; import { memo, useState } from "react"; import { get } from "src/lib/api"; -import { FetchUsersResponse } from "src/lib/oasst_api_client"; -import type { User } from "src/types/Users"; +import type { FetchUsersResponse, User } from "src/types/Users"; import useSWR from "swr"; import { DataTable, DataTableColumnDef, FilterItem } from "./DataTable"; diff --git a/website/src/lib/oasst_api_client.ts b/website/src/lib/oasst_api_client.ts index 7c48a288..0b3ecf1d 100644 --- a/website/src/lib/oasst_api_client.ts +++ b/website/src/lib/oasst_api_client.ts @@ -1,36 +1,20 @@ import type { Message } from "src/types/Conversation"; import { LeaderboardReply, LeaderboardTimeFrame } from "src/types/Leaderboard"; import type { AvailableTasks } from "src/types/Task"; -import type { BackendUser, BackendUserCore, User } from "src/types/Users"; +import type { BackendUser, BackendUserCore, FetchUsersParams, FetchUsersResponse } from "src/types/Users"; export class OasstError { message: string; errorCode: number; httpStatusCode: number; - constructor(message: string, errorCode: number, httpStatusCode?: number) { + constructor(message: string, errorCode: number, httpStatusCode: number) { this.message = message; this.errorCode = errorCode; this.httpStatusCode = httpStatusCode; } } -export type FetchUsersParams = { - limit: number; - cursor?: string; - direction: "forward" | "back"; - searchDisplayName?: string; - sortKey?: "username" | "display_name"; -}; - -export type FetchUsersResponse = { - items: T[]; - next?: string; - prev?: string; - sort_key: "username" | "display_name"; - order: "asc" | "desc"; -}; - export class OasstApiClient { oasstApiUrl: string; oasstApiKey: string; @@ -39,88 +23,6 @@ export class OasstApiClient { this.oasstApiUrl = oasstApiUrl; this.oasstApiKey = oasstApiKey; } - - private async post(path: string, body: any): Promise { - const resp = await fetch(`${this.oasstApiUrl}${path}`, { - method: "POST", - headers: { - "X-API-Key": this.oasstApiKey, - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - }); - - if (resp.status === 204) { - return null; - } - - if (resp.status >= 300) { - const errorText = await resp.text(); - let error: any; - try { - error = JSON.parse(errorText); - } catch (e) { - throw new OasstError(errorText, 0, resp.status); - } - throw new OasstError(error.message ?? error, error.error_code, resp.status); - } - - return await resp.json(); - } - - private async put(path: string): Promise { - const resp = await fetch(`${this.oasstApiUrl}${path}`, { - method: "PUT", - headers: { - "X-API-Key": this.oasstApiKey, - }, - }); - - if (resp.status === 204) { - return null; - } - - if (resp.status >= 300) { - const errorText = await resp.text(); - let error: any; - try { - error = JSON.parse(errorText); - } catch (e) { - throw new OasstError(errorText, 0, resp.status); - } - throw new OasstError(error.message ?? error, error.error_code, resp.status); - } - - return await resp.json(); - } - - private async get(path: string): Promise { - const resp = await fetch(`${this.oasstApiUrl}${path}`, { - method: "GET", - headers: { - "X-API-Key": this.oasstApiKey, - "Content-Type": "application/json", - }, - }); - - if (resp.status === 204) { - return null; - } - - if (resp.status >= 300) { - const errorText = await resp.text(); - let error: any; - try { - error = JSON.parse(errorText); - } catch (e) { - throw new OasstError(errorText, 0, resp.status); - } - throw new OasstError(error.message ?? error, error.error_code, resp.status); - } - - return await resp.json(); - } - // TODO return a strongly typed Task? // This method is used to store a task in RegisteredTask.task. // This is a raw Json type, so we can't use it to strongly type the task. @@ -133,13 +35,13 @@ export class OasstApiClient { } async ackTask(taskId: string, messageId: string): Promise { - return this.post(`/api/v1/tasks/${taskId}/ack`, { + await this.post(`/api/v1/tasks/${taskId}/ack`, { message_id: messageId, }); } async nackTask(taskId: string, reason: string): Promise { - return this.post(`/api/v1/tasks/${taskId}/nack`, { + await this.post(`/api/v1/tasks/${taskId}/nack`, { reason, }); } @@ -170,8 +72,8 @@ export class OasstApiClient { /** * Returns the tasks availability information for given `user`. */ - async fetch_tasks_availability(user: object): Promise { - return this.post("/api/v1/tasks/availability", user); + async fetch_tasks_availability(user: object): Promise { + return this.post("/api/v1/tasks/availability", user); } /** @@ -191,18 +93,12 @@ export class OasstApiClient { /** * Returns the `BackendUser` associated with `user_id` */ - async fetch_user(user_id: string): Promise { + async fetch_user(user_id: string): Promise { return this.get(`/api/v1/users/${user_id}`); } /** * Returns the set of `BackendUser`s stored by the backend. - * - * @param {number} max_count - The maximum number of users to fetch. - * @param {string} cursor - The user's `display_name` to use when paginating. - * @param {boolean} isForward - If true and `cursor` is not empty, pages - * forward. If false and `cursor` is not empty, pages backwards. - * @returns {Promise} A Promise that returns an array of `BackendUser` objects. */ async fetch_users({ direction, @@ -210,46 +106,28 @@ export class OasstApiClient { cursor, searchDisplayName, sortKey = "display_name", - }: FetchUsersParams): Promise { - const params = new URLSearchParams({ + }: FetchUsersParams): Promise { + return this.get(`/api/v1/users/cursor`, { search_text: searchDisplayName, sort_key: sortKey, - max_count: limit.toString(), + max_count: limit, + after: direction === "forward" ? cursor : undefined, + before: direction === "back" ? cursor : undefined, }); - - // The backend API uses different query parameters depending on the - // pagination direction but they both take the same cursor value. - // Depending on direction, pick the right query param. - if (cursor !== "") { - params.append(direction === "forward" ? "after" : "before", cursor); - } - const BASE_URL = `/api/v1/users/cursor`; - const url = `${BASE_URL}/?${params.toString()}`; - return this.get(url); } - // async fetch_user_by_display_name(name: string): Promise { - // const params = new URLSearchParams({ - // search_text: name, - // }); - - // const endpoint = `/api/v1/frontend_users/by_display_name`; - - // return this.get(`${endpoint}?${params.toString()}`); - // } - /** * Returns the `Message`s associated with `user_id` in the backend. */ - async fetch_user_messages(user_id: string): Promise { - return this.get(`/api/v1/users/${user_id}/messages`); + async fetch_user_messages(user_id: string): Promise { + return this.get(`/api/v1/users/${user_id}/messages`); } /** * Updates the backend's knowledge about the `user_id`. */ - async set_user_status(user_id: string, is_enabled: boolean, notes): Promise { - return this.put(`/api/v1/users/users/${user_id}?enabled=${is_enabled}¬es=${notes}`); + async set_user_status(user_id: string, is_enabled: boolean, notes: string): Promise { + await this.put(`/api/v1/users/users/${user_id}?enabled=${is_enabled}¬es=${notes}`); } /** @@ -265,18 +143,65 @@ export class OasstApiClient { async fetch_leaderboard( time_frame: LeaderboardTimeFrame, { limit = 20 }: { limit?: number } - ): Promise { - const params = new URLSearchParams({ - limit: limit.toString(), - }); - return this.get(`/api/v1/leaderboards/${time_frame}?${params.toString()}`); + ): Promise { + return this.get(`/api/v1/leaderboards/${time_frame}`, { limit }); } /** * Returns the counts of all tasks (some might be zero) */ - async fetch_available_tasks(user: BackendUserCore, lang: string): Promise { - return this.post(`/api/v1/tasks/availability?lang=${lang}`, user); + async fetch_available_tasks(user: BackendUserCore, lang: string): Promise { + return this.post(`/api/v1/tasks/availability?lang=${lang}`, user); + } + + private async post(path: string, body: unknown) { + return this.request("POST", path, { + body: JSON.stringify(body), + }); + } + + private async put(path: string) { + return this.request("PUT", path); + } + + private async get(path: string, query: Record = {}) { + const filteredQuery = Object.fromEntries( + Object.entries(query).filter(([, value]) => value !== undefined) + ) as Record; + + const params = new URLSearchParams(filteredQuery).toString(); + + return this.request("GET", `${path}${query ? `?${params}` : ""}`); + } + + private async request(method: "GET" | "POST" | "PUT", path: string, init?: RequestInit): Promise { + const resp = await fetch(`${this.oasstApiUrl}${path}`, { + method, + ...init, + headers: { + "X-API-Key": this.oasstApiKey, + "Content-Type": "application/json", + ...init?.headers, + }, + }); + + if (resp.status === 204) { + return null; + } + + if (resp.status >= 300) { + const errorText = await resp.text(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let error: any; + try { + error = JSON.parse(errorText); + } catch (e) { + throw new OasstError(errorText, 0, resp.status); + } + throw new OasstError(error.message ?? error, error.error_code, resp.status); + } + + return await resp.json(); } } diff --git a/website/src/types/Users.ts b/website/src/types/Users.ts index 39d2a663..52240b5f 100644 --- a/website/src/types/Users.ts +++ b/website/src/types/Users.ts @@ -51,3 +51,19 @@ export interface User extends BackendUser { */ role: string; } + +export type FetchUsersParams = { + limit: number; + cursor?: string; + direction: "forward" | "back"; + searchDisplayName?: string; + sortKey?: "username" | "display_name"; +}; + +export type FetchUsersResponse = { + items: T[]; + next?: string; + prev?: string; + sort_key: "username" | "display_name"; + order: "asc" | "desc"; +};