Skip to content

Commit

Permalink
feat(job-command): added bleached verbose mode
Browse files Browse the repository at this point in the history
  • Loading branch information
YuryShkoda committed Aug 14, 2023
1 parent 643fe8e commit d6af885
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 168 deletions.
2 changes: 1 addition & 1 deletion src/commands/help/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ export async function printHelpText() {
`[2spaces]NOTE: Providing output flag (--output or -o) is optional. If present, CLI will immediately print out the response JSON. If value is provided, it will be treated as file path to save the response JSON.`,
`[2spaces]NOTE: Providing log flag (--log or -l) is optional. If present, CLI will fetch and save job log to local file.`,
`[2spaces]NOTE: Providing ignore warnings (--ignoreWarnings or -i) flag is optional. If present, CLI will return status '0', when the job state is warning.`,
`[2spaces]NOTE: Providing verbose (--verbose or -v) flag is optional. If present, CLI will log summary of every HTTP response.`
`[2spaces]NOTE: Providing verbose (--verbose or -v) flag is optional. If present, CLI will log summary of every HTTP response. If set to 'bleached', , CLI will log summary of every HTTP response without extra colors.`
]
},
{
Expand Down
21 changes: 7 additions & 14 deletions src/commands/job/internal/execute/sasjs.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,25 @@
import { SASjsApiClient, SasjsRequestClient } from '@sasjs/adapter/node'
import { Target } from '@sasjs/utils'
import { getAuthConfig, isSasJsServerInServerMode } from '../../../../utils'
import { AuthConfig, Target } from '@sasjs/utils'
import { saveLog, saveOutput } from '../utils'
import SASjs from '@sasjs/adapter/node'

/**
* Triggers existing job for execution on SASJS server.
* @param {object} sasjs - configuration object of SAS adapter.
* @param target - SASJS server configuration.
* @param jobPath - location of the job.
* @param logFile - flag indicating if CLI should fetch and save log to provided file path. If filepath wasn't provided, {job}.log file will be created in current folder.
* @param output - flag indicating if CLI should save output to provided file path.
* @returns - promise that resolves into an object with log and output.
*/
export async function executeJobSasjs(
sasjs: SASjs,
target: Target,
jobPath: string,
logFile?: string,
output?: string
output?: string,
authConfig?: AuthConfig
) {
// get authentication configuration if SASJS server is in server mode.
const authConfig = (await isSasJsServerInServerMode(target))
? await getAuthConfig(target)
: undefined

const sasjsApiClient = new SASjsApiClient(
new SasjsRequestClient(target.serverUrl, target.httpsAgentOptions)
)

const response = await sasjsApiClient.executeJob(
const response = await sasjs.executeJob(
{
_program: jobPath
},
Expand Down
15 changes: 7 additions & 8 deletions src/commands/job/internal/execute/viya.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import { saveLog } from '../utils'
* @param {boolean} ignoreWarnings - flag indicating if CLI should return status '0', when the job state is warning.
* @param {string | undefined} source - an optional path to a JSON file containing macro variables.
* @param {boolean} streamLog - a flag indicating if the logs should be streamed to the supplied log path during job execution. This is useful for getting feedback on long running jobs.
* @param {boolean} verbose - enables verbose mode that logs summary of every HTTP response.
*/
export async function executeJobViya(
sasjs: SASjs,
Expand All @@ -48,13 +47,14 @@ export async function executeJobViya(
statusFile: string | undefined,
ignoreWarnings: boolean,
source: string | undefined,
streamLog: boolean,
verbose: boolean
streamLog: boolean
) {
// job status poll options
// job state poll options
// maxPollCount and pollInterval are set to 0 to use default polling options
// configured in @sasjs/adapter/src/api/viya/pollJobState.ts
const pollOptions: PollOptions = {
maxPollCount: 24 * 60 * 60,
pollInterval: 1000,
maxPollCount: 0,
pollInterval: 0,
streamLog,
logFolderPath: logFile
}
Expand Down Expand Up @@ -108,8 +108,7 @@ export async function executeJobViya(
waitForJob || !!logFile,
pollOptions,
true,
macroVars?.macroVars,
verbose || undefined
macroVars?.macroVars
)
.catch(async (err) => {
// handle error
Expand Down
129 changes: 85 additions & 44 deletions src/commands/job/jobCommand.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { ServerType, Target, decodeFromBase64 } from '@sasjs/utils'
import {
AuthConfig,
AuthConfigSas9,
ServerType,
Target,
decodeFromBase64
} from '@sasjs/utils'
import path from 'path'
import { CommandExample, ReturnCode } from '../../types/command'
import { TargetCommand } from '../../types/command/targetCommand'
import { getSASjsAndAuthConfig } from '../../utils'
import { getSASjs, getSASjsAndAuthConfig } from '../../utils'
import { getLogFilePath } from '../../utils/getLogFilePath'
import { prefixAppLoc } from '../../utils/prefixAppLoc'
import {
executeJobViya,
executeJobSasjs,
executeJobSas9
} from './internal/execute'
import { VerboseMode } from '@sasjs/adapter/node'
import SASjs from '@sasjs/adapter/node'

enum JobSubCommand {
Execute = 'execute',
Expand Down Expand Up @@ -78,16 +86,18 @@ const executeParseOptions = {
'Flag indicating whether the logs should be streamed to a local file as the job executes.'
},
verbose: {
type: 'boolean',
type: 'string',
alias: 'v',
default: false,
description:
'If present, CLI will return status 0, 1 or 2 together with HTTP response summaries.'
description: `If present, CLI will return status 0, 1 or 2 together with HTTP response summaries. If set to 'bleached', HTTP response summaries will be logged without extra colors.`
}
}

export class JobCommand extends TargetCommand {
private jobSubCommands: any[]
private verbose: VerboseMode = false
private sasjs: SASjs = new SASjs()
private authConfig?: AuthConfig | AuthConfigSas9

constructor(args: string[]) {
const jobSubCommands: string[] = (<any>Object).values(JobSubCommand)
Expand All @@ -105,21 +115,53 @@ export class JobCommand extends TargetCommand {
})

this.jobSubCommands = jobSubCommands

this.verbose = getVerbose(args, this.parsed.verbose)
}

/**
* Method responsible for command execution.
* @returns - promise that resolves into return code.
*/
public async execute() {
const { target } = await this.getTargetInfo()

// returnStatusOnly flag is deprecated and is left to display warning if used
const returnStatusOnly = !!this.parsed.returnStatusOnly
if (returnStatusOnly) {
process.logger.warn('--returnStatusOnly (-r) flag is deprecated.')
}

const { target } = await this.getTargetInfo()

const sasjsAndAuthConfig = await getSASjsAndAuthConfig(target).catch(
(err) => {
// handle getting instance of @sasjs/adapter and auth config failure
process.logger?.error(
'Unable to execute job. Error fetching auth config: ',
err
)

return {
sasjs: getSASjs(target),
authConfig: undefined,
authConfigSas9: undefined
}
}
)

const sasjs = sasjsAndAuthConfig.sasjs
const authConfig = sasjsAndAuthConfig.authConfig
const authConfigSas9 = (
sasjsAndAuthConfig as { authConfigSas9: AuthConfigSas9 }
).authConfigSas9

if (!sasjs || (!authConfig && !authConfigSas9)) {
return ReturnCode.InternalError
}

this.sasjs = sasjs
this.sasjs.setVerboseMode(this.verbose)
this.authConfig = authConfig || authConfigSas9

// use execution function based on server type
switch (target.serverType) {
case ServerType.SasViya:
Expand Down Expand Up @@ -163,7 +205,14 @@ export class JobCommand extends TargetCommand {
? (this.parsed.output as string)
: undefined

const returnCode = await executeJobSasjs(target, jobPath, log, output)
const returnCode = await executeJobSasjs(
this.sasjs,
target,
jobPath,
log,
output,
this.authConfig as AuthConfig
)
.then(() => ReturnCode.Success)
.catch((err) => {
// handle job execution failure
Expand All @@ -190,25 +239,13 @@ export class JobCommand extends TargetCommand {
? (this.parsed.output as string)
: undefined

const { sasjs, authConfigSas9 } = await getSASjsAndAuthConfig(target).catch(
(err) => {
// handle getting instance of @sasjs/adapter and auth config failure
process.logger?.error(
'Unable to execute job. Error fetching auth config: ',
err
)

return { sasjs: undefined, authConfigSas9: undefined }
}
)

if (!authConfigSas9 || !sasjs) return ReturnCode.InternalError
this.authConfig = this.authConfig as AuthConfigSas9

const userName = authConfigSas9.userName
const password = decodeFromBase64(authConfigSas9.password)
const userName = this.authConfig.userName
const password = decodeFromBase64(this.authConfig.password)

const returnCode = await executeJobSas9(
sasjs,
this.sasjs,
{ userName, password },
jobPath,
log,
Expand Down Expand Up @@ -238,7 +275,6 @@ export class JobCommand extends TargetCommand {
const log = getLogFilePath(this.parsed.log, jobPath)
const ignoreWarnings = !!this.parsed.ignoreWarnings
const streamLog = !!this.parsed.streamLog
const verbose = !!this.parsed.verbose
const source = this.parsed.source as string
let wait = (this.parsed.wait as boolean) || !!log

Expand All @@ -248,25 +284,11 @@ export class JobCommand extends TargetCommand {
? true
: false

if (verbose && !wait) wait = true

const { sasjs, authConfig } = await getSASjsAndAuthConfig(target).catch(
(err) => {
// handle getting instance of @sasjs/adapter and auth config failure
process.logger?.error(
'Unable to execute job. Error fetching auth config: ',
err
)

return { sasjs: undefined, authConfig: undefined }
}
)

if (!authConfig || !sasjs) return ReturnCode.InternalError
if (!!this.verbose && !wait) wait = true

const returnCode = await executeJobViya(
sasjs,
authConfig,
this.sasjs,
this.authConfig as AuthConfig,
jobPath,
target,
wait,
Expand All @@ -275,8 +297,7 @@ export class JobCommand extends TargetCommand {
statusFile,
ignoreWarnings,
source,
streamLog,
verbose
streamLog
)
.then(() => ReturnCode.Success)
.catch((err) => {
Expand Down Expand Up @@ -306,3 +327,23 @@ const getStatusFilePath = (statusFileArg: unknown) => {

return undefined
}

/**
* Determines verbose mode based on command arguments.
* @param args - command arguments.
* @param verboseArg - value of the verbose argument.
* @returns - verbose mode.
*/
const getVerbose = (args: string[], verboseArg: unknown) => {
const verboseArgPresent = args.includes('-v') || args.includes('--verbose')

if (verboseArgPresent) {
// if verboseArg, use the string as verbose mode(any strings not equal to
// 'bleached' will be ignored)
if (typeof verboseArg === 'string') return verboseArg as VerboseMode

return true
}

return false
}
Loading

0 comments on commit d6af885

Please sign in to comment.