Skip to content

Commit

Permalink
feat(ds/fsp): Handle remote lookup for data set URIs
Browse files Browse the repository at this point in the history
Signed-off-by: Trae Yelovich <[email protected]>
  • Loading branch information
traeok committed Aug 1, 2024
1 parent 9d086db commit db6abfd
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 66 deletions.
212 changes: 147 additions & 65 deletions packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {
Gui,
ZosEncoding,
ZoweScheme,
UriFsInfo,
FileEntry,
} from "@zowe/zowe-explorer-api";
import { IZosFilesResponse } from "@zowe/zos-files-for-zowe-sdk";
import { DatasetUtils } from "./DatasetUtils";
Expand Down Expand Up @@ -96,84 +98,164 @@ export class DatasetFSProvider extends BaseProvider implements vscode.FileSystem
return this._lookup(uri, false);
}

/**
* Reads a directory located at the given URI.
* @param uri A valid URI within the provider
* @returns An array of tuples containing each entry name and type
*/
public async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
const dsEntry = this._lookupAsDirectory(uri, false);
const uriInfo = FsAbstractUtils.getInfoForUri(uri, Profiles.getInstance());

const results: [string, vscode.FileType][] = [];

if (FsAbstractUtils.isFilterEntry(dsEntry)) {
const mvsApi = ZoweExplorerApiRegister.getMvsApi(uriInfo.profile);
const datasetResponses: IZosFilesResponse[] = [];
const dsPatterns = [
...new Set(
dsEntry.filter["pattern"]
.toUpperCase()
.split(",")
.map((p: string) => p.trim())
),
];

if (mvsApi.dataSetsMatchingPattern) {
datasetResponses.push(await mvsApi.dataSetsMatchingPattern(dsPatterns));
} else {
for (const dsp of dsPatterns) {
datasetResponses.push(await mvsApi.dataSet(dsp));
}
private async fetchEntriesForProfile(uri: vscode.Uri, uriInfo: UriFsInfo, pattern: string): Promise<FilterEntry> {
const profileEntry = this._lookupAsDirectory(uri, false) as FilterEntry;

const mvsApi = ZoweExplorerApiRegister.getMvsApi(uriInfo.profile);
const datasetResponses: IZosFilesResponse[] = [];
const dsPatterns = [
...new Set(
pattern
.toUpperCase()
.split(",")
.map((p: string) => p.trim())
),
];

if (mvsApi.dataSetsMatchingPattern) {
datasetResponses.push(await mvsApi.dataSetsMatchingPattern(dsPatterns));
} else {
for (const dsp of dsPatterns) {
datasetResponses.push(await mvsApi.dataSet(dsp));
}
}

for (const resp of datasetResponses) {
for (const ds of resp.apiResponse?.items ?? resp.apiResponse ?? []) {
let tempEntry = dsEntry.entries.get(ds.dsname);
if (tempEntry == null) {
if (ds.dsorg === "PO" || ds.dsorg === "PO-E") {
// Entry is a PDS
tempEntry = new PdsEntry(ds.dsname);
} else if (ds.dsorg === "VS") {
// TODO: Add VSAM and ZFS support in Zowe Explorer
continue;
} else if (ds.migr?.toUpperCase() === "YES") {
// migrated
tempEntry = new DsEntry(ds.dsname);
} else {
// PS
tempEntry = new DsEntry(ds.dsname);
}
tempEntry.metadata = new DsEntryMetadata({ ...dsEntry.metadata, path: path.posix.join(dsEntry.metadata.path, ds.dsname) });
dsEntry.entries.set(ds.dsname, tempEntry);
for (const resp of datasetResponses) {
for (const ds of resp.apiResponse?.items ?? resp.apiResponse ?? []) {
let tempEntry = profileEntry.entries.get(ds.dsname);
if (tempEntry == null) {
if (ds.dsorg === "PO" || ds.dsorg === "PO-E") {
// Entry is a PDS
tempEntry = new PdsEntry(ds.dsname);
} else if (ds.dsorg === "VS") {
// TODO: Add VSAM and ZFS support in Zowe Explorer
continue;
} else if (ds.migr?.toUpperCase() === "YES") {
// migrated
tempEntry = new DsEntry(ds.dsname);
} else {
// PS
tempEntry = new DsEntry(ds.dsname);
}
results.push([tempEntry.name, tempEntry instanceof DsEntry ? vscode.FileType.File : vscode.FileType.Directory]);
tempEntry.metadata = new DsEntryMetadata({
...profileEntry.metadata,
path: path.posix.join(profileEntry.metadata.path, ds.dsname),
});
profileEntry.entries.set(ds.dsname, tempEntry);
}
}
} else if (FsAbstractUtils.isDirectoryEntry(dsEntry)) {
const members = await ZoweExplorerApiRegister.getMvsApi(uriInfo.profile).allMembers(dsEntry.name);
}

for (const ds of members.apiResponse?.items || []) {
let tempEntry = dsEntry.entries.get(ds.member);
if (tempEntry == null) {
tempEntry = new MemberEntry(ds.member);
tempEntry.metadata = new DsEntryMetadata({ ...dsEntry.metadata, path: path.posix.join(dsEntry.metadata.path, ds.member) });
dsEntry.entries.set(ds.member, tempEntry);
}
results.push([tempEntry.name, vscode.FileType.File]);
return profileEntry;
}

private async fetchEntriesForDataset(entry: PdsEntry, uri: vscode.Uri, uriInfo: UriFsInfo): Promise<void> {
const members = await ZoweExplorerApiRegister.getMvsApi(uriInfo.profile).allMembers(path.posix.basename(uri.path));

for (const ds of members.apiResponse?.items || []) {
let tempEntry = entry.entries.get(ds.member);
if (tempEntry == null) {
tempEntry = new MemberEntry(ds.member);
tempEntry.metadata = new DsEntryMetadata({ ...entry.metadata, path: path.posix.join(entry.metadata.path, ds.member) });
entry.entries.set(ds.member, tempEntry);
}
}
}

private async fetchDataset(uri: vscode.Uri, uriInfo: UriFsInfo): Promise<PdsEntry | DsEntry> {
let entry: PdsEntry | DsEntry;
try {
entry = this._lookupAsDirectory(uri, false) as PdsEntry;
} catch (err) {
if (!(err instanceof vscode.FileSystemError)) {
throw err;
}

if (err.code !== "FileNotFound") {
throw err;
}
}

const entryExists = entry != null;
let entryIsDir = false;
if (!entryExists) {
const resp = await ZoweExplorerApiRegister.getMvsApi(uriInfo.profile).dataSet(path.posix.basename(uri.path), { attributes: true });
if (resp.success) {
const dsorg: string = resp.apiResponse?.items[0].dsorg;
entryIsDir = dsorg.startsWith("PO");
} else {
throw vscode.FileSystemError.FileNotFound(uri);
}
}

return results;
if (entryIsDir) {
if (!entryExists) {
this.createDirectory(uri);
entry = this._lookupAsDirectory(uri, false) as PdsEntry;
}
await this.fetchEntriesForDataset(entry as PdsEntry, uri, uriInfo);
} else {
if (!entryExists) {
await this.writeFile(uri, new Uint8Array(), { create: true, overwrite: false });
entry = this._lookupAsFile(uri) as DsEntry;
}
}

return entry;
}

public updateFilterForUri(uri: vscode.Uri, pattern: string): void {
const filterEntry = this._lookup(uri, false);
if (!FsAbstractUtils.isFilterEntry(filterEntry)) {
return;
public async remoteLookupForResource(uri: vscode.Uri): Promise<DirEntry | DsEntry> {
const uriInfo = FsAbstractUtils.getInfoForUri(uri, Profiles.getInstance());
const profileUri = vscode.Uri.from({ scheme: ZoweScheme.DS, path: uriInfo.profileName });

// Ensure that an entry exists for the given profile
if (!this.exists(profileUri)) {
this.createDirectory(profileUri);
}

if (uriInfo.isRoot) {
// profile entry; check if "pattern" filter is in query.

const urlQuery = new URLSearchParams(uri.query);
if (!urlQuery.has("pattern")) {
return this._lookupAsDirectory(profileUri, false);
}

return this.fetchEntriesForProfile(uri, uriInfo, urlQuery.get("pattern"));
} else {
// data set or one of its members
return this.fetchDataset(uri, uriInfo);
}
}

/**
* Reads a directory located at the given URI.
* @param uri A valid URI within the provider
* @returns An array of tuples containing each entry name and type
*/
public async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
let dsEntry: DirEntry | DsEntry = null;
try {
dsEntry = this._lookupAsDirectory(uri, false);
} catch (err) {
// Errors unrelated to the filesystem cannot be handled here
if (!(err instanceof vscode.FileSystemError)) {
throw err;
}

if (err.code === "FileNotFound") {
// if the entry doesn't exist in the local file system, first check to see if it exists on the remote before throwing an error.
dsEntry = await this.remoteLookupForResource(uri);
}
}

if (dsEntry == null || FsDatasetsUtils.isDsEntry(dsEntry)) {
throw vscode.FileSystemError.FileNotFound(uri);
}

filterEntry.filter["pattern"] = pattern;
// Check the remote file system to see if anything has changed since the last time the directory was read.
await this.remoteLookupForResource(uri);
return Array.from(dsEntry.entries.entries()).map((value: [string, DirEntry | FileEntry]) => [value[0], value[1].type]);
}

/**
Expand Down
13 changes: 13 additions & 0 deletions packages/zowe-explorer/src/trees/dataset/DatasetInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,19 @@ export class DatasetInit {
);
context.subscriptions.push(vscode.workspace.onDidOpenTextDocument(DatasetFSProvider.onDidOpenTextDocument));

// Perform remote lookup for workspace folders that fit the `zowe-ds` scheme.
for (const folder of (vscode.workspace.workspaceFolders ?? []).filter((f) => f.uri.scheme === ZoweScheme.DS)) {
await DatasetFSProvider.instance.remoteLookupForResource(folder.uri);
}
context.subscriptions.push(
// Perform remote lookup on workspace folders that were added
vscode.workspace.onDidChangeWorkspaceFolders(async (e) => {
for (const folder of e.added.filter((f) => f.uri.scheme === ZoweScheme.DS)) {
await DatasetFSProvider.instance.remoteLookupForResource(folder.uri);
}
})
);

SharedInit.initSubscribers(context, datasetProvider);
return datasetProvider;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/zowe-explorer/src/trees/dataset/DatasetTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,7 @@ export class DatasetTree extends ZoweTreeProvider<IZoweDatasetTreeNode> implemen
this.addSearchHistory(pattern);
}
if (!SharedContext.isFavorite(sessionNode)) {
DatasetFSProvider.instance.updateFilterForUri(sessionNode.resourceUri, pattern);
sessionNode.resourceUri = sessionNode.resourceUri.with({ query: `pattern=${pattern}` });
}
await TreeViewUtils.expandNode(sessionNode, this);
}
Expand Down

0 comments on commit db6abfd

Please sign in to comment.