zowe / vscode-extension-for-zowe
1
/*
2
* This program and the accompanying materials are made available under the terms of the *
3
* Eclipse Public License v2.0 which accompanies this distribution, and is available at *
4
* https://www.eclipse.org/legal/epl-v20.html                                      *
5
*                                                                                 *
6
* SPDX-License-Identifier: EPL-2.0                                                *
7
*                                                                                 *
8
* Copyright Contributors to the Zowe Project.                                     *
9
*                                                                                 *
10
*/
11

12 1
import * as dsUtils from "../dataset/utils";
13 1
import * as vscode from "vscode";
14 1
import * as fs from "fs";
15
import * as zowe from "@zowe/cli";
16 1
import * as globals from "../globals";
17 1
import * as path from "path";
18 1
import { errorHandling, FilterItem, resolveQuickPickHelper } from "../utils";
19 1
import { labelRefresh, refreshTree, getDocumentFilePath, concatChildNodes, checkForAddedSuffix, willForceUpload, filterTreeByString } from "../shared/utils";
20 1
import { Profiles, ValidProfileEnum } from "../Profiles";
21 1
import { ZoweExplorerApiRegister } from "../api/ZoweExplorerApiRegister";
22
import { IZoweTree } from "../api/IZoweTree";
23 1
import { TextUtils, IProfileLoaded, Session } from "@zowe/imperative";
24 1
import { getIconByNode } from "../generators/icons";
25
import { IZoweDatasetTreeNode, IZoweTreeNode, IZoweNodeType } from "../api/IZoweTreeNode";
26 1
import { ZoweDatasetNode } from "./ZoweDatasetNode";
27
import { DatasetTree } from "./DatasetTree";
28 1
import * as contextually from "../shared/context";
29 1
import { returnIconState } from "../shared/actions";
30 1
import { closeOpenedTextFile, setFileSaved } from "../utils/workspace";
31

32 1
import * as nls from "vscode-nls";
33
// Set up localization
34 1
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
35 1
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
36

37
/**
38
 * Refreshes treeView
39
 *
40
 * @param {DataSetTree} datasetProvider
41
 */
42 1
export async function refreshAll(datasetProvider: IZoweTree<IZoweDatasetTreeNode>) {
43 1
    await Profiles.getInstance().refresh();
44 1
    datasetProvider.mSessionNodes.forEach((sessNode) => {
45 1
        if (contextually.isSessionNotFav(sessNode)) {
46 1
            labelRefresh(sessNode);
47 1
            sessNode.children = [];
48 1
            sessNode.dirty = true;
49 1
            refreshTree(sessNode);
50
        }
51 1
        returnIconState(sessNode);
52
    });
53 1
    datasetProvider.refresh();
54
}
55

56
/**
57
 * Allocates a copy of a data set or member
58
 *
59
 */
60 1
export async function allocateLike(datasetProvider: IZoweTree<IZoweDatasetTreeNode>, node?: IZoweDatasetTreeNode) {
61
    let profile: IProfileLoaded;
62
    let likeDSName: string;
63
    let currSession: IZoweDatasetTreeNode;
64

65
    // User called allocateLike from the command palette
66 1
    if (!node) {
67
        // The user must choose a session
68 1
        const qpItems = [];
69 1
        const quickpick = vscode.window.createQuickPick();
70 1
        quickpick.placeholder = localize("allocateLike.options.prompt", "Select the profile to which the original data set belongs");
71 1
        quickpick.ignoreFocusOut = true;
72

73 1
        for (const thisSession of datasetProvider.mSessionNodes) { qpItems.push(new FilterItem(thisSession.label.trim())); }
74 1
        quickpick.items = [...qpItems];
75

76 1
        quickpick.show();
77 1
        const selection = await resolveQuickPickHelper(quickpick);
78 1
        if (!selection) {
79 1
            vscode.window.showInformationMessage(localize("allocateLike.noSelection", "You must select a profile."));
80 1
            return;
81
        } else {
82 1
            currSession = datasetProvider.mSessionNodes.find((thisSession) => thisSession.label === selection.label);
83 1
            profile = currSession.getProfile();
84
        }
85 1
        quickpick.dispose();
86

87
        // The user must enter the name of a data set to copy
88 1
        likeDSName = await vscode.window.showInputBox({ ignoreFocusOut: true,
89
                                                        placeHolder: localize("allocateLike.enterLikePattern", "Enter the name of the data set to \"allocate like\" from") });
90
    } else {
91
        // User called allocateLike by right-clicking a node
92 1
        profile = node.getProfile();
93 1
        likeDSName = node.label;
94
    }
95

96
    // Get new data set name
97 1
    const newDSName = await vscode.window.showInputBox({ ignoreFocusOut: true,
98
                                                         placeHolder: localize("allocateLike.enterPattern", "Enter a name for the new data set")
99
    });
100 1
    if (!newDSName) {
101 1
        vscode.window.showInformationMessage(localize("allocateLike.noNewName", "You must enter a new data set name."));
102 1
        return;
103
    } else {
104
        // Allocate the data set, or throw an error
105 1
        try {
106 1
            await (ZoweExplorerApiRegister.getMvsApi(profile).allocateLikeDataSet(newDSName.toUpperCase(), likeDSName));
107
        } catch (err) {
108 0
            globals.LOG.error(localize("createDataSet.log.error", "Error encountered when creating data set! ") + JSON.stringify(err));
109 0
            errorHandling(err, newDSName, localize("createDataSet.error", "Unable to create data set: ") + err.message);
110 0
            throw (err);
111
        }
112
    }
113

114
    // Refresh tree and open new node, if applicable
115 1
    if (!currSession) { currSession = datasetProvider.mSessionNodes.find((thisSession) => thisSession.label.trim() === profile.name); }
116 1
    const theFilter = await datasetProvider.createFilterString(newDSName, currSession);
117 1
    currSession.tooltip = currSession.pattern = theFilter.toUpperCase();
118 1
    datasetProvider.addSearchHistory(theFilter);
119 1
    datasetProvider.refresh();
120 1
    currSession.dirty = true;
121 1
    datasetProvider.refreshElement(currSession);
122 1
    const newNode = (await currSession.getChildren()).find((child) => child.label.trim() === newDSName.toUpperCase());
123 1
    await datasetProvider.getTreeView().reveal(currSession, { select: true, focus: true });
124 1
    datasetProvider.getTreeView().reveal(newNode, { select: true, focus: true });
125
}
126

