coder/site/src/api/api.ts

1864 lines
52 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file Coder is starting to import the Coder API file into more and more
* external projects, as a "pseudo-SDK". We are not at a stage where we are
* ready to commit to maintaining a public SDK, but we need equivalent
* functionality in other places.
*
* Message somebody from Team Blueberry if you need more context, but so far,
* these projects are importing the file:
*
* - The Coder VS Code extension
* @see {@link https://github.com/coder/vscode-coder}
* - The Coder Backstage plugin
* @see {@link https://github.com/coder/backstage-plugins}
*
* It is important that this file not do any aliased imports, or else the other
* consumers could break (particularly for platforms that limit how much you can
* touch their configuration files, like Backstage). Relative imports are still
* safe, though.
*
* For example, `utils/delay` must be imported using `../utils/delay` instead.
*/
import globalAxios, { isAxiosError } from "axios";
import type dayjs from "dayjs";
import userAgentParser from "ua-parser-js";
import { delay } from "../utils/delay";
import * as TypesGen from "./typesGenerated";
export const axiosInstance = globalAxios.create();
// Adds 304 for the default axios validateStatus function
// https://github.com/axios/axios#handling-errors Check status here
// https://httpstatusdogs.com/
axiosInstance.defaults.validateStatus = (status) => {
return (status >= 200 && status < 300) || status === 304;
};
export const hardCodedCSRFCookie = (): string => {
// This is a hard coded CSRF token/cookie pair for local development. In prod,
// the GoLang webserver generates a random cookie with a new token for each
// document request. For local development, we don't use the Go webserver for
// static files, so this is the 'hack' to make local development work with
// remote apis. The CSRF cookie for this token is
// "JXm9hOUdZctWt0ZZGAy9xiS/gxMKYOThdxjjMnMUyn4="
const csrfToken =
"KNKvagCBEHZK7ihe2t7fj6VeJ0UyTDco1yVUJE8N06oNqxLu5Zx1vRxZbgfC0mJJgeGkVjgs08mgPbcWPBkZ1A==";
axiosInstance.defaults.headers.common["X-CSRF-TOKEN"] = csrfToken;
return csrfToken;
};
// withDefaultFeatures sets all unspecified features to not_entitled and
// disabled.
export const withDefaultFeatures = (
fs: Partial<TypesGen.Entitlements["features"]>,
): TypesGen.Entitlements["features"] => {
for (const feature of TypesGen.FeatureNames) {
// Skip fields that are already filled.
if (fs[feature] !== undefined) {
continue;
}
fs[feature] = {
enabled: false,
entitlement: "not_entitled",
};
}
return fs as TypesGen.Entitlements["features"];
};
// Always attach CSRF token to all requests. In puppeteer the document is
// undefined. In those cases, just do nothing.
const token =
typeof document !== "undefined"
? document.head.querySelector('meta[property="csrf-token"]')
: null;
if (token !== null && token.getAttribute("content") !== null) {
if (process.env.NODE_ENV === "development") {
// Development mode uses a hard-coded CSRF token
axiosInstance.defaults.headers.common["X-CSRF-TOKEN"] =
hardCodedCSRFCookie();
token.setAttribute("content", hardCodedCSRFCookie());
} else {
axiosInstance.defaults.headers.common["X-CSRF-TOKEN"] =
token.getAttribute("content") ?? "";
}
} else {
// Do not write error logs if we are in a FE unit test.
if (process.env.JEST_WORKER_ID === undefined) {
console.error("CSRF token not found");
}
}
export const setSessionToken = (token: string) => {
axiosInstance.defaults.headers.common["Coder-Session-Token"] = token;
};
export const setHost = (host?: string) => {
axiosInstance.defaults.baseURL = host;
};
const CONTENT_TYPE_JSON = {
"Content-Type": "application/json",
};
export const provisioners: TypesGen.ProvisionerDaemon[] = [
{
id: "terraform",
name: "Terraform",
created_at: "",
provisioners: [],
tags: {},
version: "v2.34.5",
api_version: "1.0",
},
{
id: "cdr-basic",
name: "Basic",
created_at: "",
provisioners: [],
tags: {},
version: "v2.34.5",
api_version: "1.0",
},
];
export const login = async (
email: string,
password: string,
): Promise<TypesGen.LoginWithPasswordResponse> => {
const payload = JSON.stringify({
email,
password,
});
const response = await axiosInstance.post<TypesGen.LoginWithPasswordResponse>(
"/api/v2/users/login",
payload,
{
headers: { ...CONTENT_TYPE_JSON },
},
);
return response.data;
};
export const convertToOAUTH = async (request: TypesGen.ConvertLoginRequest) => {
const response = await axiosInstance.post<TypesGen.OAuthConversionResponse>(
"/api/v2/users/me/convert-login",
request,
);
return response.data;
};
export const logout = async (): Promise<void> => {
await axiosInstance.post("/api/v2/users/logout");
};
export const getAuthenticatedUser = async () => {
const response = await axiosInstance.get<TypesGen.User>("/api/v2/users/me");
return response.data;
};
export const getUserParameters = async (templateID: string) => {
const response = await axiosInstance.get<TypesGen.UserParameter[]>(
"/api/v2/users/me/autofill-parameters?template_id=" + templateID,
);
return response.data;
};
export const getAuthMethods = async (): Promise<TypesGen.AuthMethods> => {
const response = await axiosInstance.get<TypesGen.AuthMethods>(
"/api/v2/users/authmethods",
);
return response.data;
};
export const getUserLoginType = async (): Promise<TypesGen.UserLoginType> => {
const response = await axiosInstance.get<TypesGen.UserLoginType>(
"/api/v2/users/me/login-type",
);
return response.data;
};
export const checkAuthorization = async (
params: TypesGen.AuthorizationRequest,
): Promise<TypesGen.AuthorizationResponse> => {
const response = await axiosInstance.post<TypesGen.AuthorizationResponse>(
`/api/v2/authcheck`,
params,
);
return response.data;
};
export const getApiKey = async (): Promise<TypesGen.GenerateAPIKeyResponse> => {
const response = await axiosInstance.post<TypesGen.GenerateAPIKeyResponse>(
"/api/v2/users/me/keys",
);
return response.data;
};
export const getTokens = async (
params: TypesGen.TokensFilter,
): Promise<TypesGen.APIKeyWithOwner[]> => {
const response = await axiosInstance.get<TypesGen.APIKeyWithOwner[]>(
`/api/v2/users/me/keys/tokens`,
{
params,
},
);
return response.data;
};
export const deleteToken = async (keyId: string): Promise<void> => {
await axiosInstance.delete("/api/v2/users/me/keys/" + keyId);
};
export const createToken = async (
params: TypesGen.CreateTokenRequest,
): Promise<TypesGen.GenerateAPIKeyResponse> => {
const response = await axiosInstance.post(
`/api/v2/users/me/keys/tokens`,
params,
);
return response.data;
};
export const getTokenConfig = async (): Promise<TypesGen.TokenConfig> => {
const response = await axiosInstance.get(
"/api/v2/users/me/keys/tokens/tokenconfig",
);
return response.data;
};
export const getUsers = async (
options: TypesGen.UsersRequest,
signal?: AbortSignal,
): Promise<TypesGen.GetUsersResponse> => {
const url = getURLWithSearchParams("/api/v2/users", options);
const response = await axiosInstance.get<TypesGen.GetUsersResponse>(
url.toString(),
{
signal,
},
);
return response.data;
};
export const getOrganization = async (
organizationId: string,
): Promise<TypesGen.Organization> => {
const response = await axiosInstance.get<TypesGen.Organization>(
`/api/v2/organizations/${organizationId}`,
);
return response.data;
};
export const getOrganizations = async (): Promise<TypesGen.Organization[]> => {
const response = await axiosInstance.get<TypesGen.Organization[]>(
"/api/v2/users/me/organizations",
);
return response.data;
};
export const getTemplate = async (
templateId: string,
): Promise<TypesGen.Template> => {
const response = await axiosInstance.get<TypesGen.Template>(
`/api/v2/templates/${templateId}`,
);
return response.data;
};
export interface TemplateOptions {
readonly deprecated?: boolean;
}
export const getTemplates = async (
organizationId: string,
options?: TemplateOptions,
): Promise<TypesGen.Template[]> => {
const params = {} as Record<string, string>;
if (options && options.deprecated !== undefined) {
// Just want to check if it isn't undefined. If it has
// a boolean value, convert it to a string and include
// it as a param.
params["deprecated"] = String(options.deprecated);
}
const response = await axiosInstance.get<TypesGen.Template[]>(
`/api/v2/organizations/${organizationId}/templates`,
{
params,
},
);
return response.data;
};
export const getTemplateByName = async (
organizationId: string,
name: string,
): Promise<TypesGen.Template> => {
const response = await axiosInstance.get<TypesGen.Template>(
`/api/v2/organizations/${organizationId}/templates/${name}`,
);
return response.data;
};
export const getTemplateVersion = async (
versionId: string,
): Promise<TypesGen.TemplateVersion> => {
const response = await axiosInstance.get<TypesGen.TemplateVersion>(
`/api/v2/templateversions/${versionId}`,
);
return response.data;
};
export const getTemplateVersionResources = async (
versionId: string,
): Promise<TypesGen.WorkspaceResource[]> => {
const response = await axiosInstance.get<TypesGen.WorkspaceResource[]>(
`/api/v2/templateversions/${versionId}/resources`,
);
return response.data;
};
export const getTemplateVersionVariables = async (
versionId: string,
): Promise<TypesGen.TemplateVersionVariable[]> => {
const response = await axiosInstance.get<TypesGen.TemplateVersionVariable[]>(
`/api/v2/templateversions/${versionId}/variables`,
);
return response.data;
};
export const getTemplateVersions = async (
templateId: string,
): Promise<TypesGen.TemplateVersion[]> => {
const response = await axiosInstance.get<TypesGen.TemplateVersion[]>(
`/api/v2/templates/${templateId}/versions`,
);
return response.data;
};
export const getTemplateVersionByName = async (
organizationId: string,
templateName: string,
versionName: string,
): Promise<TypesGen.TemplateVersion> => {
const response = await axiosInstance.get<TypesGen.TemplateVersion>(
`/api/v2/organizations/${organizationId}/templates/${templateName}/versions/${versionName}`,
);
return response.data;
};
export type GetPreviousTemplateVersionByNameResponse =
| TypesGen.TemplateVersion
| undefined;
export const getPreviousTemplateVersionByName = async (
organizationId: string,
templateName: string,
versionName: string,
) => {
try {
const response = await axiosInstance.get<TypesGen.TemplateVersion>(
`/api/v2/organizations/${organizationId}/templates/${templateName}/versions/${versionName}/previous`,
);
return response.data;
} catch (error) {
// When there is no previous version, like the first version of a template,
// the API returns 404 so in this case we can safely return undefined
if (
isAxiosError(error) &&
error.response &&
error.response.status === 404
) {
return undefined;
}
throw error;
}
};
export const createTemplateVersion = async (
organizationId: string,
data: TypesGen.CreateTemplateVersionRequest,
): Promise<TypesGen.TemplateVersion> => {
const response = await axiosInstance.post<TypesGen.TemplateVersion>(
`/api/v2/organizations/${organizationId}/templateversions`,
data,
);
return response.data;
};
export const getTemplateVersionExternalAuth = async (
versionId: string,
): Promise<TypesGen.TemplateVersionExternalAuth[]> => {
const response = await axiosInstance.get(
`/api/v2/templateversions/${versionId}/external-auth`,
);
return response.data;
};
export const getTemplateVersionRichParameters = async (
versionId: string,
): Promise<TypesGen.TemplateVersionParameter[]> => {
const response = await axiosInstance.get(
`/api/v2/templateversions/${versionId}/rich-parameters`,
);
return response.data;
};
export const createTemplate = async (
organizationId: string,
data: TypesGen.CreateTemplateRequest,
): Promise<TypesGen.Template> => {
const response = await axiosInstance.post(
`/api/v2/organizations/${organizationId}/templates`,
data,
);
return response.data;
};
export const updateActiveTemplateVersion = async (
templateId: string,
data: TypesGen.UpdateActiveTemplateVersion,
) => {
const response = await axiosInstance.patch<TypesGen.Response>(
`/api/v2/templates/${templateId}/versions`,
data,
);
return response.data;
};
export const patchTemplateVersion = async (
templateVersionId: string,
data: TypesGen.PatchTemplateVersionRequest,
) => {
const response = await axiosInstance.patch<TypesGen.TemplateVersion>(
`/api/v2/templateversions/${templateVersionId}`,
data,
);
return response.data;
};
export const archiveTemplateVersion = async (templateVersionId: string) => {
const response = await axiosInstance.post<TypesGen.TemplateVersion>(
`/api/v2/templateversions/${templateVersionId}/archive`,
);
return response.data;
};
export const unarchiveTemplateVersion = async (templateVersionId: string) => {
const response = await axiosInstance.post<TypesGen.TemplateVersion>(
`/api/v2/templateversions/${templateVersionId}/unarchive`,
);
return response.data;
};
export const updateTemplateMeta = async (
templateId: string,
data: TypesGen.UpdateTemplateMeta,
): Promise<TypesGen.Template | null> => {
const response = await axiosInstance.patch<TypesGen.Template>(
`/api/v2/templates/${templateId}`,
data,
);
// On 304 response there is no data payload.
if (response.status === 304) {
return null;
}
return response.data;
};
export const deleteTemplate = async (
templateId: string,
): Promise<TypesGen.Template> => {
const response = await axiosInstance.delete<TypesGen.Template>(
`/api/v2/templates/${templateId}`,
);
return response.data;
};
export const getWorkspace = async (
workspaceId: string,
params?: TypesGen.WorkspaceOptions,
): Promise<TypesGen.Workspace> => {
const response = await axiosInstance.get<TypesGen.Workspace>(
`/api/v2/workspaces/${workspaceId}`,
{
params,
},
);
return response.data;
};
/**
*
* @param workspaceId
* @returns An EventSource that emits workspace event objects (ServerSentEvent)
*/
export const watchWorkspace = (workspaceId: string): EventSource => {
return new EventSource(
`${location.protocol}//${location.host}/api/v2/workspaces/${workspaceId}/watch`,
{ withCredentials: true },
);
};
interface SearchParamOptions extends TypesGen.Pagination {
q?: string;
}
export const getURLWithSearchParams = (
basePath: string,
options?: SearchParamOptions,
): string => {
if (options) {
const searchParams = new URLSearchParams();
const keys = Object.keys(options) as (keyof SearchParamOptions)[];
keys.forEach((key) => {
const value = options[key];
if (value !== undefined && value !== "") {
searchParams.append(key, value.toString());
}
});
const searchString = searchParams.toString();
return searchString ? `${basePath}?${searchString}` : basePath;
} else {
return basePath;
}
};
export const getWorkspaces = async (
options: TypesGen.WorkspacesRequest,
): Promise<TypesGen.WorkspacesResponse> => {
const url = getURLWithSearchParams("/api/v2/workspaces", options);
const response = await axiosInstance.get<TypesGen.WorkspacesResponse>(url);
return response.data;
};
export const getWorkspaceByOwnerAndName = async (
username = "me",
workspaceName: string,
params?: TypesGen.WorkspaceOptions,
): Promise<TypesGen.Workspace> => {
const response = await axiosInstance.get<TypesGen.Workspace>(
`/api/v2/users/${username}/workspace/${workspaceName}`,
{
params,
},
);
return response.data;
};
export function waitForBuild(build: TypesGen.WorkspaceBuild) {
return new Promise<TypesGen.ProvisionerJob | undefined>((res, reject) => {
void (async () => {
let latestJobInfo: TypesGen.ProvisionerJob | undefined = undefined;
while (
!["succeeded", "canceled"].some(
(status) => latestJobInfo?.status.includes(status),
)
) {
const { job } = await getWorkspaceBuildByNumber(
build.workspace_owner_name,
build.workspace_name,
build.build_number,
);
latestJobInfo = job;
if (latestJobInfo.status === "failed") {
return reject(latestJobInfo);
}
await delay(1000);
}
return res(latestJobInfo);
})();
});
}
export const postWorkspaceBuild = async (
workspaceId: string,
data: TypesGen.CreateWorkspaceBuildRequest,
): Promise<TypesGen.WorkspaceBuild> => {
const response = await axiosInstance.post(
`/api/v2/workspaces/${workspaceId}/builds`,
data,
);
return response.data;
};
export const startWorkspace = (
workspaceId: string,
templateVersionId: string,
logLevel?: TypesGen.ProvisionerLogLevel,
buildParameters?: TypesGen.WorkspaceBuildParameter[],
) =>
postWorkspaceBuild(workspaceId, {
transition: "start",
template_version_id: templateVersionId,
log_level: logLevel,
rich_parameter_values: buildParameters,
});
export const stopWorkspace = (
workspaceId: string,
logLevel?: TypesGen.ProvisionerLogLevel,
) =>
postWorkspaceBuild(workspaceId, {
transition: "stop",
log_level: logLevel,
});
export type DeleteWorkspaceOptions = Pick<
TypesGen.CreateWorkspaceBuildRequest,
"log_level" & "orphan"
>;
export const deleteWorkspace = (
workspaceId: string,
options?: DeleteWorkspaceOptions,
) =>
postWorkspaceBuild(workspaceId, {
transition: "delete",
...options,
});
export const cancelWorkspaceBuild = async (
workspaceBuildId: TypesGen.WorkspaceBuild["id"],
): Promise<TypesGen.Response> => {
const response = await axiosInstance.patch(
`/api/v2/workspacebuilds/${workspaceBuildId}/cancel`,
);
return response.data;
};
export const updateWorkspaceDormancy = async (
workspaceId: string,
dormant: boolean,
): Promise<TypesGen.Workspace> => {
const data: TypesGen.UpdateWorkspaceDormancy = {
dormant: dormant,
};
const response = await axiosInstance.put(
`/api/v2/workspaces/${workspaceId}/dormant`,
data,
);
return response.data;
};
export const updateWorkspaceAutomaticUpdates = async (
workspaceId: string,
automaticUpdates: TypesGen.AutomaticUpdates,
): Promise<void> => {
const req: TypesGen.UpdateWorkspaceAutomaticUpdatesRequest = {
automatic_updates: automaticUpdates,
};
const response = await axiosInstance.put(
`/api/v2/workspaces/${workspaceId}/autoupdates`,
req,
);
return response.data;
};
export const restartWorkspace = async ({
workspace,
buildParameters,
}: {
workspace: TypesGen.Workspace;
buildParameters?: TypesGen.WorkspaceBuildParameter[];
}) => {
const stopBuild = await stopWorkspace(workspace.id);
const awaitedStopBuild = await waitForBuild(stopBuild);
// If the restart is canceled halfway through, make sure we bail
if (awaitedStopBuild?.status === "canceled") {
return;
}
const startBuild = await startWorkspace(
workspace.id,
workspace.latest_build.template_version_id,
undefined,
buildParameters,
);
await waitForBuild(startBuild);
};
export const cancelTemplateVersionBuild = async (
templateVersionId: TypesGen.TemplateVersion["id"],
): Promise<TypesGen.Response> => {
const response = await axiosInstance.patch(
`/api/v2/templateversions/${templateVersionId}/cancel`,
);
return response.data;
};
export const createUser = async (
user: TypesGen.CreateUserRequest,
): Promise<TypesGen.User> => {
const response = await axiosInstance.post<TypesGen.User>(
"/api/v2/users",
user,
);
return response.data;
};
export const createWorkspace = async (
organizationId: string,
userId = "me",
workspace: TypesGen.CreateWorkspaceRequest,
): Promise<TypesGen.Workspace> => {
const response = await axiosInstance.post<TypesGen.Workspace>(
`/api/v2/organizations/${organizationId}/members/${userId}/workspaces`,
workspace,
);
return response.data;
};
export const patchWorkspace = async (
workspaceId: string,
data: TypesGen.UpdateWorkspaceRequest,
) => {
await axiosInstance.patch(`/api/v2/workspaces/${workspaceId}`, data);
};
export const getBuildInfo = async (): Promise<TypesGen.BuildInfoResponse> => {
const response = await axiosInstance.get("/api/v2/buildinfo");
return response.data;
};
export const getUpdateCheck =
async (): Promise<TypesGen.UpdateCheckResponse> => {
const response = await axiosInstance.get("/api/v2/updatecheck");
return response.data;
};
export const putWorkspaceAutostart = async (
workspaceID: string,
autostart: TypesGen.UpdateWorkspaceAutostartRequest,
): Promise<void> => {
const payload = JSON.stringify(autostart);
await axiosInstance.put(
`/api/v2/workspaces/${workspaceID}/autostart`,
payload,
{
headers: { ...CONTENT_TYPE_JSON },
},
);
};
export const putWorkspaceAutostop = async (
workspaceID: string,
ttl: TypesGen.UpdateWorkspaceTTLRequest,
): Promise<void> => {
const payload = JSON.stringify(ttl);
await axiosInstance.put(`/api/v2/workspaces/${workspaceID}/ttl`, payload, {
headers: { ...CONTENT_TYPE_JSON },
});
};
export const updateProfile = async (
userId: string,
data: TypesGen.UpdateUserProfileRequest,
): Promise<TypesGen.User> => {
const response = await axiosInstance.put(
`/api/v2/users/${userId}/profile`,
data,
);
return response.data;
};
export const updateAppearanceSettings = async (
userId: string,
data: TypesGen.UpdateUserAppearanceSettingsRequest,
): Promise<TypesGen.User> => {
const response = await axiosInstance.put(
`/api/v2/users/${userId}/appearance`,
data,
);
return response.data;
};
export const getUserQuietHoursSchedule = async (
userId: TypesGen.User["id"],
): Promise<TypesGen.UserQuietHoursScheduleResponse> => {
const response = await axiosInstance.get(
`/api/v2/users/${userId}/quiet-hours`,
);
return response.data;
};
export const updateUserQuietHoursSchedule = async (
userId: TypesGen.User["id"],
data: TypesGen.UpdateUserQuietHoursScheduleRequest,
): Promise<TypesGen.UserQuietHoursScheduleResponse> => {
const response = await axiosInstance.put(
`/api/v2/users/${userId}/quiet-hours`,
data,
);
return response.data;
};
export const activateUser = async (
userId: TypesGen.User["id"],
): Promise<TypesGen.User> => {
const response = await axiosInstance.put<TypesGen.User>(
`/api/v2/users/${userId}/status/activate`,
);
return response.data;
};
export const suspendUser = async (
userId: TypesGen.User["id"],
): Promise<TypesGen.User> => {
const response = await axiosInstance.put<TypesGen.User>(
`/api/v2/users/${userId}/status/suspend`,
);
return response.data;
};
export const deleteUser = async (
userId: TypesGen.User["id"],
): Promise<undefined> => {
return await axiosInstance.delete(`/api/v2/users/${userId}`);
};
// API definition:
// https://github.com/coder/coder/blob/db665e7261f3c24a272ccec48233a3e276878239/coderd/users.go#L33-L53
export const hasFirstUser = async (): Promise<boolean> => {
try {
// If it is success, it is true
await axiosInstance.get("/api/v2/users/first");
return true;
} catch (error) {
// If it returns a 404, it is false
if (isAxiosError(error) && error.response?.status === 404) {
return false;
}
throw error;
}
};
export const createFirstUser = async (
req: TypesGen.CreateFirstUserRequest,
): Promise<TypesGen.CreateFirstUserResponse> => {
const response = await axiosInstance.post(`/api/v2/users/first`, req);
return response.data;
};
export const updateUserPassword = async (
userId: TypesGen.User["id"],
updatePassword: TypesGen.UpdateUserPasswordRequest,
): Promise<undefined> =>
axiosInstance.put(`/api/v2/users/${userId}/password`, updatePassword);
export const getRoles = async (): Promise<Array<TypesGen.AssignableRoles>> => {
const response =
await axiosInstance.get<Array<TypesGen.AssignableRoles>>(
`/api/v2/users/roles`,
);
return response.data;
};
export const updateUserRoles = async (
roles: TypesGen.Role["name"][],
userId: TypesGen.User["id"],
): Promise<TypesGen.User> => {
const response = await axiosInstance.put<TypesGen.User>(
`/api/v2/users/${userId}/roles`,
{ roles },
);
return response.data;
};
export const getUserSSHKey = async (
userId = "me",
): Promise<TypesGen.GitSSHKey> => {
const response = await axiosInstance.get<TypesGen.GitSSHKey>(
`/api/v2/users/${userId}/gitsshkey`,
);
return response.data;
};
export const regenerateUserSSHKey = async (
userId = "me",
): Promise<TypesGen.GitSSHKey> => {
const response = await axiosInstance.put<TypesGen.GitSSHKey>(
`/api/v2/users/${userId}/gitsshkey`,
);
return response.data;
};
export const getWorkspaceBuilds = async (
workspaceId: string,
req?: TypesGen.WorkspaceBuildsRequest,
) => {
const response = await axiosInstance.get<TypesGen.WorkspaceBuild[]>(
getURLWithSearchParams(`/api/v2/workspaces/${workspaceId}/builds`, req),
);
return response.data;
};
export const getWorkspaceBuildByNumber = async (
username = "me",
workspaceName: string,
buildNumber: number,
): Promise<TypesGen.WorkspaceBuild> => {
const response = await axiosInstance.get<TypesGen.WorkspaceBuild>(
`/api/v2/users/${username}/workspace/${workspaceName}/builds/${buildNumber}`,
);
return response.data;
};
export const getWorkspaceBuildLogs = async (
buildId: string,
before: Date,
): Promise<TypesGen.ProvisionerJobLog[]> => {
const response = await axiosInstance.get<TypesGen.ProvisionerJobLog[]>(
`/api/v2/workspacebuilds/${buildId}/logs?before=${before.getTime()}`,
);
return response.data;
};
export const getWorkspaceAgentLogs = async (
agentID: string,
): Promise<TypesGen.WorkspaceAgentLog[]> => {
const response = await axiosInstance.get<TypesGen.WorkspaceAgentLog[]>(
`/api/v2/workspaceagents/${agentID}/logs`,
);
return response.data;
};
export const putWorkspaceExtension = async (
workspaceId: string,
newDeadline: dayjs.Dayjs,
): Promise<void> => {
await axiosInstance.put(`/api/v2/workspaces/${workspaceId}/extend`, {
deadline: newDeadline,
});
};
export const refreshEntitlements = async (): Promise<void> => {
await axiosInstance.post("/api/v2/licenses/refresh-entitlements");
};
export const getEntitlements = async (): Promise<TypesGen.Entitlements> => {
try {
const response = await axiosInstance.get("/api/v2/entitlements");
return response.data;
} catch (ex) {
if (isAxiosError(ex) && ex.response?.status === 404) {
return {
errors: [],
features: withDefaultFeatures({}),
has_license: false,
require_telemetry: false,
trial: false,
warnings: [],
refreshed_at: "",
};
}
throw ex;
}
};
export const getExperiments = async (): Promise<TypesGen.Experiment[]> => {
try {
const response = await axiosInstance.get("/api/v2/experiments");
return response.data;
} catch (error) {
if (isAxiosError(error) && error.response?.status === 404) {
return [];
}
throw error;
}
};
export const getAvailableExperiments =
async (): Promise<TypesGen.AvailableExperiments> => {
try {
const response = await axiosInstance.get("/api/v2/experiments/available");
return response.data;
} catch (error) {
if (isAxiosError(error) && error.response?.status === 404) {
return { safe: [] };
}
throw error;
}
};
export const getExternalAuthProvider = async (
provider: string,
): Promise<TypesGen.ExternalAuth> => {
const resp = await axiosInstance.get(`/api/v2/external-auth/${provider}`);
return resp.data;
};
export const getExternalAuthDevice = async (
provider: string,
): Promise<TypesGen.ExternalAuthDevice> => {
const resp = await axiosInstance.get(
`/api/v2/external-auth/${provider}/device`,
);
return resp.data;
};
export const exchangeExternalAuthDevice = async (
provider: string,
req: TypesGen.ExternalAuthDeviceExchange,
): Promise<void> => {
const resp = await axiosInstance.post(
`/api/v2/external-auth/${provider}/device`,
req,
);
return resp.data;
};
export const getUserExternalAuthProviders =
async (): Promise<TypesGen.ListUserExternalAuthResponse> => {
const resp = await axiosInstance.get(`/api/v2/external-auth`);
return resp.data;
};
export const unlinkExternalAuthProvider = async (
provider: string,
): Promise<string> => {
const resp = await axiosInstance.delete(`/api/v2/external-auth/${provider}`);
return resp.data;
};
export const getOAuth2ProviderApps = async (
filter?: TypesGen.OAuth2ProviderAppFilter,
): Promise<TypesGen.OAuth2ProviderApp[]> => {
const params = filter?.user_id
? new URLSearchParams({ user_id: filter.user_id })
: "";
const resp = await axiosInstance.get(
`/api/v2/oauth2-provider/apps?${params}`,
);
return resp.data;
};
export const getOAuth2ProviderApp = async (
id: string,
): Promise<TypesGen.OAuth2ProviderApp> => {
const resp = await axiosInstance.get(`/api/v2/oauth2-provider/apps/${id}`);
return resp.data;
};
export const postOAuth2ProviderApp = async (
data: TypesGen.PostOAuth2ProviderAppRequest,
): Promise<TypesGen.OAuth2ProviderApp> => {
const response = await axiosInstance.post(
`/api/v2/oauth2-provider/apps`,
data,
);
return response.data;
};
export const putOAuth2ProviderApp = async (
id: string,
data: TypesGen.PutOAuth2ProviderAppRequest,
): Promise<TypesGen.OAuth2ProviderApp> => {
const response = await axiosInstance.put(
`/api/v2/oauth2-provider/apps/${id}`,
data,
);
return response.data;
};
export const deleteOAuth2ProviderApp = async (id: string): Promise<void> => {
await axiosInstance.delete(`/api/v2/oauth2-provider/apps/${id}`);
};
export const getOAuth2ProviderAppSecrets = async (
id: string,
): Promise<TypesGen.OAuth2ProviderAppSecret[]> => {
const resp = await axiosInstance.get(
`/api/v2/oauth2-provider/apps/${id}/secrets`,
);
return resp.data;
};
export const postOAuth2ProviderAppSecret = async (
id: string,
): Promise<TypesGen.OAuth2ProviderAppSecretFull> => {
const resp = await axiosInstance.post(
`/api/v2/oauth2-provider/apps/${id}/secrets`,
);
return resp.data;
};
export const deleteOAuth2ProviderAppSecret = async (
appId: string,
secretId: string,
): Promise<void> => {
await axiosInstance.delete(
`/api/v2/oauth2-provider/apps/${appId}/secrets/${secretId}`,
);
};
export const revokeOAuth2ProviderApp = async (appId: string): Promise<void> => {
await axiosInstance.delete(`/oauth2/tokens?client_id=${appId}`);
};
export const getAuditLogs = async (
options: TypesGen.AuditLogsRequest,
): Promise<TypesGen.AuditLogResponse> => {
const url = getURLWithSearchParams("/api/v2/audit", options);
const response = await axiosInstance.get(url);
return response.data;
};
export const getTemplateDAUs = async (
templateId: string,
): Promise<TypesGen.DAUsResponse> => {
const response = await axiosInstance.get(
`/api/v2/templates/${templateId}/daus`,
);
return response.data;
};
export const getDeploymentDAUs = async (
// Default to user's local timezone.
// As /api/v2/insights/daus only accepts whole-number values for tz_offset
// we truncate the tz offset down to the closest hour.
offset = Math.trunc(new Date().getTimezoneOffset() / 60),
): Promise<TypesGen.DAUsResponse> => {
const response = await axiosInstance.get(
`/api/v2/insights/daus?tz_offset=${offset}`,
);
return response.data;
};
export const getTemplateACLAvailable = async (
templateId: string,
options: TypesGen.UsersRequest,
): Promise<TypesGen.ACLAvailable> => {
const url = getURLWithSearchParams(
`/api/v2/templates/${templateId}/acl/available`,
options,
);
const response = await axiosInstance.get(url.toString());
return response.data;
};
export const getTemplateACL = async (
templateId: string,
): Promise<TypesGen.TemplateACL> => {
const response = await axiosInstance.get(
`/api/v2/templates/${templateId}/acl`,
);
return response.data;
};
export const updateTemplateACL = async (
templateId: string,
data: TypesGen.UpdateTemplateACL,
): Promise<{ message: string }> => {
const response = await axiosInstance.patch(
`/api/v2/templates/${templateId}/acl`,
data,
);
return response.data;
};
export const getApplicationsHost =
async (): Promise<TypesGen.AppHostResponse> => {
const response = await axiosInstance.get(`/api/v2/applications/host`);
return response.data;
};
export const getGroups = async (
organizationId: string,
): Promise<TypesGen.Group[]> => {
const response = await axiosInstance.get(
`/api/v2/organizations/${organizationId}/groups`,
);
return response.data;
};
export const createGroup = async (
organizationId: string,
data: TypesGen.CreateGroupRequest,
): Promise<TypesGen.Group> => {
const response = await axiosInstance.post(
`/api/v2/organizations/${organizationId}/groups`,
data,
);
return response.data;
};
export const getGroup = async (groupId: string): Promise<TypesGen.Group> => {
const response = await axiosInstance.get(`/api/v2/groups/${groupId}`);
return response.data;
};
export const patchGroup = async (
groupId: string,
data: TypesGen.PatchGroupRequest,
): Promise<TypesGen.Group> => {
const response = await axiosInstance.patch(`/api/v2/groups/${groupId}`, data);
return response.data;
};
export const addMember = async (groupId: string, userId: string) => {
return patchGroup(groupId, {
name: "",
add_users: [userId],
remove_users: [],
});
};
export const removeMember = async (groupId: string, userId: string) => {
return patchGroup(groupId, {
name: "",
display_name: "",
add_users: [],
remove_users: [userId],
});
};
export const deleteGroup = async (groupId: string): Promise<void> => {
await axiosInstance.delete(`/api/v2/groups/${groupId}`);
};
export const getWorkspaceQuota = async (
username: string,
): Promise<TypesGen.WorkspaceQuota> => {
const response = await axiosInstance.get(
`/api/v2/workspace-quota/${encodeURIComponent(username)}`,
);
return response.data;
};
export const getAgentListeningPorts = async (
agentID: string,
): Promise<TypesGen.WorkspaceAgentListeningPortsResponse> => {
const response = await axiosInstance.get(
`/api/v2/workspaceagents/${agentID}/listening-ports`,
);
return response.data;
};
export const getWorkspaceAgentSharedPorts = async (
workspaceID: string,
): Promise<TypesGen.WorkspaceAgentPortShares> => {
const response = await axiosInstance.get(
`/api/v2/workspaces/${workspaceID}/port-share`,
);
return response.data;
};
export const upsertWorkspaceAgentSharedPort = async (
workspaceID: string,
req: TypesGen.UpsertWorkspaceAgentPortShareRequest,
): Promise<TypesGen.WorkspaceAgentPortShares> => {
const response = await axiosInstance.post(
`/api/v2/workspaces/${workspaceID}/port-share`,
req,
);
return response.data;
};
export const deleteWorkspaceAgentSharedPort = async (
workspaceID: string,
req: TypesGen.DeleteWorkspaceAgentPortShareRequest,
): Promise<TypesGen.WorkspaceAgentPortShares> => {
const response = await axiosInstance.delete(
`/api/v2/workspaces/${workspaceID}/port-share`,
{
data: req,
},
);
return response.data;
};
// getDeploymentSSHConfig is used by the VSCode-Extension.
export const getDeploymentSSHConfig =
async (): Promise<TypesGen.SSHConfigResponse> => {
const response = await axiosInstance.get(`/api/v2/deployment/ssh`);
return response.data;
};
export type DeploymentConfig = {
readonly config: TypesGen.DeploymentValues;
readonly options: TypesGen.SerpentOption[];
};
export const getDeploymentConfig = async (): Promise<DeploymentConfig> => {
const response = await axiosInstance.get(`/api/v2/deployment/config`);
return response.data;
};
export const getDeploymentStats =
async (): Promise<TypesGen.DeploymentStats> => {
const response = await axiosInstance.get(`/api/v2/deployment/stats`);
return response.data;
};
export const getReplicas = async (): Promise<TypesGen.Replica[]> => {
const response = await axiosInstance.get(`/api/v2/replicas`);
return response.data;
};
export const getFile = async (fileId: string): Promise<ArrayBuffer> => {
const response = await axiosInstance.get<ArrayBuffer>(
`/api/v2/files/${fileId}`,
{
responseType: "arraybuffer",
},
);
return response.data;
};
export const getWorkspaceProxyRegions = async (): Promise<
TypesGen.RegionsResponse<TypesGen.Region>
> => {
const response =
await axiosInstance.get<TypesGen.RegionsResponse<TypesGen.Region>>(
`/api/v2/regions`,
);
return response.data;
};
export const getWorkspaceProxies = async (): Promise<
TypesGen.RegionsResponse<TypesGen.WorkspaceProxy>
> => {
const response = await axiosInstance.get<
TypesGen.RegionsResponse<TypesGen.WorkspaceProxy>
>(`/api/v2/workspaceproxies`);
return response.data;
};
export const createWorkspaceProxy = async (
b: TypesGen.CreateWorkspaceProxyRequest,
): Promise<TypesGen.UpdateWorkspaceProxyResponse> => {
const response = await axiosInstance.post(`/api/v2/workspaceproxies`, b);
return response.data;
};
export const getAppearance = async (): Promise<TypesGen.AppearanceConfig> => {
try {
const response = await axiosInstance.get(`/api/v2/appearance`);
return response.data || {};
} catch (ex) {
if (isAxiosError(ex) && ex.response?.status === 404) {
return {
application_name: "",
logo_url: "",
service_banner: {
enabled: false,
},
};
}
throw ex;
}
};
export const updateAppearance = async (
b: TypesGen.AppearanceConfig,
): Promise<TypesGen.AppearanceConfig> => {
const response = await axiosInstance.put(`/api/v2/appearance`, b);
return response.data;
};
export const getTemplateExamples = async (
organizationId: string,
): Promise<TypesGen.TemplateExample[]> => {
const response = await axiosInstance.get(
`/api/v2/organizations/${organizationId}/templates/examples`,
);
return response.data;
};
export const uploadFile = async (
file: File,
): Promise<TypesGen.UploadResponse> => {
const response = await axiosInstance.post("/api/v2/files", file, {
headers: {
"Content-Type": "application/x-tar",
},
});
return response.data;
};
export const getTemplateVersionLogs = async (
versionId: string,
): Promise<TypesGen.ProvisionerJobLog[]> => {
const response = await axiosInstance.get<TypesGen.ProvisionerJobLog[]>(
`/api/v2/templateversions/${versionId}/logs`,
);
return response.data;
};
export const updateWorkspaceVersion = async (
workspace: TypesGen.Workspace,
): Promise<TypesGen.WorkspaceBuild> => {
const template = await getTemplate(workspace.template_id);
return startWorkspace(workspace.id, template.active_version_id);
};
export const getWorkspaceBuildParameters = async (
workspaceBuildId: TypesGen.WorkspaceBuild["id"],
): Promise<TypesGen.WorkspaceBuildParameter[]> => {
const response = await axiosInstance.get<TypesGen.WorkspaceBuildParameter[]>(
`/api/v2/workspacebuilds/${workspaceBuildId}/parameters`,
);
return response.data;
};
type Claims = {
license_expires: number;
account_type?: string;
account_id?: string;
trial: boolean;
all_features: boolean;
version: number;
features: Record<string, number>;
require_telemetry?: boolean;
};
export type GetLicensesResponse = Omit<TypesGen.License, "claims"> & {
claims: Claims;
expires_at: string;
};
export const getLicenses = async (): Promise<GetLicensesResponse[]> => {
const response = await axiosInstance.get(`/api/v2/licenses`);
return response.data;
};
export const createLicense = async (
data: TypesGen.AddLicenseRequest,
): Promise<TypesGen.AddLicenseRequest> => {
const response = await axiosInstance.post(`/api/v2/licenses`, data);
return response.data;
};
export const removeLicense = async (licenseId: number): Promise<void> => {
await axiosInstance.delete(`/api/v2/licenses/${licenseId}`);
};
export class MissingBuildParameters extends Error {
parameters: TypesGen.TemplateVersionParameter[] = [];
versionId: string;
constructor(
parameters: TypesGen.TemplateVersionParameter[],
versionId: string,
) {
super("Missing build parameters.");
this.parameters = parameters;
this.versionId = versionId;
}
}
/** Steps to change the workspace version
* - Get the latest template to access the latest active version
* - Get the current build parameters
* - Get the template parameters
* - Update the build parameters and check if there are missed parameters for the new version
* - If there are missing parameters raise an error
* - Create a build with the version and updated build parameters
*/
export const changeWorkspaceVersion = async (
workspace: TypesGen.Workspace,
templateVersionId: string,
newBuildParameters: TypesGen.WorkspaceBuildParameter[] = [],
): Promise<TypesGen.WorkspaceBuild> => {
const [currentBuildParameters, templateParameters] = await Promise.all([
getWorkspaceBuildParameters(workspace.latest_build.id),
getTemplateVersionRichParameters(templateVersionId),
]);
const missingParameters = getMissingParameters(
currentBuildParameters,
newBuildParameters,
templateParameters,
);
if (missingParameters.length > 0) {
throw new MissingBuildParameters(missingParameters, templateVersionId);
}
return postWorkspaceBuild(workspace.id, {
transition: "start",
template_version_id: templateVersionId,
rich_parameter_values: newBuildParameters,
});
};
/** Steps to update the workspace
* - Get the latest template to access the latest active version
* - Get the current build parameters
* - Get the template parameters
* - Update the build parameters and check if there are missed parameters for
* the newest version
* - If there are missing parameters raise an error
* - Create a build with the latest version and updated build parameters
*/
export const updateWorkspace = async (
workspace: TypesGen.Workspace,
newBuildParameters: TypesGen.WorkspaceBuildParameter[] = [],
): Promise<TypesGen.WorkspaceBuild> => {
const [template, oldBuildParameters] = await Promise.all([
getTemplate(workspace.template_id),
getWorkspaceBuildParameters(workspace.latest_build.id),
]);
const activeVersionId = template.active_version_id;
const templateParameters =
await getTemplateVersionRichParameters(activeVersionId);
const missingParameters = getMissingParameters(
oldBuildParameters,
newBuildParameters,
templateParameters,
);
if (missingParameters.length > 0) {
throw new MissingBuildParameters(missingParameters, activeVersionId);
}
return postWorkspaceBuild(workspace.id, {
transition: "start",
template_version_id: activeVersionId,
rich_parameter_values: newBuildParameters,
});
};
export const getWorkspaceResolveAutostart = async (
workspaceId: string,
): Promise<TypesGen.ResolveAutostartResponse> => {
const response = await axiosInstance.get(
`/api/v2/workspaces/${workspaceId}/resolve-autostart`,
);
return response.data;
};
const getMissingParameters = (
oldBuildParameters: TypesGen.WorkspaceBuildParameter[],
newBuildParameters: TypesGen.WorkspaceBuildParameter[],
templateParameters: TypesGen.TemplateVersionParameter[],
) => {
const missingParameters: TypesGen.TemplateVersionParameter[] = [];
const requiredParameters: TypesGen.TemplateVersionParameter[] = [];
templateParameters.forEach((p) => {
// It is mutable and required. Mutable values can be changed after so we
// don't need to ask them if they are not required.
const isMutableAndRequired = p.mutable && p.required;
// Is immutable, so we can check if it is its first time on the build
const isImmutable = !p.mutable;
if (isMutableAndRequired || isImmutable) {
requiredParameters.push(p);
}
});
for (const parameter of requiredParameters) {
// Check if there is a new value
let buildParameter = newBuildParameters.find(
(p) => p.name === parameter.name,
);
// If not, get the old one
if (!buildParameter) {
buildParameter = oldBuildParameters.find(
(p) => p.name === parameter.name,
);
}
// If there is a value from the new or old one, it is not missed
if (buildParameter) {
continue;
}
missingParameters.push(parameter);
}
// Check if parameter "options" changed and we can't use old build parameters.
templateParameters.forEach((templateParameter) => {
if (templateParameter.options.length === 0) {
return;
}
// Check if there is a new value
let buildParameter = newBuildParameters.find(
(p) => p.name === templateParameter.name,
);
// If not, get the old one
if (!buildParameter) {
buildParameter = oldBuildParameters.find(
(p) => p.name === templateParameter.name,
);
}
if (!buildParameter) {
return;
}
const matchingOption = templateParameter.options.find(
(option) => option.value === buildParameter?.value,
);
if (!matchingOption) {
missingParameters.push(templateParameter);
}
});
return missingParameters;
};
/**
*
* @param agentId
* @returns An EventSource that emits agent metadata event objects
* (ServerSentEvent)
*/
export const watchAgentMetadata = (agentId: string): EventSource => {
return new EventSource(
`${location.protocol}//${location.host}/api/v2/workspaceagents/${agentId}/watch-metadata`,
{ withCredentials: true },
);
};
type WatchBuildLogsByTemplateVersionIdOptions = {
after?: number;
onMessage: (log: TypesGen.ProvisionerJobLog) => void;
onDone?: () => void;
onError: (error: Error) => void;
};
export const watchBuildLogsByTemplateVersionId = (
versionId: string,
{
onMessage,
onDone,
onError,
after,
}: WatchBuildLogsByTemplateVersionIdOptions,
) => {
const searchParams = new URLSearchParams({ follow: "true" });
if (after !== undefined) {
searchParams.append("after", after.toString());
}
const proto = location.protocol === "https:" ? "wss:" : "ws:";
const socket = new WebSocket(
`${proto}//${
location.host
}/api/v2/templateversions/${versionId}/logs?${searchParams.toString()}`,
);
socket.binaryType = "blob";
socket.addEventListener("message", (event) =>
onMessage(JSON.parse(event.data) as TypesGen.ProvisionerJobLog),
);
socket.addEventListener("error", () => {
onError(new Error("Connection for logs failed."));
socket.close();
});
socket.addEventListener("close", () => {
// When the socket closes, logs have finished streaming!
onDone?.();
});
return socket;
};
type WatchWorkspaceAgentLogsOptions = {
after: number;
onMessage: (logs: TypesGen.WorkspaceAgentLog[]) => void;
onDone?: () => void;
onError: (error: Error) => void;
};
export const watchWorkspaceAgentLogs = (
agentId: string,
{ after, onMessage, onDone, onError }: WatchWorkspaceAgentLogsOptions,
) => {
// WebSocket compression in Safari (confirmed in 16.5) is broken when
// the server sends large messages. The following error is seen:
//
// WebSocket connection to 'wss://.../logs?follow&after=0' failed: The operation couldnt be completed. Protocol error
//
const noCompression =
userAgentParser(navigator.userAgent).browser.name === "Safari"
? "&no_compression"
: "";
const proto = location.protocol === "https:" ? "wss:" : "ws:";
const socket = new WebSocket(
`${proto}//${location.host}/api/v2/workspaceagents/${agentId}/logs?follow&after=${after}${noCompression}`,
);
socket.binaryType = "blob";
socket.addEventListener("message", (event) => {
const logs = JSON.parse(event.data) as TypesGen.WorkspaceAgentLog[];
onMessage(logs);
});
socket.addEventListener("error", () => {
onError(new Error("socket errored"));
});
socket.addEventListener("close", () => {
onDone && onDone();
});
return socket;
};
type WatchBuildLogsByBuildIdOptions = {
after?: number;
onMessage: (log: TypesGen.ProvisionerJobLog) => void;
onDone?: () => void;
onError?: (error: Error) => void;
};
export const watchBuildLogsByBuildId = (
buildId: string,
{ onMessage, onDone, onError, after }: WatchBuildLogsByBuildIdOptions,
) => {
const searchParams = new URLSearchParams({ follow: "true" });
if (after !== undefined) {
searchParams.append("after", after.toString());
}
const proto = location.protocol === "https:" ? "wss:" : "ws:";
const socket = new WebSocket(
`${proto}//${
location.host
}/api/v2/workspacebuilds/${buildId}/logs?${searchParams.toString()}`,
);
socket.binaryType = "blob";
socket.addEventListener("message", (event) =>
onMessage(JSON.parse(event.data) as TypesGen.ProvisionerJobLog),
);
socket.addEventListener("error", () => {
onError && onError(new Error("Connection for logs failed."));
socket.close();
});
socket.addEventListener("close", () => {
// When the socket closes, logs have finished streaming!
onDone && onDone();
});
return socket;
};
export const issueReconnectingPTYSignedToken = async (
params: TypesGen.IssueReconnectingPTYSignedTokenRequest,
): Promise<TypesGen.IssueReconnectingPTYSignedTokenResponse> => {
const response = await axiosInstance.post(
"/api/v2/applications/reconnecting-pty-signed-token",
params,
);
return response.data;
};
export const getWorkspaceParameters = async (workspace: TypesGen.Workspace) => {
const latestBuild = workspace.latest_build;
const [templateVersionRichParameters, buildParameters] = await Promise.all([
getTemplateVersionRichParameters(latestBuild.template_version_id),
getWorkspaceBuildParameters(latestBuild.id),
]);
return {
templateVersionRichParameters,
buildParameters,
};
};
export type InsightsParams = {
start_time: string;
end_time: string;
template_ids: string;
};
export const getInsightsUserLatency = async (
filters: InsightsParams,
): Promise<TypesGen.UserLatencyInsightsResponse> => {
const params = new URLSearchParams(filters);
const response = await axiosInstance.get(
`/api/v2/insights/user-latency?${params}`,
);
return response.data;
};
export const getInsightsUserActivity = async (
filters: InsightsParams,
): Promise<TypesGen.UserActivityInsightsResponse> => {
const params = new URLSearchParams(filters);
const response = await axiosInstance.get(
`/api/v2/insights/user-activity?${params}`,
);
return response.data;
};
export type InsightsTemplateParams = InsightsParams & {
interval: "day" | "week";
};
export const getInsightsTemplate = async (
params: InsightsTemplateParams,
): Promise<TypesGen.TemplateInsightsResponse> => {
const searchParams = new URLSearchParams(params);
const response = await axiosInstance.get(
`/api/v2/insights/templates?${searchParams}`,
);
return response.data;
};
export const getHealth = async (force: boolean = false) => {
const params = new URLSearchParams({ force: force.toString() });
const response = await axiosInstance.get<TypesGen.HealthcheckReport>(
`/api/v2/debug/health?${params}`,
);
return response.data;
};
export const getHealthSettings = async () => {
return (
await axiosInstance.get<TypesGen.HealthSettings>(
`/api/v2/debug/health/settings`,
)
).data;
};
export const updateHealthSettings = async (
data: TypesGen.UpdateHealthSettings,
) => {
const response = await axiosInstance.put<TypesGen.HealthSettings>(
`/api/v2/debug/health/settings`,
data,
);
return response.data;
};
export const putFavoriteWorkspace = async (workspaceID: string) => {
await axiosInstance.put(`/api/v2/workspaces/${workspaceID}/favorite`);
};
export const deleteFavoriteWorkspace = async (workspaceID: string) => {
await axiosInstance.delete(`/api/v2/workspaces/${workspaceID}/favorite`);
};
export type GetJFrogXRayScanParams = {
workspaceId: string;
agentId: string;
};
export const getJFrogXRayScan = async (options: GetJFrogXRayScanParams) => {
const searchParams = new URLSearchParams({
workspace_id: options.workspaceId,
agent_id: options.agentId,
});
try {
const res = await axiosInstance.get<TypesGen.JFrogXrayScan>(
`/api/v2/integrations/jfrog/xray-scan?${searchParams}`,
);
return res.data;
} catch (error) {
if (isAxiosError(error) && error.response?.status === 404) {
// react-query library does not allow undefined to be returned as a query result
return null;
}
}
};