import {z} from 'zod' export type GetApiRolesOptions = {baseUrl: string, fetch?: typeof fetch | undefined}; export const roleResponse = z.object({key: z.string().optional(), name: z.string().optional(), description: z.string().optional(), type: z.enum(["Company", "Team"]).optional(), privileges: z.array(z.enum(["ADMIN", "DEV", "ALL_API_KEYS_READ", "API_KEYS_READ", "API_KEYS_WRITE", "COMPANIES_READ", "COMPANIES_WRITE", "COMPANY_INTEGRATIONS_READ", "COMPANY_INTEGRATIONS_WRITE", "COMPANY_PREFERENCES_WRITE", "COMPANY_SECURITY_WRITE", "COMPANY_SECURITY_READ", "COMPANY_SAMLCLAIM_ALIAS_READ", "COMPANY_SAMLCLAIM_ALIAS_WRITE", "COMPANY_USERS_READ", "COMPANY_USERS_WRITE", "USERS_BATCH_WRITE", "HALT_ALL", "LARGE_SCALE_TESTS_READ", "LARGE_SCALE_TESTS_WRITE", "DISASTER_RECOVERY_TESTS_READ", "DISASTER_RECOVERY_TESTS_WRITE", "MINIMUM_COMPANY_PRIVILEGES", "PROMOTE_DEMOTE_OWNER", "RELIABILITY_REPORTS_READ", "REPORTS_READ", "ROLES_WRITE", "SECURITY_REPORTS_READ", "TEAMS_READ", "TEST_SUITES_READ", "TEST_SUITES_WRITE", "CLIENTS_READ", "CLIENTS_WRITE", "EXPERIMENTS_READ", "EXPERIMENTS_RUN", "EXPERIMENTS_WRITE", "FAULT_CPU", "FAULT_IO", "FAULT_MEMORY", "FAULT_DISK", "FAULT_BLACKHOLE", "FAULT_DNS", "FAULT_LATENCY", "FAULT_PACKET_LOSS", "FAULT_COLLECT_CERTS", "FAULT_SHUTDOWN", "FAULT_TIME_TRAVEL", "FAULT_PROCESS_KILLER", "FAULT_PROCESS_EXHAUSTION", "FAULT_GPU", "GIT_QUERY_READ", "HALT_WRITE", "IMAGES_READ", "IMAGES_WRITE", "INTEGRATIONS_READ", "INTEGRATIONS_WRITE", "MINIMUM_TEAM_PRIVILEGES", "SCENARIO_SHARE_WRITE", "SCENARIOS_READ", "SCENARIOS_RUN", "SCENARIOS_WRITE", "SERVICES_READ", "SERVICES_RUN", "SERVICES_WRITE", "TEAM_SECURITY_READ", "TEAM_SECURITY_WRITE", "TEAMS_WRITE", "TPM_WRITE", "USERS_READ", "USERS_WRITE", "WEBHOOKS_READ", "WEBHOOKS_WRITE"])).optional()}); export const roles = z.object({company: z.record(z.string(), roleResponse).optional(), team: z.record(z.string(), roleResponse).optional()}); export const privilegeDefinition = z.object({key: z.string().optional(), description: z.string().optional(), category: z.enum(["NONE", "REQUIRED", "ADMINISTRATIVE", "ALL_API_KEYS", "API_KEYS", "CLIENTS", "COMPANIES", "COMPANY_PREFERENCES", "COMPANY_SECURITY", "COMPANY_USERS", "EXPERIMENTS", "FAULT", "HALT", "HALT_ALL", "IMAGES", "INTEGRATIONS", "DISASTER_RECOVERY_TESTS", "RELIABILITY_REPORTS", "REPORTS", "ROLES", "SCENARIOS", "SCENARIO_SHARE", "SECURITY_REPORTS", "SERVICES", "TEAMS", "TEAM_SECURITY", "TEST_SUITES", "TPM", "USERS", "WEBHOOKS"]).optional()}); export const privileges = z.object({company: z.record(z.string(), privilegeDefinition).optional(), team: z.record(z.string(), privilegeDefinition).optional()}); export const rolesResponse = z.object({customRoles: roles.optional(), roles: roles.optional(), privileges: privileges.optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Returns all roles and privileges * * GET /roles */ export const getApiRoles = async (options: GetApiRolesOptions) => { const {baseUrl, fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") const url = `${baseUrl.replace(/\/+$/, '')}/roles${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(rolesResponse, await res.json()) };