127 1
export async function uploadDialog(node: ZoweDatasetNode, datasetProvider: IZoweTree<IZoweDatasetTreeNode>) {
128 1
    const fileOpenOptions = {
129
       canSelectFiles: true,
130
       openLabel: "Upload File",
131
       canSelectMany: true
132
    };
133

134 1
    const value = await vscode.window.showOpenDialog(fileOpenOptions);
135

136 1
    if (value && value.length) {
137 1
        await Promise.all(
138 1
            value.map(async (item) => {
139
                    // Convert to vscode.TextDocument
140 1
                    const doc = await vscode.workspace.openTextDocument(item);
141 1
                    await uploadFile(node, doc);
142
                }
143
            ));
144

145
        // refresh Tree View & favorites
146 1
        datasetProvider.refreshElement(node);
147 1
        if (contextually.isFavorite(node) || contextually.isFavoriteContext(node.getParent())) {
148 1
            const nonFavNode = datasetProvider.findNonFavoritedNode(node);
149 1
            if (nonFavNode) {
150 1
                datasetProvider.refreshElement(nonFavNode);
151
            }
152
        } else {
153 1
            const favNode = datasetProvider.findFavoritedNode(node);
154 1
            if (favNode) {
155 1
                datasetProvider.refreshElement(favNode);
156
            }
157
        }
158
    } else {
159 1
        vscode.window.showInformationMessage(localize("enterPattern.pattern", "No selection made."));
160
    }
161
}
162

163 1
export async function uploadFile(node: ZoweDatasetNode, doc: vscode.TextDocument) {
164 1
    try {
165 1
        const datasetName = dsUtils.getDatasetLabel(node);
166 1
        const prof = node.getProfile();
167 1
        await ZoweExplorerApiRegister.getMvsApi(prof).putContents(doc.fileName, datasetName, {
168
            encoding: prof.profile.encoding
169
        });
170
    } catch (e) {
171 1
        errorHandling(e, node.getProfileName(), e.message);
172
    }
173
}
174

175
/**
176
 * Creates a PDS member
177
 *
178
 * @export
179
 * @param {IZoweDatasetTreeNode} parent - The parent Node
180
 * @param {DatasetTree} datasetProvider - the tree which contains the nodes
181
 */
182 1
export async function createMember(parent: IZoweDatasetTreeNode, datasetProvider: IZoweTree<IZoweDatasetTreeNode>) {
183 1
    const name = await vscode.window.showInputBox({ placeHolder: localize("createMember.inputBox", "Name of Member") });
184 1
    globals.LOG.debug(localize("createMember.log.debug.createNewDataSet", "creating new data set member of name ") + name);
185 1
    if (name) {
186 1
        let label = parent.label.trim();
187 1
        if (contextually.isFavoritePds(parent)) {
188 1
            label = parent.label.substring(parent.label.indexOf(":") + 2); // TODO MISSED TESTING
189
        }
190

191 1
        try {
192 1
            await ZoweExplorerApiRegister.getMvsApi(parent.getProfile()).createDataSetMember(label + "(" + name + ")");
193
        } catch (err) {
194 1
            globals.LOG.error(localize("createMember.log.error", "Error encountered when creating member! ") + JSON.stringify(err));
195 1
            errorHandling(err, label, localize("createMember.error", "Unable to create member: ") + err.message);
196 1
            throw (err);
197
        }
198 1
        parent.dirty = true;
199 1
        datasetProvider.refreshElement(parent);
200 1
        openPS(
201
            new ZoweDatasetNode(name, vscode.TreeItemCollapsibleState.None, parent, null, undefined, undefined, parent.getProfile()),
202
            true, datasetProvider);
203 1
        datasetProvider.refresh();
204
    }
205
}
206

207
/**
208
 * Downloads and displays a PS in a text editor view
209
 *
210
 * @param {IZoweDatasetTreeNode} node
211
 */
212 1
export async function openPS(node: IZoweDatasetTreeNode, previewMember: boolean, datasetProvider?: IZoweTree<IZoweDatasetTreeNode>) {
213 1
    if (datasetProvider) { await datasetProvider.checkCurrentProfile(node); }
214 1
    if (Profiles.getInstance().validProfile === ValidProfileEnum.VALID) {
215 1
        try {
216
            let label: string;
217 1
            switch (true) {
218 1
                case contextually.isFavoriteContext(node.getParent()):
219 1
                    label = node.label.substring(node.label.indexOf(":") + 1).trim();
220 1
                    break;
221 1
                case contextually.isFavoritePds(node.getParent()):
222 1
                    label = node.getParent().getLabel().substring(node.getParent().getLabel().indexOf(":") + 1).trim() + "(" + node.getLabel()+ ")";
223 1
                    break;
224 1
                case contextually.isSessionNotFav(node.getParent()):
225 1
                    label = node.label.trim();
226 1
                    break;
227 1
                case contextually.isPdsNotFav(node.getParent()):
228 1
                    label = node.getParent().getLabel().trim() + "(" + node.getLabel()+ ")";
229 1
                    break;
230 0
                default:
231 0
                    vscode.window.showErrorMessage(localize("openPS.invalidNode", "openPS() called from invalid node."));
232 0
                    throw Error(localize("openPS.error.invalidNode", "openPS() called from invalid node. "));
233
            }
234 1
            globals.LOG.debug(localize("openPS.log.debug.openDataSet", "opening physical sequential data set from label ") + label);
235
            // if local copy exists, open that instead of pulling from mainframe
236 1
            const documentFilePath = getDocumentFilePath(label, node);
237 1
            if (!fs.existsSync(documentFilePath)) {
238 1
                const response = await vscode.window.withProgress({
239
                    location: vscode.ProgressLocation.Notification,
240
                    title: "Opening data set..."
241 0
                }, function downloadDataset() {
242 0
                    const prof = node.getProfile();
243 0
                    return ZoweExplorerApiRegister.getMvsApi(prof).getContents(label, {
244
                        file: documentFilePath,
245
                        returnEtag: true,
246
                        encoding: prof.profile.encoding
247
                    });
248
                });
249 1
                node.setEtag(response.apiResponse.etag);
250
            }
251 1
            const document = await vscode.workspace.openTextDocument(getDocumentFilePath(label, node));
252 1
            if (previewMember === true) {
253 1
                await vscode.window.showTextDocument(document);
254
            } else {
255 0
                await vscode.window.showTextDocument(document, {preview: false});
256
            }
257 1
            if (datasetProvider) { datasetProvider.addFileHistory(`[${node.getProfileName()}]: ${label}`); }
258
        } catch (err) {
259 1
            globals.LOG.error(localize("openPS.log.error.openDataSet", "Error encountered when opening data set! ") + JSON.stringify(err));
260 1
            errorHandling(err, node.getProfileName(), err.message);
261 1
            throw (err);
262
        }
263
    }
264
}
265

