Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 24 additions & 15 deletions src/api/components/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface ComponentSearchProps { ignoreState?: boolean };

export class ComponentRegistry {
private readonly components: Map<string, IBMiComponent[]> = new Map;
private readonly disabled: Map<string, string[]> = new Map;

public registerComponent(context: ExtensionContextI | string, component: IBMiComponent) {
const key = typeof context === `object` ? context.extension.id : context;
Expand All @@ -31,15 +32,35 @@ export class ComponentRegistry {
}

public getComponents() {
return this.components;
const filtered = new Map<string, IBMiComponent[]>();

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]);
}
}
}

export const extensionComponentRegistry = new ComponentRegistry();

export class ComponentManager {
private readonly registered: IBMiComponentRuntime[] = [];
private readonly disabled: string[] = [];

constructor(private readonly connection: IBMi) { }

Expand All @@ -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<ComponentInstallState> {
Expand Down
9 changes: 7 additions & 2 deletions src/api/tests/components/customCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
3 changes: 3 additions & 0 deletions src/api/tests/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
21 changes: 10 additions & 11 deletions src/api/tests/suites/components.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('Component Tests', () => {
});

it('Can get component no matter the state', async () => {
const componentA = await connection.getComponent<CustomCLI>(CustomCLI.ID, { ignoreState: true });
const componentA = await connection.getComponent<CustomCLI>(CustomCLI.DEFAULT_ID, { ignoreState: true });
expect(componentA).toBeDefined();
expect(componentA?.getIdentification().version).toBe(1);
expect(componentA?.getIdentification().userManaged).toBe(true);
Expand All @@ -24,46 +24,45 @@ 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>(CustomCLI.ID);
const version1 = await connection.getComponent<CustomCLI>(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);
}

// 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();
});
});
Loading