Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for local and server profiles and actions #2341

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
Draft
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
24 changes: 22 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,17 @@
"jsonValidation": [
{
"fileMatch": [
".vscode/actions.json"
".vscode/actions.json",
"/etc/.vscode/actions.json"
],
"url": "./schemas/actions.json"
},
{
"fileMatch": [
".vscode/profiles.json",
"/etc/.vscode/profiles.json"
],
"url": "./schemas/profiles.json"
}
],
"configuration": {
Expand Down Expand Up @@ -1242,6 +1250,13 @@
"category": "IBM i",
"icon": "$(arrow-circle-right)"
},
{
"command": "code-for-ibmi.profiles.copyAsJson",
"enablement": "code-for-ibmi:connected",
"title": "Copy as JSON",
"category": "IBM i",
"icon": "$(clippy)"
},
{
"command": "code-for-ibmi.searchSourceFile",
"enablement": "code-for-ibmi:connected",
Expand Down Expand Up @@ -2536,7 +2551,7 @@
},
{
"command": "code-for-ibmi.loadConnectionProfile",
"when": "view == profilesView && viewItem == profile",
"when": "view == profilesView && (viewItem == profile || viewItem == localProfile)",
"group": "inline"
},
{
Expand Down Expand Up @@ -2579,6 +2594,11 @@
"when": "view == profilesView && viewItem == profile",
"group": "profiles@1"
},
{
"command": "code-for-ibmi.profiles.copyAsJson",
"when": "view == profilesView && viewItem == profile",
"group": "profiles@3"
},
{
"command": "code-for-ibmi.manageCommandProfile",
"when": "view == profilesView && viewItem == commandProfile",
Expand Down
12 changes: 12 additions & 0 deletions schemas/actions.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@
"description": "The command that will be run when executing this Action.",
"default": ""
},
"type": {
"$id": "#/actions/items/anyOf/0/properties/type",
"type": "string",
"title": "File system type",
"description": "Which environment the command will run in.",
"default": "file",
"enum": [
"file",
"member",
"streamfile"
]
},
"environment": {
"$id": "#/actions/items/anyOf/0/properties/commandEnvironment",
"type": "string",
Expand Down
70 changes: 70 additions & 0 deletions schemas/profiles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"FilterType": {
"type": "string",
"enum": ["simple", "regex"]
},
"ObjectFilters": {
"type": "object",
"properties": {
"name": { "type": "string" },
"filterType": { "$ref": "#/definitions/FilterType" },
"library": { "type": "string" },
"object": { "type": "string" },
"types": {
"type": "array",
"items": { "type": "string" }
},
"member": { "type": "string" },
"memberType": { "type": "string" },
"protected": { "type": "boolean" }
},
"required": ["name", "filterType", "library", "object", "types", "member", "memberType", "protected"]
},
"CustomVariable": {
"type": "object",
"properties": {
"name": { "type": "string" },
"value": { "type": "string" }
},
"required": ["name", "value"]
},
"ConnectionProfile": {
"type": "object",
"properties": {
"name": { "type": "string" },
"homeDirectory": { "type": "string" },
"currentLibrary": { "type": "string" },
"libraryList": {
"type": "array",
"items": { "type": "string" }
},
"objectFilters": {
"type": "array",
"items": { "$ref": "#/definitions/ObjectFilters" }
},
"ifsShortcuts": {
"type": "array",
"items": { "type": "string" }
},
"customVariables": {
"type": "array",
"items": { "$ref": "#/definitions/CustomVariable" }
}
},
"required": ["name"]
},
"TopLevel": {
"type": "object",
"properties": {
"profiles": {
"type": "array",
"items": { "$ref": "#/definitions/ConnectionProfile" }
}
},
"required": ["profiles"]
}
},
"$ref": "#/definitions/TopLevel"
}
3 changes: 1 addition & 2 deletions src/api/CompileTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import IBMi from './IBMi';
import Instance from './Instance';
import { Tools } from './Tools';
import { EvfEventInfo, refreshDiagnosticsFromLocal, refreshDiagnosticsFromServer, registerDiagnostics } from './errors/diagnostics';
import { getLocalActions } from './local/actions';
import { DeployTools } from './local/deployTools';
import { getBranchLibraryName, getEnvConfig } from './local/env';
import { getGitBranch } from './local/git';
Expand Down Expand Up @@ -102,7 +101,7 @@ export namespace CompileTools {
// Then, if we're being called from a local file
// we fetch the Actions defined from the workspace.
if (workspaceFolder && uri.scheme === `file`) {
const localActions = await getLocalActions(workspaceFolder);
const localActions = await connection.getConfigFile<Action[]>(`actions`).get(workspaceFolder);
allActions.push(...localActions);
}

Expand Down
42 changes: 41 additions & 1 deletion src/api/IBMi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IBMiComponent } from "../components/component";
import { CopyToImport } from "../components/copyToImport";
import { CustomQSh } from '../components/cqsh';
import { ComponentManager } from "../components/manager";
import { CommandData, CommandResult, ConnectionData, IBMiMember, RemoteCommand, WrapResult } from "../typings";
import { Action, CommandData, CommandResult, ConnectionData, IBMiMember, RemoteCommand, WrapResult } from "../typings";
import { CompileTools } from "./CompileTools";
import { ConnectionConfiguration } from "./Configuration";
import IBMiContent from "./IBMiContent";
Expand All @@ -17,6 +17,9 @@ import { Tools } from './Tools';
import * as configVars from './configVars';
import { DebugConfiguration } from "./debug/config";
import { debugPTFInstalled } from "./debug/server";
import { ConfigFile } from './config/configFile';
import { getProfilesConfig, ProfilesConfigFile } from './config/profiles';
import { getActionsConfig } from './config/actions';