266 1
export function getDataSetTypeAndOptions(type: string) {
267
    let typeEnum;
268
    let createOptions;
269 1
    switch (type) {
270 1
        case localize("createFile.dataSetBinary", "Data Set Binary"):
271 1
            typeEnum = zowe.CreateDataSetTypeEnum.DATA_SET_BINARY;
272 1
            createOptions = vscode.workspace.getConfiguration("Zowe-Default-Datasets-Binary");
273 1
            break;
274 1
        case localize("createFile.dataSetC", "Data Set C"):
275 1
            typeEnum = zowe.CreateDataSetTypeEnum.DATA_SET_C;
276 1
            createOptions = vscode.workspace.getConfiguration("Zowe-Default-Datasets-C");
277 1
            break;
278 1
        case localize("createFile.dataSetClassic", "Data Set Classic"):
279 1
            typeEnum = zowe.CreateDataSetTypeEnum.DATA_SET_CLASSIC;
280 1
            createOptions = vscode.workspace.getConfiguration("Zowe-Default-Datasets-Classic");
281 1
            break;
282 1
        case localize("createFile.dataSetPartitioned", "Data Set Partitioned"):
283 1
            typeEnum = zowe.CreateDataSetTypeEnum.DATA_SET_PARTITIONED;
284 1
            createOptions = vscode.workspace.getConfiguration("Zowe-Default-Datasets-PDS");
285 1
            break;
286 1
        case localize("createFile.dataSetSequential", "Data Set Sequential"):
287 1
            typeEnum = zowe.CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL;
288 1
            createOptions = vscode.workspace.getConfiguration("Zowe-Default-Datasets-PS");
289 1
            break;
290
    }
291 1
    return {
292
        typeEnum,
293
        createOptions
294
    };
295
}
296

297 1
export function getDataSetTypeAsString(type: zowe.CreateDataSetTypeEnum) {
298 0
    switch (type) {
299 1
        case zowe.CreateDataSetTypeEnum.DATA_SET_BINARY:
300 0
            return "Data Set Binary";
301 0
        case zowe.CreateDataSetTypeEnum.DATA_SET_C:
302 0
            return "Data Set C";
303 0
        case zowe.CreateDataSetTypeEnum.DATA_SET_CLASSIC:
304 0
            return "Data Set Classic";
305 0
        case zowe.CreateDataSetTypeEnum.DATA_SET_PARTITIONED:
306 0
            return "Data Set Partitioned";
307 0
        case zowe.CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL:
308 0
            return "Data Set Sequential";
309
    }
310
}
311

312
/**
313
 * Creates a new file and uploads to the server
314
 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
315
 * TODO: Consider changing configuration to allow "custom" data set specifications
316
 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
317
 * @export
318
 * @param {IZoweDatasetTreeNode} node - Desired Zowe session
319
 * @param {DatasetTree} datasetProvider - the tree which contains the nodes
320
 */
321 1
export async function createFile(node: IZoweDatasetTreeNode, datasetProvider: IZoweTree<IZoweDatasetTreeNode>) {
322 1
    const quickPickOptions: vscode.QuickPickOptions = {
323
        placeHolder: localize("createFile.quickPickOption.dataSetType", "Type of Data Set to be Created"),
324
        ignoreFocusOut: true,
325
        canPickMany: false
326
    };
327 1
    const types = [
328
        localize("createFile.dataSetBinary", "Data Set Binary"),
329
        localize("createFile.dataSetC", "Data Set C"),
330
        localize("createFile.dataSetClassic", "Data Set Classic"),
331
        localize("createFile.dataSetPartitioned", "Data Set Partitioned"),
332
        localize("createFile.dataSetSequential", "Data Set Sequential")
333
    ];
334

335 1
    datasetProvider.checkCurrentProfile(node);
336 1
    if (Profiles.getInstance().validProfile === ValidProfileEnum.VALID) {
337
        // get data set type
338 1
        const type = await vscode.window.showQuickPick(types, quickPickOptions);
339 1
        if (type == null) {
340 1
            globals.LOG.debug(localize("createFile.log.debug.noValidTypeSelected", "No valid data type selected"));
341 1
            return;
342
        } else {
343 1
            globals.LOG.debug(localize("createFile.log.debug.creatingNewDataSet", "Creating new data set"));
344
        }
345

346 1
        const typeEnumAndOptions = this.getDataSetTypeAndOptions(type);
347

348
        // get name of data set
349 1
        let name = await vscode.window.showInputBox({placeHolder: localize("dataset.name", "Name of Data Set")});
350 1
        if (name) {
351 1
            name = name.trim().toUpperCase();
352

353 1
            try {
354 1
                await ZoweExplorerApiRegister.getMvsApi(node.getProfile()).createDataSet(typeEnumAndOptions.typeEnum,
355
                                                                                         name,
356
                                                                                         typeEnumAndOptions.createOptions);
357 1
                node.dirty = true;
358

359 1
                const theFilter = await datasetProvider.createFilterString(name, node);
360 1
                datasetProvider.addSearchHistory(theFilter);
361 1
                datasetProvider.refresh();
362

363
                // Show newly-created data set in expanded tree view
364 1
                if (name) {
365 1
                    node.label = `${node.label} `;
366 1
                    node.label = node.label.trim();
367 1
                    node.tooltip = node.pattern = theFilter.toUpperCase();
368 1
                    node.collapsibleState = vscode.TreeItemCollapsibleState.Expanded;
369 1
                    const icon = getIconByNode(node);
370 1
                    if (icon) {
371 1
                        node.iconPath = icon.path;
372
                    }
373 1
                    node.dirty = true;
374

375 1
                    const newNode = await node.getChildren().then((children) => children.find((child) => child.label === name));
376 1
                    datasetProvider.getTreeView().reveal(newNode, { select: true });
377
                }
378
            } catch (err) {
379 1
                globals.LOG.error(localize("createDataSet.error", "Error encountered when creating data set! ") + JSON.stringify(err));
380 1
                errorHandling(err, node.getProfileName(), localize("createDataSet.error", "Error encountered when creating data set! ") +
381
                    err.message);
382 1
                throw (err);
383
            }
384
        }
385
    }
386
}
387

388
/**
389
 * Shows data set attributes in a new text editor
390
 *
391
 * @export
392
 * @param {IZoweDatasetTreeNode} parent - The parent Node
393
 * @param {DatasetTree} datasetProvider - the tree which contains the nodes
394
 */
