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

Refactor ASP handling to use current iASP name and fetch additional information #2339

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 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
8 changes: 0 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,6 @@
"default": "QTEMP",
"description": "Library used as the current library and &CURLIB variable when running actions."
},
"sourceASP": {
"type": [
"string",
"null"
],
"default": null,
"description": "If source files live within a specific ASP, please specify it here. Leave blank/null otherwise. You can ignore this column if you have access to `QSYS2.ASP_INFO` as Code for IBM i will fetch ASP information automatically."
},
"sourceFileCCSID": {
"type": "string",
"default": "*FILE",
Expand Down
2 changes: 0 additions & 2 deletions src/api/Configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ export namespace ConnectionConfiguration {
autoSortIFSShortcuts: boolean;
tempLibrary: string;
tempDir: string;
sourceASP: string;
sourceFileCCSID: string;
autoConvertIFSccsid: boolean;
hideCompileErrors: string[];
Expand Down Expand Up @@ -192,7 +191,6 @@ export namespace ConnectionConfiguration {
tempLibrary: parameters.tempLibrary || `ILEDITOR`,
tempDir: parameters.tempDir || `/tmp`,
currentLibrary: parameters.currentLibrary || ``,
sourceASP: parameters.sourceASP || ``,
sourceFileCCSID: parameters.sourceFileCCSID || `*FILE`,
autoConvertIFSccsid: (parameters.autoConvertIFSccsid === true),
hideCompileErrors: parameters.hideCompileErrors || [],
Expand Down
122 changes: 115 additions & 7 deletions 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 { CommandData, CommandResult, ConnectionData, IBMiMember, RemoteCommand, WrapResult, AspInfo } from "../typings";
import { CompileTools } from "./CompileTools";
import { ConnectionConfiguration } from "./Configuration";
import IBMiContent from "./IBMiContent";
Expand Down Expand Up @@ -90,7 +90,8 @@ export default class IBMi {
* Their names usually maps up to a directory in
* the root of the IFS, thus why we store it.
*/
aspInfo: { [id: number]: string } = {};
private iAspInfo: AspInfo[] = [];
private currentAsp: string|undefined;
remoteFeatures: { [name: string]: string | undefined };

variantChars: {
Expand Down Expand Up @@ -916,20 +917,26 @@ export default class IBMi {

if (this.sqlRunnerAvailable()) {
// Check for ASP information?
if (quickConnect() && cachedServerSettings?.aspInfo) {
this.aspInfo = cachedServerSettings.aspInfo;
if (quickConnect() && cachedServerSettings?.iAspInfo) {
this.iAspInfo = cachedServerSettings.iAspInfo;
} else {
progress.report({
message: `Checking for ASP information.`
message: `Checking for iASP information.`
});

//This is mostly a nice to have. We grab the ASP info so user's do
//not have to provide the ASP in the settings.
try {
const resultSet = await this.runSQL(`SELECT * FROM QSYS2.ASP_INFO`);
resultSet.forEach(row => {
// Does not ever include SYSBAS/SYSTEM, only iASPs
if (row.DEVICE_DESCRIPTION_NAME && row.DEVICE_DESCRIPTION_NAME && row.DEVICE_DESCRIPTION_NAME !== `null`) {
this.aspInfo[Number(row.ASP_NUMBER)] = String(row.DEVICE_DESCRIPTION_NAME);
this.iAspInfo.push({
id: Number(row.ASP_NUMBER),
name: String(row.DEVICE_DESCRIPTION_NAME),
type: String(row.ASP_TYPE),
rdbName: String(row.RDB_NAME)
});
}
});
} catch (e) {
Expand All @@ -940,6 +947,22 @@ export default class IBMi {
}
}

progress.report({
message: `Fetching current iASP information.`
});

const [currentRdb] = await this.runSQL(`values current_server`);

if (currentRdb) {
const key = Object.keys(currentRdb)[0];
const rdbName = currentRdb[key];
const currentAsp = this.iAspInfo.find(asp => asp.rdbName === rdbName);

if (currentAsp) {
this.currentAsp = currentAsp.name;
}
}

// Fetch conversion values?
if (quickConnect() && cachedServerSettings?.jobCcsid !== null && cachedServerSettings?.userDefaultCCSID && cachedServerSettings?.qccsid) {
this.qccsid = cachedServerSettings.qccsid;
Expand Down Expand Up @@ -1085,7 +1108,7 @@ export default class IBMi {

GlobalStorage.get().setServerSettingsCache(this.currentConnectionName, {
lastCheckedOnVersion: currentExtensionVersion,
aspInfo: this.aspInfo,
iAspInfo: this.iAspInfo,
qccsid: this.qccsid,
jobCcsid: this.userJobCcsid,
remoteFeatures: this.remoteFeatures,
Expand Down Expand Up @@ -1279,6 +1302,63 @@ export default class IBMi {
return this.remoteFeatures[`QZDFMDB2.PGM`] !== undefined;
}

getAllIAsps() {
return this.iAspInfo;
}

getIAspDetail(by: string|number) {
let asp: AspInfo|undefined;
if (typeof by === 'string') {
asp = this.iAspInfo.find(asp => asp.name === by);
} else {
asp = this.iAspInfo.find(asp => asp.id === by);
}

if (asp) {
return asp;
}
}

getIAspName(by: string|number): string|undefined {
let asp: AspInfo|undefined;
if (typeof by === 'string') {
asp = this.iAspInfo.find(asp => asp.name === by);
} else {
asp = this.iAspInfo.find(asp => asp.id === by);
}

if (asp) {
return asp.name;
}
worksofliam marked this conversation as resolved.
Show resolved Hide resolved
}

getCurrentIAspName() {
return this.currentAsp;
}

private libraryAsps: { [library: string]: number } = {};
async lookupLibraryIAsp(library: string) {
let foundNumber: number|undefined = this.libraryAsps[library];

if (!foundNumber) {
const [row] = await this.runSQL(`SELECT IASP_NUMBER FROM TABLE(QSYS2.LIBRARY_INFO('${this.sysNameInAmerican(library)}'))`);
const iaspNumber = Number(row?.IASP_NUMBER);
if (iaspNumber >= 0) {
this.libraryAsps[library] = iaspNumber;
foundNumber = iaspNumber;
}
}

return this.getIAspName(foundNumber);
}

getLibraryIAsp(library: string) {
const found = this.libraryAsps[library];
if (found >= 0) {
return this.getIAspName(found);
}
}

/**
* Generates path to a temp file on the IBM i
* @param {string} key Key to the temp file to be re-used
Expand Down Expand Up @@ -1589,4 +1669,32 @@ export default class IBMi {
sshdCcsid: this.sshdCcsid
};
}

/**
* @deprecated Use {@link IBMiContent.uploadFiles} instead.
*/
uploadFiles(files: { local: string | vscode.Uri, remote: string }[], options?: node_ssh.SSHPutFilesOptions) {
return this.content.uploadFiles(files, options);
}

/**
* @deprecated Use {@link IBMiContent.downloadFiles} instead.
*/
async downloadFile(localFile: string | vscode.Uri, remoteFile: string) {
await this.content.downloadFile(localFile, remoteFile);
}

/**
* @deprecated Use {@link IBMiContent.uploadDirectory} instead.
*/
async uploadDirectory(localDirectory: string | vscode.Uri, remoteDirectory: string, options?: node_ssh.SSHGetPutDirectoryOptions) {
await this.content.uploadDirectory(localDirectory, remoteDirectory, options);
}

/**
* @deprecated Use {@link IBMiContent.downloadDirectory} instead.
*/
async downloadDirectory(localDirectory: string | vscode.Uri, remoteDirectory: string, options?: node_ssh.SSHGetPutDirectoryOptions) {
await this.content.downloadDirectory(localDirectory, remoteDirectory, options);
}
}
34 changes: 18 additions & 16 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 @@ -139,7 +139,6 @@ export default class IBMiContent {
* Download the contents of a source member
*/
async downloadMemberContent(asp: string | undefined, library: string, sourceFile: string, member: string, localPath?: string) {
asp = asp || this.config.sourceASP;
library = this.ibmi.upperCaseName(library);
sourceFile = this.ibmi.upperCaseName(sourceFile);
member = this.ibmi.upperCaseName(member);
Expand Down Expand Up @@ -207,12 +206,11 @@ export default class IBMiContent {
* Upload to a member
*/
async uploadMemberContent(asp: string | undefined, library: string, sourceFile: string, member: string, content: string | Uint8Array) {
asp = asp || this.config.sourceASP;
library = this.ibmi.upperCaseName(library);
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 @@ -374,6 +372,8 @@ export default class IBMiContent {
`;
const results = await this.ibmi.runSQL(statement);

const asp = this.ibmi.getIAspName(Number(results[0]?.IASP_NUMBER));

objects = results.map(object => ({
library: 'QSYS',
name: this.ibmi.sysNameInLocal(String(object.NAME)),
Expand All @@ -387,7 +387,7 @@ export default class IBMiContent {
changed: new Date(Number(object.CHANGED)),
created_by: object.CREATED_BY,
owner: object.OWNER,
asp: this.ibmi.aspInfo[Number(object.IASP_NUMBER)]
asp
} as IBMiObject));
} else {
let results = await this.getQTempTable(libraries.map(library => `@DSPOBJD OBJ(QSYS/${library}) OBJTYPE(*LIB) DETAIL(*TEXTATR) OUTPUT(*OUTFILE) OUTFILE(QTEMP/LIBLIST) OUTMBR(*FIRST *ADD)`), "LIBLIST");
Expand Down Expand Up @@ -600,6 +600,8 @@ export default class IBMiContent {

const objects = (await this.runStatements(createOBJLIST.join(`\n`)));

const asp = this.ibmi.getIAspName(Number(objects[0]?.IASP_NUMBER));

return objects.map(object => ({
library: localLibrary,
name: Boolean(object.IS_SOURCE) ? this.ibmi.sysNameInLocal(String(object.NAME)) : String(object.NAME),
Expand All @@ -613,7 +615,7 @@ export default class IBMiContent {
changed: new Date(Number(object.CHANGED)),
created_by: object.CREATED_BY,
owner: object.OWNER,
asp: this.ibmi.aspInfo[Number(object.IASP_NUMBER)]
asp
} as IBMiObject))
.filter(object => !typeFilter || typeFilter(object.type))
.filter(object => objectFilter || nameFilter.test(object.name))
Expand Down Expand Up @@ -672,7 +674,7 @@ export default class IBMiContent {

const results = await this.ibmi.runSQL(statement);
if (results.length) {
const asp = this.ibmi.aspInfo[Number(results[0].ASP)];
const asp = this.ibmi.getIAspName(Number(results[0]?.ASP));
return results.map(result => ({
asp,
library,
Expand Down Expand Up @@ -792,7 +794,7 @@ export default class IBMiContent {
// Escape names for shell
const pathList = files
.map(file => {
const asp = file.asp || this.config.sourceASP;
const asp = file.asp || this.ibmi.getCurrentIAspName();
if (asp && asp.length > 0) {
return [
Tools.qualifyPath(inAmerican(file.library), inAmerican(file.name), inAmerican(member), asp, true),
Expand Down Expand Up @@ -1093,20 +1095,20 @@ 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);
uploadFiles(files: { local: string | Uri, remote: string }[], options?: node_ssh.SSHPutFilesOptions) {
return 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);
downloadFile(localFile: string | Uri, remoteFile: string) {
return 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);
uploadDirectory(localDirectory: string | Uri, remoteDirectory: string, options?: node_ssh.SSHGetPutDirectoryOptions) {
return 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);
downloadDirectory(localDirectory: string | Uri, remoteDirectory: string, options?: node_ssh.SSHGetPutDirectoryOptions) {
return this.ibmi.client!.getDirectory(Tools.fileToPath(localDirectory), remoteDirectory, options);
}
}

Expand Down
13 changes: 3 additions & 10 deletions src/api/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,10 @@ export namespace Search {
}

// First, let's fetch the ASP info
const foundAsp = await connection.lookupLibraryIAsp(library);
let asp = ``;
if (config.sourceASP) {
asp = `/${config.sourceASP}`;
} else if (connection.enableSQL) {
try {
const [row] = await content.runSQL(`SELECT IASP_NUMBER FROM TABLE(QSYS2.LIBRARY_INFO('${library}'))`);
const iaspNumber = row?.IASP_NUMBER;
if (iaspNumber && typeof iaspNumber === 'number' && connection.aspInfo[iaspNumber]) {
asp = `/${connection.aspInfo[iaspNumber]}`;
}
} catch (e) { }
if (foundAsp) {
asp = `/${foundAsp}`;
}

// Then search the members
Expand Down
4 changes: 2 additions & 2 deletions src/api/Storage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import vscode from 'vscode';
import { ConnectionData } from '../typings';
import { AspInfo, ConnectionData } from '../typings';

const PREVIOUS_CUR_LIBS_KEY = `prevCurLibs`;
const LAST_PROFILE_KEY = `currentProfile`;
Expand Down Expand Up @@ -52,8 +52,8 @@ export type LastConnection = {
};

export type CachedServerSettings = {
iAspInfo: AspInfo[];
lastCheckedOnVersion: string | undefined;
aspInfo: { [id: number]: string }
qccsid: number | null;
jobCcsid: number | null
remoteFeatures: { [name: string]: string | undefined }
Expand Down
2 changes: 1 addition & 1 deletion src/api/errors/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export function handleEvfeventLines(lines: string[], instance: Instance, evfeven
if (relativeCompilePath) {
if (connection) {
// Belive it or not, sometimes if the deploy directory is symlinked into as ASP, this can be a problem
const aspNames = Object.values(connection.aspInfo);
const aspNames = connection.getAllIAsps().map(asp => asp.name);
for (const aspName of aspNames) {
const aspRoot = `/${aspName}`;
if (relativeCompilePath.startsWith(aspRoot)) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/getMemberInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class GetMemberInfo implements IBMiComponent {

if (results.length === 1 && results[0].ISSOURCE === 'Y') {
const result = results[0];
const asp = connection.aspInfo[Number(results[0].ASP)];
const asp = connection.getIAspName(Number(results[0]?.ASP))
return {
asp,
library: result.LIBRARY,
Expand Down Expand Up @@ -102,7 +102,7 @@ export class GetMemberInfo implements IBMiComponent {
}

return results.filter(row => row.ISSOURCE === 'Y').map(result => {
const asp = connection.aspInfo[Number(result.ASP)];
const asp = connection.getIAspName(Number(result.ASP));
return {
asp,
library: result.LIBRARY,
Expand Down
Loading
Loading