export interface MemberParts extends IBMiMember {
basename: string
Expand Down Expand Up @@ -53,6 +56,11 @@ const remoteApps = [ // All names MUST also be defined as key in 'remoteFeatures

type DisconnectCallback = (conn: IBMi) => Promise<void>;

interface ConnectionConfigFiles {
profiles: ConfigFile<ProfilesConfigFile>
actions: ConfigFile<Action[]>
worksofliam marked this conversation as resolved.
Show resolved Hide resolved
}

export default class IBMi {
static readonly CCSID_NOCONVERSION = 65535;
static readonly CCSID_SYSVAL = -2;
Expand All @@ -71,6 +79,12 @@ export default class IBMi {
* @deprecated Will become private in v3.0.0 - use {@link IBMi.getConfig} instead.
*/
config?: ConnectionConfiguration.Parameters;

private configFiles: ConnectionConfigFiles = {
profiles: getProfilesConfig(this),
actions: getActionsConfig(this)
}

/**
* @deprecated Will become private in v3.0.0 - use {@link IBMi.getContent} instead.
*/
Expand Down Expand Up @@ -115,6 +129,10 @@ export default class IBMi {
this.disconnectedCallback = callback;
}

getConfigFile<T>(id: keyof ConnectionConfigFiles) {
return this.configFiles[id] as ConfigFile<T>;
}

get canUseCqsh() {
return this.getComponent(CustomQSh.ID) !== undefined;
}
Expand Down Expand Up @@ -482,6 +500,14 @@ export default class IBMi {
}
this.appendOutput(`\n`);

// Next, load in all the config files!

progress.report({
message: `Loading remote configuration files.`
});

await this.loadRemoteConfigs();

progress.report({
message: `Checking library list configuration.`
});
Expand Down Expand Up @@ -1127,6 +1153,20 @@ export default class IBMi {
}
}

async loadRemoteConfigs() {
for (const configFile in this.configFiles) {
const currentConfig = this.configFiles[configFile as keyof ConnectionConfigFiles];

this.configFiles[configFile as keyof ConnectionConfigFiles].reset();

try {
await this.configFiles[configFile as keyof ConnectionConfigFiles].loadFromServer();
} catch (e) { }

this.appendOutput(`${configFile} config state: ` + JSON.stringify(currentConfig.getState()) + `\n`);
}
}

/**
* Can return 0 if the OS version was not detected.
*/
Expand Down
12 changes: 6 additions & 6 deletions src/api/IBMiContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export default class IBMiContent {
* @param encoding Optional encoding to write.
*/
async writeStreamfileRaw(originalPath: string, content: Uint8Array, encoding?: string) {
const client = this.ibmi.client;
const client = this.ibmi.client!;
const features = this.ibmi.remoteFeatures;
const tmpobj = await tmpFile();

Expand Down Expand Up @@ -212,7 +212,7 @@ export default class IBMiContent {
sourceFile = this.ibmi.upperCaseName(sourceFile);
member = this.ibmi.upperCaseName(member);

const client = this.ibmi.client;
const client = this.ibmi.client!;
const tmpobj = await tmpFile();

let retry = false;
Expand Down Expand Up @@ -1094,19 +1094,19 @@ export default class IBMiContent {
}

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

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

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

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

Expand Down
37 changes: 37 additions & 0 deletions src/api/config/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Action } from "../../typings";
import { ConnectionConfiguration } from "../Configuration";
import IBMi from "../IBMi";
import { ConfigFile } from "./configFile";

export function getActionsConfig(connection: IBMi) {
const ActionsConfig = new ConfigFile<Action[]>(connection, `actions`, []);

ActionsConfig.hasServerFile = true;
ActionsConfig.mergeArrays = true;

ActionsConfig.validateAndCleanInPlace = (loadedConfig) => {
let actions: Action[] = [];
// Maybe one day replace this with real schema validation
if (Array.isArray(loadedConfig)) {
loadedConfig.forEach((action, index) => {
if (
typeof action.name === `string` &&
typeof action.command === `string` &&
[`ile`, `pase`, `qsh`].includes(action.environment) &&
Array.isArray(action.extensions)
) {
actions.push({
type: `file`,
...action,
});
} else {
throw new Error(`Invalid Action defined at index ${index}.`);
}
})
}

return actions;
}

return ActionsConfig;
}
Loading
Loading