395 1
export async function showDSAttributes(parent: IZoweDatasetTreeNode, datasetProvider: IZoweTree<IZoweDatasetTreeNode>) {
396 1
    await datasetProvider.checkCurrentProfile(parent);
397 1
    if (Profiles.getInstance().validProfile === ValidProfileEnum.VALID) {
398 1
        let label = parent.label.trim();
399 1
        if (contextually.isFavoritePds(parent) || contextually.isFavoriteDs(parent)) {
400 1
            label = parent.label.trim().substring(parent.label.trim().indexOf(":") + 2);
401
        }
402

403 1
        globals.LOG.debug(localize("showDSAttributes.debug", "showing attributes of data set ") + label);
404
        let attributes: any;
405 1
        try {
406 1
            attributes = await ZoweExplorerApiRegister.getMvsApi(parent.getProfile()).dataSet(label, { attributes: true });
407 1
            attributes = attributes.apiResponse.items;
408 1
            attributes = attributes.filter((dataSet) => {
409 1
                return dataSet.dsname.toUpperCase() === label.toUpperCase();
410
            });
411 1
            if (attributes.length === 0) {
412 1
                throw new Error(localize("showDSAttributes.lengthError", "No matching data set names found for query: ") + label);
413
            }
414
        } catch (err) {
415 1
            globals.LOG.error(localize("showDSAttributes.log.error", "Error encountered when listing attributes! ") + JSON.stringify(err));
416 1
            errorHandling(err, parent.getProfileName(), localize("showDSAttributes.error", "Unable to list attributes: ") + err.message);
417 1
            throw (err);
418
        }
419

420
        // shouldn't be possible for there to be two cataloged data sets with the same name,
421
        // but just in case we'll display all of the results
422
        // if there's only one result (which there should be), we will just pass in attributes[0]
423
        // so that prettyJson doesn't display the attributes as an array with a hyphen character
424 1
        const attributesText = TextUtils.prettyJson(attributes.length > 1 ? attributes : attributes[0], undefined, false);
425
        // const attributesFilePath = path.join(ZOWETEMPFOLDER, label + ".yaml");
426
        // fs.writeFileSync(attributesFilePath, attributesText);
427
        // const document = await vscode.workspace.openTextDocument(attributesFilePath);
428
        // await vscode.window.showTextDocument(document);
429 1
        const attributesMessage = localize("attributes.title", "Attributes");
430 1
        const webviewHTML = `<!DOCTYPE html>
431
        <html lang="en">
432
        <head>
433
            <meta charset="UTF-8">
434
            <title>${label} "${attributesMessage}"</title>
435
        </head>
436
        <body>
437
        ${attributesText.replace(/\n/g, "</br>")}
438
        </body>
439
        </html>`;
440 1
        const column = vscode.window.activeTextEditor
441 1
            ? vscode.window.activeTextEditor.viewColumn
442 1
            : undefined;
443 1
        const panel: vscode.WebviewPanel = vscode.window.createWebviewPanel(
444
            "zowe",
445
            label + " " + localize("attributes.title", "Attributes"),
446 1
            column || 1,
447
            {}
448
        );
449 1
        panel.webview.html = webviewHTML;
450
    }
451
}
452

453
/**
454
 * Submit the contents of the editor as JCL.
455
 *
456
 * @export
457
 * @param {DatasetTree} datasetProvider - our DatasetTree object
458
 */
459 1
export async function submitJcl(datasetProvider: IZoweTree<IZoweDatasetTreeNode>) {
460 1
    if (!vscode.window.activeTextEditor) {
461 0
        vscode.window.showErrorMessage(
462
            localize("submitJcl.noDocumentOpen", "No editor with a document that could be submitted as JCL is currently open."));
463 0
        return;
464
    }
465 1
    const doc = vscode.window.activeTextEditor.document;
466 1
    globals.LOG.debug(localize("submitJcl.log.debug", "Submitting JCL in document ") + doc.fileName);
467
    // get session name
468 1
    const sessionregex = /\[(.*)(\])(?!.*\])/g;
469 1
    const regExp = sessionregex.exec(doc.fileName);
470 1
    const profiles = Profiles.getInstance();
471
    let sessProfileName;
472 1
    if (regExp === null) {
473 1
        const allProfiles: IProfileLoaded[] = profiles.allProfiles;
474 1
        const profileNamesList = allProfiles.map((profile) => {
475 1
            return profile.name;
476
        });
477 1
        if (profileNamesList.length) {
478 1
            const quickPickOptions: vscode.QuickPickOptions = {
479
                placeHolder: localize("submitJcl.quickPickOption", "Select the Profile to use to submit the job"),
480
                ignoreFocusOut: true,
481
                canPickMany: false
482
            };
483 1
            sessProfileName = await vscode.window.showQuickPick(profileNamesList, quickPickOptions);
484
        } else {
485 0
            vscode.window.showInformationMessage(localize("submitJcl.noProfile", "No profiles available"));
486
        }
487
    } else {
488 0
        sessProfileName = regExp[1];
489 1
        if (sessProfileName.includes("[")) {
490
            // if submitting from favorites, sesName might be the favorite node, so extract further
491 0
            sessProfileName = sessionregex.exec(sessProfileName)[1];
492
        }
493
    }
494

495
    // get profile from session name
496
    let sessProfile: IProfileLoaded;
497 1
    const sesNode = (await datasetProvider.getChildren()).find((child) => child.label.trim() === sessProfileName);
498 1
    if (sesNode) {
499 1
        sessProfile = sesNode.getProfile();
500
    } else {
501
        // if submitting from favorites, a session might not exist for this node
502 1
        sessProfile = profiles.loadNamedProfile(sessProfileName);
503
    }
504 1
    if (sessProfile == null) {
505 1
        globals.LOG.error(localize("submitJcl.log.error.nullSession", "Session for submitting JCL was null or undefined!"));
506 1
        return;
507
    }
508 1
    await Profiles.getInstance().checkCurrentProfile(sessProfile);
509 1
    if (Profiles.getInstance().validProfile === ValidProfileEnum.VALID) {
510 1
        try {
511 1
            const job = await ZoweExplorerApiRegister.getJesApi(sessProfile).submitJcl(doc.getText());
512 1
            const args = [sessProfileName, job.jobid];
513 1
            const setJobCmd = `command:zowe.setJobSpool?${encodeURIComponent(JSON.stringify(args))}`;
514 1
            vscode.window.showInformationMessage(localize("submitJcl.jobSubmitted", "Job submitted ") + `[${job.jobid}](${setJobCmd})`);
515
        } catch (error) {
516 0
            errorHandling(error, sessProfileName, localize("submitJcl.jobSubmissionFailed", "Job submission failed\n") + error.message);
517
        }
518
    } else {
519 0
        vscode.window.showErrorMessage(localize("submitJcl.checkProfile", "Profile is invalid"));
520 0
        return;
521
    }
