diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3c56d87308..abf9756c6f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,3 +26,6 @@ jobs: - run: npm run lint - run: npm audit --audit-level=critical - run: npm run build-with-tests && npm run test-transpiled + - name: Create k8s Kind Cluster + uses: helm/kind-action@v1 + - run: npm run integration-test diff --git a/examples/typescript/patch/patch-example.ts b/examples/typescript/patch/patch-example.ts index 8056f2b17b..f3d5c03d8e 100644 --- a/examples/typescript/patch/patch-example.ts +++ b/examples/typescript/patch/patch-example.ts @@ -1,4 +1,4 @@ -import { CoreV1Api, KubeConfig, setHeaderOptions, JsonPatch } from '@kubernetes/client-node'; +import { CoreV1Api, KubeConfig, setHeaderOptions, PatchStrategy } from '@kubernetes/client-node'; const kc = new KubeConfig(); kc.loadFromDefault(); @@ -28,7 +28,7 @@ try { namespace: 'default', body: patch, }, - setHeaderOptions('Content-Type', JsonPatch), + setHeaderOptions('Content-Type', PatchStrategy.JsonPatch), ); console.log('Patched.'); diff --git a/package.json b/package.json index 03cb12fc74..3310bd46ca 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "watch": "tsc --watch", "test": "c8 mocha", "test-transpiled": "mocha --no-config dist", + "integration-test": "tsx src/test/integration/index.ts", "prepare": "npm run build && husky", "prepack": "npm run build", "docs": "typedoc src/gen" diff --git a/src/index.ts b/src/index.ts index 75af4daf50..10fb300ede 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,7 @@ export * from './metrics.js'; export * from './object.js'; export * from './health.js'; export * from './middleware.js'; +export * from './patch.js'; export { type ConfigOptions, type User, type Cluster, type Context } from './config_types.js'; // Export FetchError so that instanceof checks in user code will definitely use the same instance diff --git a/src/middleware.ts b/src/middleware.ts index 2ea2b51923..0397e26674 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,27 +1,29 @@ -import type { RequestContext, ResponseContext, ConfigurationOptions, Middleware } from './gen/index.js'; -import { PromiseMiddleware } from './gen/middleware.js'; +import type { + RequestContext, + ResponseContext, + ConfigurationOptions, + ObservableMiddleware, +} from './gen/index.js'; +import { of } from './gen/rxjsStub.js'; -// setHeaderMiddleware returns Middleware[] that sets a header value -export function setHeaderMiddleware(key: string, value: string): Middleware[] { - return [ - { - pre: async (c: RequestContext) => { - c.setHeaderParam(key, value); - return c; - }, - post: async (c: ResponseContext) => { - return c; - }, +export function setHeaderMiddleware(key: string, value: string): ObservableMiddleware { + return { + pre: (request: RequestContext) => { + request.setHeaderParam(key, value); + return of(request); + }, + post: (response: ResponseContext) => { + return of(response); }, - ]; + }; } // Returns ConfigurationOptions that set a header export function setHeaderOptions( key: string, value: string, - opt?: ConfigurationOptions, -): ConfigurationOptions { + opt?: ConfigurationOptions, +): ConfigurationOptions { const newMiddlware = setHeaderMiddleware(key, value); const existingMiddlware = opt?.middleware || []; return { diff --git a/src/middleware_test.ts b/src/middleware_test.ts index 51e8e63585..ec022dba62 100644 --- a/src/middleware_test.ts +++ b/src/middleware_test.ts @@ -1,7 +1,6 @@ -import { RequestContext, ConfigurationOptions, HttpMethod } from './gen/index.js'; +import { RequestContext, ConfigurationOptions, HttpMethod, ObservableMiddleware } from './gen/index.js'; import { deepStrictEqual } from 'node:assert'; import { setHeaderMiddleware, setHeaderOptions } from './middleware.js'; -import { PromiseMiddleware } from './gen/middleware.js'; describe('Middleware', async () => { describe('setHeaderMiddleware', async () => { @@ -9,7 +8,8 @@ describe('Middleware', async () => { const reqContext = new RequestContext('http://nowhere.com', HttpMethod.GET); deepStrictEqual(reqContext.getHeaders(), {}); const headerMiddleware = setHeaderMiddleware('test-key', 'test-value'); - const postMiddlewareRequest = await headerMiddleware[0].pre(reqContext); + const postMiddlewareRequestObservable = await headerMiddleware.pre(reqContext); + const postMiddlewareRequest = await postMiddlewareRequestObservable.toPromise(); deepStrictEqual(postMiddlewareRequest.getHeaders(), { 'test-key': 'test-value' }); }); @@ -18,7 +18,7 @@ describe('Middleware', async () => { reqContext.setHeaderParam('test-key', 'wrong-value'); deepStrictEqual(reqContext.getHeaders(), { 'test-key': 'wrong-value' }); const headerMiddleware = setHeaderMiddleware('test-key', 'test-value'); - const postMiddlewareRequest = await headerMiddleware[0].pre(reqContext); + const postMiddlewareRequest = await headerMiddleware.pre(reqContext).toPromise(); deepStrictEqual(postMiddlewareRequest.getHeaders(), { 'test-key': 'test-value' }); }); }); @@ -27,7 +27,7 @@ describe('Middleware', async () => { it('should add middleware to set header with no input options arg', async () => { const reqContext = new RequestContext('http://nowhere.com', HttpMethod.GET); deepStrictEqual(reqContext.getHeaders(), {}); - const testConfigurationOptions: ConfigurationOptions = {}; + const testConfigurationOptions: ConfigurationOptions = {}; const headerConfigurationOptions = setHeaderOptions( 'test-key', 'test-value', @@ -39,7 +39,10 @@ describe('Middleware', async () => { ) { throw new Error('missing middleware in ConfigurationOptions'); } - const postMiddlewareRequest = await headerConfigurationOptions.middleware[0].pre(reqContext); + const postMiddlewareRequest = await headerConfigurationOptions.middleware[0] + .pre(reqContext) + .toPromise(); + deepStrictEqual(postMiddlewareRequest.getHeaders(), { 'test-key': 'test-value' }); }); }); diff --git a/src/test/integration/index.ts b/src/test/integration/index.ts new file mode 100644 index 0000000000..5065dcd429 --- /dev/null +++ b/src/test/integration/index.ts @@ -0,0 +1,5 @@ +import patchNamespace from './patchNamespace.js'; + +console.log('Integration testing'); + +await patchNamespace(); diff --git a/src/test/integration/name.ts b/src/test/integration/name.ts new file mode 100644 index 0000000000..be6537ae26 --- /dev/null +++ b/src/test/integration/name.ts @@ -0,0 +1,7 @@ +/* + * This function is used to generate a random name for the resources created in the tests. + * This is to avoid conflicts with existing resources. + */ +export function generateName(name: string) { + return name + '-' + Math.random().toString(36).substring(7); +} diff --git a/src/test/integration/patchNamespace.ts b/src/test/integration/patchNamespace.ts new file mode 100644 index 0000000000..5c575fcbd6 --- /dev/null +++ b/src/test/integration/patchNamespace.ts @@ -0,0 +1,48 @@ +import assert from 'node:assert'; +import { PatchStrategy, CoreV1Api, KubeConfig, setHeaderOptions, V1Namespace } from '../../index.js'; +import { generateName } from './name.js'; + +export default async function patchNamespace() { + const kc = new KubeConfig(); + kc.loadFromDefault(); + + const coreV1Client = kc.makeApiClient(CoreV1Api); + let newNS = new V1Namespace(); + const testNSName = generateName('patch-ns'); + newNS.metadata = { + name: testNSName, + labels: { + initialLabel: 'initialValue', + }, + }; + console.log(`Creating namespace ${testNSName}`); + await coreV1Client.createNamespace({ body: newNS }); + newNS = await coreV1Client.readNamespace({ name: testNSName }); + assert.strictEqual(newNS.metadata?.name, testNSName); + assert.strictEqual(newNS.metadata?.labels?.initialLabel, 'initialValue'); + console.log('Namespace created with initial label.'); + + const patch = [ + { + op: 'replace', + path: '/metadata/labels', + value: { + foo: 'bar', + }, + }, + ]; + + console.log(`Patching namespace ${testNSName} to replace labels`); + await coreV1Client.patchNamespace( + { + name: testNSName, + body: patch, + }, + setHeaderOptions('Content-Type', PatchStrategy.JsonPatch), + ); + + const patchedNS = await coreV1Client.readNamespace({ name: testNSName }); + assert.strictEqual(patchedNS.metadata?.labels?.foo, 'bar'); + assert.strictEqual(patchedNS.metadata?.labels?.initialLabel, undefined); + console.log('Namespace patched with new label.'); +}