import { ZosmfHeaders , ZosmfRestClient } from "@zowe/core-for-zowe-sdk" ; import { AbstractSession , Headers , IHeaderContent , ImperativeError , ImperativeExpect , IO , Logger , TaskProgress } from "@zowe/imperative" ; import { IJob , ISubmitJclNotifyParm , ISubmitJclParms , ISubmitJobNotifyParm , ISubmitJobParms , JOB_STATUS } from "./" ; import { JobsConstants } from "./JobsConstants" ; import { ZosJobsMessages } from "./JobsMessages" ; import { ISubmitParms } from "./doc/input/ISubmitParms" ; import { GetJobs } from "./GetJobs" ; import { IJobFile } from "./doc/response/IJobFile" ; import { IDownloadAllSpoolContentParms } from "./doc/input/IDownloadAllSpoolContentParms" ; import { DownloadJobs } from "./DownloadJobs" ; import { MonitorJobs } from "./MonitorJobs" ; import { ISpoolFile } from "./doc/response/ISpoolFile" ; export class export class SubmitJobs { private static readonly singleQuote = "'" ; static param { AbstractSession } session param { string } jobDataSet returns { Promise < IJob > } memberof public static submitJob ( session , jobDataSet ) { this . log . trace ( "submitJob called with data set %s" , jobDataSet ) ; return SubmitJobs . submitJobCommon ( session , { jobDataSet } ) ; } static param { AbstractSession } session param { ISubmitJobParms } parms returns { Promise < IJob > } memberof public static async submitJobCommon ( session , parms ) { this . log . trace ( "submitJobCommon called with parms %s" , JSON . stringify ( parms ) ) ; ImperativeExpect . keysToBeDefined ( parms , [ "jobDataSet" ] , "You must provide a data set containing JCL to submit in parms.jobDataSet" ) ; this . log . debug ( "Submitting a job located in the data set '%s'" , parms . jobDataSet ) ; const fullyQualifiedDataset = "//'" + parms . jobDataSet + "'" ; const jobObj = { file : fullyQualifiedDataset } ; let extraHeaders = [ ] ; if ( parms . jclSymbols ) { extraHeaders = this . getSubstitutionHeaders ( parms . jclSymbols ) ; } return ZosmfRestClient . putExpectJSON < IJob > ( session , JobsConstants . RESOURCE , [ Headers . APPLICATION_JSON , ... extraHeaders ] , jobObj ) ; } static param { AbstractSession } session param { string } jcl param { string } internalReaderRecfm param { string } internalReaderLrecl returns { Promise < IJob > } memberof public static submitJcl ( session , jcl , internalReaderRecfm ? , internalReaderLrecl ? ) { this . log . trace ( "submitJcl called with jcl of length %d. internalReaderRecfm %s internalReaderLrecl %s" , jcl == null ? "no jcl!" : jcl . length , internalReaderRecfm , internalReaderLrecl ) ; return SubmitJobs . submitJclCommon ( session , { jcl , internalReaderRecfm , internalReaderLrecl } ) ; } public static async submitJclString ( session , jcl , parms ) { ImperativeExpect . toNotBeNullOrUndefined ( jcl , ZosJobsMessages . missingJcl . message ) ; ImperativeExpect . toNotBeEqual ( jcl , "" , ZosJobsMessages . missingJcl . message ) ; const responseJobInfo = await SubmitJobs . submitJclCommon ( session , { jcl , jclSymbols : parms . jclSymbols } ) ; const response = this . checkSubmitOptions ( session , parms , responseJobInfo ) ; return response ; } static param { AbstractSession } session param { ISubmitJclParms } parms returns { Promise < IJob > } memberof public static async submitJclCommon ( session , parms ) { this . log . trace ( "submitJclCommon called with parms %s" , JSON . stringify ( parms ) ) ; ImperativeExpect . keysToBeDefined ( parms , [ "jcl" ] , "You must provide a JCL string to submit. The 'jcl' field of the provided parameters was undefined. " ) ; this . log . debug ( "Submitting JCL of length %d" , parms . jcl . length ) ; const headers = [ Headers . TEXT_PLAIN_UTF8 , ZosmfHeaders . X_IBM_INTRDR_MODE_TEXT ] ; if ( parms . internalReaderLrecl ) { this . log . debug ( "Custom internal reader logical record length (internalReaderLrecl) '%s' specified " , parms . internalReaderLrecl ) ; headers . push ( { "X-IBM-Intrdr-Lrecl" : parms . internalReaderLrecl } ) ; } else { headers . push ( ZosmfHeaders . X_IBM_INTRDR_LRECL_80 ) ; } if ( parms . internalReaderRecfm ) { this . log . debug ( "Custom internal reader record format (internalReaderRecfm) '%s' specified " , parms . internalReaderRecfm ) ; headers . push ( { "X-IBM-Intrdr-Recfm" : parms . internalReaderRecfm } ) ; } else { headers . push ( ZosmfHeaders . X_IBM_INTRDR_RECFM_F ) ; } if ( parms . jclSymbols ) { const extraHeaders = this . getSubstitutionHeaders ( parms . jclSymbols ) ; headers . push ( ... extraHeaders ) ; } return ZosmfRestClient . putExpectJSON < IJob > ( session , JobsConstants . RESOURCE , headers , parms . jcl ) ; } static param { AbstractSession } session param { string } jcl param { string } internalReaderRecfm param { string } internalReaderLrecl returns { Promise < IJob > } memberof public static async submitJclNotify ( session , jcl , internalReaderRecfm ? , internalReaderLrecl ? ) { this . log . trace ( "submitJclNotiy called with jcl of length %s, internalReaderRecfm %s, internalReaderLrecl %s" , jcl == null ? "no jcl!" : jcl . length , internalReaderRecfm , internalReaderLrecl ) ; return SubmitJobs . submitJclNotifyCommon ( session , { jcl , internalReaderRecfm , internalReaderLrecl } ) ; } static param { AbstractSession } session param { ISubmitJclNotifyParm } parms returns { Promise < IJob > } memberof public static async submitJclNotifyCommon ( session , parms ) { this . log . trace ( "submitJclNotifyCommon called with parms %s" , JSON . stringify ( parms ) ) ; const job = await SubmitJobs . submitJclCommon ( session , parms ) ; return SubmitJobs . submitNotifyCommon ( session , job , parms . status , parms . watchDelay ) ; } static param { AbstractSession } session param { string } jobDataSet returns { Promise < IJob > } memberof public static async submitJobNotify ( session , jobDataSet ) { this . log . trace ( "submitJobNotify called with data set %s" , jobDataSet ) ; return SubmitJobs . submitJobNotifyCommon ( session , { jobDataSet } ) ; } static param { AbstractSession } session param { ISubmitJobNotifyParm } parms returns { Promise < IJob > } memberof public static async submitJobNotifyCommon ( session , parms ) { this . log . trace ( "submitJobNotifyCommon called with parms %s" , JSON . stringify ( parms ) ) ; ImperativeExpect . keysToBeDefined ( parms , [ "jobDataSet" ] , "You must provide a data set containing JCL to submit in parms.jobDataSet." ) ; const job = await SubmitJobs . submitJobCommon ( session , parms ) ; return SubmitJobs . submitNotifyCommon ( session , job , parms . status , parms . watchDelay ) ; } public static param { AbstractSession } session param { ISubmitParms } parms param { IJob } responseJobInfo returns { Promise < IJob | ISpoolFile [ ] > } memberof public static async checkSubmitOptions ( session , parms , responseJobInfo ) { if ( parms . waitForActive ) { const activeJob = await MonitorJobs . waitForStatusCommon ( session , { jobid : responseJobInfo . jobid , jobname : responseJobInfo . jobname , status : "ACTIVE" } ) ; return activeJob ; } if ( parms . viewAllSpoolContent || parms . waitForOutput ) { if ( parms . task != null ) { parms . task . statusMessage = "Waiting for " + responseJobInfo . jobid + " to enter OUTPUT" ; parms . task . percentComplete = TaskProgress . THIRTY_PERCENT ; } const job = await MonitorJobs . waitForJobOutputStatus ( session , responseJobInfo ) ; if ( ! parms . viewAllSpoolContent ) { return job ; } if ( parms . task != null ) { parms . task . statusMessage = "Retrieving spool content for " + job . jobid + ( job . retcode == null ? "" : ", " + job . retcode ) ; parms . task . percentComplete = TaskProgress . SEVENTY_PERCENT ; } const spoolFiles = await GetJobs . getSpoolFilesForJob ( session , job ) ; const arrOfSpoolFile = [ ] ; for ( const file of spoolFiles ) { const spoolContent = await GetJobs . getSpoolContent ( session , file ) ; arrOfSpoolFile . push ( { id : file . id , ddName : file . ddname , stepName : file . stepname , procName : file . procstep , data : spoolContent } ) ; } return arrOfSpoolFile ; } else if ( parms . directory ) { if ( parms . task != null ) { parms . task . statusMessage = "Waiting for " + responseJobInfo . jobid + " to enter OUTPUT" ; parms . task . percentComplete = TaskProgress . THIRTY_PERCENT ; } const job = await MonitorJobs . waitForJobOutputStatus ( session , responseJobInfo ) ; const downloadParms = { jobid : job . jobid , jobname : job . jobname , outDir : parms . directory } ; if ( parms . extension ) { downloadParms . extension = IO . normalizeExtension ( parms . extension ) ; } if ( parms . task != null ) { parms . task . statusMessage = "Downloading spool content for " + job . jobid + ( job . retcode == null ? "" : ", " + job . retcode ) ; parms . task . percentComplete = TaskProgress . SEVENTY_PERCENT ; } ( await DownloadJobs . downloadAllSpoolContentCommon ( session , downloadParms ) ) ; return job ; } return responseJobInfo ; } private static param { AbstractSession } session param { IJob } job param { JOB_STATUS } status param { number } watchDelay returns { Promise < IJob > } memberof private static async submitNotifyCommon ( session , job , status , watchDelay ) { this . log . trace ( "submitNotiyCommon called with job %s, status %s, watchDelay %s" , JSON . stringify ( job ) , status , watchDelay ) ; ImperativeExpect . keysToBeDefined ( job , [ "jobname" , "jobid" ] , "The job object you provide must contain both 'jobname' and 'jobid'." ) ; this . log . debug ( "Waiting to be notified of job completion from Monitor Jobs API for job %s (%s)" , job . jobname , job . jobid ) ; return MonitorJobs . waitForStatusCommon ( session , { jobname : job . jobname , jobid : job . jobid , status , watchDelay } ) ; } param { string } symbols returns { IHeaderContent [ ] } memberof private static getSubstitutionHeaders ( symbols ) { const headers = [ ] ; const blank = " " ; const equals = "=" ; const maxSymLen = 8 ; let symStartInx = 0 ; moreSymLoop : while ( symStartInx < symbols . length ) { while ( symbols [ symStartInx ] === blank ) { if ( ++ symStartInx >= symbols . length ) { break moreSymLoop ; } } let symName = null ; let symEndInx ; for ( symEndInx = symStartInx ; symEndInx < symbols . length ; symEndInx ++ ) { if ( symbols [ symEndInx ] === equals ) { symName = symbols . substring ( symStartInx , symEndInx ) ; break ; } } if ( symName == null ) { throw new ImperativeError ( { msg : `No equals '${ equals }' character was specified to define a symbol name.` } ) ; } if ( symName . length === 0 ) { throw new ImperativeError ( { msg : `No symbol name specified before the equals '${ equals }' character.` } ) ; } if ( symName . length > maxSymLen ) { throw new ImperativeError ( { msg : `The symbol name '${ symName }' is too long. It must 1 to ${ maxSymLen } characters.` } ) ; } let valStartInx = ++ symEndInx ; if ( valStartInx >= symbols . length ) { throw new ImperativeError ( { msg : `No value specified for symbol name '${ symName }'.` } ) ; } let valEndChar = blank ; if ( symbols [ valStartInx ] === SubmitJobs . singleQuote ) { if ( ++ valStartInx >= symbols . length ) { throw new ImperativeError ( { msg : "The value for symbol '" + symName + "' is missing a terminating quote (" + SubmitJobs . singleQuote + ")." } ) ; } if ( symbols [ valStartInx ] === SubmitJobs . singleQuote ) { -- valStartInx ; } else { valEndChar = SubmitJobs . singleQuote ; } } let valEndInx ; for ( valEndInx = valStartInx ; valEndInx < symbols . length ; valEndInx ++ ) { if ( symbols [ valEndInx ] === valEndChar ) { if ( valEndChar === SubmitJobs . singleQuote ) { if ( valEndInx + 1 < symbols . length && symbols [ valEndInx + 1 ] === SubmitJobs . singleQuote ) { valEndInx ++ ; continue ; } } const header = SubmitJobs . formSubstitutionHeader ( symName , symbols , valStartInx , valEndInx ) ; headers . push ( header ) ; break ; } } if ( valEndInx >= symbols . length ) { if ( valEndChar === SubmitJobs . singleQuote ) { throw new ImperativeError ( { msg : "The value for symbol '" + symName + "' is missing a terminating quote (" + SubmitJobs . singleQuote + ")." } ) ; } else { const header = SubmitJobs . formSubstitutionHeader ( symName , symbols , valStartInx , symbols . length ) ; headers . push ( header ) ; } } symStartInx = ++ valEndInx ; } let logMsg = "Formed the following JCL symbol headers:\n" ; headers . forEach ( ( nextHeader ) => { for ( const key in nextHeader ) { if ( Object . prototype . hasOwnProperty . call ( nextHeader , key ) ) { logMsg += "    " + key + " = " + nextHeader [ key ] + "\n" ; } } } ) ; this . log . debug ( logMsg ) ; return headers ; } param { string } symName param { string } symDefs param { string } valStartInx param { string } valEndInx returns { IHeaderContent } memberof private static formSubstitutionHeader ( symName , symDefs , valStartInx , valEndInx ) { const twoQuoteRegex = new RegExp ( SubmitJobs . singleQuote + SubmitJobs . singleQuote , "g" ) ; let symVal = symDefs . substring ( valStartInx , valEndInx ) ; symVal = symVal . replace ( twoQuoteRegex , SubmitJobs . singleQuote ) ; const key = ZosmfHeaders . X_IBM_JCL_SYMBOL_PARTIAL + symName . toUpperCase ( ) ; return { [ key ] : symVal } ; } returns { Logger } private static get log ( ) : Logger { return Logger . getAppLogger ( ) ; } }