import {z} from 'zod' export type Status = 'not_started' | 'in_progress' | 'addressed' | 'ignored'; export type SortKeyEnum = 'cost_impact_last_30_days'; export type SortOrderEnum = 'desc'; export type GetApiV2OptimizeRecommendationsArgs = {status?: (Array | null) | undefined, recommendation_type_id?: (Array | null) | undefined, recommendation_type_name?: (Array | null) | undefined, category?: (Array | null) | undefined, effort?: (Array | null) | undefined, source?: (Array | null) | undefined, 'cost_impact_last_30_days[gte]'?: (number | string) | undefined, group_by?: string | undefined, search?: string | undefined, limit?: number | undefined, offset?: number | undefined, sort_key?: SortKeyEnum | undefined, sort_order?: SortOrderEnum | undefined, cursor?: (string | null) | undefined}; export type GetApiV2OptimizeRecommendationsOptions = {baseUrl?: string | undefined, APIKeyAuth?: string | undefined, fetch?: typeof fetch | undefined}; export const recommendation = z.object({recommendation_id: z.string(), title: z.string(), category: z.string(), optimization_id: z.string().optional(), recommendation_type_id: z.string(), source: z.string().nullable().optional(), effort: z.string().optional(), status: z.string().optional(), work_item: z.string().nullable().optional(), cost_impact_last_30_days: z.number().optional(), realized_savings_last_30_days: z.number().optional(), resource: z.string().nullable().optional(), resource_type: z.string().nullable().optional(), resource_name: z.string().nullable().optional(), account: z.array(z.string()).optional(), addressed_on_ts: z.number().int().nullable().optional(), cloud_provider: z.array(z.string()).optional(), region: z.array(z.string()).optional(), service: z.array(z.string()).optional(), created: z.number().int().optional(), cluster: z.array(z.string()).nullable().optional(), namespace: z.array(z.string()).nullable().optional(), pod: z.array(z.string()).nullable().optional()}).and(z.record(z.string(), z.unknown())); export const recommendationsGroup = z.object({group: z.string().optional(), recommendations: z.number().int().optional(), cost_impact_last_30_days: z.number().optional()}); export const sortKeyEnum = z.literal("cost_impact_last_30_days"); export const sortOrderEnum = z.literal("desc"); export const availableSortOptions = z.object({sort_keys: z.array(sortKeyEnum).optional(), sort_orders: z.array(sortOrderEnum).optional()}); export const actualSortOptions = z.object({sort_key: sortKeyEnum.optional(), sort_order: sortOrderEnum.optional()}); export const sortOptions = z.object({available: availableSortOptions.optional(), current: actualSortOptions.optional()}); export const filterOptions = z.object({available: z.record(z.string(), z.unknown()).optional(), current: z.record(z.string(), z.unknown()).optional()}); export const groupByOptions = z.object({available: z.array(z.string()).optional(), current: z.array(z.string()).optional()}); export const cursorResponse = z.object({next_cursor: z.string().nullable().optional(), previous_cursor: z.string().nullable().optional(), has_next: z.boolean().optional(), has_previous: z.boolean().optional()}); export const paginationResponse = z.object({page_count: z.number().int().optional(), item_count: z.number().int().optional(), total_count: z.number().int().optional(), cursor: cursorResponse.optional()}); export const uniqueRecommendationResponse = z.object({total_count: z.number().int().optional(), total_cost_impact_last_30_days: z.number().optional()}); export const listRecommendationsResponse = z.object({recommendations: z.array(z.union([recommendation, recommendationsGroup])).optional(), sorting: sortOptions.optional(), filtering: filterOptions.optional(), group_by: groupByOptions.optional(), columns: z.array(z.string()).optional(), multiple_value_columns: z.array(z.string()).optional(), display_names: z.record(z.string(), z.string()).optional(), pagination: paginationResponse.optional(), unique_recommendation: uniqueRecommendationResponse.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 }; /** * List recommendations * * GET /v2/optimize/recommendations */ export const getApiV2OptimizeRecommendations = async (args: GetApiV2OptimizeRecommendationsArgs, options?: GetApiV2OptimizeRecommendationsOptions) => { const {baseUrl = "https://api.cloudzero.com", fetch: fetchFn = fetch, APIKeyAuth} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") for (const item of args.status ?? []) query.append("status", String(item)) for (const item of args.recommendation_type_id ?? []) query.append("recommendation_type_id", String(item)) for (const item of args.recommendation_type_name ?? []) query.append("recommendation_type_name", String(item)) for (const item of args.category ?? []) query.append("category", String(item)) for (const item of args.effort ?? []) query.append("effort", String(item)) for (const item of args.source ?? []) query.append("source", String(item)) if (args['cost_impact_last_30_days[gte]'] != null) query.append("cost_impact_last_30_days[gte]", String(args['cost_impact_last_30_days[gte]'])) if (args.group_by != null) query.append("group_by", String(args.group_by)) if (args.search != null) query.append("search", String(args.search)) if (args.limit != null) query.append("limit", String(args.limit)) if (args.offset != null) query.append("offset", String(args.offset)) if (args.sort_key != null) query.append("sort_key", String(args.sort_key)) if (args.sort_order != null) query.append("sort_order", String(args.sort_order)) if (args.cursor != null) query.append("cursor", String(args.cursor)) if (APIKeyAuth !== undefined) headers.set("Authorization", APIKeyAuth) const url = `${baseUrl.replace(/\/+$/, '')}/v2/optimize/recommendations${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(listRecommendationsResponse, await res.json()) };