diff --git a/packages/cli/src/zosjobs/download/download-output/Output.definition.ts b/packages/cli/src/zosjobs/download/download-output/Output.definition.ts index 75e5c89d42..ecee1dd46f 100644 --- a/packages/cli/src/zosjobs/download/download-output/Output.definition.ts +++ b/packages/cli/src/zosjobs/download/download-output/Output.definition.ts @@ -86,6 +86,12 @@ export const OutputDefinition: ICommandDefinition = { type: "boolean", conflictsWith: ["wait-for-active"] }, + { + name: "record-range", aliases: ["rr"], + description: "placeholder.", + type: "string", + optional: true + } ] as ICommandOptionDefinition[]), examples: [ { diff --git a/packages/cli/src/zosjobs/download/download-output/Output.handler.ts b/packages/cli/src/zosjobs/download/download-output/Output.handler.ts index 76725fbba0..f732369f61 100644 --- a/packages/cli/src/zosjobs/download/download-output/Output.handler.ts +++ b/packages/cli/src/zosjobs/download/download-output/Output.handler.ts @@ -37,6 +37,8 @@ export default class OutputHandler extends ZosmfBaseHandler { const encoding: string = this.mArguments.encoding; const waitForActive: boolean = this.mArguments.waitForActive; const waitForOutput: boolean = this.mArguments.waitForOutput; + const recordRange: string = this.mArguments.recordRange; + // Get the job details const job: IJob = await GetJobs.getJob(this.mSession, jobid); const options: IDownloadAllSpoolContentParms = { @@ -49,7 +51,8 @@ export default class OutputHandler extends ZosmfBaseHandler { record, encoding, waitForActive, - waitForOutput + waitForOutput, + recordRange }; // Download 'em all await DownloadJobs.downloadAllSpoolContentCommon(this.mSession, options); diff --git a/packages/zosjobs/src/DownloadJobs.ts b/packages/zosjobs/src/DownloadJobs.ts index 14728422eb..e6b573a57d 100644 --- a/packages/zosjobs/src/DownloadJobs.ts +++ b/packages/zosjobs/src/DownloadJobs.ts @@ -104,20 +104,20 @@ export class DownloadJobs { this.log.trace("Entering downloadSpoolContentCommon with parms %s", JSON.stringify(parms)); ImperativeExpect.keysToBeDefined(parms, ["jobFile"], "You must specify a job file on your 'parms' parameter" + " object to the downloadSpoolContentCommon API."); - - //waiting for job to be active before continuing with job download + + // Waiting for job to be active before continuing with job download if (parms.waitForActive) { await MonitorJobs.waitForActiveStatus(session, parms.jobname, parms.jobid); } - - //waiting for job status to be output before continuing on with job download + + // Waiting for job status to be output before continuing on with job download if (parms.waitForOutput) { await MonitorJobs.waitForJobOutputStatus(session, { jobname: parms.jobname, jobid: parms.jobid } as IJob); } - + const job = parms.jobFile; let debugMessage = `Downloading spool file ${job.ddname} for job ${job.jobname}(${job.jobid})`; let file: string; @@ -127,27 +127,45 @@ export class DownloadJobs { IO.createFileSync(file); debugMessage += ` to ${file}`; } - + this.log.debug(debugMessage); - + let parameters: string = "/" + encodeURIComponent(job.jobname) + "/" + encodeURIComponent(job.jobid) + JobsConstants.RESOURCE_SPOOL_FILES + "/" + encodeURIComponent(job.id) + JobsConstants.RESOURCE_SPOOL_CONTENT; - + if (parms.binary) { parameters += "?mode=binary"; } else if (parms.record) { parameters += "?mode=record"; } - + if (!parms.binary && !parms.record && parms.encoding?.trim()) { parameters += "?fileEncoding=" + parms.encoding; } - + + // Handle record range + if (parms.recordRange) { + const recordRangeMatch = parms.recordRange.match(/^(\d+)-(\d+)$/); // Match multi-digit numbers + if (recordRangeMatch) { + const start = parseInt(recordRangeMatch[1], 10); + const end = parseInt(recordRangeMatch[2], 10); + + if (start >= 0 && end >= start) { // Ensure valid range + parameters += (parameters.includes("?") ? "&" : "?") + `start=${start}&end=${end}`; + } else { + throw new Error(`Invalid record range specified: ${parms.recordRange}. Ensure the format is x-y with x <= y.`); + } + } else { + throw new Error(`Invalid record range format: ${parms.recordRange}. Expected format is x-y.`); + } + } + const writeStream = parms.stream ?? IO.createWriteStream(file); const normalizeResponseNewLines = !(parms.binary || parms.record); await ZosmfRestClient.getStreamed(session, JobsConstants.RESOURCE + parameters, [Headers.TEXT_PLAIN_UTF8], writeStream, normalizeResponseNewLines); } + /** * Get the file where a specified spool file (IJobFile) would be downloaded to diff --git a/packages/zosjobs/src/doc/input/IDownloadAllSpoolContentParms.ts b/packages/zosjobs/src/doc/input/IDownloadAllSpoolContentParms.ts index 13ce91987c..f0968e8af5 100644 --- a/packages/zosjobs/src/doc/input/IDownloadAllSpoolContentParms.ts +++ b/packages/zosjobs/src/doc/input/IDownloadAllSpoolContentParms.ts @@ -78,6 +78,14 @@ export interface IDownloadAllSpoolContentParms { */ encoding?: string; + /** + * Optional record range + * e.g. 0-100 + * @type {string} + * @memberof IDownloadAllSpoolContentParms + */ + recordRange?: string; + /** * Wait for the job to reach output status */