diff --git a/packages/api-v4/.changeset/pr-9386-added-1689191794116.md b/packages/api-v4/.changeset/pr-9386-added-1689191794116.md new file mode 100644 index 00000000000..d29b610863d --- /dev/null +++ b/packages/api-v4/.changeset/pr-9386-added-1689191794116.md @@ -0,0 +1,5 @@ +--- +"@linode/api-v4": Added +--- + +Support for self serve beta endpoints ([#9386](https://github.com/linode/manager/pull/9386)) diff --git a/packages/api-v4/src/account/betas.ts b/packages/api-v4/src/account/betas.ts new file mode 100644 index 00000000000..5b184ac5beb --- /dev/null +++ b/packages/api-v4/src/account/betas.ts @@ -0,0 +1,49 @@ +import { API_ROOT } from '../constants'; +import Request, { + setMethod, + setParams, + setURL, + setXFilter, + setData, +} from '../request'; +import { Filter, Params, ResourcePage } from '../types'; +import { AccountBeta, EnrollInBetaPayload } from './types'; + +/** + * getBetas + * Retrieve a paginated list of betas your account is enrolled in. + * + */ +export const getAccountBetas = (params?: Params, filter?: Filter) => + Request>( + setURL(`${API_ROOT}/account/betas`), + setMethod('GET'), + setParams(params), + setXFilter(filter) + ); + +/** + * getBeta + * Retrieve details of a single beta your account is enrolled in. + * @param betaId { string } The ID of the beta you want to be retrieved + * + */ +export const getAccountBeta = (betaId: string) => + Request( + setURL(`${API_ROOT}/account/betas/${encodeURIComponent(betaId)}`), + setMethod('GET') + ); + +/** + * enrollInBeta + * Enrolls your account in the specified beta program. + * @param data { object } + * @param data.id { string } ID of the beta you want to be enrolled in. + * + */ +export const enrollInBeta = (data: EnrollInBetaPayload) => + Request<{}>( + setURL(`${API_ROOT}/account/betas`), + setMethod('POST'), + setData(data) + ); diff --git a/packages/api-v4/src/account/index.ts b/packages/api-v4/src/account/index.ts index d839a8c9f0d..9674eae1fc2 100644 --- a/packages/api-v4/src/account/index.ts +++ b/packages/api-v4/src/account/index.ts @@ -1,5 +1,7 @@ export * from './account'; +export * from './betas'; + export * from './events'; export * from './invoices'; diff --git a/packages/api-v4/src/account/types.ts b/packages/api-v4/src/account/types.ts index c0e7d854f2b..6dbba2df579 100644 --- a/packages/api-v4/src/account/types.ts +++ b/packages/api-v4/src/account/types.ts @@ -1,4 +1,5 @@ import { APIWarning } from '../types'; +import { Beta } from '../betas/types'; export interface User { username: string; @@ -455,3 +456,11 @@ export interface AccountLogin { username: string; status: AccountLoginStatus; } + +export interface AccountBeta extends Beta { + enrolled: string; +} + +export interface EnrollInBetaPayload { + id: string; +} diff --git a/packages/api-v4/src/betas/betas.ts b/packages/api-v4/src/betas/betas.ts new file mode 100644 index 00000000000..27421f0b90c --- /dev/null +++ b/packages/api-v4/src/betas/betas.ts @@ -0,0 +1,32 @@ +import { API_ROOT } from '../constants'; +import Request, { setMethod, setParams, setURL, setXFilter } from '../request'; +import { Filter, Params, ResourcePage } from '../types'; +import { Beta } from './types'; + +/** + * getBetas + * + * Retrieve a paginated list of active beta programs. + * + **/ +export const getBetas = (params?: Params, filter?: Filter) => + Request>( + setURL(`${API_ROOT}/betas`), + setMethod('GET'), + setParams(params), + setXFilter(filter) + ); + +/** + * getBeta + * + * Retrieve details for a single beta program. + * + * @param betaId { string } The ID of the beta to be retrieved + * + */ +export const getBeta = (betaId: string) => + Request( + setURL(`${API_ROOT}/betas/${encodeURIComponent(betaId)}`), + setMethod('GET') + ); diff --git a/packages/api-v4/src/betas/index.ts b/packages/api-v4/src/betas/index.ts new file mode 100644 index 00000000000..9dc8cc15670 --- /dev/null +++ b/packages/api-v4/src/betas/index.ts @@ -0,0 +1,3 @@ +export * from './types'; + +export * from './betas'; diff --git a/packages/api-v4/src/betas/types.ts b/packages/api-v4/src/betas/types.ts new file mode 100644 index 00000000000..b0202aa6e2d --- /dev/null +++ b/packages/api-v4/src/betas/types.ts @@ -0,0 +1,8 @@ +export interface Beta { + label: string; + started: string; + id: string; + ended?: string; + more_info?: string; + description?: string; +} diff --git a/packages/api-v4/src/index.ts b/packages/api-v4/src/index.ts index c11e2032ffa..35dbc473339 100644 --- a/packages/api-v4/src/index.ts +++ b/packages/api-v4/src/index.ts @@ -44,6 +44,8 @@ export * from './volumes'; export * from './vpcs'; +export * from './betas'; + export { baseRequest, setToken, diff --git a/packages/manager/.changeset/pr-9386-added-1689191860871.md b/packages/manager/.changeset/pr-9386-added-1689191860871.md new file mode 100644 index 00000000000..985bd59d153 --- /dev/null +++ b/packages/manager/.changeset/pr-9386-added-1689191860871.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Added +--- + +Queries, server handlers, and factories for self-serve betas ([#9386](https://github.com/linode/manager/pull/9386)) diff --git a/packages/manager/src/factories/betas.ts b/packages/manager/src/factories/betas.ts new file mode 100644 index 00000000000..5bbc0d4a277 --- /dev/null +++ b/packages/manager/src/factories/betas.ts @@ -0,0 +1,14 @@ +import { Beta, AccountBeta } from '@linode/api-v4'; +import * as Factory from 'factory.ts'; +import { DateTime } from 'luxon'; + +export const betaFactory = Factory.Sync.makeFactory({ + id: Factory.each((i) => `beta-${i}`), + label: Factory.each((i) => `Beta ${i}`), + started: DateTime.now().toISO(), +}); + +export const accountBetaFactory = Factory.Sync.makeFactory({ + ...betaFactory.build({ started: DateTime.now().minus({ days: 30 }).toISO() }), + enrolled: DateTime.now().toISO(), +}); diff --git a/packages/manager/src/factories/index.ts b/packages/manager/src/factories/index.ts index 2f6adf51164..cd52fdf2d33 100644 --- a/packages/manager/src/factories/index.ts +++ b/packages/manager/src/factories/index.ts @@ -4,6 +4,7 @@ export * from './accountMaintenance'; export * from './accountOAuth'; export * from './accountPayment'; export * from './aglb'; +export * from './betas'; export * from './billing'; export * from './config'; export * from './databases'; diff --git a/packages/manager/src/mocks/serverHandlers.ts b/packages/manager/src/mocks/serverHandlers.ts index 5a52a0195e9..88ca80336b8 100644 --- a/packages/manager/src/mocks/serverHandlers.ts +++ b/packages/manager/src/mocks/serverHandlers.ts @@ -10,10 +10,12 @@ import cachedRegions from 'src/cachedData/regions.json'; import { MockData } from 'src/dev-tools/mockDataController'; import { abuseTicketNotificationFactory, + accountBetaFactory, accountFactory, accountMaintenanceFactory, accountTransferFactory, appTokenFactory, + betaFactory, contactFactory, credentialFactory, creditPaymentResponseFactory, @@ -1350,6 +1352,21 @@ export const handlers = [ rest.delete('*/profile/tokens/:id', (req, res, ctx) => { return res(ctx.json({})); }), + rest.get('*/betas', (req, res, ctx) => { + return res(ctx.json(makeResourcePage(betaFactory.buildList(5)))); + }), + rest.get('*/betas/:id', (req, res, ctx) => { + return res(ctx.json(betaFactory.build({ id: req.params.id }))); + }), + rest.get('*/account/betas', (req, res, ctx) => { + return res(ctx.json(makeResourcePage(accountBetaFactory.buildList(5)))); + }), + rest.get('*/account/betas/:id', (req, res, ctx) => { + return res(ctx.json(accountBetaFactory.build({ id: req.params.id }))); + }), + rest.post('*/account/betas', (req, res, ctx) => { + return res(ctx.json({})); + }), ...entityTransfers, ...statusPage, ...databases, diff --git a/packages/manager/src/queries/accountBetas.ts b/packages/manager/src/queries/accountBetas.ts new file mode 100644 index 00000000000..a6aa706c63c --- /dev/null +++ b/packages/manager/src/queries/accountBetas.ts @@ -0,0 +1,44 @@ +import { + AccountBeta, + getAccountBetas, + EnrollInBetaPayload, + getAccountBeta, + enrollInBeta, +} from '@linode/api-v4/lib/account'; +import { useQuery, useQueryClient, useMutation } from 'react-query'; +import { + APIError, + Filter, + Params, + ResourcePage, +} from '@linode/api-v4/lib/types'; + +export const queryKey = 'account-betas'; + +export const useAccountBetasQuery = (params?: Params, filter?: Filter) => + useQuery, APIError[]>( + [queryKey, 'paginated', params, filter], + () => getAccountBetas(params, filter), + { + keepPreviousData: true, + } + ); + +export const useAccountBetaQuery = (id: string) => + useQuery([queryKey, 'account-beta', id], () => + getAccountBeta(id) + ); + +export const useCreateAccountBetaMutation = () => { + const queryClient = useQueryClient(); + return useMutation<{}, APIError[], EnrollInBetaPayload>( + (data) => { + return enrollInBeta(data); + }, + { + onSuccess() { + queryClient.invalidateQueries([queryKey, 'paginated']); + }, + } + ); +}; diff --git a/packages/manager/src/queries/betas.ts b/packages/manager/src/queries/betas.ts new file mode 100644 index 00000000000..4d199c808e2 --- /dev/null +++ b/packages/manager/src/queries/betas.ts @@ -0,0 +1,22 @@ +import { Beta, getBetas, getBeta } from '@linode/api-v4/lib/betas'; +import { useQuery } from 'react-query'; +import { + APIError, + Filter, + Params, + ResourcePage, +} from '@linode/api-v4/lib/types'; + +export const queryKey = 'betas'; + +export const useBetasQuery = (params?: Params, filter?: Filter) => + useQuery, APIError[]>( + [queryKey, 'paginated', params, filter], + () => getBetas(params, filter), + { + keepPreviousData: true, + } + ); + +export const useBetaQuery = (id: string) => + useQuery([queryKey, 'beta', id], () => getBeta(id));