From 9a25efea6c6f7bf7e0425ef0a9639a62f571c26c Mon Sep 17 00:00:00 2001 From: Sanjula Ganepola Date: Wed, 20 May 2026 14:39:11 -0400 Subject: [PATCH] Fix disabling of components Signed-off-by: Sanjula Ganepola --- src/api/components/manager.ts | 39 +++++++++++++++---------- src/api/tests/components/customCli.ts | 9 ++++-- src/api/tests/connection.ts | 3 ++ src/api/tests/suites/components.test.ts | 21 +++++++------ 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/src/api/components/manager.ts b/src/api/components/manager.ts index f33f11926..2232320cb 100644 --- a/src/api/components/manager.ts +++ b/src/api/components/manager.ts @@ -13,6 +13,7 @@ export interface ComponentSearchProps { ignoreState?: boolean }; export class ComponentRegistry { private readonly components: Map = new Map; + private readonly disabled: Map = new Map; public registerComponent(context: ExtensionContextI | string, component: IBMiComponent) { const key = typeof context === `object` ? context.extension.id : context; @@ -31,7 +32,28 @@ export class ComponentRegistry { } public getComponents() { - return this.components; + const filtered = new Map(); + + for (const [key, components] of this.components) { + const disabledIds = this.disabled.get(key) || []; + const enabledComponents = components.filter(c => !disabledIds.includes(c.getIdentification().name)); + if (enabledComponents.length > 0) { + filtered.set(key, enabledComponents); + } + } + + return filtered; + } + + disableComponent(key: string, id: string) { + const disabledIds = this.disabled.get(key); + if (disabledIds) { + if (!disabledIds.includes(id)) { + disabledIds.push(id); + } + } else { + this.disabled.set(key, [id]); + } } } @@ -39,7 +61,6 @@ export const extensionComponentRegistry = new ComponentRegistry(); export class ComponentManager { private readonly registered: IBMiComponentRuntime[] = []; - private readonly disabled: string[] = []; constructor(private readonly connection: IBMi) { } @@ -56,23 +77,11 @@ export class ComponentManager { }); } - disable(id: string) { - if (!this.disabled.includes(id)) { - this.disabled.push(id); - } - - const registeredIndex = this.registered.findIndex(c => c.component.getIdentification().name === id); - - if (registeredIndex >= 0) { - this.registered.splice(registeredIndex, 1); - } - } - /** * Returns all components, user managed or not */ getAllAvailableComponents() { - return Array.from(extensionComponentRegistry.getComponents().values()).flatMap(a => a.flat()).filter(c => !this.disabled.includes(c.getIdentification().name)); + return Array.from(extensionComponentRegistry.getComponents().values()).flatMap(a => a.flat()); } public async installComponent(key: string): Promise { diff --git a/src/api/tests/components/customCli.ts b/src/api/tests/components/customCli.ts index 6916bfba9..141b239be 100644 --- a/src/api/tests/components/customCli.ts +++ b/src/api/tests/components/customCli.ts @@ -4,13 +4,18 @@ import IBMi from "../../IBMi"; import { ComponentIdentification, IBMiComponent, SecureComponentState } from "../../components/component"; export class CustomCLI implements IBMiComponent { - static ID = "customCli"; + static DEFAULT_ID = "customCli"; static SIGNATURE = "ForTests"; + id: string; installPath = ""; + constructor(id: string = CustomCLI.DEFAULT_ID) { + this.id = id; + } + getIdentification(): ComponentIdentification { - return { name: CustomCLI.ID, version: 1, userManaged: true, signature: CustomCLI.SIGNATURE }; + return { name: this.id, version: 1, userManaged: true, signature: CustomCLI.SIGNATURE }; } getFileName() { diff --git a/src/api/tests/connection.ts b/src/api/tests/connection.ts index fc527da66..c4ea87d32 100644 --- a/src/api/tests/connection.ts +++ b/src/api/tests/connection.ts @@ -70,6 +70,9 @@ export async function newConnection(reloadSettings?: boolean) { const testingId = `testing`; extensionComponentRegistry.registerComponent(testingId, mapepire); extensionComponentRegistry.registerComponent(testingId, new CustomCLI()); + const componentId = `toBeDeleted`; + extensionComponentRegistry.registerComponent(testingId, new CustomCLI(componentId)); + extensionComponentRegistry.disableComponent(testingId, componentId); const creds: ConnectionData = { ...ENV_CREDS, diff --git a/src/api/tests/suites/components.test.ts b/src/api/tests/suites/components.test.ts index 6c38b42a0..809050d7c 100644 --- a/src/api/tests/suites/components.test.ts +++ b/src/api/tests/suites/components.test.ts @@ -14,7 +14,7 @@ describe('Component Tests', () => { }); it('Can get component no matter the state', async () => { - const componentA = await connection.getComponent(CustomCLI.ID, { ignoreState: true }); + const componentA = await connection.getComponent(CustomCLI.DEFAULT_ID, { ignoreState: true }); expect(componentA).toBeDefined(); expect(componentA?.getIdentification().version).toBe(1); expect(componentA?.getIdentification().userManaged).toBe(true); @@ -24,33 +24,33 @@ describe('Component Tests', () => { const manager = connection.getComponentManager(); try { - await manager.uninstallComponent(CustomCLI.ID); + await manager.uninstallComponent(CustomCLI.DEFAULT_ID); } catch (e) { console.log(e); console.log(`Component not installed, skipping uninstall.`); } - const requiredCheckA = await manager.getRemoteState(CustomCLI.ID); + const requiredCheckA = await manager.getRemoteState(CustomCLI.DEFAULT_ID); expect(requiredCheckA).toBeDefined(); expect(requiredCheckA?.status).toBe(`NotInstalled`); const allComponents = manager.getComponentStates(); expect(allComponents.length > 1).toBeTruthy(); - const state = allComponents.some(c => c.id.name === CustomCLI.ID && c.state.status === `NotInstalled`); + const state = allComponents.some(c => c.id.name === CustomCLI.DEFAULT_ID && c.state.status === `NotInstalled`); expect(state).toBeTruthy(); - const version1 = await connection.getComponent(CustomCLI.ID); + const version1 = await connection.getComponent(CustomCLI.DEFAULT_ID); expect(version1).toBeUndefined(); - const resultA = await manager.installComponent(CustomCLI.ID); + const resultA = await manager.installComponent(CustomCLI.DEFAULT_ID); expect(resultA.state.status).toBe(`Installed`); - const requiredCheckB = await manager.getRemoteState(CustomCLI.ID); + const requiredCheckB = await manager.getRemoteState(CustomCLI.DEFAULT_ID); expect(requiredCheckB).toBeTruthy(); expect(requiredCheckB?.status).toBe(`Installed`); try { - await manager.installComponent(CustomCLI.ID); + await manager.installComponent(CustomCLI.DEFAULT_ID); expect.fail(`Should not be able to install the same component twice.`); } catch (e) { expect(e).toBeInstanceOf(Error); @@ -58,12 +58,11 @@ describe('Component Tests', () => { // Let's check we can disable it. - const requiredCheckC = await manager.getRemoteState(CustomCLI.ID); + const requiredCheckC = await manager.getRemoteState(CustomCLI.DEFAULT_ID); expect(requiredCheckC).toBeTruthy(); expect(requiredCheckC?.status).toBe(`Installed`); - manager.disable(CustomCLI.ID); - const requiredCheckD = await manager.getRemoteState(CustomCLI.ID); + const requiredCheckD = await manager.getRemoteState(`toBeDeleted`); expect(requiredCheckD).toBeUndefined(); }); });