Skip to content

Commit

Permalink
Merge pull request #1142 from halcyon-tech/feature/disconnectionPropa…
Browse files Browse the repository at this point in the history
…gation

Propagate and handle connection/disconnection events
  • Loading branch information
worksofliam authored Mar 22, 2023
2 parents c205ebd + a03edd8 commit 1df788a
Show file tree
Hide file tree
Showing 30 changed files with 1,743 additions and 1,905 deletions.
223 changes: 127 additions & 96 deletions package.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion src/api/Configuration.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

import * as vscode from 'vscode';

export type SourceDateMode = "edit"|"diff";

const getConfiguration = (): vscode.WorkspaceConfiguration => {
return vscode.workspace.getConfiguration(`code-for-ibmi`);
}
Expand Down Expand Up @@ -29,7 +31,7 @@ export namespace ConnectionConfiguration {
autoConvertIFSccsid: boolean;
hideCompileErrors: string[];
enableSourceDates: boolean;
sourceDateMode: "edit"|"diff";
sourceDateMode: SourceDateMode;
sourceDateGutter: boolean;
encodingFor5250: string;
terminalFor5250: string;
Expand Down
51 changes: 33 additions & 18 deletions src/api/IBMi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import * as vscode from "vscode";
import * as node_ssh from "node-ssh";
import { ConnectionConfiguration } from "./Configuration";

import {Tools} from './Tools';
import { Tools } from './Tools';
import path from 'path';
import { ConnectionData, CommandData, StandardIO, CommandResult } from "../typings";
import * as configVars from './configVars';
import { instance } from "../instantiate";
import IBMiContent from "./IBMiContent";

export interface MemberParts {
asp?: string
Expand Down Expand Up @@ -113,7 +115,7 @@ export default class IBMi {
if (!connectionObject.privateKey) (connectionObject.privateKey = null);

configVars.replaceAll(connectionObject);

return await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: `Connecting`,
Expand Down Expand Up @@ -576,8 +578,8 @@ export default class IBMi {
// give user option to set bash as default shell.
try {
// make sure sql is enabled and bash is installed on system
if (this.config.enableSQL &&
this.remoteFeatures[`bash`]) {
if (this.config.enableSQL &&
this.remoteFeatures[`bash`]) {
const bashShellPath = '/QOpenSys/pkgs/bin/bash';
const commandShellResult = await this.sendCommand({
command: `echo $SHELL`
Expand All @@ -586,7 +588,7 @@ export default class IBMi {
let userDefaultShell = commandShellResult.stdout.trim();
if (userDefaultShell !== bashShellPath) {
vscode.window.showInformationMessage(`IBM recommends using bash as your default shell.`, `Set shell to bash?`, `Read More`,).then(async choice => {
switch (choice) {
switch (choice) {
case `Set shell to bash?`:
statement = `CALL QSYS2.SET_PASE_SHELL_INFO('*CURRENT', '/QOpenSys/pkgs/bin/bash')`;
output = await this.sendCommand({
Expand Down Expand Up @@ -646,7 +648,10 @@ export default class IBMi {
vscode.window.showWarningMessage(`Code for IBM i may not function correctly until your user has a home directory. Please set a home directory using CHGUSRPRF USRPRF(${connectionObject.username.toUpperCase()}) HOMEDIR('/home/${connectionObject.username.toLowerCase()}')`);
}

instance.setConnection(this);
vscode.workspace.getConfiguration().update(`workbench.editor.enablePreview`, false, true);
await vscode.commands.executeCommand(`setContext`, `code-for-ibmi:connected`, true);
instance.fire("connected");

return {
success: true
Expand All @@ -664,7 +669,7 @@ export default class IBMi {
error: e
};
}
finally{
finally {
ConnectionConfiguration.update(this.config!);
}
}
Expand Down Expand Up @@ -711,9 +716,8 @@ export default class IBMi {
async sendCommand(options: CommandData): Promise<CommandResult> {
let commands: string[] = [];
if (options.env) {
commands.push(...Object.entries(options.env).map(([key, value]) => `export ${key}="${
value?.replace(/\$/g, `\\$`).replace(/"/g, `\\"`) || ``
}"`))
commands.push(...Object.entries(options.env).map(([key, value]) => `export ${key}="${value?.replace(/\$/g, `\\$`).replace(/"/g, `\\"`) || ``
}"`))
}

commands.push(options.command);
Expand Down Expand Up @@ -774,14 +778,25 @@ export default class IBMi {
this.commandsExecuted += 1;
}

end() {
async end() {
this.client.connection.removeAllListeners();
this.client.dispose();

if (this.outputChannel) {
this.outputChannel.hide();
this.outputChannel.dispose();
}

await Promise.all([
vscode.commands.executeCommand("code-for-ibmi.refreshObjectBrowser"),
vscode.commands.executeCommand("code-for-ibmi.refreshLibraryListView"),
vscode.commands.executeCommand("code-for-ibmi.refreshIFSBrowser")
]);

instance.setConnection(undefined);
instance.fire(`disconnected`);
await vscode.commands.executeCommand(`setContext`, `code-for-ibmi:connected`, false);
vscode.window.showInformationMessage(`Disconnected from ${this.currentHost}.`);
}

/**
Expand Down Expand Up @@ -887,27 +902,27 @@ export default class IBMi {

return result;
}
async uploadFiles(files: {local : string | vscode.Uri, remote : string}[], options?: node_ssh.SSHPutFilesOptions){
await this.client.putFiles(files.map(f => {return {local: this.fileToPath(f.local), remote: f.remote}}), options);
async uploadFiles(files: { local: string | vscode.Uri, remote: string }[], options?: node_ssh.SSHPutFilesOptions) {
await this.client.putFiles(files.map(f => { return { local: this.fileToPath(f.local), remote: f.remote } }), options);
}

async downloadFile(localFile: string | vscode.Uri, remoteFile: string){
async downloadFile(localFile: string | vscode.Uri, remoteFile: string) {
await this.client.getFile(this.fileToPath(localFile), remoteFile);
}

async uploadDirectory(localDirectory: string | vscode.Uri, remoteDirectory : string, options?: node_ssh.SSHGetPutDirectoryOptions){
async uploadDirectory(localDirectory: string | vscode.Uri, remoteDirectory: string, options?: node_ssh.SSHGetPutDirectoryOptions) {
await this.client.putDirectory(this.fileToPath(localDirectory), remoteDirectory, options);
}

async downloadDirectory(localDirectory: string | vscode.Uri, remoteDirectory: string, options?: node_ssh.SSHGetPutDirectoryOptions){
async downloadDirectory(localDirectory: string | vscode.Uri, remoteDirectory: string, options?: node_ssh.SSHGetPutDirectoryOptions) {
await this.client.getDirectory(this.fileToPath(localDirectory), remoteDirectory, options);
}

fileToPath(file : string | vscode.Uri) : string{
if(typeof file === "string"){
fileToPath(file: string | vscode.Uri): string {
if (typeof file === "string") {
return file;
}
else{
else {
return file.fsPath;
}
}
Expand Down
85 changes: 56 additions & 29 deletions src/api/Instance.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,66 @@
import * as vscode from "vscode";
import IBMi from "./IBMi";
import IBMiContent from "./IBMiContent";
import {ConnectionStorage} from "./Storage";
import { ConnectionStorage, GlobalStorage } from "./Storage";
import { ConnectionConfiguration } from "./Configuration";
import { IBMiEvent } from "../typings";

export default class Instance {
connection: IBMi|undefined;
content: IBMiContent|undefined;
storage: ConnectionStorage|undefined;
emitter: vscode.EventEmitter<any>|undefined;
events: {event: string, func: Function}[];

constructor() {
this.events = [];
}

getConnection() {
return this.connection;
}
private connection: IBMi | undefined;
private content: IBMiContent | undefined;
private storage: ConnectionStorage;
private emitter: vscode.EventEmitter<IBMiEvent> = new vscode.EventEmitter();
private events: { event: IBMiEvent, func: Function }[] = [];

async setConfig(newConfig: ConnectionConfiguration.Parameters) {
await ConnectionConfiguration.update(newConfig);
if (this.connection) this.connection.config = newConfig;
}
getConfig() {
return this.connection?.config;
}
getContent () {
return this.content;
constructor(context: vscode.ExtensionContext) {
this.events = [];
this.storage = new ConnectionStorage(context);
this.emitter.event(e => {
this.events.filter(event => event.event === e)
.forEach(event => event.func());
})
}

async setConnection(connection?: IBMi) {
if (connection) {
this.connection = connection;
this.storage.setConnectionName(connection.currentConnectionName);
this.content = new IBMiContent(connection);
await GlobalStorage.get().setLastConnection(connection.currentConnectionName);
}
getStorage () {
return this.storage;
else {
this.connection = undefined;
this.content = undefined;
this.storage.setConnectionName("");
}
}

onEvent(event: "connected" | "disconnected" | "deployLocation", func: Function): void {
this.events.push({event, func});
}
};
getConnection() {
return this.connection;
}

async setConfig(newConfig: ConnectionConfiguration.Parameters) {
await ConnectionConfiguration.update(newConfig);
if (this.connection) this.connection.config = newConfig;
}

getConfig() {
return this.connection?.config;
}

getContent() {
return this.content;
}

getStorage() {
return this.storage.ready ? this.storage : undefined;
}

onEvent(event: IBMiEvent, func: Function): void {
this.events.push({ event, func });
}

fire(event: IBMiEvent) {
this.emitter?.fire(event);
}
}
16 changes: 15 additions & 1 deletion src/api/Storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,24 @@ export class GlobalStorage extends Storage {
}

export class ConnectionStorage extends Storage {
constructor(context: vscode.ExtensionContext, readonly connectionName: string) {
private connectionName: string = "";
constructor(context: vscode.ExtensionContext) {
super(context);
}

get ready(): boolean {
if (this.connectionName) {
return true;
}
else {
return false;
}
}

setConnectionName(connectionName: string) {
this.connectionName = connectionName;
}

protected getStorageKey(key: string): string {
return `${this.connectionName}.${key}`;
}
Expand Down
32 changes: 16 additions & 16 deletions src/api/debug/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import path from "path";
import * as certificates from "./certificates";
import * as server from "./server";
import { copyFileSync } from "fs";
import { instance } from "../../instantiate";

const debugExtensionId = `IBM.ibmidebug`;

Expand All @@ -17,7 +18,7 @@ const localCertContext = `code-for-ibmi:debug.local`;
let connectionConfirmed = false;
let temporaryPassword: string | undefined;

export async function initialise(instance: Instance, context: ExtensionContext) {
export async function initialize(context: ExtensionContext) {
const debugExtensionAvailable = () => {
const debugclient = vscode.extensions.getExtension(debugExtensionId);
return debugclient !== undefined;
Expand Down Expand Up @@ -102,9 +103,9 @@ export async function initialise(instance: Instance, context: ExtensionContext)

vscode.debug.onDidTerminateDebugSession(async session => {
if (session.configuration.type === `IBMiDebug`) {
const connection = instance.connection;
const connection = instance.getConnection();

server.getStuckJobs(connection?.currentUser!, instance.content!).then(jobIds => {
server.getStuckJobs(connection?.currentUser!, instance.getContent()!).then(jobIds => {
if (jobIds.length > 0) {
vscode.window.showInformationMessage(`You have ${jobIds.length} debug job${jobIds.length !== 1 ? `s` : ``} stuck at MSGW under your user profile.`, `End jobs`, `Ignore`)
.then(selection => {
Expand All @@ -119,7 +120,7 @@ export async function initialise(instance: Instance, context: ExtensionContext)

vscode.commands.registerCommand(`code-for-ibmi.debug.activeEditor`, async () => {
if (debugExtensionAvailable()) {
const connection = instance.connection;
const connection = instance.getConnection();
if (connection) {
if (connection.remoteFeatures[`startDebugService.sh`]) {
const activeEditor = vscode.window.activeTextEditor;
Expand Down Expand Up @@ -159,7 +160,7 @@ export async function initialise(instance: Instance, context: ExtensionContext)
}),

vscode.commands.registerCommand(`code-for-ibmi.debug.setup.remote`, async () => {
const connection = instance.connection;
const connection = instance.getConnection();
if (connection) {
const ptfInstalled = await debugPTFInstalled();

Expand Down Expand Up @@ -207,7 +208,7 @@ export async function initialise(instance: Instance, context: ExtensionContext)
}),

vscode.commands.registerCommand(`code-for-ibmi.debug.setup.local`, async () => {
const connection = instance.connection;
const connection = instance.getConnection();

if (connection) {
const ptfInstalled = await debugPTFInstalled();
Expand Down Expand Up @@ -259,7 +260,7 @@ export async function initialise(instance: Instance, context: ExtensionContext)
}),

vscode.commands.registerCommand(`code-for-ibmi.debug.start`, async () => {
const connection = instance.connection;
const connection = instance.getConnection();
if (connection) {
const ptfInstalled = await debugPTFInstalled();
if (ptfInstalled) {
Expand All @@ -272,7 +273,7 @@ export async function initialise(instance: Instance, context: ExtensionContext)


progress.report({ increment: 33, message: `Checking if service is already running.` });
const existingDebugService = await server.getRunningJob(connection.config?.debugPort || "8005", instance.content!);
const existingDebugService = await server.getRunningJob(connection.config?.debugPort || "8005", instance.getContent()!);

if (existingDebugService) {
const confirmEndServer = await vscode.window.showInformationMessage(`Starting debug service`, {
Expand Down Expand Up @@ -316,18 +317,18 @@ export async function initialise(instance: Instance, context: ExtensionContext)
);

// Run during startup:

if (instance.connection) {
if (instance.connection.remoteFeatures[`startDebugService.sh`]) {
instance.onEvent("connected", async () => {
const connection = instance.getConnection();
if (connection && connection?.remoteFeatures[`startDebugService.sh`]) {
vscode.commands.executeCommand(`setContext`, ptfContext, true);

const remoteCerts = await certificates.checkRemoteExists(instance.connection);
const remoteCerts = await certificates.checkRemoteExists(connection);

if (remoteCerts) {
vscode.commands.executeCommand(`setContext`, remoteCertContext, true);

if (instance.connection.config!.debugIsSecure) {
const localExists = await certificates.checkLocalExists(instance.connection);
if (connection.config!.debugIsSecure) {
const localExists = await certificates.checkLocalExists(connection);

if (localExists) {
vscode.commands.executeCommand(`setContext`, localCertContext, true);
Expand All @@ -342,8 +343,7 @@ export async function initialise(instance: Instance, context: ExtensionContext)
}
}
}
}

});
}

interface DebugOptions {
Expand Down
Loading

0 comments on commit 1df788a

Please sign in to comment.