diff --git a/packages/sdk/src/core/token/TokenClient.ts b/packages/sdk/src/core/token/TokenClient.ts index 75ff9b09fa..f9ef91b00a 100644 --- a/packages/sdk/src/core/token/TokenClient.ts +++ b/packages/sdk/src/core/token/TokenClient.ts @@ -1,14 +1,16 @@ import { getQueryString } from "../../helper"; import { APIResource } from "../../main/resource"; import { - ApiToken, - CreateApiTokenRequest, - CreateApiTokenResponse, - DeleteApiTokenRequest, - GetApiTokenRequest, - GetApiTokenResponse, - ListApiTokenRequest, - ListApiTokensResponse, + APIToken, + CreateAPITokenRequest, + CreateAPITokenResponse, + DeleteAPITokenRequest, + GetAPITokenRequest, + GetAPITokenResponse, + ListAPITokenRequest, + ListAPITokensResponse, + ListPaginatedAPITokenRequest, + ListPaginatedAPITokensResponse, } from "./type"; export class TokenClient extends APIResource { @@ -19,51 +21,59 @@ export class TokenClient extends APIResource { /** * Returns a paginated list of the API tokens of the authenticated user. */ - async listAPITokens( - props: ListApiTokenRequest & { enablePagination: true }, - ): Promise; - async listAPITokens( - props: ListApiTokenRequest & { enablePagination: false }, - ): Promise; - async listAPITokens( - props: ListApiTokenRequest & { enablePagination: undefined }, - ): Promise; - async listAPITokens( - props: ListApiTokenRequest & { enablePagination?: boolean }, - ): Promise; - async listAPITokens( - props: ListApiTokenRequest & { enablePagination?: boolean }, - ) { - const { pageSize, pageToken, enablePagination } = props; + async listPaginatedAPITokens({ + pageSize, + pageToken, + }: ListPaginatedAPITokenRequest) { + const queryString = getQueryString({ + baseURL: "/tokens", + pageSize, + pageToken, + }); try { - const tokens: ApiToken[] = []; + const data = + await this._client.get(queryString); + return Promise.resolve(data); + } catch (error) { + return Promise.reject(error); + } + } - const queryString = getQueryString({ - baseURL: "/tokens", - pageSize, - pageToken, - }); + /** + * Returns a list of the API tokens of the authenticated user. + */ + async listAPITokens({ pageSize = 100, pageToken }: ListAPITokenRequest) { + const queryString = getQueryString({ + baseURL: "/tokens", + pageSize, + pageToken, + }); - const data = await this._client.get(queryString); + try { + const tokens: APIToken[] = []; - if (enablePagination) { - return Promise.resolve(data); - } + const res = + await this._client.get(queryString); - tokens.push(...data.tokens); + tokens.push(...res.tokens); - if (data.nextPageToken) { + if (res.nextPageToken) { tokens.push( - ...(await this.listAPITokens({ - pageSize, - pageToken: data.nextPageToken, - enablePagination: false, - })), + ...( + await this.listAPITokens({ + pageSize, + pageToken: res.nextPageToken, + }) + ).tokens, ); } - return Promise.resolve(tokens); + const response: ListAPITokensResponse = { + tokens, + }; + + return Promise.resolve(response); } catch (error) { return Promise.reject(error); } @@ -72,10 +82,14 @@ export class TokenClient extends APIResource { /** * Returns the details of an API token. */ - async getApiToken({ tokenName }: GetApiTokenRequest) { + async getAPIToken({ tokenId }: GetAPITokenRequest) { + const queryString = getQueryString({ + baseURL: `/tokens/${tokenId}`, + }); + try { - const data = await this._client.get(`/${tokenName}`); - return Promise.resolve(data.token); + const data = await this._client.get(queryString); + return Promise.resolve(data); } catch (error) { return Promise.reject(error); } @@ -88,15 +102,22 @@ export class TokenClient extends APIResource { /** * Creates an API token for the authenticated user. */ - async createApiToken({ id, ttl }: CreateApiTokenRequest) { + async createAPIToken({ id, ttl }: CreateAPITokenRequest) { + const queryString = getQueryString({ + baseURL: "/tokens", + }); + try { - const data = await this._client.post("/tokens", { - body: JSON.stringify({ - id, - ttl, - }), - }); - return Promise.resolve(data.token); + const data = await this._client.post( + queryString, + { + body: JSON.stringify({ + id, + ttl, + }), + }, + ); + return Promise.resolve(data); } catch (error) { return Promise.reject(error); } @@ -105,9 +126,13 @@ export class TokenClient extends APIResource { /** * Deletes an API token. */ - async deleteApiToken({ tokenName }: DeleteApiTokenRequest) { + async deleteAPIToken({ tokenId }: DeleteAPITokenRequest) { + const queryString = getQueryString({ + baseURL: `/tokens/${tokenId}`, + }); + try { - await this._client.delete(`/${tokenName}`); + await this._client.delete(queryString); } catch (error) { return Promise.reject(error); } diff --git a/packages/sdk/src/core/token/type.ts b/packages/sdk/src/core/token/type.ts index d2a6525f6f..bd1ef8b58c 100644 --- a/packages/sdk/src/core/token/type.ts +++ b/packages/sdk/src/core/token/type.ts @@ -1,4 +1,4 @@ -export type ApiToken = { +export type APIToken = { name: string; uid: string; id: string; @@ -16,42 +16,43 @@ export type ApiTokenState = | "STATE_ACTIVE" | "STATE_EXPIRED"; -export type ListApiTokenRequest = { +export type ListPaginatedAPITokenRequest = { pageSize?: number; pageToken?: string; }; -export type ListApiTokensResponse = { - tokens: ApiToken[]; +export type ListPaginatedAPITokensResponse = { + tokens: APIToken[]; nextPageToken: string; totalSize: string; }; -export type GetApiTokenRequest = { - /** - * The resource name of the token, which allows its access by ID. - * Format: tokens/{token.id}. - */ - tokenName: string; +export type ListAPITokenRequest = { + pageSize?: number; + pageToken?: string; +}; + +export type ListAPITokensResponse = { + tokens: APIToken[]; +}; + +export type GetAPITokenRequest = { + tokenId: string; }; -export type GetApiTokenResponse = { - token: ApiToken; +export type GetAPITokenResponse = { + token: APIToken; }; -export type CreateApiTokenRequest = { +export type CreateAPITokenRequest = { id: string; ttl: number; }; -export type CreateApiTokenResponse = { - token: ApiToken; +export type CreateAPITokenResponse = { + token: APIToken; }; -export type DeleteApiTokenRequest = { - /** - * The resource name of the token, which allows its access by ID. - * Format: tokens/{token.id}. - */ - tokenName: string; +export type DeleteAPITokenRequest = { + tokenId: string; }; diff --git a/packages/toolkit/src/lib/react-query-service/mgmt/index.ts b/packages/toolkit/src/lib/react-query-service/mgmt/index.ts index 33a85ce069..83d1deeb9d 100644 --- a/packages/toolkit/src/lib/react-query-service/mgmt/index.ts +++ b/packages/toolkit/src/lib/react-query-service/mgmt/index.ts @@ -1,7 +1,4 @@ -export { useApiToken } from "./useApiToken"; export { useAuthenticatedUserSubscription } from "./useAuthenticatedUserSubscription"; -export { useCreateApiToken } from "./useCreateApiToken"; -export { useDeleteApiToken } from "./useDeleteApiToken"; export { useMgmtDefinition } from "./useMgmtDefinition"; export { useNamespaceType } from "./use-namespace-type/client"; @@ -13,6 +10,9 @@ export * from "./use-namespace-type"; export * from "./use-user"; export * from "./use-api-tokens"; +export * from "./useAPIToken"; +export * from "./useCreateAPIToken"; +export * from "./useDeleteAPIToken"; export * from "./onTriggerInvalidateCredits"; export * from "./useGetNamespaceRemainingInstillCredit"; export * from "./useListNamespacesRemainingInstillCredit"; diff --git a/packages/toolkit/src/lib/react-query-service/mgmt/use-api-tokens/client.ts b/packages/toolkit/src/lib/react-query-service/mgmt/use-api-tokens/client.ts index 105dedebf2..d635911e85 100644 --- a/packages/toolkit/src/lib/react-query-service/mgmt/use-api-tokens/client.ts +++ b/packages/toolkit/src/lib/react-query-service/mgmt/use-api-tokens/client.ts @@ -1,33 +1,24 @@ "use client"; +import type { Nullable } from "instill-sdk"; import { useQuery } from "@tanstack/react-query"; -import { getInstillAPIClient } from "../../../sdk-helper"; -import { Nullable } from "../../../type"; -import { getUseApiTokensQueryKey } from "./server"; +import { queryKeyStore } from "../../queryKeyStore"; +import { fetchAPITokens } from "./server"; -export function useApiTokens({ +export function useAPITokens({ accessToken, enabled, }: { accessToken: Nullable; enabled: boolean; }) { - const queryKey = getUseApiTokensQueryKey(); return useQuery({ - queryKey, + queryKey: queryKeyStore.mgmt.getUseAPITokensQueryKey(), queryFn: async () => { - if (!accessToken) { - return Promise.reject(new Error("accessToken not provided")); - } - - const client = getInstillAPIClient({ accessToken }); - - const tokens = await client.core.token.listAPITokens({ - enablePagination: false, + return await fetchAPITokens({ + accessToken, }); - - return Promise.resolve(tokens); }, enabled, }); diff --git a/packages/toolkit/src/lib/react-query-service/mgmt/use-api-tokens/server.ts b/packages/toolkit/src/lib/react-query-service/mgmt/use-api-tokens/server.ts index eccb209678..45d7941514 100644 --- a/packages/toolkit/src/lib/react-query-service/mgmt/use-api-tokens/server.ts +++ b/packages/toolkit/src/lib/react-query-service/mgmt/use-api-tokens/server.ts @@ -3,47 +3,41 @@ import { QueryClient } from "@tanstack/react-query"; import { env } from "../../../../server"; import { getInstillAPIClient } from "../../../sdk-helper"; import { Nullable } from "../../../type"; +import { queryKeyStore } from "../../queryKeyStore"; -export async function fetchApiTokens({ +export async function fetchAPITokens({ accessToken, }: { accessToken: Nullable; }) { if (!accessToken) { - return Promise.reject(new Error("accessToken not provided")); + return Promise.reject(new Error("accessToken is required")); } try { const client = getInstillAPIClient({ accessToken }); - const apiTokens = await client.core.token.listAPITokens({ + const res = await client.core.token.listAPITokens({ pageSize: env("NEXT_PUBLIC_QUERY_PAGE_SIZE"), - enablePagination: false, }); - return Promise.resolve(apiTokens); + return Promise.resolve(res.tokens); } catch (error) { return Promise.reject(error); } } -export function getUseApiTokensQueryKey() { - return ["api-tokens"]; -} - -export function prefetchApiTokens({ +export function prefetchAPITokens({ accessToken, queryClient, }: { accessToken: Nullable; queryClient: QueryClient; }) { - const queryKey = getUseApiTokensQueryKey(); - return queryClient.prefetchQuery({ - queryKey, + queryKey: queryKeyStore.mgmt.getUseAPITokensQueryKey(), queryFn: async () => { - return await fetchApiTokens({ + return await fetchAPITokens({ accessToken, }); }, diff --git a/packages/toolkit/src/lib/react-query-service/mgmt/useApiToken.ts b/packages/toolkit/src/lib/react-query-service/mgmt/useAPIToken.ts similarity index 54% rename from packages/toolkit/src/lib/react-query-service/mgmt/useApiToken.ts rename to packages/toolkit/src/lib/react-query-service/mgmt/useAPIToken.ts index 69da3ee5fc..410df16c53 100644 --- a/packages/toolkit/src/lib/react-query-service/mgmt/useApiToken.ts +++ b/packages/toolkit/src/lib/react-query-service/mgmt/useAPIToken.ts @@ -4,30 +4,31 @@ import type { Nullable } from "instill-sdk"; import { useQuery } from "@tanstack/react-query"; import { getInstillAPIClient } from "../../sdk-helper"; +import { queryKeyStore } from "../queryKeyStore"; -export function useApiToken({ - tokenName, +export function useAPIToken({ + tokenId, accessToken, enabled, }: { - tokenName: string; + tokenId: string; accessToken: Nullable; enabled: boolean; }) { return useQuery({ - queryKey: ["api-tokens", tokenName], + queryKey: queryKeyStore.mgmt.getUseAPITokenQueryKey({ tokenId }), queryFn: async () => { if (!accessToken) { - return Promise.reject(new Error("accessToken not provided")); + return Promise.reject(new Error("accessToken is required")); } const client = getInstillAPIClient({ accessToken }); - const token = await client.core.token.getApiToken({ - tokenName, + const res = await client.core.token.getAPIToken({ + tokenId, }); - return Promise.resolve(token); + return Promise.resolve(res.token); }, enabled, }); diff --git a/packages/toolkit/src/lib/react-query-service/mgmt/useCreateAPIToken.ts b/packages/toolkit/src/lib/react-query-service/mgmt/useCreateAPIToken.ts new file mode 100644 index 0000000000..d4774a403c --- /dev/null +++ b/packages/toolkit/src/lib/react-query-service/mgmt/useCreateAPIToken.ts @@ -0,0 +1,41 @@ +"use client"; + +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { APIToken, CreateAPITokenRequest, Nullable } from "instill-sdk"; + +import { getInstillAPIClient } from "../../sdk-helper"; +import { queryKeyStore } from "../queryKeyStore"; + +export function useCreateAPIToken() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ + payload, + accessToken, + }: { + payload: CreateAPITokenRequest; + accessToken: Nullable; + }) => { + if (!accessToken) { + return Promise.reject(new Error("accessToken is required")); + } + + const client = getInstillAPIClient({ accessToken }); + + const res = await client.core.token.createAPIToken(payload); + + return Promise.resolve(res.token); + }, + onSuccess: (token) => { + queryClient.invalidateQueries({ + queryKey: queryKeyStore.mgmt.getUseAPITokensQueryKey(), + }); + queryClient.setQueryData( + queryKeyStore.mgmt.getUseAPITokenQueryKey({ + tokenId: token.id, + }), + token, + ); + }, + }); +} diff --git a/packages/toolkit/src/lib/react-query-service/mgmt/useCreateApiToken.ts b/packages/toolkit/src/lib/react-query-service/mgmt/useCreateApiToken.ts deleted file mode 100644 index 96aa77ff3d..0000000000 --- a/packages/toolkit/src/lib/react-query-service/mgmt/useCreateApiToken.ts +++ /dev/null @@ -1,36 +0,0 @@ -"use client"; - -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { ApiToken, CreateApiTokenRequest } from "instill-sdk"; - -import type { Nullable } from "../../type"; -import { getInstillAPIClient } from "../../sdk-helper"; - -export function useCreateApiToken() { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async ({ - payload, - accessToken, - }: { - payload: CreateApiTokenRequest; - accessToken: Nullable; - }) => { - if (!accessToken) { - return Promise.reject(new Error("accessToken not provided")); - } - - const client = getInstillAPIClient({ accessToken }); - - const token = await client.core.token.createApiToken(payload); - - return Promise.resolve({ token }); - }, - onSuccess: ({ token }) => { - queryClient.setQueryData(["api-tokens"], (old) => - old ? [...old, token] : [token], - ); - queryClient.setQueryData(["api-tokens", token.name], token); - }, - }); -} diff --git a/packages/toolkit/src/lib/react-query-service/mgmt/useDeleteAPIToken.ts b/packages/toolkit/src/lib/react-query-service/mgmt/useDeleteAPIToken.ts new file mode 100644 index 0000000000..4164100602 --- /dev/null +++ b/packages/toolkit/src/lib/react-query-service/mgmt/useDeleteAPIToken.ts @@ -0,0 +1,40 @@ +"use client"; + +import type { Nullable } from "instill-sdk"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +import { getInstillAPIClient } from "../../sdk-helper"; +import { queryKeyStore } from "../queryKeyStore"; + +export function useDeleteAPIToken() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ + tokenId, + accessToken, + }: { + tokenId: string; + accessToken: Nullable; + }) => { + if (!accessToken) { + return Promise.reject(new Error("accessToken is required")); + } + + const client = getInstillAPIClient({ accessToken }); + + await client.core.token.deleteAPIToken({ tokenId }); + + return Promise.resolve(tokenId); + }, + onSuccess: (tokenId) => { + queryClient.invalidateQueries({ + queryKey: queryKeyStore.mgmt.getUseAPITokensQueryKey(), + }); + queryClient.removeQueries({ + queryKey: queryKeyStore.mgmt.getUseAPITokenQueryKey({ + tokenId, + }), + }); + }, + }); +} diff --git a/packages/toolkit/src/lib/react-query-service/mgmt/useDeleteApiToken.ts b/packages/toolkit/src/lib/react-query-service/mgmt/useDeleteApiToken.ts deleted file mode 100644 index 74c8c49f19..0000000000 --- a/packages/toolkit/src/lib/react-query-service/mgmt/useDeleteApiToken.ts +++ /dev/null @@ -1,38 +0,0 @@ -"use client"; - -import type { ApiToken, Nullable } from "instill-sdk"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; - -import { getInstillAPIClient } from "../../sdk-helper"; - -export function useDeleteApiToken() { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async ({ - tokenName, - accessToken, - }: { - tokenName: string; - accessToken: Nullable; - }) => { - if (!accessToken) { - return Promise.reject(new Error("accessToken not provided")); - } - - const client = getInstillAPIClient({ accessToken }); - - await client.core.token.deleteApiToken({ tokenName }); - - return Promise.resolve(tokenName); - }, - onSuccess: (tokenName) => { - queryClient.setQueryData(["api-tokens"], (old) => - old ? old.filter((e) => e.name !== tokenName) : [], - ); - queryClient.removeQueries({ - queryKey: ["api-tokens", tokenName], - exact: true, - }); - }, - }); -} diff --git a/packages/toolkit/src/lib/react-query-service/queryKeyStore.ts b/packages/toolkit/src/lib/react-query-service/queryKeyStore.ts index 1d1f6a0a5d..99c5343dbc 100644 --- a/packages/toolkit/src/lib/react-query-service/queryKeyStore.ts +++ b/packages/toolkit/src/lib/react-query-service/queryKeyStore.ts @@ -518,6 +518,12 @@ export const mgmtQueryKeyStore = { }) { return [namespaceId, "remaining-instill-credit"]; }, + getUseAPITokensQueryKey() { + return ["tokens"]; + }, + getUseAPITokenQueryKey({ tokenId }: { tokenId: string }) { + return ["tokens", tokenId]; + }, }; export const catalogQueryKeyStore = { diff --git a/packages/toolkit/src/view/settings/api-tokens/APITokenTable.tsx b/packages/toolkit/src/view/settings/api-tokens/APITokenTable.tsx index d970bd4e66..e5949429d5 100644 --- a/packages/toolkit/src/view/settings/api-tokens/APITokenTable.tsx +++ b/packages/toolkit/src/view/settings/api-tokens/APITokenTable.tsx @@ -1,7 +1,7 @@ "use client"; import type { ColumnDef } from "@tanstack/react-table"; -import type { ApiToken } from "instill-sdk"; +import type { APIToken } from "instill-sdk"; import { Button, DataTable } from "@instill-ai/design-system"; @@ -13,12 +13,12 @@ import { DeleteAPITokenDialog } from "./DeleteAPITokenDialog"; export type APITokenTableProps = { isError: boolean; isLoading: boolean; - tokens: ApiToken[]; + tokens: APIToken[]; }; export const APITokenTable = (props: APITokenTableProps) => { const { isError, isLoading, tokens } = props; - const columns: ColumnDef[] = [ + const columns: ColumnDef[] = [ { accessorKey: "state", header: () =>
Status
, @@ -113,7 +113,7 @@ export const APITokenTable = (props: APITokenTableProps) => { cell: ({ row }) => { return (
- +
); }, diff --git a/packages/toolkit/src/view/settings/api-tokens/CreateAPITokenDialog.tsx b/packages/toolkit/src/view/settings/api-tokens/CreateAPITokenDialog.tsx index b413d30f28..b5d1a5b92f 100644 --- a/packages/toolkit/src/view/settings/api-tokens/CreateAPITokenDialog.tsx +++ b/packages/toolkit/src/view/settings/api-tokens/CreateAPITokenDialog.tsx @@ -20,7 +20,7 @@ import { sendAmplitudeData, toastInstillError, useAmplitudeCtx, - useCreateApiToken, + useCreateAPIToken, useInstillStore, } from "../../../lib"; import { validateInstillResourceID } from "../../../server"; @@ -54,7 +54,7 @@ export const CreateAPITokenDialog = () => { }, }); - const createAPIToken = useCreateApiToken(); + const createAPIToken = useCreateAPIToken(); const handleCreateAPIToken = async ( data: z.infer, ) => { diff --git a/packages/toolkit/src/view/settings/api-tokens/DeleteAPITokenDialog.tsx b/packages/toolkit/src/view/settings/api-tokens/DeleteAPITokenDialog.tsx index 6c395bf657..46b34e1467 100644 --- a/packages/toolkit/src/view/settings/api-tokens/DeleteAPITokenDialog.tsx +++ b/packages/toolkit/src/view/settings/api-tokens/DeleteAPITokenDialog.tsx @@ -18,7 +18,7 @@ import { sendAmplitudeData, toastInstillError, useAmplitudeCtx, - useDeleteApiToken, + useDeleteAPIToken, useInstillStore, } from "../../../lib"; @@ -26,7 +26,7 @@ const DeleteAPITokenSchema = z.object({ code: z.string().min(1, "Code is required"), }); -export const DeleteAPITokenDialog = ({ tokenName }: { tokenName: string }) => { +export const DeleteAPITokenDialog = ({ tokenId }: { tokenId: string }) => { const { amplitudeIsInit } = useAmplitudeCtx(); const [open, setOpen] = React.useState(false); const [isLoading, setIsLoading] = React.useState(false); @@ -41,7 +41,7 @@ export const DeleteAPITokenDialog = ({ tokenName }: { tokenName: string }) => { const { toast } = useToast(); - const deleteAPIToken = useDeleteApiToken(); + const deleteAPIToken = useDeleteAPIToken(); const handleDeleteApiToken = async () => { if (!accessToken) return; @@ -49,7 +49,7 @@ export const DeleteAPITokenDialog = ({ tokenName }: { tokenName: string }) => { try { await deleteAPIToken.mutateAsync({ - tokenName, + tokenId, accessToken, }); setIsLoading(false); @@ -122,7 +122,7 @@ export const DeleteAPITokenDialog = ({ tokenName }: { tokenName: string }) => { Please type - {tokenName} + {tokenId} to confirm. @@ -157,7 +157,7 @@ export const DeleteAPITokenDialog = ({ tokenName }: { tokenName: string }) => { className="w-full flex-1" variant="primary" size="lg" - disabled={form.watch("code") === tokenName ? false : true} + disabled={form.watch("code") === tokenId ? false : true} > {isLoading ? : "Delete Token"} diff --git a/packages/toolkit/src/view/settings/user/UserAPITokenTab.tsx b/packages/toolkit/src/view/settings/user/UserAPITokenTab.tsx index 01e04e0fc1..a6c1093a06 100644 --- a/packages/toolkit/src/view/settings/user/UserAPITokenTab.tsx +++ b/packages/toolkit/src/view/settings/user/UserAPITokenTab.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import { Setting } from ".."; -import { GeneralAppPageProp, useApiTokens } from "../../../lib"; +import { GeneralAppPageProp, useAPITokens } from "../../../lib"; import { env } from "../../../server"; import { APITokenTable, @@ -16,7 +16,7 @@ export type UserAPITokenTabProps = GeneralAppPageProp; export const UserAPITokenTab = (props: UserAPITokenTabProps) => { const { accessToken, enableQuery, router } = props; - const apiTokens = useApiTokens({ + const apiTokens = useAPITokens({ accessToken, enabled: enableQuery, });