522
}
523

524
/**
525
 * Submit the selected dataset member as a Job.
526
 *
527
 * @export
528
 * @param node The dataset member
529
 */
530 1
export async function submitMember(node: IZoweTreeNode) {
531 1
    const labelregex = /\[(.+)\]\: (.+)/g;
532
    let label;
533
    let sesName;
534
    let sessProfile;
535
    let regex;
536 1
    const profiles = Profiles.getInstance();
537 1
    profiles.checkCurrentProfile(node.getProfile());
538 1
    if (Profiles.getInstance().validProfile === ValidProfileEnum.VALID) {
539 1
        switch (true) {
540 1
            case contextually.isFavoriteContext(node.getParent()):
541 1
                regex = labelregex.exec(node.getLabel());
542 1
                sesName = regex[1];
543 1
                label = regex[2];
544 1
                sessProfile = profiles.loadNamedProfile(sesName);
545 1
                break;
546 1
            case contextually.isFavoritePds(node.getParent()):
547 1
                regex = labelregex.exec(node.getParent().getLabel());
548 1
                sesName = regex[1];
549 1
                label = regex[2] + "(" + node.label.trim()+ ")";
550 1
                sessProfile = node.getParent().getProfile();
551 1
                break;
552 1
            case contextually.isSessionNotFav(node.getParent()):
553 1
                sesName = node.getParent().getLabel();
554 1
                label = node.label;
555 1
                sessProfile = node.getParent().getProfile();
556 1
                break;
557 1
            case contextually.isPdsNotFav(node.getParent()):
558 1
                sesName = node.getParent().getParent().getLabel();
559 1
                label = node.getParent().getLabel() + "(" + node.label.trim()+ ")";
560 1
                sessProfile = node.getParent().getParent().getProfile();
561 1
                break;
562 1
            default:
563 1
                vscode.window.showErrorMessage(localize("submitMember.invalidNode", "submitMember() called from invalid node."));
564 1
                throw Error(localize("submitMember.error.invalidNode", "submitMember() called from invalid node."));
565
        }
566 1
        try {
567 1
            const job = await ZoweExplorerApiRegister.getJesApi(sessProfile).submitJob(label);
568 1
            const args = [sesName, job.jobid];
569 1
            const setJobCmd = `command:zowe.setJobSpool?${encodeURIComponent(JSON.stringify(args))}`;
570 1
            vscode.window.showInformationMessage(localize("submitMember.jobSubmitted", "Job submitted ") + `[${job.jobid}](${setJobCmd})`);
571
        } catch (error) {
572 0
            errorHandling(error, sesName, localize("submitMember.jobSubmissionFailed", "Job submission failed\n") + error.message);
573
        }
574
    }
575
}
576

577
/**
578
 * Deletes a dataset
579
 *
580
 * @export
581
 * @param {IZoweTreeNode} node - The node to be deleted
582
 * @param {IZoweTree<IZoweDatasetTreeNode>} datasetProvider - the tree which contains the nodes
583
 */
584 1
export async function deleteDataset(node: IZoweTreeNode, datasetProvider: IZoweTree<IZoweDatasetTreeNode>) {
585 1
    globals.LOG.debug(localize("deleteDataset.log.debug", "Deleting data set ") + node.label);
586 1
    const quickPickOptions: vscode.QuickPickOptions = {
587
        placeHolder: localize("deleteDataset.quickPickOption", "Delete {0}? This will permanently remove it from your system.", node.label),
588
        ignoreFocusOut: true,
589
        canPickMany: false
590
    };
591
    // confirm that the user really wants to delete
592 1
    if (await vscode.window.showQuickPick([localize("deleteDataset.showQuickPick.delete", "Delete"),
593
        localize("deleteDataset.showQuickPick.Cancel", "Cancel")], quickPickOptions) !== localize("deleteDataset.showQuickPick.delete", "Delete")) {
594 1
        globals.LOG.debug(localize("deleteDataset.showQuickPick.log.debug", "User picked Cancel. Cancelling delete of data set"));
595 1
        return;
596
    }
597

598 1
    let label = "";
599 1
    let fav = false;
600 1
    try {
601 1
        if (node.getParent().contextValue.includes(globals.FAVORITE_CONTEXT)) {
602 1
            label = node.label.substring(node.label.indexOf(":") + 1).trim();
603 1
            fav = true;
604 1
        } else if (node.getParent().contextValue.includes(globals.DS_PDS_CONTEXT + globals.FAV_SUFFIX)) {
605 1
            label = node.getParent().getLabel().substring(node.getParent().getLabel().indexOf(":") + 1).trim() + "(" + node.getLabel() + ")";
606 1
            fav = true;
607 1
        } else if (node.getParent().contextValue.includes(globals.DS_SESSION_CONTEXT)) {
608 1
            label = node.getLabel();
609 1
        } else if (node.getParent().contextValue.includes(globals.DS_PDS_CONTEXT)) {
610 0
            label = node.getParent().getLabel() + "(" + node.getLabel() + ")";
611
        } else {
612 1
            throw Error(localize("deleteDataSet.invalidNode.error", "deleteDataSet() called from invalid node."));
613
        }
614 1
        await datasetProvider.checkCurrentProfile(node);
615 1
        if (Profiles.getInstance().validProfile === ValidProfileEnum.VALID) {
616 1
            await ZoweExplorerApiRegister.getMvsApi(node.getProfile()).deleteDataSet(label);
617
        } else {
618 0
            return;
619
        }
620
    } catch (err) {
621 1
        globals.LOG.error(localize("deleteDataSet.delete.log.error", "Error encountered when deleting data set! ") + JSON.stringify(err));
622 1
        if (err.message.includes(localize("deleteDataSet.error.notFound", "not found"))) {
623 1
            vscode.window.showInformationMessage(localize("deleteDataSet.notFound.error1", "Unable to find file: ") + label +
624
                localize("deleteDataSet.notFound.error2", " was probably already deleted."));
625
        } else {
626 1
            errorHandling(err, node.getProfileName(), err.message);
627
        }
628 1
        throw err;
629
    }
630

631
    // remove node from tree
632 1
    if (fav) {
633 1
        datasetProvider.mSessionNodes.forEach((ses) => {
634 1
            if (node.label.substring(node.label.indexOf("[") + 1, node.label.indexOf("]")) === ses.label.trim()||
635 1
                node.getParent().getLabel().substring(node.getParent().getLabel().indexOf("["),
636
                        node.getParent().getLabel().indexOf("]")) === ses.label) {
637 1
                ses.dirty = true;
638
            }
639
        });
640 1
        datasetProvider.removeFavorite(node);
641
    } else {
642 1
        node.getSessionNode().dirty = true;
643 1
        const temp = node.label;
644 1
        node.label = "[" + node.getSessionNode().label.trim() + "]: " + node.label;
645 1
        datasetProvider.removeFavorite(node);
646 1
        node.label = temp;
647
    }
648

649
    // refresh Tree View & favorites
650 1
    if (node.getParent() && node.getParent().contextValue !== globals.DS_SESSION_CONTEXT) {
651 1
        datasetProvider.refreshElement(node.getParent());
652 1
        if (contextually.isFavorite(node) || contextually.isFavoriteContext(node.getParent())) {
653 1
            const nonFavNode = datasetProvider.findNonFavoritedNode(node.getParent());
654 1
            if (nonFavNode) { datasetProvider.refreshElement(nonFavNode); }
655
        } else {
656 1
            const favNode = datasetProvider.findFavoritedNode(node.getParent());
657 1
            if (favNode) { datasetProvider.refreshElement(favNode); }
658
        }
659
    } else {
660 1
        datasetProvider.refresh();
661
    }
662

663
    // remove local copy of file
664 1
    const fileName = getDocumentFilePath(label, node);
665 1
    try {
666 1
        if (fs.existsSync(fileName)) {
667 1
            fs.unlinkSync(fileName);
668
        }
669
    } catch (err) {
670
        // do nothing
671
    }
672
}
673

