-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2232 from codefori/feature/extendable_components
Extend the base API to allow other extensions to add their own components.
- Loading branch information
Showing
12 changed files
with
298 additions
and
250 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,87 @@ | ||
import IBMi from "../api/IBMi"; | ||
import { CopyToImport } from "./copyToImport"; | ||
import { GetMemberInfo } from "./getMemberInfo"; | ||
import { GetNewLibl } from "./getNewLibl"; | ||
|
||
export enum ComponentState { | ||
NotChecked = `NotChecked`, | ||
NotInstalled = `NotInstalled`, | ||
Installed = `Installed`, | ||
Error = `Error`, | ||
} | ||
interface ComponentRegistry { | ||
GetNewLibl?: GetNewLibl; | ||
CopyToImport?: CopyToImport; | ||
GetMemberInfo?: GetMemberInfo; | ||
} | ||
|
||
export type ComponentId = keyof ComponentRegistry; | ||
|
||
export abstract class ComponentT { | ||
public state: ComponentState = ComponentState.NotChecked; | ||
public currentVersion: number = 0; | ||
export type ComponentState = `NotChecked` | `NotInstalled` | `Installed` | `NeedsUpdate` | `Error`; | ||
|
||
constructor(public connection: IBMi) { } | ||
|
||
abstract getInstalledVersion(): Promise<number | undefined>; | ||
abstract checkState(): Promise<boolean> | ||
abstract getState(): ComponentState; | ||
export type ComponentIdentification = { | ||
name: string | ||
version: number | ||
} | ||
|
||
export class ComponentManager { | ||
private registered: ComponentRegistry = {}; | ||
export type IBMiComponentType<T extends IBMiComponent> = new (c: IBMi) => T; | ||
|
||
public async startup(connection: IBMi) { | ||
this.registered.GetNewLibl = new GetNewLibl(connection); | ||
await ComponentManager.checkState(this.registered.GetNewLibl); | ||
/** | ||
* 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, when the extension providing the component gets activated: | ||
* ``` | ||
* export async function activate(context: ExtensionContext) { | ||
* const codeForIBMiExtension = vscode.extensions.getExtension<CodeForIBMi>('halcyontechltd.code-for-ibmi'); | ||
* if (codeForIBMiExtension) { | ||
* codeForIBMi = codeForIBMiExtension.isActive ? codeForIBMiExtension.exports : await codeForIBMiExtension.activate(); | ||
* codeForIBMi.componentRegistry.registerComponent(context, MyIBMIComponent); | ||
* } | ||
* } | ||
* ``` | ||
* | ||
*/ | ||
export abstract class IBMiComponent { | ||
private state: ComponentState = `NotChecked`; | ||
|
||
this.registered.CopyToImport = new CopyToImport(connection); | ||
await ComponentManager.checkState(this.registered.CopyToImport); | ||
constructor(protected readonly connection: IBMi) { | ||
|
||
this.registered.GetMemberInfo = new GetMemberInfo(connection); | ||
await ComponentManager.checkState(this.registered.GetMemberInfo); | ||
} | ||
|
||
// TODO: return type based on ComponentIds | ||
get<T>(id: ComponentId): T | undefined { | ||
const component = this.registered[id]; | ||
if (component && component.getState() === ComponentState.Installed) { | ||
return component as T; | ||
} | ||
getState() { | ||
return this.state; | ||
} | ||
|
||
private static async checkState(component: ComponentT) { | ||
async check() { | ||
try { | ||
await component.checkState(); | ||
} catch (e) { | ||
console.log(component); | ||
console.log(`Error checking state for ${component.constructor.name}`, e); | ||
this.state = await this.getRemoteState(); | ||
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 = `Error`; | ||
} | ||
|
||
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 getIdentification(): ComponentIdentification; | ||
|
||
/** | ||
* @returns the component's {@link ComponentState state} on the IBM i | ||
*/ | ||
protected abstract getRemoteState(): ComponentState | Promise<ComponentState>; | ||
|
||
/** | ||
* 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<ComponentState> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.