diff --git a/package.json b/package.json index 9258f6199..088cec11b 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/api/Configuration.ts b/src/api/Configuration.ts index 4a551f247..219369b91 100644 --- a/src/api/Configuration.ts +++ b/src/api/Configuration.ts @@ -112,7 +112,6 @@ export namespace ConnectionConfiguration { autoSortIFSShortcuts: boolean; tempLibrary: string; tempDir: string; - sourceASP: string; sourceFileCCSID: string; autoConvertIFSccsid: boolean; hideCompileErrors: string[]; @@ -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 || [], diff --git a/src/api/IBMi.ts b/src/api/IBMi.ts index 358f34e6e..4ad427939 100644 --- a/src/api/IBMi.ts +++ b/src/api/IBMi.ts @@ -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"; @@ -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: { @@ -916,11 +917,11 @@ 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 @@ -928,8 +929,14 @@ export default class IBMi { 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) { @@ -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; @@ -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, @@ -1279,6 +1302,54 @@ 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 { + 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 @@ -1589,4 +1660,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); + } } \ No newline at end of file diff --git a/src/api/IBMiContent.ts b/src/api/IBMiContent.ts index 8455e5f1c..f7a519094 100644 --- a/src/api/IBMiContent.ts +++ b/src/api/IBMiContent.ts @@ -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(); @@ -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); @@ -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; @@ -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)), @@ -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"); @@ -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), @@ -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)) @@ -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, @@ -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), @@ -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); } } diff --git a/src/api/Search.ts b/src/api/Search.ts index 57b153085..4c896d2bb 100644 --- a/src/api/Search.ts +++ b/src/api/Search.ts @@ -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) { @@ -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 { diff --git a/src/api/Storage.ts b/src/api/Storage.ts index 869a28c2e..99149b33d 100644 --- a/src/api/Storage.ts +++ b/src/api/Storage.ts @@ -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`; @@ -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 } diff --git a/src/api/errors/diagnostics.ts b/src/api/errors/diagnostics.ts index b029a18a1..dcc66eceb 100644 --- a/src/api/errors/diagnostics.ts +++ b/src/api/errors/diagnostics.ts @@ -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)) { diff --git a/src/components/getMemberInfo.ts b/src/components/getMemberInfo.ts index c1ee97c01..901c37b0f 100644 --- a/src/components/getMemberInfo.ts +++ b/src/components/getMemberInfo.ts @@ -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, @@ -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, diff --git a/src/filesystems/qsys/QSysFs.ts b/src/filesystems/qsys/QSysFs.ts index 39d649f97..8e38617d1 100644 --- a/src/filesystems/qsys/QSysFs.ts +++ b/src/filesystems/qsys/QSysFs.ts @@ -44,7 +44,6 @@ export function isProtectedFilter(filter?: string): boolean { } export class QSysFS implements vscode.FileSystemProvider { - private readonly libraryASP: Map = new Map; private readonly savedAsMembers: Set = new Set; private readonly sourceDateHandler: SourceDateHandler; private readonly extendedContent: ExtendedIBMiContent; @@ -71,7 +70,6 @@ export class QSysFS implements vscode.FileSystemProvider { 'disconnected', `Update member support & clear library ASP cache`, () => { - this.libraryASP.clear(); this.updateMemberSupport(); }); } @@ -129,42 +127,25 @@ export class QSysFS implements vscode.FileSystemProvider { } async getMemberAttributes(connection: IBMi, path: QsysPath & { member?: string }) { - const loadAttributes = async () => await connection.content.getAttributes(path, "CREATE_TIME", "MODIFY_TIME", "DATA_SIZE"); + const loadAttributes = async () => await connection.getContent().getAttributes(path, "CREATE_TIME", "MODIFY_TIME", "DATA_SIZE"); - path.asp = path.asp || this.getLibraryASP(connection, path.library); + path.asp = path.asp || await connection.lookupLibraryIAsp(path.library); let attributes = await loadAttributes(); - if (!attributes && !path.asp) { - for (const asp of Object.values(connection.aspInfo)) { - path.asp = asp; - attributes = await loadAttributes(); - if (attributes) { - this.setLibraryASP(connection, path.library, path.asp); - break; - } - } - } return attributes; } parseMemberPath(connection: IBMi, path: string) { const memberParts = connection.parserMemberPath(path); - memberParts.asp = memberParts.asp || this.getLibraryASP(connection, memberParts.library); + memberParts.asp = memberParts.asp || connection.getLibraryIAsp(memberParts.library); return memberParts; } - setLibraryASP(connection: IBMi, library: string, asp: string) { - this.libraryASP.set(connection.upperCaseName(library), asp); - } - - getLibraryASP(connection: IBMi, library: string) { - return this.libraryASP.get(connection.upperCaseName(library)); - } - async readFile(uri: vscode.Uri, retrying?: boolean): Promise { const contentApi = instance.getContent(); const connection = instance.getConnection(); if (connection && contentApi) { - const { asp, library, file, name: member } = this.parseMemberPath(connection, uri.path); + let { asp, library, file, name: member } = this.parseMemberPath(connection, uri.path); + asp = asp || await connection.lookupLibraryIAsp(library); let memberContent; try { @@ -179,9 +160,6 @@ export class QSysFS implements vscode.FileSystemProvider { throw error; } if (memberContent !== undefined) { - if (asp && !this.getLibraryASP(connection, library)) { - this.setLibraryASP(connection, library, asp); - } return new Uint8Array(Buffer.from(memberContent, `utf8`)); } else { @@ -208,7 +186,9 @@ export class QSysFS implements vscode.FileSystemProvider { const contentApi = instance.getContent(); const connection = instance.getConnection(); if (connection && contentApi) { - const { asp, library, file, name: member, extension } = this.parseMemberPath(connection, uri.path); + let { asp, library, file, name: member, extension } = this.parseMemberPath(connection, uri.path); + asp = asp || await connection.lookupLibraryIAsp(library); + if (!content.length) { //Coming from "Save as" const addMember = await connection.runCommand({ command: `ADDPFM FILE(${library}/${file}) MBR(${member}) SRCTYPE(${extension || '*NONE'})`, @@ -221,6 +201,7 @@ export class QSysFS implements vscode.FileSystemProvider { throw new FileSystemError(addMember.stderr); } } + else { this.savedAsMembers.delete(uri.path); this.extendedMemberSupport ? diff --git a/src/testing/asp.ts b/src/testing/asp.ts new file mode 100644 index 000000000..ad5fe4864 --- /dev/null +++ b/src/testing/asp.ts @@ -0,0 +1,76 @@ +import assert from "assert"; +import { randomInt } from "crypto"; +import { posix } from "path"; +import tmp from 'tmp'; +import util, { TextDecoder } from 'util'; +import { Uri, workspace } from "vscode"; +import { TestSuite } from "."; +import { Tools } from "../api/Tools"; +import { getMemberUri } from "../filesystems/qsys/QSysFs"; +import { instance } from "../instantiate"; +import { CommandResult } from "../typings"; +import IBMi from "../api/IBMi"; +import { Search } from "../api/Search"; + +const LIBNAME = `VSCODELIBT`; +const SPFNAME = `VSCODESPFT`; +const MBRNAME = `VSCODEMBRT`; + +function checkAsps(connection: IBMi) { + const asps = connection.getAllIAsps(); + assert.ok(asps?.length, `ASP list is empty`); + + const currentAsp = connection.getCurrentIAspName(); + assert.ok(currentAsp, `Current ASP not defined`); +} + +async function ensureLibExists(connection: IBMi) { + const detail = connection.getIAspDetail(connection.getCurrentIAspName()!)!; + const res = await connection.runCommand({command: `CRTLIB LIB(${LIBNAME}) ASP(${detail.id})`}); +} + +async function createTempRpgle(connection: IBMi) { + const content = connection.getContent(); + + await connection.runCommand({ + command: `CRTSRCPF ${LIBNAME}/${SPFNAME} MBR(*NONE)`, + environment: `ile` + }); + + await connection.runCommand({ + command: `ADDPFM FILE(${LIBNAME}/${SPFNAME}) MBR(${MBRNAME}) `, + environment: `ile` + }); + + const baseContent = `**FREE\ndsply 'hello world';`; + + const uploadResult = await content?.uploadMemberContent(undefined, LIBNAME, SPFNAME, MBRNAME, baseContent); + assert.ok(uploadResult); +} + +export const AspSuite: TestSuite = { + name: `ASP API tests`, + before: async () => { + const connection = instance.getConnection()!; + checkAsps(connection); + await ensureLibExists(connection); + }, + tests: [ + { + name: `Read members in ASP and base`, test: async () => { + const connection = instance.getConnection()!; + checkAsps(connection); + + await ensureLibExists(connection); + await createTempRpgle(connection); + + const aspUri = getMemberUri({ asp: connection.getCurrentIAspName(), library: LIBNAME, file: SPFNAME, name: MBRNAME, extension: `rpgle` }); + + // We have to read it first to create the alias! + const aspMbrContents = await workspace.fs.readFile(aspUri); + + assert.ok(aspMbrContents); + } + }, + ] +}; diff --git a/src/testing/connection.ts b/src/testing/connection.ts index e876543da..54447673b 100644 --- a/src/testing/connection.ts +++ b/src/testing/connection.ts @@ -325,6 +325,16 @@ export const ConnectionSuite: TestSuite = { connection.variantChars.local = variantsBackup; } } + }, + { + name: `Test getLibraryIAsp against QSYSINC`, test: async () => { + const connection = instance.getConnection()!; + + const library = `QSYSINC`; + + const asp = await connection.lookupLibraryIAsp(library); + assert.strictEqual(asp, undefined); // Because QSYSINC is not an iASP + } } ] }; diff --git a/src/testing/search.ts b/src/testing/search.ts index b2a732b97..63790561b 100644 --- a/src/testing/search.ts +++ b/src/testing/search.ts @@ -42,7 +42,7 @@ export const SearchSuite: TestSuite = { const filter = parseFilter(memberFilter); const checkNames = (names: string[]) => names.every(filter.test); - const members = await getConnection().content.getMemberList({ library, sourceFile, members: memberFilter }); + const members = await getConnection().getContent().getMemberList({ library, sourceFile, members: memberFilter }); assert.ok(checkNames(members.map(member => member.name))); const result = await Search.searchMembers(instance, "QSYSINC", "QRPGLESRC", "SQL", members); diff --git a/src/typings.ts b/src/typings.ts index 210b6215a..ba66e9e1f 100644 --- a/src/typings.ts +++ b/src/typings.ts @@ -16,6 +16,13 @@ export interface CodeForIBMi { componentRegistry: ComponentRegistry } +export interface AspInfo { + id: number; + name: string; + type: string; + rdbName: string; +} + export type DeploymentMethod = "all" | "staged" | "unstaged" | "changed" | "compare"; export interface DeploymentParameters { diff --git a/src/views/objectBrowser.ts b/src/views/objectBrowser.ts index 538d1d7a7..3eb5989b2 100644 --- a/src/views/objectBrowser.ts +++ b/src/views/objectBrowser.ts @@ -922,8 +922,9 @@ Do you want to replace it?`, item.name), skipAllLabel, overwriteLabel, overwrite filter: node?.filter } + const connection = getConnection(); + if (!parameters.path) { - const connection = getConnection(); const input = await vscode.window.showInputBox({ prompt: vscode.l10n.t(`Enter LIB/SPF/member.ext to search (member.ext is optional and can contain wildcards)`), title: vscode.l10n.t(`Search source file`), @@ -959,11 +960,10 @@ Do you want to replace it?`, item.name), skipAllLabel, overwriteLabel, overwrite } if (parameters.path) { - const config = getConfig(); - const pathParts = parameters.path.split(`/`); if (pathParts[1] !== `*ALL`) { - const aspText = ((config.sourceASP && config.sourceASP.length > 0) ? vscode.l10n.t(`(in ASP {0})`, config.sourceASP) : ``); + const selectedAsp = connection.getCurrentIAspName(); + const aspText = (selectedAsp ? vscode.l10n.t(`(in ASP {0})`, selectedAsp) : ``); const list = GlobalStorage.get().getPreviousSearchTerms(); const listHeader: vscode.QuickPickItem[] = [ @@ -1348,7 +1348,6 @@ function storeMemberList(path: string, list: string[]) { } async function doSearchInSourceFile(searchTerm: string, path: string, filter?: ConnectionConfiguration.ObjectFilters) { - const content = getContent(); const [library, sourceFile] = path.split(`/`); try { await vscode.window.withProgress({ diff --git a/src/webviews/settings/index.ts b/src/webviews/settings/index.ts index 7df552708..d5d42885c 100644 --- a/src/webviews/settings/index.ts +++ b/src/webviews/settings/index.ts @@ -77,7 +77,7 @@ export class SettingsUI { const sourceTab = new Section(); sourceTab - .addInput(`sourceASP`, `Source ASP`, `If source files live within a specific ASP, please specify it here. Leave blank otherwise. You can ignore this if you have access to QSYS2.ASP_INFO as Code for IBM i will fetch ASP information automatically.`, { default: config.sourceASP }) + .addInput(`sourceASP`, `Source ASP`, `Current ASP is based on the user profile job description and cannot be changed here.`, { default: connection?.getCurrentIAspName() || `*SYSBAS`, readonly: true }) .addInput(`sourceFileCCSID`, `Source file CCSID`, `The CCSID of source files on your system. You should only change this setting from *FILE if you have a source file that is 65535 - otherwise use *FILE. Note that this config is used to fetch all members. If you have any source files using 65535, you have bigger problems.`, { default: config.sourceFileCCSID, minlength: 1, maxlength: 5 }) .addHorizontalRule() .addCheckbox(`enableSourceDates`, `Enable Source Dates`, `When enabled, source dates will be retained and updated when editing source members. Requires restart when changed.`, config.enableSourceDates) @@ -289,7 +289,7 @@ export class SettingsUI { //In case we need to play with the data switch (key) { case `sourceASP`: - if (data[key].trim() === ``) data[key] = null; + data[key] = null; break; case `hideCompileErrors`: data[key] = String(data[key]).split(`,`)