Skip to content

Instantly share code, notes, and snippets.

@nexdrew
Created July 13, 2018 12:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nexdrew/6c3598ea534aa6788ff1c11d4e3eade5 to your computer and use it in GitHub Desktop.
Save nexdrew/6c3598ea534aa6788ff1c11d4e3eade5 to your computer and use it in GitHub Desktop.
Example of nested commands for sywac/sywac#21
module.exports = {
flags: 'schedule-delivery',
desc: 'Launch scheduled mail delivery, max of three days in advance.',
setup: sywac => {
sywac
.showHelpByDefault(false) // disable this here since schedule-delivery is a runnable command
.option('-l, --email-list <email-list>', {
type: 'string',
desc: 'The mailgun email list you would like to use.',
strinct: true, // this is misspelled, and strict does not apply to strings; use `required: true` to require this option
defaultValue: 'something'
})
.option('-b, --email-body-file <email-body-file>', {
type: 'file',
desc: 'File containing the html for the body of the email. Relative to the emailBodyFileDir directory you set in the configuration.',
strinct: true // this is misspelled, and strict does not apply to files; use `mustExist: true` to guarantee the path given exists on the local filesystem and is a file.
})
.option('-f, --from <sent-from-for-replies>', {
type: 'string',
desc: 'The value that the receiver will see that your emails appear to be sent from, in the form of "Kim <services@binarymist.net>"',
strict: true // strict does not apply to strings; use `required: true` to require this option
})
.option('-s, --subject <subject-for-email>', {
type: 'string',
desc: 'The subject for the email',
strict: true // strict does not apply to strings; use `required: true` to require this option
})
.option('-t, --schedule-time <time-to-schedule-email-send-for>', {
type: 'mailgunDateTimeFormat', // since this custom type was registered at the top-level, it is safe to use everywhere in this app and does not need to be re-registered anywhere
desc: 'The time that all emails will be sent (in RFC 2822 time).',
strict: true // this will guarantee the validateValue method for the custom type is called
})
.option('-tm, --test-mode', { // -tm is not valid, flags with more than one character must use double-hyphen on the command line; otherwise -tm is parsed as -t and -m
type: 'boolean',
desc: 'Whether or not to send in test mode "o:testmode".',
strict: true, // strict does not apply to booleans
defaultValue: false
})
.command('list', { // could potentially replace the -o flag with a positional arg by using 'list [order=asc]' here, in which case you could remove the setup handler entirely
desc: 'List members in order based on latest or oldest mailgunMateScheduledSends datetimes.',
paramsDesc: 'The order to list the items in: "des" for descending, "asc" for ascending.',
setup: sywac => { // shadowing the existing sywac variable can lead to bugs and is thus discouraged
sywac
// no need to redefine this option since it is inherited from the parent command's configuration
// .option('-l, --email-list <email-list>', {
// type: 'string',
// desc: 'The mailgun email list you would like to use.',
// strinct: true,
// defaultValue: 'something'
// })
.option('-o, --order [des|asc(default)]', {
type: 'string', // consider using 'enum' here and define a `choices` property as an array with all valid values
desc: 'The order you would like the items displayed in.',
defaultValue: 'asc'
})
},
run: async (parsedArgv, context) => {
// if this is called, then the run handler for schedule-delivery will not be called
// this artificial delay just demonstrates that it will be awaited
await new Promise(resolve => {
setTimeout(() => {
console.log('list:', parsedArgv)
resolve()
}, 500)
})
}
})
},
run: (parsedArgv, context) => {
// this will not be called if any subcommands are run
console.log('schedule-delivery:', parsedArgv)
}
}
const moment = require('moment')
const Type = require('sywac/types/type')
const internals = {}
class mailgunDateTimeFormat extends Type {
get datatype () {
return 'mailgunDateTimeFormat'
}
// this is superfluous
// setValue (context, value) {
// context.assignValue(this.id, value)
// }
validateValue (value) {
// https://momentjs.com/docs/#/parsing/string-format/
const scheduledTime = moment(value, 'YYYY-MM-DD')
internals.scheduleDateIsValid = scheduledTime.isValid()
if (!internals.scheduleDateIsValid) return false
internals.scheduleDateIsBeforeDeadline = scheduledTime.isBefore(moment().add(3, 'days'))
if (!internals.scheduleDateIsBeforeDeadline) return false
internals.scheduleDateIsAfterNow = scheduledTime.isAfter(moment())
if (!internals.scheduleDateIsAfterNow) return false
internals.scheduledSendDateTime = value
return true
}
buildInvalidMessage (context, msgAndArgs) {
super.buildInvalidMessage(context, msgAndArgs)
let customMessage
if (!internals.scheduleDateIsValid) customMessage = `The datetime you entered was not valid according to mailgun\'s rules. RFC 2822, formatted as mailgun requires "YYYY-MM-DD", See (https://documentation.mailgun.com/en/latest/user_manual.html#scheduling-delivery) for more information.`
else if (!internals.scheduleDateIsBeforeDeadline) customMessage = `The datetime you entered was outside of the mailgun 3 days schedule linmmit.`
else if (!internals.scheduleDateIsAfterNow) customMessage = `The datetime you entered was in the past.`
msgAndArgs.msg += ` Please specify a valid schedule datetime. ${customMessage} Please try again.`
}
}
require('sywac')
.registerFactory('mailgunDateTimeFormat', opts => new mailgunDateTimeFormat(opts))
.command(require('./issue21-schedule-delivery'))
.help('-h, --help')
.showHelpByDefault() // this is fine here and can be disabled at lower levels (commands) if necessary
.parseAndExit() // this initiates parsing of process.argv and will exit the process if any parsing/validation errors occur
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment