diff --git a/examples/generate-all-k8s-client/k8s-client/client.ts b/examples/generate-all-k8s-client/k8s-client/client.ts index fae2a4b7..672e2613 100644 --- a/examples/generate-all-k8s-client/k8s-client/client.ts +++ b/examples/generate-all-k8s-client/k8s-client/client.ts @@ -1,30 +1,18 @@ -import * as k8s from "@kubernetes/client-node"; -import NodeCache from "node-cache"; -import fetch, { FetchError } from "node-fetch"; -import fs from "node:fs"; -import * as https from "node:https"; - -const fileCache = new NodeCache({ stdTTL: 3600, checkperiod: 120 }); - -async function readFile(filePath: string) { - if (!fileCache.has(filePath)) { - fileCache.set(filePath, await fs.promises.readFile(filePath, "utf8")); - } - - return fileCache.get(filePath); -} +import * as k8s from '@kubernetes/client-node' +import fetch, { FetchError } from 'node-fetch' +import * as https from 'node:https' type RemoveUndefined = { - [K in keyof T]: Exclude; -}; + [K in keyof T]: Exclude +} export function removeNullableProperties< - T extends Record | undefined, + T extends Record | undefined >(object: T): RemoveUndefined { - if (!object) return object as RemoveUndefined; + if (!object) return object as RemoveUndefined for (const key of Object.keys(object)) - (object[key] === undefined || object[key] === null) && delete object[key]; - return object as RemoveUndefined; + (object[key] === undefined || object[key] === null) && delete object[key] + return object as RemoveUndefined } /** @@ -41,119 +29,119 @@ export function removeNullableProperties< * @param maxRetries - Maximum number of retries */ async function defaultBackoff(attempt: number, maxRetries: number) { - const attempts = Math.min(attempt, maxRetries); + const attempts = Math.min(attempt, maxRetries) - const timeout = Math.trunc((Math.random() + 0.4) * (300 << attempts)); + const timeout = Math.trunc((Math.random() + 0.4) * (300 << attempts)) await new Promise((resolve) => - setTimeout((response: any) => resolve(response), timeout), - ); + setTimeout((response: any) => resolve(response), timeout) + ) } -const isPlainObject = (value: any) => value?.constructor === Object; +const isPlainObject = (value: any) => value?.constructor === Object type QueryArgumentsSpec = { - path: string | undefined; + path: string | undefined method?: - | "GET" - | "DELETE" - | "PATCH" - | "POST" - | "PUT" - | "OPTIONS" - | "HEAD" - | undefined; - body?: any | undefined; - contentType?: string | undefined; - params?: any | undefined; -}; + | 'GET' + | 'DELETE' + | 'PATCH' + | 'POST' + | 'PUT' + | 'OPTIONS' + | 'HEAD' + | undefined + body?: any | undefined + contentType?: string | undefined + params?: any | undefined +} -type MaybePromise = T | Promise; +type MaybePromise = T | Promise type InterceptorArguments = { - args: QueryArgumentsSpec; - opts: https.RequestOptions; -}; + args: QueryArgumentsSpec + opts: https.RequestOptions +} type Interceptor = ( arguments_: InterceptorArguments, - options: Options, -) => MaybePromise; + options: Options +) => MaybePromise const interceptors: Interceptor[] = [ async function injectKubernetesParameters({ opts }) { - const kc = new k8s.KubeConfig(); - kc.loadFromDefault(); - const nextOptions: https.RequestOptions = { ...opts }; + const kc = new k8s.KubeConfig() + kc.loadFromDefault() + const nextOptions: https.RequestOptions = { ...opts } await kc.applyToHTTPSOptions(nextOptions) const cluster = kc.getCurrentCluster() if (cluster?.server) { - const url = new URL(cluster.server); - nextOptions.host = url.hostname; - nextOptions.protocol = url.protocol; - nextOptions.port = url.port; + const url = new URL(cluster.server) + nextOptions.host = url.hostname + nextOptions.protocol = url.protocol + nextOptions.port = url.port } - return nextOptions; + return nextOptions }, -]; +] type RetryConditionFunction = (extraArguments: { - res?: Response; - error: unknown; - args: QueryArgumentsSpec; - attempt: number; - options: RetryOptions; -}) => boolean | Promise; + res?: Response + error: unknown + args: QueryArgumentsSpec + attempt: number + options: RetryOptions +}) => boolean | Promise type RetryOptions = { - retryCondition?: RetryConditionFunction; - maxRetries?: number; -}; + retryCondition?: RetryConditionFunction + maxRetries?: number +} type HttpHeaderOptions = { - headers?: Record | undefined; -}; + headers?: Record | undefined +} -export type Options = RetryOptions & HttpHeaderOptions; +export type Options = RetryOptions & HttpHeaderOptions export async function apiClient( arguments_: QueryArgumentsSpec, - extraOptions?: Options, + extraOptions?: Options ): Promise { - const maxRetries = extraOptions?.maxRetries ?? 3; + const maxRetries = extraOptions?.maxRetries ?? 3 const defaultRetryCondition: RetryConditionFunction = ({ ...object }) => { - const { res, attempt, error } = object; + const { res, attempt, error } = object if (attempt > maxRetries) { - return false; + return false } if (error instanceof FetchError) { - return true; + return true } if (res && res.status >= 500) { - return true; + return true } - return false; - }; + return false + } const options = { maxRetries, backoff: defaultBackoff, retryCondition: defaultRetryCondition, ...removeNullableProperties(extraOptions), - }; + } - let { path, method, params, body, contentType } = { ...arguments_ }; + let { path, method, params, body, contentType } = { ...arguments_ } let httpsOptions: https.RequestOptions = { path, headers: { ...options.headers, }, - }; + } if (method) { - httpsOptions.method = method; + httpsOptions.method = method } for (const interceptor of interceptors) { @@ -162,8 +150,8 @@ export async function apiClient( args: arguments_, opts: httpsOptions, }, - options, - ); + options + ) } if ( @@ -176,42 +164,42 @@ export async function apiClient( cert: httpsOptions.cert, key: httpsOptions.key, port: httpsOptions.port ? Number(httpsOptions.port) : undefined, - }), - ); - httpsOptions.agent = agent; + }) + ) + httpsOptions.agent = agent } if (!httpsOptions.protocol) { - httpsOptions.protocol = "http:"; + httpsOptions.protocol = 'http:' } - const host = httpsOptions.host || httpsOptions.hostname; - let baseUrl = `${httpsOptions.protocol}//${host}`; - const searchParameters = toSearchParameters(params); + const host = httpsOptions.host || httpsOptions.hostname + let baseUrl = `${httpsOptions.protocol}//${host}` + const searchParameters = toSearchParameters(params) if (searchParameters.size > 0) { - baseUrl += (baseUrl.includes("?") ? "&" : "?") + toSearchParameters(params); + baseUrl += (baseUrl.includes('?') ? '&' : '?') + toSearchParameters(params) } - const url = new URL(baseUrl); + const url = new URL(baseUrl) if (httpsOptions.port) { - url.port = httpsOptions.port.toString(); + url.port = httpsOptions.port.toString() } if (httpsOptions.path) { - url.pathname = httpsOptions.path; + url.pathname = httpsOptions.path } - let isJson = false; + let isJson = false if (isPlainObject(body) || Array.isArray(body)) { - isJson = true; - body = JSON.stringify(body); + isJson = true + body = JSON.stringify(body) } const headers: Record = { ...(httpsOptions.headers as any), - }; + } if (contentType) { - headers["Content-Type"] = contentType; - } else if (!httpsOptions.headers?.["Content-Type"] && isJson) { - headers["Content-Type"] = "application/json"; + headers['Content-Type'] = contentType + } else if (!httpsOptions.headers?.['Content-Type'] && isJson) { + headers['Content-Type'] = 'application/json' } - let retry = 0; + let retry = 0 while (true) { try { const response = await fetch( @@ -222,27 +210,27 @@ export async function apiClient( method, agent: httpsOptions.agent, body, - }), - ); + }) + ) - const isSuccess = response.status >= 200 && response.status < 300; - const contentType = response.headers.get("content-type"); - const isJsonResponse = contentType?.includes("application/json") ?? false; + const isSuccess = response.status >= 200 && response.status < 300 + const contentType = response.headers.get('content-type') + const isJsonResponse = contentType?.includes('application/json') ?? false if (isSuccess && isJsonResponse) { - return (await response.json()) as Response; + return (await response.json()) as Response } // helpful message for debugging - const text = await response.text(); - if (response.status === 404 && text.includes("404 page not found")) { + const text = await response.text() + if (response.status === 404 && text.includes('404 page not found')) { console.info( - `Did you forget to install your Custom Resources Definitions? path: ${httpsOptions.path}`, - ); + `Did you forget to install your Custom Resources Definitions? path: ${httpsOptions.path}` + ) } - throw new Error(text); + throw new Error(text) } catch (error: any) { - retry++; + retry++ if ( !(await options.retryCondition({ @@ -253,14 +241,14 @@ export async function apiClient( options: options, })) ) { - throw error; + throw error } - await options.backoff(retry, options.maxRetries); + await options.backoff(retry, options.maxRetries) } } } const toSearchParameters = (parameters: Record) => { - return new URLSearchParams(removeNullableProperties(parameters)); -}; + return new URLSearchParams(removeNullableProperties(parameters)) +}