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

Closed
wants to merge 17 commits into from
Closed
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
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
113 changes: 106 additions & 7 deletions src/api/IBMi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
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 @@
* 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 @@

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 @@
}
}

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 @@

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,54 @@
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);

Check failure on line 1314 in src/api/IBMi.ts

View workflow job for this annotation

GitHub Actions / Validation

Cannot find name 'getIAspDetail'. Did you mean the instance member 'this.getIAspDetail'?
}

if (asp) {
return asp;
}
}

getIAspName(by: string|number): string|undefined {
return getIAspDetail(by)?.name;
}

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 +1660,32 @@
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
19 changes: 4 additions & 15 deletions src/api/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,12 @@ export namespace Search {
}

// First, let's fetch the ASP info
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) { }
}
const asp = await connection.lookupLibraryIAsp(library);

// Then search the members
const result = await connection.sendQsh({
command: `/usr/bin/grep -inHR -F "${sanitizeSearchTerm(searchTerm)}" ${memberFilter}`,
directory: connection.sysNameInAmerican(`${asp}/QSYS.LIB/${library}.LIB/${sourceFile}.FILE`)
directory: connection.sysNameInAmerican(`${asp ? `/${asp}` : ``}/QSYS.LIB/${library}.LIB/${sourceFile}.FILE`)
});

if (!result.stderr) {
Expand Down Expand Up @@ -87,13 +76,13 @@ export namespace Search {
const foundMember = detailedMembers?.find(member => member.name === name && member.library === lib && member.file === spf);

if (foundMember) {
hit.path = connection.sysNameInLocal(`${lib}/${spf}/${name}.${foundMember.extension}`);
hit.path = connection.sysNameInLocal(`${asp ? `${asp}/` : ``}${lib}/${spf}/${name}.${foundMember.extension}`);
}
}

return {
term: searchTerm,
hits: hits
hits
}
}
else {
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
Loading
Loading