674
/**
675
 * Refreshes the passed node with current mainframe data
676
 *
677
 * @param {IZoweDatasetTreeNode} node - The node which represents the dataset
678
 */
679 1
export async function refreshPS(node: IZoweDatasetTreeNode) {
680
    let label;
681 1
    try {
682 1
        switch (true) {
683 1
            case contextually.isFavoriteContext(node.getParent()):
684 1
                label = node.label.substring(node.label.indexOf(":") + 1).trim();
685 1
                break;
686 1
            case contextually.isFavoritePds(node.getParent()):
687 1
                label = node.getParent().getLabel().substring(node.getParent().getLabel().indexOf(":") + 1).trim() + "(" + node.getLabel()+ ")";
688 1
                break;
689 1
            case contextually.isSessionNotFav(node.getParent()):
690 1
                label = node.label.trim();
691 1
                break;
692 1
            case contextually.isPdsNotFav(node.getParent()):
693 1
                label = node.getParent().getLabel() + "(" + node.getLabel() + ")";
694 1
                break;
695 0
            default:
696 0
                throw Error(localize("refreshPS.error.invalidNode", "refreshPS() called from invalid node."));
697
        }
698 1
        const documentFilePath = getDocumentFilePath(label, node);
699 1
        const prof = node.getProfile();
700 1
        const response = await ZoweExplorerApiRegister.getMvsApi(prof).getContents(label, {
701
            file: documentFilePath,
702
            returnEtag: true,
703
            encoding: prof.profile.encoding
704
        });
705 1
        node.setEtag(response.apiResponse.etag);
706

707 1
        const document = await vscode.workspace.openTextDocument(documentFilePath);
708 1
        vscode.window.showTextDocument(document);
709
        // if there are unsaved changes, vscode won't automatically display the updates, so close and reopen
710 1
        if (document.isDirty) {
711 1
            await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
712 1
            vscode.window.showTextDocument(document);
713
        }
714
    } catch (err) {
715 1
        globals.LOG.error(localize("refreshPS.log.error.refresh", "Error encountered when refreshing data set view: ") + JSON.stringify(err));
716 1
        if (err.message.includes(localize("refreshPS.error.notFound", "not found"))) {
717 1
            vscode.window.showInformationMessage(localize("refreshPS.file1", "Unable to find file: ") + label +
718
                localize("refreshPS.file2", " was probably deleted."));
719
        } else {
720 1
            errorHandling(err, node.getProfileName(), err.message);
721
        }
722
    }
723
}
724

725
/**
726
 * Prompts the user for a pattern, and populates the [TreeView]{@link vscode.TreeView} based on the pattern
727
 *
728
 * @param {IZoweDatasetTreeNode} node - The session node
729
 * @param {DatasetTree} datasetProvider - Current DatasetTree used to populate the TreeView
730
 * @returns {Promise<void>}
731
 */
732 1
export async function enterPattern(node: IZoweDatasetTreeNode, datasetProvider: DatasetTree) {
733 1
    if (globals.LOG) {
734 1
        globals.LOG.debug(localize("enterPattern.log.debug.prompt", "Prompting the user for a data set pattern"));
735
    }
736
    let pattern: string;
737 1
    if (contextually.isSessionNotFav(node)) {
738
        // manually entering a search
739 1
        const options: vscode.InputBoxOptions = {
740
            prompt: localize("enterPattern.options.prompt",
741
                                     "Search data sets by entering patterns: use a comma to separate multiple patterns"),
742
            value: node.pattern
743
        };
744
        // get user input
745 1
        pattern = await vscode.window.showInputBox(options);
746 1
        if (!pattern) {
747 1
            vscode.window.showInformationMessage(localize("enterPattern.pattern", "You must enter a pattern."));
748 1
            return;
749
        }
750
    } else {
751
        // executing search from saved search in favorites
752 1
        pattern = node.label.trim().substring(node.label.trim().indexOf(":") + 2);
753 1
        const session = node.label.trim().substring(node.label.trim().indexOf("[") + 1, node.label.trim().indexOf("]"));
754 1
        await datasetProvider.addSession(session);
755 1
        node = datasetProvider.mSessionNodes.find((tempNode) => tempNode.label.trim() === session);
756
    }
757

758
    // update the treeview with the new pattern
759
    // TODO figure out why a label change is needed to refresh the treeview,
760
    // instead of changing the collapsible state
761
    // change label so the treeview updates
762 1
    node.label = node.label.trim() + " ";
763 1
    node.label = node.label.trim();
764 1
    node.tooltip = node.pattern = pattern.toUpperCase();
765 1
    node.collapsibleState = vscode.TreeItemCollapsibleState.Expanded;
766 1
    node.dirty = true;
767 1
    const icon = getIconByNode(node);
768 1
    if (icon) {
769 1
        node.iconPath = icon.path;
770
    }
771 1
    datasetProvider.addSearchHistory(node.pattern);
772
}
773

774
/**
775
 * Copy data sets
776
 *
777
 * @export
778
 * @param {IZoweNodeType} node - The node to copy
779
 */
780 1
export async function copyDataSet(node: IZoweNodeType) {
781 1
    return vscode.env.clipboard.writeText(JSON.stringify(dsUtils.getNodeLabels(node)));
782
}
783

