From c4de790602133dda1e9cf2633bc63071a9ed218a Mon Sep 17 00:00:00 2001 From: worksofliam Date: Wed, 28 Aug 2024 10:49:54 -0400 Subject: [PATCH 1/7] Extend the base API to allow other extensions to add their own components. Signed-off-by: worksofliam --- src/api/IBMi.ts | 4 +-- src/components/component.ts | 50 +++---------------------------------- src/components/manager.ts | 49 ++++++++++++++++++++++++++++++++++++ src/extension.ts | 9 ++++++- src/typings.ts | 4 ++- 5 files changed, 66 insertions(+), 50 deletions(-) create mode 100644 src/components/manager.ts diff --git a/src/api/IBMi.ts b/src/api/IBMi.ts index b53ba312f..4317e81c9 100644 --- a/src/api/IBMi.ts +++ b/src/api/IBMi.ts @@ -6,7 +6,6 @@ import { parse } from 'csv-parse/sync'; import { existsSync } from "fs"; import os from "os"; import path from 'path'; -import { ComponentId, ComponentManager } from "../components/component"; import { CopyToImport } from "../components/copyToImport"; import { instance } from "../instantiate"; import { CommandData, CommandResult, ConnectionData, IBMiMember, RemoteCommand, SpecialAuthorities, WrapResult } from "../typings"; @@ -17,6 +16,7 @@ import { Tools } from './Tools'; import * as configVars from './configVars'; import { DebugConfiguration } from "./debug/config"; import { debugPTFInstalled } from "./debug/server"; +import { ComponentManager } from "../components/manager"; export interface MemberParts extends IBMiMember { basename: string @@ -1341,7 +1341,7 @@ export default class IBMi { } } - getComponent(id: ComponentId) { + getComponent(id: string) { return this.components.get(id); } diff --git a/src/components/component.ts b/src/components/component.ts index e40002c62..4f5518d17 100644 --- a/src/components/component.ts +++ b/src/components/component.ts @@ -1,7 +1,4 @@ import IBMi from "../api/IBMi"; -import { CopyToImport } from "./copyToImport"; -import { GetMemberInfo } from "./getMemberInfo"; -import { GetNewLibl } from "./getNewLibl"; export enum ComponentState { NotChecked = `NotChecked`, @@ -9,53 +6,14 @@ export enum ComponentState { Installed = `Installed`, Error = `Error`, } -interface ComponentRegistry { - GetNewLibl?: GetNewLibl; - CopyToImport?: CopyToImport; - GetMemberInfo?: GetMemberInfo; -} - -export type ComponentId = keyof ComponentRegistry; -export abstract class ComponentT { +export class ComponentT { public state: ComponentState = ComponentState.NotChecked; public currentVersion: number = 0; constructor(public connection: IBMi) { } - abstract getInstalledVersion(): Promise; - abstract checkState(): Promise - abstract getState(): ComponentState; -} - -export class ComponentManager { - private registered: ComponentRegistry = {}; - - public async startup(connection: IBMi) { - this.registered.GetNewLibl = new GetNewLibl(connection); - await ComponentManager.checkState(this.registered.GetNewLibl); - - this.registered.CopyToImport = new CopyToImport(connection); - await ComponentManager.checkState(this.registered.CopyToImport); - - this.registered.GetMemberInfo = new GetMemberInfo(connection); - await ComponentManager.checkState(this.registered.GetMemberInfo); - } - - // TODO: return type based on ComponentIds - get(id: ComponentId): T | undefined { - const component = this.registered[id]; - if (component && component.getState() === ComponentState.Installed) { - return component as T; - } - } - - private static async checkState(component: ComponentT) { - try { - await component.checkState(); - } catch (e) { - console.log(component); - console.log(`Error checking state for ${component.constructor.name}`, e); - } - } + async getInstalledVersion(): Promise {return}; + async checkState(): Promise {return false} + getState(): ComponentState {return this.state}; } \ No newline at end of file diff --git a/src/components/manager.ts b/src/components/manager.ts new file mode 100644 index 000000000..bf66cea0b --- /dev/null +++ b/src/components/manager.ts @@ -0,0 +1,49 @@ +import IBMi from "../api/IBMi"; +import { ComponentState, ComponentT } from "./component"; +import { CopyToImport } from "./copyToImport"; +import { GetMemberInfo } from "./getMemberInfo"; +import { GetNewLibl } from "./getNewLibl"; + +export class ComponentRegistry { + private allComponents: (typeof ComponentT)[] = [GetNewLibl, CopyToImport, GetMemberInfo]; + + public registerComponent(component: typeof ComponentT) { + this.allComponents.push(component); + } + + public getComponents() { + return this.allComponents; + } +} + +export const ExtensionComponentRegistry = new ComponentRegistry(); + +interface ComponentList {[name: string]: ComponentT}; + +export class ComponentManager { + private registered: ComponentList = {}; + + public async startup(connection: IBMi) { + for (const Component of ExtensionComponentRegistry.getComponents()) { + const instance = new Component(connection); + this.registered[Component.name] = instance; + await ComponentManager.checkState(instance); + } + } + + get(id: string): T | undefined { + const component = this.registered[id]; + if (component && component.getState() === ComponentState.Installed) { + return component as T; + } + } + + private static async checkState(component: ComponentT) { + try { + await component.checkState(); + } catch (e) { + console.log(component); + console.log(`Error checking state for ${component.constructor.name}`, e); + } + } +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index a2b4c97bc..67f89f50f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -31,6 +31,7 @@ import { initializeIFSBrowser } from "./views/ifsBrowser"; import { initializeObjectBrowser } from "./views/objectBrowser"; import { initializeSearchView } from "./views/searchView"; import { SettingsUI } from "./webviews/settings"; +import { ExtensionComponentRegistry } from "./components/manager"; export async function activate(context: ExtensionContext): Promise { // Use the console to output diagnostic information (console.log) and errors (console.error) @@ -125,7 +126,13 @@ export async function activate(context: ExtensionContext): Promise commands.executeCommand("code-for-ibmi.refreshProfileView"); }); - return { instance, customUI: () => new CustomUI(), deployTools: DeployTools, evfeventParser: parseErrors, tools: Tools }; + return { + instance, customUI: () => new CustomUI(), + deployTools: DeployTools, + evfeventParser: parseErrors, + tools: Tools, + componentRegistry: ExtensionComponentRegistry + }; } async function fixLoginSettings() { diff --git a/src/typings.ts b/src/typings.ts index cceef76f0..381b4e6cb 100644 --- a/src/typings.ts +++ b/src/typings.ts @@ -5,13 +5,15 @@ import { CustomUI } from "./api/CustomUI"; import Instance from "./api/Instance"; import { Tools } from "./api/Tools"; import { DeployTools } from "./api/local/deployTools"; +import { ComponentRegistry } from './components/manager'; export interface CodeForIBMi { instance: Instance, customUI: () => CustomUI, deployTools: typeof DeployTools, evfeventParser: (lines: string[]) => Map, - tools: typeof Tools + tools: typeof Tools, + componentRegistry: ComponentRegistry } export type DeploymentMethod = "all" | "staged" | "unstaged" | "changed" | "compare"; From 51e55df71ef1b2900d1b62156398c26f6d6787c3 Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Wed, 18 Sep 2024 17:47:53 +0200 Subject: [PATCH 2/7] Code cleanup Signed-off-by: Seb Julliand --- src/components/manager.ts | 14 ++++++-------- src/extension.ts | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/components/manager.ts b/src/components/manager.ts index bf66cea0b..a34ccf12a 100644 --- a/src/components/manager.ts +++ b/src/components/manager.ts @@ -5,7 +5,7 @@ import { GetMemberInfo } from "./getMemberInfo"; import { GetNewLibl } from "./getNewLibl"; export class ComponentRegistry { - private allComponents: (typeof ComponentT)[] = [GetNewLibl, CopyToImport, GetMemberInfo]; + private readonly allComponents: (typeof ComponentT)[] = [GetNewLibl, CopyToImport, GetMemberInfo]; public registerComponent(component: typeof ComponentT) { this.allComponents.push(component); @@ -16,23 +16,21 @@ export class ComponentRegistry { } } -export const ExtensionComponentRegistry = new ComponentRegistry(); - -interface ComponentList {[name: string]: ComponentT}; +export const extensionComponentRegistry = new ComponentRegistry(); export class ComponentManager { - private registered: ComponentList = {}; + private readonly registered: Map = new Map; public async startup(connection: IBMi) { - for (const Component of ExtensionComponentRegistry.getComponents()) { + for (const Component of extensionComponentRegistry.getComponents()) { const instance = new Component(connection); - this.registered[Component.name] = instance; + this.registered.set(Component.name, instance); await ComponentManager.checkState(instance); } } get(id: string): T | undefined { - const component = this.registered[id]; + const component = this.registered.get(id); if (component && component.getState() === ComponentState.Installed) { return component as T; } diff --git a/src/extension.ts b/src/extension.ts index 67f89f50f..0f0965f4d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -16,6 +16,7 @@ import * as Debug from './api/debug'; import { parseErrors } from "./api/errors/parser"; import { DeployTools } from "./api/local/deployTools"; import { Deployment } from "./api/local/deployment"; +import { extensionComponentRegistry } from "./components/manager"; import { IFSFS } from "./filesystems/ifsFs"; import { LocalActionCompletionItemProvider } from "./languages/actions/completion"; import { updateLocale } from "./locale"; @@ -31,7 +32,6 @@ import { initializeIFSBrowser } from "./views/ifsBrowser"; import { initializeObjectBrowser } from "./views/objectBrowser"; import { initializeSearchView } from "./views/searchView"; import { SettingsUI } from "./webviews/settings"; -import { ExtensionComponentRegistry } from "./components/manager"; export async function activate(context: ExtensionContext): Promise { // Use the console to output diagnostic information (console.log) and errors (console.error) @@ -131,7 +131,7 @@ export async function activate(context: ExtensionContext): Promise deployTools: DeployTools, evfeventParser: parseErrors, tools: Tools, - componentRegistry: ExtensionComponentRegistry + componentRegistry: extensionComponentRegistry }; } From d440c7286f495265f385afb7609a88b42d2870d3 Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Wed, 18 Sep 2024 23:41:37 +0200 Subject: [PATCH 3/7] Components management refactoring Signed-off-by: Seb Julliand --- src/api/IBMi.ts | 13 ++-- src/components/component.ts | 77 +++++++++++++++++--- src/components/copyToImport.ts | 35 ++++----- src/components/getMemberInfo.ts | 122 ++++++++++++-------------------- src/components/getNewLibl.ts | 84 +++++++++------------- src/components/manager.ts | 31 ++++---- src/instantiate.ts | 14 ++-- src/testing/components.ts | 4 +- src/views/ProfilesView.ts | 2 +- 9 files changed, 192 insertions(+), 190 deletions(-) diff --git a/src/api/IBMi.ts b/src/api/IBMi.ts index 4317e81c9..599bf0104 100644 --- a/src/api/IBMi.ts +++ b/src/api/IBMi.ts @@ -6,7 +6,9 @@ import { parse } from 'csv-parse/sync'; import { existsSync } from "fs"; import os from "os"; import path from 'path'; +import { IBMiComponent, IBMiComponentType } from "../components/component"; import { CopyToImport } from "../components/copyToImport"; +import { ComponentManager } from "../components/manager"; import { instance } from "../instantiate"; import { CommandData, CommandResult, ConnectionData, IBMiMember, RemoteCommand, SpecialAuthorities, WrapResult } from "../typings"; import { CompileTools } from "./CompileTools"; @@ -16,7 +18,6 @@ import { Tools } from './Tools'; import * as configVars from './configVars'; import { DebugConfiguration } from "./debug/config"; import { debugPTFInstalled } from "./debug/server"; -import { ComponentManager } from "../components/manager"; export interface MemberParts extends IBMiMember { basename: string @@ -55,7 +56,7 @@ export default class IBMi { /** User default CCSID is job default CCSID */ private userDefaultCCSID: number = 0; - private components: ComponentManager = new ComponentManager(); + private componentManager = new ComponentManager(this); client: node_ssh.NodeSSH; currentHost: string = ``; @@ -927,7 +928,7 @@ export default class IBMi { } progress.report({ message: `Checking Code for IBM i components.` }); - await this.components.startup(this); + await this.componentManager.startup(); if (!reconnecting) { vscode.workspace.getConfiguration().update(`workbench.editor.enablePreview`, false, true); @@ -1341,8 +1342,8 @@ export default class IBMi { } } - getComponent(id: string) { - return this.components.get(id); + getComponent(type: IBMiComponentType): T | undefined { + return this.componentManager.get(type); } /** @@ -1372,7 +1373,7 @@ export default class IBMi { if (lastStmt) { if ((asUpper?.startsWith(`SELECT`) || asUpper?.startsWith(`WITH`))) { - const copyToImport = this.getComponent(`CopyToImport`); + const copyToImport = this.getComponent(CopyToImport); if (copyToImport) { returningAsCsv = copyToImport.wrap(lastStmt); list.push(...returningAsCsv.newStatements); diff --git a/src/components/component.ts b/src/components/component.ts index 4f5518d17..f7ca0b92d 100644 --- a/src/components/component.ts +++ b/src/components/component.ts @@ -1,19 +1,80 @@ import IBMi from "../api/IBMi"; -export enum ComponentState { +export const enum ComponentState { NotChecked = `NotChecked`, NotInstalled = `NotInstalled`, Installed = `Installed`, + NeedUpdate = `NeedUpdate`, Error = `Error`, } +export type IBMiComponentType = new (c: IBMi) => T; -export class ComponentT { - public state: ComponentState = ComponentState.NotChecked; - public currentVersion: number = 0; +/** + * Defines a component that is managed per IBM i. + * + * Any class extending {@link IBMiComponent} needs to register itself in the Component Registry. + * + * For example, this class: + * ``` + * class MyIBMIComponent extends IBMiComponent { + * //implements getName(), getRemoteState() and update() + * } + * ``` + * Must be registered like this: + * ``` + * const codeForIBMiExtension = vscode.extensions.getExtension('halcyontechltd.code-for-ibmi'); + * if (codeForIBMiExtension) { + * codeForIBMi = codeForIBMiExtension.isActive ? codeForIBMiExtension.exports : await codeForIBMiExtension.activate(); + * codeForIBMi.componentRegistry.registerComponent(MyIBMIComponent); + * } + * ``` + * + */ +export abstract class IBMiComponent { + private state = ComponentState.NotChecked; - constructor(public connection: IBMi) { } + constructor(protected readonly connection: IBMi) { - async getInstalledVersion(): Promise {return}; - async checkState(): Promise {return false} - getState(): ComponentState {return this.state}; + } + + getState() { + return this.state; + } + + async check() { + try { + this.state = await this.getRemoteState(); + if (this.state !== ComponentState.Installed) { + this.state = await this.update(); + } + } + catch (error) { + console.log(`Error occurred while checking component ${this.getName()}`); + console.log(error); + this.state = ComponentState.Error; + } + + return this; + } + + /** + * The name of this component; mainly used for display and logging purposes + * + * @returns a human-readable name + */ + abstract getName(): string; + + /** + * @returns the component's {@link ComponentState state} on the IBM i + */ + protected abstract getRemoteState(): ComponentState | Promise; + + /** + * Called whenever the components needs to be installed or updated, depending on its {@link ComponentState state}. + * + * The Component Manager is responsible for calling this, so the {@link ComponentState state} doesn't need to be checked here. + * + * @returns the component's {@link ComponentState state} after the update is done + */ + protected abstract update(): ComponentState | Promise } \ No newline at end of file diff --git a/src/components/copyToImport.ts b/src/components/copyToImport.ts index ca1b57d4a..24b2d0458 100644 --- a/src/components/copyToImport.ts +++ b/src/components/copyToImport.ts @@ -1,27 +1,8 @@ -import IBMi from "../api/IBMi"; import { Tools } from "../api/Tools"; import { WrapResult } from "../typings"; -import { ComponentState, ComponentT } from "./component"; - -export class CopyToImport implements ComponentT { - private readonly name = 'CPYTOIMPF'; - public state: ComponentState = ComponentState.Installed; - public currentVersion: number = 1; - - constructor(public connection: IBMi) { } - - async getInstalledVersion(): Promise { - return 1; - } - - async checkState(): Promise { - return true; - } - - getState(): ComponentState { - return this.state; - } +import { ComponentState, IBMiComponent } from "./component"; +export class CopyToImport extends IBMiComponent { static isSimple(statement: string): boolean { statement = statement.trim(); if (statement.endsWith(';')) { @@ -32,6 +13,18 @@ export class CopyToImport implements ComponentT { return parts.length === 4 && parts[0].toUpperCase() === `SELECT` && parts[1] === `*` && parts[2].toUpperCase() === `FROM` && parts[3].includes(`.`); } + getName() { + return 'CPYTOIMPF'; + } + + protected getRemoteState() { + return ComponentState.Installed; + } + + protected update(): ComponentState | Promise { + return this.getRemoteState(); + } + wrap(statement: string): WrapResult { const outStmf = this.connection.getTempRemote(Tools.makeid())!; diff --git a/src/components/getMemberInfo.ts b/src/components/getMemberInfo.ts index 7ec9834c7..c78aeb258 100644 --- a/src/components/getMemberInfo.ts +++ b/src/components/getMemberInfo.ts @@ -1,107 +1,79 @@ import { posix } from "path"; -import IBMi from "../api/IBMi"; import { Tools } from "../api/Tools"; -import { instance } from "../instantiate"; import { IBMiMember } from "../typings"; -import { ComponentState, ComponentT } from "./component"; +import { ComponentState, IBMiComponent } from "./component"; -export class GetMemberInfo implements ComponentT { - public readonly name = 'GETMBRINFO'; - public state: ComponentState = ComponentState.NotInstalled; - public currentVersion: number = 1; +export class GetMemberInfo extends IBMiComponent { + private readonly currentVersion = 1; - constructor(public connection: IBMi) { } + getName() { + return "GETMBRINFO"; + } - async getInstalledVersion(): Promise { - const config = this.connection.config! - const lib = config.tempLibrary!; - const sql = `select LONG_COMMENT from qsys2.sysroutines where routine_schema = '${lib.toUpperCase()}' and routine_name = '${this.name}'` - const [result] = await this.connection.runSQL(sql); - if (result && result.LONG_COMMENT) { + protected async getRemoteState(): Promise { + let installedVersion = 0; + const [result] = await this.connection.runSQL(`select LONG_COMMENT from qsys2.sysroutines where routine_schema = '${this.connection.config?.tempLibrary.toUpperCase()}' and routine_name = 'GETMBRINFO'`); + if (result.LONG_COMMENT) { const comment = result.LONG_COMMENT as string; const dash = comment.indexOf('-'); if (dash > -1) { - const version = comment.substring(0, dash).trim(); - return parseInt(version); + installedVersion = Number(comment.substring(0, dash).trim()); } } - - return 0; - } - - async checkState(): Promise { - const installedVersion = await this.getInstalledVersion(); - - if (installedVersion === this.currentVersion) { - this.state = ComponentState.Installed; - return true; + if (installedVersion < this.currentVersion) { + return ComponentState.NeedUpdate; } - const config = this.connection.config! - const content = instance.getContent(); + return ComponentState.Installed; + } + protected async update() { + const config = this.connection.config!; return this.connection.withTempDirectory(async tempDir => { const tempSourcePath = posix.join(tempDir, `getMemberInfo.sql`); - - await content!.writeStreamfileRaw(tempSourcePath, getSource(config.tempLibrary, this.name, this.currentVersion)); + await this.connection.content.writeStreamfileRaw(tempSourcePath, getSource(config.tempLibrary, this.getName(), this.currentVersion)); const result = await this.connection.runCommand({ command: `RUNSQLSTM SRCSTMF('${tempSourcePath}') COMMIT(*NONE) NAMING(*SQL)`, cwd: `/`, noLibList: true }); - if (result.code === 0) { - this.state = ComponentState.Installed; + if (result.code) { + return ComponentState.Error; } else { - this.state = ComponentState.Error; + return ComponentState.Installed; } - - return this.state === ComponentState.Installed; }); } - getState(): ComponentState { - return this.state; - } - - - /** - * - * @param filter: the criterias used to list the members - * @returns - */ async getMemberInfo(library: string, sourceFile: string, member: string): Promise { - if (this.state === ComponentState.Installed) { - const config = this.connection.config!; - const tempLib = config.tempLibrary; - const statement = `select * from table(${tempLib}.GETMBRINFO('${library}', '${sourceFile}', '${member}'))`; - - let results: Tools.DB2Row[] = []; - if (config.enableSQL) { - try { - results = await this.connection.runSQL(statement); - } catch (e) { }; // Ignore errors, will return undefined. - } - else { - results = await this.connection.content.getQTempTable([`create table QTEMP.MEMBERINFO as (${statement}) with data`], "MEMBERINFO"); - } + const config = this.connection.config!; + const tempLib = config.tempLibrary; + const statement = `select * from table(${tempLib}.${this.getName()}('${library}', '${sourceFile}', '${member}'))`; + + let results: Tools.DB2Row[] = []; + if (config.enableSQL) { + try { + results = await this.connection.runSQL(statement); + } catch (e) { } // Ignore errors, will return undefined. + } + else { + results = await this.connection.content.getQTempTable([`create table QTEMP.MEMBERINFO as (${statement}) with data`], "MEMBERINFO"); + } - if (results.length === 1 && results[0].ISSOURCE === 'Y') { - const result = results[0]; - const asp = this.connection.aspInfo[Number(results[0].ASP)]; - return { - library: result.LIBRARY, - file: result.FILE, - name: result.MEMBER, - extension: result.EXTENSION, - text: result.DESCRIPTION, - created: new Date(result.CREATED ? Number(result.CREATED) : 0), - changed: new Date(result.CHANGED ? Number(result.CHANGED) : 0) - } as IBMiMember - } - else { - return undefined; - } + if (results.length === 1 && results[0].ISSOURCE === 'Y') { + const result = results[0]; + const asp = this.connection.aspInfo[Number(results[0].ASP)]; + return { + asp, + library: result.LIBRARY, + file: result.FILE, + name: result.MEMBER, + extension: result.EXTENSION, + text: result.DESCRIPTION, + created: new Date(result.CREATED ? Number(result.CREATED) : 0), + changed: new Date(result.CHANGED ? Number(result.CHANGED) : 0) + } as IBMiMember } } } diff --git a/src/components/getNewLibl.ts b/src/components/getNewLibl.ts index 914d1dc51..06d0928a9 100644 --- a/src/components/getNewLibl.ts +++ b/src/components/getNewLibl.ts @@ -1,26 +1,19 @@ import { posix } from "path"; -import IBMi from "../api/IBMi"; import { instance } from "../instantiate"; -import { ComponentState, ComponentT } from "./component"; +import { ComponentState, IBMiComponent } from "./component"; -export class GetNewLibl implements ComponentT { - public state: ComponentState = ComponentState.NotInstalled; - public currentVersion: number = 1; +export class GetNewLibl extends IBMiComponent { + private readonly currentVersion = 1; - constructor(public connection: IBMi) { } - - async getInstalledVersion(): Promise { - return (this.connection.remoteFeatures[`GETNEWLIBL.PGM`] ? 1 : 0); + getName() { + return 'GETNEWLIBL'; } - async checkState(): Promise { - const installedVersion = await this.getInstalledVersion(); - - if (installedVersion === this.currentVersion) { - this.state = ComponentState.Installed; - return true; - } + protected async getRemoteState() { + return this.connection.remoteFeatures[`GETNEWLIBL.PGM`] ? ComponentState.Installed : ComponentState.NotInstalled; + } + protected update() { const config = this.connection.config! const content = instance.getContent(); return this.connection.withTempDirectory(async tempDir => { @@ -33,48 +26,37 @@ export class GetNewLibl implements ComponentT { noLibList: true }); - if (result.code === 0) { - this.state = ComponentState.Installed; + if (!result.code) { + return ComponentState.Installed; } else { - this.state = ComponentState.Error; + return ComponentState.Error; } - - return this.state === ComponentState.Installed; }); } - getState(): ComponentState { - return this.state; - } - - async getLibraryListFromCommand(ileCommand: string): Promise<{ currentLibrary: string; libraryList: string[]; } | undefined> { - if (this.state === ComponentState.Installed) { - const tempLib = this.connection.config!.tempLibrary; - const resultSet = await this.connection.runSQL(`CALL ${tempLib}.GETNEWLIBL('${ileCommand.replace(new RegExp(`'`, 'g'), `''`)}')`); - - const result = { - currentLibrary: `QGPL`, - libraryList: [] as string[] - }; - - resultSet.forEach(row => { - const libraryName = String(row.SYSTEM_SCHEMA_NAME); - switch (row.PORTION) { - case `CURRENT`: - result.currentLibrary = libraryName; - break; - case `USER`: - result.libraryList.push(libraryName); - break; - } - }) - - return result; - } + async getLibraryListFromCommand(ileCommand: string) { + const tempLib = this.connection.config!.tempLibrary; + const resultSet = await this.connection.runSQL(`CALL ${tempLib}.GETNEWLIBL('${ileCommand.replace(new RegExp(`'`, 'g'), `''`)}')`); + + const result = { + currentLibrary: `QGPL`, + libraryList: [] as string[] + }; + + resultSet.forEach(row => { + const libraryName = String(row.SYSTEM_SCHEMA_NAME); + switch (row.PORTION) { + case `CURRENT`: + result.currentLibrary = libraryName; + break; + case `USER`: + result.libraryList.push(libraryName); + break; + } + }) - return undefined; + return result; } - } function getSource(library: string) { diff --git a/src/components/manager.ts b/src/components/manager.ts index a34ccf12a..40fc00e59 100644 --- a/src/components/manager.ts +++ b/src/components/manager.ts @@ -1,13 +1,13 @@ import IBMi from "../api/IBMi"; -import { ComponentState, ComponentT } from "./component"; +import { ComponentState, IBMiComponent, IBMiComponentType } from "./component"; import { CopyToImport } from "./copyToImport"; import { GetMemberInfo } from "./getMemberInfo"; import { GetNewLibl } from "./getNewLibl"; export class ComponentRegistry { - private readonly allComponents: (typeof ComponentT)[] = [GetNewLibl, CopyToImport, GetMemberInfo]; + private readonly allComponents: (IBMiComponentType)[] = [GetNewLibl, CopyToImport, GetMemberInfo]; - public registerComponent(component: typeof ComponentT) { + public registerComponent(component: IBMiComponentType) { this.allComponents.push(component); } @@ -19,29 +19,22 @@ export class ComponentRegistry { export const extensionComponentRegistry = new ComponentRegistry(); export class ComponentManager { - private readonly registered: Map = new Map; + private readonly registered: Map, IBMiComponent> = new Map; - public async startup(connection: IBMi) { + constructor(private readonly connection: IBMi) { + + } + + public async startup() { for (const Component of extensionComponentRegistry.getComponents()) { - const instance = new Component(connection); - this.registered.set(Component.name, instance); - await ComponentManager.checkState(instance); + this.registered.set(Component, await new Component(this.connection).check()); } } - get(id: string): T | undefined { - const component = this.registered.get(id); + get(type: IBMiComponentType): T | undefined { + const component = this.registered.get(type); if (component && component.getState() === ComponentState.Installed) { return component as T; } } - - private static async checkState(component: ComponentT) { - try { - await component.checkState(); - } catch (e) { - console.log(component); - console.log(`Error checking state for ${component.constructor.name}`, e); - } - } } \ No newline at end of file diff --git a/src/instantiate.ts b/src/instantiate.ts index dd90c0c62..b74d1b1e4 100644 --- a/src/instantiate.ts +++ b/src/instantiate.ts @@ -466,25 +466,25 @@ export async function loadAllofExtension(context: vscode.ExtensionContext) { const selectionSplit = connection!.upperCaseName(selection).split('/') if (selectionSplit.length === 3 || selection.startsWith(`/`)) { - const infoComponent = connection?.getComponent(`GetMemberInfo`); + const infoComponent = connection?.getComponent(GetMemberInfo); // When selection is QSYS path - if (!selection.startsWith(`/`) && infoComponent) { - const lib = selectionSplit[0]; + if (!selection.startsWith(`/`) && infoComponent && connection) { + const library = selectionSplit[0]; const file = selectionSplit[1]; const member = path.parse(selectionSplit[2]); member.ext = member.ext.substring(1); - const memberInfo = await infoComponent.getMemberInfo(lib, file, member.name); + const memberInfo = await infoComponent.getMemberInfo(library, file, member.name); if (!memberInfo) { - vscode.window.showWarningMessage(`Source member ${lib}/${file}/${member.base} does not exist.`); + vscode.window.showWarningMessage(`Source member ${library}/${file}/${member.base} does not exist.`); return; } else if (memberInfo.name !== member.name || (member.ext && memberInfo.extension !== member.ext)) { - vscode.window.showWarningMessage(`Member ${lib}/${file}/${member.name} of type ${member.ext} does not exist.`); + vscode.window.showWarningMessage(`Member ${library}/${file}/${member.name} of type ${member.ext} does not exist.`); return; } member.base = `${member.name}.${member.ext || memberInfo.extension}`; - selection = `${lib}/${file}/${member.base}`; + selection = `${library}/${file}/${member.base}`; }; // When select is IFS path diff --git a/src/testing/components.ts b/src/testing/components.ts index 491351a83..0893c70a8 100644 --- a/src/testing/components.ts +++ b/src/testing/components.ts @@ -16,7 +16,7 @@ export const ComponentSuite: TestSuite = { { name: `Get new libl`, test: async () => { const connection = instance.getConnection()! - const component = connection.getComponent(`GetNewLibl`); + const component = connection.getComponent(GetNewLibl); if (component) { const newLibl = await component.getLibraryListFromCommand(`CHGLIBL CURLIB(SYSTOOLS)`); @@ -31,7 +31,7 @@ export const ComponentSuite: TestSuite = { { name: `Check getMemberInfo`, test: async () => { const connection = instance.getConnection(); - const component = connection?.getComponent(`GetMemberInfo`)!; + const component = connection?.getComponent(GetMemberInfo)!; assert.ok(component); diff --git a/src/views/ProfilesView.ts b/src/views/ProfilesView.ts index f9c6e00be..c77005c93 100644 --- a/src/views/ProfilesView.ts +++ b/src/views/ProfilesView.ts @@ -121,7 +121,7 @@ export class ProfilesView { if (storedProfile) { try { - const component = connection?.getComponent(`GetNewLibl`) + const component = connection?.getComponent(GetNewLibl) const newSettings = await component?.getLibraryListFromCommand(storedProfile.command); if (newSettings) { From 5e5e0af4a674c6f38d69954d74b34b39c01c5a28 Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Thu, 19 Sep 2024 11:12:02 +0200 Subject: [PATCH 4/7] Require context to register a component + display components list Signed-off-by: Seb Julliand --- src/api/IBMi.ts | 4 ++-- src/components/component.ts | 18 ++++++++++-------- src/components/manager.ts | 26 ++++++++++++++++---------- src/extension.ts | 19 +++++++++++++------ src/webviews/settings/index.ts | 23 +++++++++++++++++++++-- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/src/api/IBMi.ts b/src/api/IBMi.ts index d1f713f55..29c923057 100644 --- a/src/api/IBMi.ts +++ b/src/api/IBMi.ts @@ -1354,8 +1354,8 @@ export default class IBMi { } } - getComponent(type: IBMiComponentType): T | undefined { - return this.componentManager.get(type); + getComponent(type: IBMiComponentType, ignoreState?:boolean): T | undefined { + return this.componentManager.get(type, ignoreState); } /** diff --git a/src/components/component.ts b/src/components/component.ts index f7ca0b92d..7ed33838b 100644 --- a/src/components/component.ts +++ b/src/components/component.ts @@ -1,10 +1,10 @@ import IBMi from "../api/IBMi"; export const enum ComponentState { - NotChecked = `NotChecked`, - NotInstalled = `NotInstalled`, + NotChecked = `Not checked`, + NotInstalled = `Not installed`, Installed = `Installed`, - NeedUpdate = `NeedUpdate`, + NeedUpdate = `Need update`, Error = `Error`, } export type IBMiComponentType = new (c: IBMi) => T; @@ -20,12 +20,14 @@ export type IBMiComponentType = new (c: IBMi) => T; * //implements getName(), getRemoteState() and update() * } * ``` - * Must be registered like this: + * Must be registered like this, when the extension providing the component gets activated: * ``` - * const codeForIBMiExtension = vscode.extensions.getExtension('halcyontechltd.code-for-ibmi'); - * if (codeForIBMiExtension) { - * codeForIBMi = codeForIBMiExtension.isActive ? codeForIBMiExtension.exports : await codeForIBMiExtension.activate(); - * codeForIBMi.componentRegistry.registerComponent(MyIBMIComponent); + * export async function activate(context: ExtensionContext) { + * const codeForIBMiExtension = vscode.extensions.getExtension('halcyontechltd.code-for-ibmi'); + * if (codeForIBMiExtension) { + * codeForIBMi = codeForIBMiExtension.isActive ? codeForIBMiExtension.exports : await codeForIBMiExtension.activate(); + * codeForIBMi.componentRegistry.registerComponent(context, MyIBMIComponent); + * } * } * ``` * diff --git a/src/components/manager.ts b/src/components/manager.ts index 40fc00e59..ec0c19152 100644 --- a/src/components/manager.ts +++ b/src/components/manager.ts @@ -1,18 +1,23 @@ +import vscode from "vscode"; import IBMi from "../api/IBMi"; import { ComponentState, IBMiComponent, IBMiComponentType } from "./component"; -import { CopyToImport } from "./copyToImport"; -import { GetMemberInfo } from "./getMemberInfo"; -import { GetNewLibl } from "./getNewLibl"; export class ComponentRegistry { - private readonly allComponents: (IBMiComponentType)[] = [GetNewLibl, CopyToImport, GetMemberInfo]; + private readonly components: Map)[]> = new Map; - public registerComponent(component: IBMiComponentType) { - this.allComponents.push(component); + public registerComponent(context: vscode.ExtensionContext, component: IBMiComponentType) { + const key = context.extension.id; + const extensionComponents = this.components.get(key); + if (extensionComponents) { + extensionComponents.push(component); + } + else { + this.components.set(key, [component]); + } } public getComponents() { - return this.allComponents; + return this.components; } } @@ -26,14 +31,15 @@ export class ComponentManager { } public async startup() { - for (const Component of extensionComponentRegistry.getComponents()) { + const components = Array.from(extensionComponentRegistry.getComponents().values()).flatMap(a => a.flat()); + for (const Component of components) { this.registered.set(Component, await new Component(this.connection).check()); } } - get(type: IBMiComponentType): T | undefined { + get(type: IBMiComponentType, ignoreState?: boolean): T | undefined { const component = this.registered.get(type); - if (component && component.getState() === ComponentState.Installed) { + if (component && (ignoreState || component.getState() === ComponentState.Installed)) { return component as T; } } diff --git a/src/extension.ts b/src/extension.ts index 0f0965f4d..f4e04ab97 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -16,6 +16,9 @@ import * as Debug from './api/debug'; import { parseErrors } from "./api/errors/parser"; import { DeployTools } from "./api/local/deployTools"; import { Deployment } from "./api/local/deployment"; +import { CopyToImport } from "./components/copyToImport"; +import { GetMemberInfo } from "./components/getMemberInfo"; +import { GetNewLibl } from "./components/getNewLibl"; import { extensionComponentRegistry } from "./components/manager"; import { IFSFS } from "./filesystems/ifsFs"; import { LocalActionCompletionItemProvider } from "./languages/actions/completion"; @@ -126,12 +129,16 @@ export async function activate(context: ExtensionContext): Promise commands.executeCommand("code-for-ibmi.refreshProfileView"); }); - return { - instance, customUI: () => new CustomUI(), - deployTools: DeployTools, - evfeventParser: parseErrors, - tools: Tools, - componentRegistry: extensionComponentRegistry + extensionComponentRegistry.registerComponent(context, GetNewLibl); + extensionComponentRegistry.registerComponent(context, GetMemberInfo); + extensionComponentRegistry.registerComponent(context, CopyToImport); + + return { + instance, customUI: () => new CustomUI(), + deployTools: DeployTools, + evfeventParser: parseErrors, + tools: Tools, + componentRegistry: extensionComponentRegistry }; } diff --git a/src/webviews/settings/index.ts b/src/webviews/settings/index.ts index 7694d390d..80b7e4130 100644 --- a/src/webviews/settings/index.ts +++ b/src/webviews/settings/index.ts @@ -6,6 +6,8 @@ import { Tools } from "../../api/Tools"; import { isManaged } from "../../api/debug"; import * as certificates from "../../api/debug/certificates"; import { isSEPSupported } from "../../api/debug/server"; +import { IBMiComponent } from "../../components/component"; +import { extensionComponentRegistry } from "../../components/manager"; import { instance } from "../../instantiate"; import { t } from "../../locale"; import { ConnectionData, Server } from '../../typings'; @@ -215,12 +217,29 @@ export class SettingsUI { debuggerTab.addParagraph('Connect to the server to see these settings.'); } - let tabs: ComplexTab[] = [ + const componentsTab = new Section(); + if (connection) { + componentsTab.addParagraph(`The following extensions contribute these components:`); + extensionComponentRegistry.getComponents().forEach((components, extensionId) => { + const extension = vscode.extensions.getExtension(extensionId); + componentsTab.addParagraph(`

+

${extension?.packageJSON.displayName || extension?.id || "Unnamed extension"}

+
    + ${components.map(type => connection.getComponent(type, true)).map(component => `
  • ${component?.getName()}: ${component?.getState()}
  • `).join(``)} +
+

`); + }) + } else { + componentsTab.addParagraph('Connect to the server to see these settings.'); + } + + const tabs: ComplexTab[] = [ { label: `Features`, fields: featuresTab.fields }, { label: `Source Code`, fields: sourceTab.fields }, { label: `Terminals`, fields: terminalsTab.fields }, { label: `Debugger`, fields: debuggerTab.fields }, { label: `Temporary Data`, fields: tempDataTab.fields }, + { label: `Components`, fields: componentsTab.fields }, ]; const ui = new CustomUI(); @@ -380,7 +399,7 @@ export class SettingsUI { await ConnectionManager.deleteStoredPassword(context, name); vscode.window.showInformationMessage(t(`login.privateKey.updated`, name)); } - else{ + else { delete data.privateKeyPath; } break; From 56427caef3c89ea28899c6da5b51b783dcd9a5b7 Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Thu, 19 Sep 2024 15:29:27 +0200 Subject: [PATCH 5/7] Show version of each component Signed-off-by: Seb Julliand --- src/components/component.ts | 15 ++++++- src/components/copyToImport.ts | 4 +- src/components/getMemberInfo.ts | 80 ++++++++++++++++----------------- src/components/getNewLibl.ts | 6 +-- src/webviews/settings/index.ts | 2 +- 5 files changed, 56 insertions(+), 51 deletions(-) diff --git a/src/components/component.ts b/src/components/component.ts index 7ed33838b..0612c6f11 100644 --- a/src/components/component.ts +++ b/src/components/component.ts @@ -7,6 +7,12 @@ export const enum ComponentState { NeedUpdate = `Need update`, Error = `Error`, } + +export type ComponentIdentification = { + name: string + version: number +} + export type IBMiComponentType = new (c: IBMi) => T; /** @@ -51,7 +57,7 @@ export abstract class IBMiComponent { } } catch (error) { - console.log(`Error occurred while checking component ${this.getName()}`); + console.log(`Error occurred while checking component ${this.toString()}`); console.log(error); this.state = ComponentState.Error; } @@ -59,12 +65,17 @@ export abstract class IBMiComponent { return this; } + toString() { + const identification = this.getIdentification(); + return `${identification.name} (version ${identification.version})` + } + /** * The name of this component; mainly used for display and logging purposes * * @returns a human-readable name */ - abstract getName(): string; + abstract getIdentification(): ComponentIdentification; /** * @returns the component's {@link ComponentState state} on the IBM i diff --git a/src/components/copyToImport.ts b/src/components/copyToImport.ts index 24b2d0458..7232acc18 100644 --- a/src/components/copyToImport.ts +++ b/src/components/copyToImport.ts @@ -13,8 +13,8 @@ export class CopyToImport extends IBMiComponent { return parts.length === 4 && parts[0].toUpperCase() === `SELECT` && parts[1] === `*` && parts[2].toUpperCase() === `FROM` && parts[3].includes(`.`); } - getName() { - return 'CPYTOIMPF'; + getIdentification() { + return { name: 'CopyToImport', version: 1 }; } protected getRemoteState() { diff --git a/src/components/getMemberInfo.ts b/src/components/getMemberInfo.ts index 1d1b7126c..2f86720e2 100644 --- a/src/components/getMemberInfo.ts +++ b/src/components/getMemberInfo.ts @@ -4,23 +4,24 @@ import { IBMiMember } from "../typings"; import { ComponentState, IBMiComponent } from "./component"; export class GetMemberInfo extends IBMiComponent { + private readonly procedureName = 'GETMBRINFO'; private readonly currentVersion = 1; + private installedVersion = 0; - getName() { - return "GETMBRINFO"; + getIdentification() { + return { name: 'GetMemberInfo', version: this.installedVersion }; } protected async getRemoteState(): Promise { - let installedVersion = 0; - const [result] = await this.connection.runSQL(`select LONG_COMMENT from qsys2.sysroutines where routine_schema = '${this.connection.config?.tempLibrary.toUpperCase()}' and routine_name = 'GETMBRINFO'`); + const [result] = await this.connection.runSQL(`select LONG_COMMENT from qsys2.sysroutines where routine_schema = '${this.connection.config?.tempLibrary.toUpperCase()}' and routine_name = '${this.procedureName}'`); if (result.LONG_COMMENT) { const comment = result.LONG_COMMENT as string; const dash = comment.indexOf('-'); if (dash > -1) { - installedVersion = Number(comment.substring(0, dash).trim()); + this.installedVersion = Number(comment.substring(0, dash).trim()); } } - if (installedVersion < this.currentVersion) { + if (this.installedVersion < this.currentVersion) { return ComponentState.NeedUpdate; } @@ -31,7 +32,7 @@ export class GetMemberInfo extends IBMiComponent { const config = this.connection.config!; return this.connection.withTempDirectory(async tempDir => { const tempSourcePath = posix.join(tempDir, `getMemberInfo.sql`); - await this.connection.content.writeStreamfileRaw(tempSourcePath, getSource(config.tempLibrary, this.getName(), this.currentVersion)); + await this.connection.content.writeStreamfileRaw(tempSourcePath, getSource(config.tempLibrary, this.procedureName, this.currentVersion)); const result = await this.connection.runCommand({ command: `RUNSQLSTM SRCSTMF('${tempSourcePath}') COMMIT(*NONE) NAMING(*SQL)`, cwd: `/`, @@ -49,7 +50,7 @@ export class GetMemberInfo extends IBMiComponent { async getMemberInfo(library: string, sourceFile: string, member: string): Promise { const config = this.connection.config!; const tempLib = config.tempLibrary; - const statement = `select * from table(${tempLib}.${this.getName()}('${library}', '${sourceFile}', '${member}'))`; + const statement = `select * from table(${tempLib}.${this.procedureName}('${library}', '${sourceFile}', '${member}'))`; let results: Tools.DB2Row[] = []; if (config.enableSQL) { @@ -77,41 +78,36 @@ export class GetMemberInfo extends IBMiComponent { } } - async getMultipleMemberInfo(members: IBMiMember[]): Promise { - if (this.state === ComponentState.Installed) { - const config = this.connection.config!; - const tempLib = config.tempLibrary; - const statement = members - .map(member => `select * from table(${this.connection.config!.tempLibrary}.GETMBRINFO('${member.library}', '${member.file}', '${member.name}'))`) - .join(' union all '); - - let results: Tools.DB2Row[] = []; - if (config.enableSQL) { - try { - results = await this.connection.runSQL(statement); - } catch (e) { }; // Ignore errors, will return undefined. - } - else { - results = await this.connection.content.getQTempTable([`create table QTEMP.MEMBERINFO as (${statement}) with data`], "MEMBERINFO"); - } - - return results.filter(row => row.ISSOURCE === 'Y').map(result => { - const asp = this.connection.aspInfo[Number(result.ASP)]; - return { - asp, - library: result.LIBRARY, - file: result.FILE, - name: result.MEMBER, - extension: result.EXTENSION, - text: result.DESCRIPTION, - created: new Date(result.CREATED ? Number(result.CREATED) : 0), - changed: new Date(result.CHANGED ? Number(result.CHANGED) : 0) - } as IBMiMember - }); + async getMultipleMemberInfo(members: IBMiMember[]): Promise { + const config = this.connection.config!; + const tempLib = config.tempLibrary; + const statement = members + .map(member => `select * from table(${tempLib}.${this.procedureName}('${member.library}', '${member.file}', '${member.name}'))`) + .join(' union all '); - } else { - return undefined; + let results: Tools.DB2Row[] = []; + if (config.enableSQL) { + try { + results = await this.connection.runSQL(statement); + } catch (e) { }; // Ignore errors, will return undefined. + } + else { + results = await this.connection.content.getQTempTable([`create table QTEMP.MEMBERINFO as (${statement}) with data`], "MEMBERINFO"); } + + return results.filter(row => row.ISSOURCE === 'Y').map(result => { + const asp = this.connection.aspInfo[Number(result.ASP)]; + return { + asp, + library: result.LIBRARY, + file: result.FILE, + name: result.MEMBER, + extension: result.EXTENSION, + text: result.DESCRIPTION, + created: new Date(result.CREATED ? Number(result.CREATED) : 0), + changed: new Date(result.CHANGED ? Number(result.CHANGED) : 0) + } as IBMiMember + }); } } @@ -142,7 +138,7 @@ function getSource(library: string, name: string, version: number) { `, Description varchar( 50 )`, `, isSource char( 1 )`, `)`, - `specific GETMBRINFO`, + `specific ${name}`, `modifies sql data`, `begin`, ` declare buffer char( 135 ) for bit data not null default '';`, diff --git a/src/components/getNewLibl.ts b/src/components/getNewLibl.ts index 06d0928a9..64014eef7 100644 --- a/src/components/getNewLibl.ts +++ b/src/components/getNewLibl.ts @@ -3,10 +3,8 @@ import { instance } from "../instantiate"; import { ComponentState, IBMiComponent } from "./component"; export class GetNewLibl extends IBMiComponent { - private readonly currentVersion = 1; - - getName() { - return 'GETNEWLIBL'; + getIdentification() { + return { name: 'GetNewLibl', version: 1 }; } protected async getRemoteState() { diff --git a/src/webviews/settings/index.ts b/src/webviews/settings/index.ts index 80b7e4130..e3d660b5c 100644 --- a/src/webviews/settings/index.ts +++ b/src/webviews/settings/index.ts @@ -225,7 +225,7 @@ export class SettingsUI { componentsTab.addParagraph(`

${extension?.packageJSON.displayName || extension?.id || "Unnamed extension"}

    - ${components.map(type => connection.getComponent(type, true)).map(component => `
  • ${component?.getName()}: ${component?.getState()}
  • `).join(``)} + ${components.map(type => connection.getComponent(type, true)).map(component => `
  • ${component?.toString()}: ${component?.getState()}
  • `).join(``)}

`); }) From 226d0ce05f6201b88c7cd64f74d4b0cb2cf9c868 Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Thu, 3 Oct 2024 18:06:34 +0200 Subject: [PATCH 6/7] Fixed wrong import Signed-off-by: Seb Julliand --- src/api/IBMi.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/IBMi.ts b/src/api/IBMi.ts index 838bce179..06ec6b5d8 100644 --- a/src/api/IBMi.ts +++ b/src/api/IBMi.ts @@ -4,7 +4,7 @@ import * as node_ssh from "node-ssh"; import os from "os"; import path, { parse as parsePath } from 'path'; import * as vscode from "vscode"; -import { IBMiComponent, IBMiComponentType } from "../components/componen +import { IBMiComponent, IBMiComponentType } from "../components/component"; import { CopyToImport } from "../components/copyToImport"; import { ComponentManager } from "../components/manager"; import { instance } from "../instantiate"; @@ -1353,7 +1353,7 @@ export default class IBMi { } } - getComponent(type: IBMiComponentType, ignoreState?:boolean): T | undefined { + getComponent(type: IBMiComponentType, ignoreState?: boolean): T | undefined { return this.componentManager.get(type, ignoreState); } From f79e370e08962acc2a8032ca719257dbbced4144 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Fri, 4 Oct 2024 13:35:44 -0400 Subject: [PATCH 7/7] Use type for component state instead of enum Signed-off-by: worksofliam --- src/components/component.ts | 14 ++++---------- src/components/copyToImport.ts | 4 ++-- src/components/getMemberInfo.ts | 10 +++++----- src/components/getNewLibl.ts | 12 ++++++------ src/components/manager.ts | 2 +- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/components/component.ts b/src/components/component.ts index 0612c6f11..74e2d1e47 100644 --- a/src/components/component.ts +++ b/src/components/component.ts @@ -1,12 +1,6 @@ import IBMi from "../api/IBMi"; -export const enum ComponentState { - NotChecked = `Not checked`, - NotInstalled = `Not installed`, - Installed = `Installed`, - NeedUpdate = `Need update`, - Error = `Error`, -} +export type ComponentState = `NotChecked` | `NotInstalled` | `Installed` | `NeedsUpdate` | `Error`; export type ComponentIdentification = { name: string @@ -39,7 +33,7 @@ export type IBMiComponentType = new (c: IBMi) => T; * */ export abstract class IBMiComponent { - private state = ComponentState.NotChecked; + private state: ComponentState = `NotChecked`; constructor(protected readonly connection: IBMi) { @@ -52,14 +46,14 @@ export abstract class IBMiComponent { async check() { try { this.state = await this.getRemoteState(); - if (this.state !== ComponentState.Installed) { + if (this.state !== `Installed`) { this.state = await this.update(); } } catch (error) { console.log(`Error occurred while checking component ${this.toString()}`); console.log(error); - this.state = ComponentState.Error; + this.state = `Error`; } return this; diff --git a/src/components/copyToImport.ts b/src/components/copyToImport.ts index 7232acc18..854b73062 100644 --- a/src/components/copyToImport.ts +++ b/src/components/copyToImport.ts @@ -17,8 +17,8 @@ export class CopyToImport extends IBMiComponent { return { name: 'CopyToImport', version: 1 }; } - protected getRemoteState() { - return ComponentState.Installed; + protected getRemoteState(): ComponentState { + return `Installed`; } protected update(): ComponentState | Promise { diff --git a/src/components/getMemberInfo.ts b/src/components/getMemberInfo.ts index 2f86720e2..0caa3bcb0 100644 --- a/src/components/getMemberInfo.ts +++ b/src/components/getMemberInfo.ts @@ -22,13 +22,13 @@ export class GetMemberInfo extends IBMiComponent { } } if (this.installedVersion < this.currentVersion) { - return ComponentState.NeedUpdate; + return `NeedsUpdate`; } - return ComponentState.Installed; + return `Installed`; } - protected async update() { + protected async update(): Promise { const config = this.connection.config!; return this.connection.withTempDirectory(async tempDir => { const tempSourcePath = posix.join(tempDir, `getMemberInfo.sql`); @@ -40,9 +40,9 @@ export class GetMemberInfo extends IBMiComponent { }); if (result.code) { - return ComponentState.Error; + return `Error`; } else { - return ComponentState.Installed; + return `Installed`; } }); } diff --git a/src/components/getNewLibl.ts b/src/components/getNewLibl.ts index 64014eef7..7d0b98860 100644 --- a/src/components/getNewLibl.ts +++ b/src/components/getNewLibl.ts @@ -7,14 +7,14 @@ export class GetNewLibl extends IBMiComponent { return { name: 'GetNewLibl', version: 1 }; } - protected async getRemoteState() { - return this.connection.remoteFeatures[`GETNEWLIBL.PGM`] ? ComponentState.Installed : ComponentState.NotInstalled; + protected async getRemoteState(): Promise { + return this.connection.remoteFeatures[`GETNEWLIBL.PGM`] ? `Installed` : `NotInstalled`; } - protected update() { + protected update(): Promise { const config = this.connection.config! const content = instance.getContent(); - return this.connection.withTempDirectory(async tempDir => { + return this.connection.withTempDirectory(async (tempDir): Promise => { const tempSourcePath = posix.join(tempDir, `getnewlibl.sql`); await content!.writeStreamfileRaw(tempSourcePath, getSource(config.tempLibrary)); @@ -25,9 +25,9 @@ export class GetNewLibl extends IBMiComponent { }); if (!result.code) { - return ComponentState.Installed; + return `Installed`; } else { - return ComponentState.Error; + return `Error`; } }); } diff --git a/src/components/manager.ts b/src/components/manager.ts index ec0c19152..60b8d9e0e 100644 --- a/src/components/manager.ts +++ b/src/components/manager.ts @@ -39,7 +39,7 @@ export class ComponentManager { get(type: IBMiComponentType, ignoreState?: boolean): T | undefined { const component = this.registered.get(type); - if (component && (ignoreState || component.getState() === ComponentState.Installed)) { + if (component && (ignoreState || component.getState() === `Installed`)) { return component as T; } }