784
/**
785
 * Migrate data sets
786
 *
787
 * @export
788
 * @param {IZoweDatasetTreeNode} node - The node to paste to
789
 */
790 1
export async function hMigrateDataSet(node: ZoweDatasetNode) {
791 1
    await Profiles.getInstance().checkCurrentProfile(node.getProfile());
792 1
    if (Profiles.getInstance().validProfile === ValidProfileEnum.VALID) {
793 1
        const { dataSetName } = dsUtils.getNodeLabels(node);
794 1
        vscode.window.showInformationMessage(localize("hMigrate.requestSent1", "Migration of dataset: ") + dataSetName +
795
        localize("hMigrate.requestSent2", " requested."));
796 1
        return ZoweExplorerApiRegister.getMvsApi(node.getProfile()).hMigrateDataSet(dataSetName);
797
    } else {
798 0
        vscode.window.showErrorMessage(localize("hMigrateDataSet.checkProfile", "Profile is invalid"));
799 0
        return;
800
    }
801
}
802

803
/**
804
 * Recall data sets
805
 *
806
 * @export
807
 * @param {IZoweDatasetTreeNode} node - The node to paste to
808
 */
809 1
export async function hRecallDataSet(node: ZoweDatasetNode) {
810 1
    await Profiles.getInstance().checkCurrentProfile(node.getProfile());
811 1
    if (Profiles.getInstance().validProfile === ValidProfileEnum.VALID) {
812 1
        const { dataSetName } = dsUtils.getNodeLabels(node);
813 1
        vscode.window.showInformationMessage(localize("hRecall.requestSent1", "Recall of dataset: ") + dataSetName +
814
        localize("hRecall.requestSent2", " requested."));
815 1
        return ZoweExplorerApiRegister.getMvsApi(node.getProfile()).hRecallDataSet(dataSetName);
816
    } else {
817 0
        vscode.window.showErrorMessage(localize("hMigrateDataSet.checkProfile", "Profile is invalid"));
818 0
        return;
819
    }
820
}
821

822
/**
823
 * Paste data sets
824
 *
825
 * @export
826
 * @param {ZoweNode} node - The node to paste to
827
 * @param {DatasetTree} datasetProvider - the tree which contains the nodes
828
 */
829 1
export async function pasteDataSet(node: IZoweDatasetTreeNode, datasetProvider: IZoweTree<IZoweDatasetTreeNode>) {
830 1
    const { profileName, dataSetName } = dsUtils.getNodeLabels(node);
831
    let memberName;
832
    let beforeDataSetName;
833
    let beforeProfileName;
834
    let beforeMemberName;
835

836 1
    await Profiles.getInstance().checkCurrentProfile(node.getProfile());
837 1
    if (Profiles.getInstance().validProfile === ValidProfileEnum.VALID) {
838 1
        if (node.contextValue.includes(globals.DS_PDS_CONTEXT)) {
839 1
            memberName = await vscode.window.showInputBox({placeHolder: localize("renameDataSet.name", "Name of Data Set Member")});
840 1
            if (!memberName) {
841 1
                return;
842
            }
843
        }
844

845 1
        try {
846 1
            ({
847
                dataSetName: beforeDataSetName,
848
                memberName: beforeMemberName,
849
                profileName: beforeProfileName,
850
            } = JSON.parse(await vscode.env.clipboard.readText()));
851
        } catch (err) {
852 1
            throw Error("Invalid clipboard. Copy from data set first");
853
        }
854

855 1
        if (beforeProfileName === profileName) {
856 1
            if (memberName) {
857 1
                const responseItem: zowe.IZosFilesResponse = await ZoweExplorerApiRegister.getMvsApi(node.getProfile()).allMembers(`${dataSetName}`);
858 1
                if (responseItem.apiResponse.items.some( (singleItem) => singleItem.member === memberName.toUpperCase())) {
859 1
                    throw Error(`${dataSetName}(${memberName}) already exists. You cannot replace a member`);
860
                }
861
            }
862 1
            await ZoweExplorerApiRegister.getMvsApi(node.getProfile()).copyDataSetMember(
863
                { dataSetName: beforeDataSetName, memberName: beforeMemberName },
864
                { dataSetName, memberName }
865
            );
866

867 1
            if (memberName) {
868 1
                datasetProvider.refreshElement(node);
869
                let node2;
870 1
                if (node.contextValue.includes(globals.FAV_SUFFIX)) {
871 1
                    node2 = datasetProvider.findNonFavoritedNode(node);
872
                } else {
873 1
                    node2 = datasetProvider.findFavoritedNode(node);
874
                }
875 1
                if (node2) {
876 1
                    datasetProvider.refreshElement(node2);
877
                }
878
            } else {
879 1
                refreshPS(node);
880
            }
881
        }
882
    }
883
}
884

885
/**
886
 * Uploads the file to the mainframe
887
 *
888
 * @export
889
 * @param {vscode.TextDocument} doc - TextDocument that is being saved
890
 */
891 1
export async function saveFile(doc: vscode.TextDocument, datasetProvider: IZoweTree<IZoweDatasetTreeNode>) {
892
    // Check if file is a data set, instead of some other file
893 1
    globals.LOG.debug(localize("saveFile.log.debug.request", "requested to save data set: ") + doc.fileName);
894 1
    const docPath = path.join(doc.fileName, "..");
895 1
    globals.LOG.debug("requested to save data set: " + doc.fileName);
896 1
    if (docPath.toUpperCase().indexOf(globals.DS_DIR.toUpperCase()) === -1) {
897 1
        globals.LOG.debug(localize("saveFile.log.debug.path", "path.relative returned a non-blank directory.") +
898
            localize("saveFile.log.debug.directory",
899
                             "Assuming we are not in the DS_DIR directory: ") + path.relative(docPath, globals.DS_DIR));
900 1
        return;
901
    }
902 1
    const start = path.join(globals.DS_DIR + path.sep).length;
903 1
    const ending = doc.fileName.substring(start);
904 1
    const sesName = ending.substring(0, ending.indexOf(path.sep));
905 1
    const profile = Profiles.getInstance().loadNamedProfile(sesName);
906 1
    if (!profile) {
907 1
        globals.LOG.error(localize("saveFile.log.error.session", "Couldn't locate session when saving data set!"));
908 1
        return vscode.window.showErrorMessage(localize("saveFile.log.error.session", "Couldn't locate session when saving data set!"));
909
    }
910

911
    // get session from session name
912
    let documentSession: Session;
913
    let node: IZoweDatasetTreeNode;
914 1
    const sesNode = (await datasetProvider.getChildren()).find((child) =>
915 1
        child.label.trim() === sesName);
916 1
    if (sesNode) {
917 1
        globals.LOG.debug(localize("saveFile.log.debug.load", "Loading session from session node in saveFile()"));
918 1
        documentSession = sesNode.getSession();
919
    } else {
920
        // if saving from favorites, a session might not exist for this node
921 1
        globals.LOG.debug(localize("saveFile.log.debug.sessionNode", "couldn't find session node, loading profile with CLI profile manager"));
922 1
        documentSession = ZoweExplorerApiRegister.getMvsApi(profile).getSession();
923
    }
924

925
    // If not a member
926 1
    let label = doc.fileName.substring(doc.fileName.lastIndexOf(path.sep) + 1,
927 1
        checkForAddedSuffix(doc.fileName) ? doc.fileName.lastIndexOf(".") : doc.fileName.length);
928 1
    label = label.toUpperCase();
929 1
    globals.LOG.debug(localize("saveFile.log.debug.saving", "Saving file ") + label);
930 1
    if (!label.includes("(")) {
931 1
        try {
932
            // Checks if file still exists on server
933 1
            const response = await ZoweExplorerApiRegister.getMvsApi(profile).dataSet(label);
934 1
            if (!response.apiResponse.items.length) {
935 1
                return vscode.window.showErrorMessage(
936
                    localize("saveFile.error.saveFailed", "Data set failed to save. Data set may have been deleted on mainframe."));
937
            }
938
        } catch (err) {
939 1
            errorHandling(err, sesName, err.message);
940
        }
941
    }
942
    // Get specific node based on label and parent tree (session / favorites)
943
    let nodes: IZoweNodeType[];
944
    let isFromFavorites: boolean;
945 1
    if (!sesNode || sesNode.children.length === 0) {
946
        // saving from favorites
947 1
        nodes = concatChildNodes(datasetProvider.mFavorites);
948 1
        isFromFavorites = true;
949
    } else {
950
        // saving from session
951 1
        nodes = concatChildNodes([sesNode]);
952 1
        isFromFavorites = false;
953
    }
954 1
    node = nodes.find((zNode) => {
955
        // dataset in Favorites
956 1
        if (contextually.isFavoriteDs(zNode)) {
957 0
            return (zNode.label === `[${sesName}]: ${label}`);
958
            // member in Favorites
959 1
        } else if (contextually.isDsMember(zNode) && isFromFavorites) {
960 1
            const zNodeDetails = dsUtils.getProfileAndDataSetName(zNode);
961 1
            return (`${zNodeDetails.profileName}(${zNodeDetails.dataSetName})` === `[${sesName}]: ${label}`);
962 1
        } else if (contextually.isDsMember(zNode) && !isFromFavorites) {
963 0
            const zNodeDetails = dsUtils.getProfileAndDataSetName(zNode);
964 0
            return (`${zNodeDetails.profileName}(${zNodeDetails.dataSetName})` === `${label}`);
965 1
        } else if (contextually.isDs(zNode)) {
966 1
            return (zNode.label.trim() === label);
967
        } else {
968 1
            return false;
969
        }
970
    });
971

972
    // define upload options
973
    let uploadOptions: zowe.IUploadOptions;
974 1
    if (node) {
975 1
        uploadOptions = {
976
            etag: node.getEtag(),
977
            returnEtag: true
978
        };
979
    }
980

981 1
    try {
982 1
        const uploadResponse = await vscode.window.withProgress({
983
            location: vscode.ProgressLocation.Notification,
984
            title: localize("saveFile.response.save.title", "Saving data set...")
985 1
        }, () => {
986 1
            const prof = (node) ? node.getProfile() : profile;
987 1
            if (prof.profile.encoding) {
988 1
                uploadOptions.encoding = prof.profile.encoding;
989
            }
990 1
            return ZoweExplorerApiRegister.getMvsApi(prof).putContents(doc.fileName, label, uploadOptions);
991
        });
992 1
        if (uploadResponse.success) {
993 1
            vscode.window.showInformationMessage(uploadResponse.commandResponse);
994
            // set local etag with the new etag from the updated file on mainframe
995 1
            if (node) {
996 1
                node.setEtag(uploadResponse.apiResponse[0].etag);
997 1
                setFileSaved(true);
998
            }
999 1
        } else if (!uploadResponse.success && uploadResponse.commandResponse.includes(
1000
            localize("saveFile.error.ZosmfEtagMismatchError", "Rest API failure with HTTP(S) status 412"))) {
1001 1
            if (globals.ISTHEIA) {
1002 1
                await willForceUpload(node, doc, label, node ? node.getProfile(): profile);
1003
            } else {
1004 1
                const oldDoc = doc;
1005 1
                const oldDocText = oldDoc.getText();
1006 1
                const prof = (node) ? node.getProfile() : profile;
1007 1
                if (prof.profile.encoding) {
1008 1
                    uploadOptions.encoding = prof.profile.encoding;
1009
                }
1010 1
                const downloadResponse = await ZoweExplorerApiRegister.getMvsApi(prof).getContents(label, {
1011
                    file: doc.fileName,
1012
                    returnEtag: true,
1013
                    encoding: prof.profile.encoding
1014
                });
1015
                // re-assign etag, so that it can be used with subsequent requests
1016 1
                const downloadEtag = downloadResponse.apiResponse.etag;
1017 1
                if (node && downloadEtag !== node.getEtag()) {
1018 1
                    node.setEtag(downloadEtag);
1019
                }
1020 1
                vscode.window.showWarningMessage(localize("saveFile.error.etagMismatch",
1021
                "Remote file has been modified in the meantime.\nSelect 'Compare' to resolve the conflict."));
1022 1
                if (vscode.window.activeTextEditor) {
1023
                    // Store document in a separate variable, to be used on merge conflict
1024 0
                    const startPosition = new vscode.Position(0, 0);
1025 0
                    const endPosition = new vscode.Position(oldDoc.lineCount, 0);
1026 0
                    const deleteRange = new vscode.Range(startPosition, endPosition);
1027 0
                    await vscode.window.activeTextEditor.edit((editBuilder) => {
1028
                        // re-write the old content in the editor view
1029 0
                        editBuilder.delete(deleteRange);
1030 0
                        editBuilder.insert(startPosition, oldDocText);
1031
                    });
1032 0
                    await vscode.window.activeTextEditor.document.save();
1033
                }
1034
            }
1035
        } else {
1036 1
            vscode.window.showErrorMessage(uploadResponse.commandResponse);
1037
        }
1038
    } catch (err) {
1039 1
        vscode.window.showErrorMessage(err.message);
1040
    }
1041
}

Read our documentation on viewing source code .

Loading