Base for Domapic Node.js packages.
- Introduction
- Quick Start
- Options
- Server
- Adding API resources
- Client
- Traces
- Errors
- Storage
- Utils
- Info
- Authentication
- CLI
- Test suites
This package is used as a base for domapic-controller and domapic-service packages.
Maybe you´ll better want to read documentation about that pieces and learn how use them directly, because this is an internal dependency. Anyway, if you think it can be useful for you separately...
It provides:
- Server with an extensible API:
- Optional ssl protocol, just provide ssl certificate and key paths.
- OAUTH. Jwt and/or apiKey customizable authentication to api resources of choice.
- Authentication can be disabled in a range of IPs of choice, or completely.
- Customizable authorization level for each API resource.
- Add API resources using OpenApi 3.0 definitions.
- Http OPTIONS method created for each API resource, auto describing it.
- Automatic api parameters and body validation using the openapi schemas.
- API operations as Promises.
- Automatic error handling mapped to HTTP errors.
- Openapi.json auto generated and served.
- Built-in Swagger web interface.
- Allows serving statics under paths of your choice.
- Client to other Domapic services:
- Automatic authentication if API resource requires it.
- Requests as Promises.
- Configuration
- Built-in service command line options. (port, host, etc...).
- Fully extensible with your own options.
- Storable. Next executions will remember options if
--saveConfig
is specified in one execution.
- Traces
- Six different log levels.
- Ansi colored, at your choice.
- Daily file, last ten stored.
- Errors
- Custom errors for easy error handling.
- Mapping to HTTP errors.
- Storage
- Javascript objects to JSON at file system and viceversa.
- File system access scoped to unique service instance folder.
- CLI. Easy implementable in your own package, it provides:
- If the service is started using the CLI, the process will be executed in background, and managed using PM2.
- Multi-instanciable. Start many services instances providing different names.
- CLI commands to stop or display logs.
- Extensible with your own commands.
// server.js file
const path = require('path')
const domapic = require('domapic-base')
domapic.Service({
packagePath: path.resolve(__dirname),
type: 'module'
}).then(service => {
return service.server.start()
})
- The
packagePath
option must be the path where your package.json file is, in order to automatically create the/api/about
api resource that can provide useful information about the package to other domapic services. - The
type
option will be exposed in theapi/about
api resource directly, in order to inform about the service type.
# Start server
node ./server.js
Browse to http://localhost:3000 to open Swagger interface and inspect API.
# Display help with detailed information about all options
node ./server.js --help
option | type | description | default |
---|---|---|---|
name |
String | Service instance name. If not received, the service name will be the package name | - |
port |
Number | Http port used | 3000 |
hostName |
String | Hostname for the server | - |
sslCert |
String | Path to an ssl certificate | - |
sslKey |
String | Path to an ssl key | - |
authDisabled |
Array | Array of IPs or CIDR IP ranges with authentication disabled | ['127.0.0.1', '::1/128'] |
auth |
Boolean | If false, the authentication will be disabled for all api resources and origins | true |
color |
Boolean | Use ANSI colors in traces | true |
logLevel |
String | Tracing level. Choices are 'log', 'trace', 'debug', 'info', 'warn' and 'error' | info |
path |
String | Path to be used as home path, instead of user´s default (.domapic folder will be created inside) | ~ |
saveConfig |
Boolean | Save current options for next execution (except name and path ) |
false |
rejectUntrusted |
Boolean | Reject untrusted ssl certificates when using built-in client to make requests to another services | false |
Example of setting options from command line:
node ./server.js --name=fooName --authDisabled=192.168.1.1 172.0.0.1 --logLevel=debug --color=false --auth=true
Options defined from command line are available in the config
object of the service.
service.config.get([key])
domapic.Service({
packagePath: path.resolve(__dirname)
}).then(service => {
return service.config.get()
}).then(configuration => {
console.log(configuration)
})
It is not recommended, but, it if has sense, config
properties can be modified programmatically. If you want to store some data, you´ll better want to read about the storage feature.
service.config.set(key [, value])
domapic.Service({
packagePath: path.resolve(__dirname)
}).then(service => {
return service.config.set('fooKey', 'fooValue')
.then(value => {
console.log(value)
// fooValue
})
})
You can add your own custom configuration options. They will be seteable from command line execution, displayed in help and validated as the rest of options. Use customConfig
parameter to define them.
Yargs is used as underlayer to manage options, so you can read its documentation for more details about how to define them:
// Usage of customConfig parameter
domapic.Service({
packagePath: path.resolve(__dirname),
customConfig: {
fooOption: {
type: 'boolean',
alias: ['foo-option'],
describe: 'Testing a custom configuration option',
default: true
}
}
}).then(service => {
return service.config.get()
}).then(configuration => {
console.log(configuration)
})
node ./server.js --fooOption=false
Custom options defined for a service should be defined in CLI implementation too, to make them available from command line interface. Read the CLI custom options and commands chapter for further info.
Default options values (or saved values if the --saveConfig
option is used) are saved into a file at ~/.domapic/<serviceName>/config/service.json
. This file can be edited manually, and the new values will be applied next time the service is started.
The service.server
object has methods:
start
- Starts the server. Returns a promise, resolved when the server is running. Once the server is started, it is not possible to add more open api definitions, operations, or authentication implementations.init
- Only initialize the server, adding all internal middlewares and routers, and returns the server instance. This allows you to add more custom middlewares, sockets, etc. This method should be called just before calling to the "start" method.extendOpenApi
- Add open api definitions to the server. Read the Adding API resources chapter for further info.addOperations
- Add operations related to api paths. Read Adding API resources).addAuthentication
- Add authentication implementations. Read Authentication.addAuthorization
- Add authorization roles. Read Authentication.addMiddleware
- Add custom middlewares to api. Read Custom middlewares.addStatic
- Serve statics. First argument defines server path, second argument defines fileSystem path.addStatic("/assets", path.resolve(__dirname, "assets"))
. Statics added with this method will be served using gzip compression.
You can add your own API resources. They will be automatically added to the openapi.json definition, and will be available under the /api
path of the server.
// server.js file
const path = require('path')
const domapic = require('domapic-base')
const myOpenApi = require('./api/myOpenApi.json')
domapic.Service({
packagePath: path.resolve(__dirname)
}).then(service => {
return Promise.all([
service.server.extendOpenApi(myOpenApi),
service.server.addOperations({
myApiOperation: {
handler: (params, body, res) => {
return Promise.resolve({
hello: 'world'
})
}
}
})
]).then(() => {
return service.server.start()
})
})
service.server.extendOpenApi(openApiDefinition)
Openapi 3.0 spec is used to define new API paths. Read more about how to define paths in Swagger specification docs. You can even use the Swagger editor to define and design your API, and afterwards, load the resultant .json files in domapic base.
You can add as many openApi definitions as you want, and for each one you can define "components", "tags", or "paths". The resultant openapi.json
will be the result of extending all of them, after adding all needed base properties.
See here an openApi definition example, which is used internally to create the built-in /about
api resource.
service.server.addOperations(apiOperationsObject)
Each openApi path should contain a property called operationId
. This value defines which operation will be executed when the api resource is requested.
Use the addOperations
server method to add the operations. The operation key should match with the openApi operationId
property.
Each operation can have properties:
handler
- The function that will be executed when the api resource is requested. Mandatory.- Arguments:
- params - Request parameters.
params.path
andparams.query
. - body - Request body
- res - Allows to set custom headers and statusCode to response. Examples:
res.status(201)
,res.header('location', '/api/books/new-book')
- userData - If user is "logged in", The userData returned by the correspondant authentication method
verify
handler will be received in this property.
- params - Request parameters.
- Returns:
- Can return a Promise. If rejected, the error will be mapped to a correspondant html error. If resolved, the resolved value will be returned as response body.
- If returns a value, the value will be returned as response body.
- If throws an error, the error will be mapped to a correspondant html error.
- Arguments:
parse
- Parse parameters from request.- It must be an object, with first level keys as request object where the parameter will be found, and second level keys as parameter name to be parsed. The
parser
function will receive the original value of the parameter as argument, and should return the parsed value. - Useful, for example, to convert numeric values from request params or query strings, that are received as strings, to real numbers.
- It must be an object, with first level keys as request object where the parameter will be found, and second level keys as parameter name to be parsed. The
auth
- This method will be executed to check if the user has enough permissions to perform an operation. Can be a function, or a string that defines which authorization role function has to be executed. If this method is not defined, the api resource will be available for all users. Read Authentication for further info.- Arguments:
- userData - The decoded data about the user that is making the request.
- params - Request parameters.
params.path
andparams.query
. - body - Request body
- Returns:
- Promise.resolve, or
true
to validate the user. - Any other returned value will result in a "Forbidden" response.
- Promise.resolve, or
- Arguments:
service.server.addOperations({
myApiOperation: {
auth: (userData, params, body) => {
if (userIsAllowed(userData)) {
return Promise.resolve()
}
return Promise.reject()
},
handler: (params, body, res, userData) => {
res.status(201)
res.header('location', '/api/books/new-book')
return Promise.resolve({
hello: 'world'
})
},
parse: {
params: {
id: (id) => {
return parseInt(id, 10)
}
},
query: {
page: (page) => {
return parseInt(page, 10)
}
}
}
}
})
service.server.addMiddleware(expressMiddleware)
Custom express middlewares can be added. Will be added before all other internal middlewares, such as operation handlers.
service.server.addMiddleware((req, res, next) => {
console.log('Executing middleware before operation')
next()
})
Make requests to other Domapic services. Automatic authentication and error handling is provided.
domapic.Service().then(service => {
const client = new service.client.Connection('http://localhost:3000')
return client.get('/about').then(response => {
console.log(response)
})
})
// Client with two authentication methods example
domapic.Service().then(service => {
const client = new service.client.Connection('http://localhost:3000',{
apiKey: 'thisIsaFooApiKey',
jwt: {
user: 'fooUserName',
password: 'fooPassword'
}
})
return client.get('/about').then(response => {
console.log(response)
})
})
There are six different levels of traces. Depending of the choiced log level when started the service, the trace will be printed to the console and written to the daily file or not.
All traces in a day are saved to a file, into ~/.domapic/<serviceName>/logs/<serviceName>.<date>.log
. Trace files older than ten days are automatically deleted.
When the service is started at background using the built-in CLI, logs are also saved to a file at ~/.domapic/<serviceName>/logs/<serviceName>.pm2.log
. It is recommended to install PM2 log rotate to avoid this file growing too much.
Sorted tracer levels are: 'log', 'trace', 'debug', 'info', 'warn' and 'error'.
Tracer usage:
domapic.Service().then(service => {
return service.tracer.debug('testing').then(() => {
return service.tracer.log('testing log')
}).then(() => {
return service.tracer.warn('This is a warning', 'This is part of the same warning')
}).then(() => {
return service.tracer.error(new Error('This will print the error stack'))
}).then(() => {
return service.tracer.error('Printed with error style, but no stack')
})
})
There is an extra method called group
, that allows to invoque different levels of tracers at a time:
domapic.Service().then(service => {
return service.tracer.group([
{
log: 'This is a log'
},
{
trace: 'This is a trace'
},
{
warn: 'This is a warn'
}
])
})
Custom errors constructors are provided through the service.errors
object.
Custom errors usage:
const Promise = require('bluebird')
domapic.Service().then(service => {
return Promise.reject(new service.errors.BadData('Received bad data'))
.catch(service.errors.BadData, () => {
console.log('Bad data error caught')
throw new service.errors.BadImplementation()
})
})
Consult all available error constructors and its correspondences with html errors.
In addition to error constructors, three methods are provided in the errors
object. These methods are used internally by domapic-base in order to map the returned errors to HTML errors and viceversa:
isControlled
- Allows to know if error has been created with a custom error constructorservice.errors.isControlled(error)
const error = new service.errors.Conflict()
console.log(service.errors.isControlled(error))
// true
error = new Error()
console.log(service.errors.isControlled(error))
// false
FromCode
- Returns an error created with the constructor correspondent to the provided html error status code:service.errors.FromCode(code, message [, stack] [, extraData])
return Promise.reject(service.errors.FromCode(403, 'Custom message'))
.catch(service.errors.Forbidden, error => {
console.log('Forbidden error caught')
console.log(error.message)
// Custom message
})
toHTML
- Returns a Boomified error correspondent to the used error constructor. Each error constructor is mapped to an specific status code, ready to be returned by the API:service.errors.toHTML(error)
const error = service.errors.Forbidden()
console.log( service.errors.toHTML(error).output.payload.statusCode )
// 403
Storage methods read and save json data from a file stored as ~/.domapic/<serviceName>/storage/service.json
.
service.storage.set('fooProperty', {test: 'testing'})
.then(() => {
return service.storage.get('fooProperty')
})
.then(data => {
console.log(data)
// {test: 'testing'}
return service.storage.remove('fooProperty')
})
Methods
get
- Read data from storage file.service.storage.get([key])
- Arguments:
- key - Optional, key of the object to get. If no provided, entire data is returned.
- Returns:
- A promise, resolved with the correspondant data.
set
- Save data into the storage file.service.storage.set([key,] value)
- Arguments:
- key - Optional. Key of the object to set. If no provided, entire data is overwritten by the given value.
- value - Data to be saved.
remove
- Removes a property from the stored object.service.storage.remove(key)
- Arguments:
- key - Key of the object to be removed.
getPath
- Get storage folder.- Returns:
- A promise, resolved with the absolute path to the storage folder.
- Returns:
Set of utilities:
templates
compile
- Received an object containing a set ofkey:'string'
, will use Handlebars to compile each string, and return an object with same keys, but containing the compiled templates.compiled
- Set of precompiled templates, used internally.
const templates = service.utils.templates.compile({
myTemplate1: 'Value is: {{value}}'
})
console.log(templates.myTemplate1({
value: 123
}))
// Value is: 123
cli
usedCommand
- Used internally by CLI. Returns the command used to start the current process.
services
- Set of utilities used internally to normalize services names, etc..
Static object containing information about the package, from the package.json
file.
name
- Mandatory. Thepackage.json
must contain this property.type
- Category of the service. It is defined with an argument when service is created. Possible values when using Domapic packages aremodule
,controller
orplugin
.version
- Mandatory. Thepackage.json
must contain this property.description
- Mandatory. Thepackage.json
must contain this property.homepage
author
license
console.log(service.info)
The server supports two types of OAUTH authentication methods, with built-in api "login" urls and token validations.
Each authentication strategy need some different methods to be provided in order to be activated. This externally provided methods have the responsibility of checking the user data, and then delegate the rest of the flow into the built-in security modules. In the case of Json Web Token, as the rest of the process is "token-based", this methods will be only invoqued at "login" or "refresh token" points.
To require an authentication method in your API operations, you must define the openapi security
property in the correspondant API path:
"paths": {
"/fooOperationPath": {
"post": {
"security": [{
"jwt": []
}, {
"apiKey": []
}]
}
}
}
Use the server addAuthentication
method to define your authentication implementations. The parameter must be an object containing keys jwt
, apiKey
and/or disabled
, which will contain the specific configuration for each method:
service.server.addAuthentication(authConfigObject)
Must contain properties:
verify
- Checks if the received api key is still allowed to be used.- Arguments:
- apiKey - Api key received in the request header.
- Returns:
- Promise.resolve(userData) -> Allowed, pass the user data to authorization methods.
- Rejected promise -> Unauthorized.
- Arguments:
authenticate
- API operation for requesting a new api key. This api point needs authentication as well, so, if your system authentication is only api key based, you have to define an initial api key that could be used to request more in case it´s needed. This method supports Json Web Token authentication as well, if it is implemented.auth
- Authorization method for the/api/auth/apikey
POST api resource.- Arguments:
- userData - The decoded data about the user that is making the request.
- params - Request parameters.
params.path
andparams.query
. - body - Request body
- Arguments:
handler
- Operation handler for the/api/auth/apikey
POST api resource.- Arguments:
- params - Request parameters.
params.path
andparams.query
. - body - Request body
- res - Allows to set custom headers and statusCode to response. Examples:
res.status(201)
,res.header('location', '/api/books/new-book')
- userData - If user is "loged", here will be received the userData returned by the correspondant authentication method
verify
handler.
- params - Request parameters.
- Should return a new api key.
- Arguments:
revoke
- API operation for removing an api key. This api resource needs authentication as well.auth
- Authorization method for the/api/auth/apikey
DELETE api resource.- Arguments:
- userData - The decoded data about the user that is making the request.
- params - Request parameters.
params.path
andparams.query
. - body - Request body
- Arguments:
handler
- Operation handler for the/api/auth/apikey
DELETE api resource.- Arguments:
- params - Request parameters.
params.path
andparams.query
. - body - Request body
- res - Allows to set custom headers and statusCode to response. Examples:
res.status(201)
,res.header('location', '/api/books/new-book')
- userData - If user is "loged", here will be received the userData returned by the correspondant authentication method
verify
handler.
- params - Request parameters.
- Any returned value will be ignored, and not exposed to the api response.
- Arguments:
Implementation example:
service.server.addAuthentication({
apiKey: {
verify: apiKey => {
// Check if apiKey is allowed, and return correspondant user data, or reject.
return getUserDataFromApiKey(apiKey)
},
authenticate: {
auth: (userData, params, body) => {
// Check if user is allowed to create a new api key, resolve or reject
return checkUserPermissionToManageApiKeys(userData)
},
handler: (params, body, res, userData) => {
// Returns a new api key
return getNewApiKey()
}
},
revoke: {
auth: (userData, params, body) => {
// Check if user is allowed to remove an existant api key, resolve or reject
return checkUserPermissionToManageApiKeys(userData)
},
handler: (params, body, res, userData) => {
// Remove existant api key
return removeApiKey(body.apiKey)
}
}
}
})
Properties:
secret
- Optional. String used to generate tokens. If not provided, a random secret is used.expiresIn
- Number. Time of tokens expiration time in miliseconds. By default, 300 will be used if this option is not provided. It is not recommended to use a high value for this option. Tokens should expire in short time, and then, refresh tokens should be used to request a new token again.authenticate
- API operation for requesting a new token. Will receiveuser
andpassword
, orrefreshToken
. Your implementation should be able to identify the user, and then return the correspondant data that will be stored in the Json Web Token. Afterwards, the API will use that data in each request to apply the correspondant authorization policy for each api resource. Refresh Token is used to renew the user credentials without providing the user data again when the token expires.handler
- Operation handler for the/api/auth/jwt
POST api resource.- Arguments:
- params - Request parameters.
params.path
andparams.query
. - body - Request body
- res - Allows to set custom headers and statusCode to response. Examples:
res.status(201)
,res.header('location', '/api/books/new-book')
- params - Request parameters.
- Should return an object containing
userData
(an object with all user data that will be passed as argument to the API resources authorization handlers), andrefreshToken
if the request has not received it.
- Arguments:
revoke
- API operation for removing a refresh token. This api resource needs authentication as well, and it only supports thejwt
authentication method.auth
- Authorization method for the/api/auth/jwt
DELETE api resource.- Arguments:
- userData - The decoded data about the user that is making the request.
- params - Request parameters.
params.path
andparams.query
. - body - Request body
- Arguments:
handler
- Operation handler for the/api/auth/jwt
DELETE api resource.- Arguments:
- params - Request parameters.
params.path
andparams.query
. - body - Request body
- res - Allows to set custom headers and statusCode to response. Examples:
res.status(201)
,res.header('location', '/api/books/new-book')
- userData - If user is "loged", here will be received the userData returned by the correspondant authentication method
verify
handler.
- params - Request parameters.
- Any returned value will be ignored, and not exposed to the api response.
- Arguments:
Implementation example:
service.server.addAuthentication({
jwt: {
secret: 'thisIsNotaRealTokenSecretPleaseReplaceIt',
expiresIn: 180,
authenticate: {
handler: (params, body, res) => {
// Check if user has right credentials, or refresh token. Returns user data (with new refresh token if not provided)
if (body.refreshToken) {
return getUserDataFromRefreshToken(body.refreshToken)
}
return checkUserData({
name: body.user,
password: body.password
}).then((userData) => {
return createNewRefreshToken(userData)
.then((refreshToken) => {
return Promise.resolve({
userData: userData,
refreshToken: refreshToken
})
})
})
}
},
revoke: {
auth: (userData, params, body) => {
// Check if user is allowed to remove an existant refresh token
return checkUserPermissionToManageApiKeys(userData)
},
handler: (params, body, res, userData) => {
// Remove existant refresh token
return removeRefreshToken(body.refreshToken)
}
}
}
})
When authentication is disabled for an specific origin because of the authDisabled
, or auth
options, the authorization handlers will not be executed. By default, an user with the format {user: 'anonymous'}
will be passed to action handlers, but you can define your own "disabled" strategy, and return an user of your convenience to be passed.
Properties:
verify
- Function that should return the user to be passed to action handlers when authentication is disabled for an specific origin.
Implementation example:
service.server.addAuthentication({
disabled: {
verify: () => Promise.resolve({
name: 'your-auth-disabled-user',
role: 'your-anonymous-role'
})
}
})
About authorization, each operation defined in the API can have its own auth
method, that will receive the decoded user data as argument for each request, allowing to reject or allow an specific request based on your own security policy implementation.
The authorization method is agnostic in relation with the used authentication method, because it only receives the user data, no matter the method used to store or recover this data from the request.
Read more about how to define and use the auth
methods in the operations chapter.
An operation auth
method can be defined as a function, or as a string that defines which "authorization role" function has to be executed. This "authorization roles" must to be defined in the server, using the addAuthorization
method:
service.server.addAuthorization(roleName, authHandler)
service.server.addAuthorization('fooRoleName', userData => {
if (roleIsAllowed(userData.role)) {
return Promise.resolve()
// Execute the operation handler
}
return false
// Forbidden response
}).then(() => {
return service.server.addOperations({
fooOperation: {
auth: 'fooRoleName',
handler: () => {
return {}
}
}
})
})
A built-in CLI is provided, but it needs some steps in your package in order to expose it. Once it is implemented, it allows to start the service in background, managed using PM2. It also provides logs displaying, and a command to stop the service. Also an API is at your disposal for defining new commands.
Follow these steps to implement the built-in CLI in your package:
- Create a
/bin/<your-cli-name>
file in your package. The name of the file should be equal to the command name that you want to use for your CLI. The content of this file must be:
#!/usr/bin/env node
require('../cli/index')
- Add a
bin
property to yourpackage.json
, and add an npm script to allow using the CLI through npm alternatively:
"bin": {
"your-cli-name": "./bin/your-cli-name"
},
"scripts": {
"your-cli-name": "./bin/your-cli-name"
}
- Create a
/cli/index.js
file in your package. It must contains the CLI initialization:
const path = require('path')
const domapic = require('domapic-base')
domapic.cli({
script: path.resolve(__dirname, '..', 'server.js')
})
The script
parameter must be the path to the file where you have your service initialization. This will be the process that will be started in background.
Once you have installed globally your package, you´ll have the CLI available from command line. If not installed globally, all available commands can be executed as well using npm scripts, or executing directly the /bin/your-cli-name
file. Next, the start
example include the different invocation methods examples.
Default available commands are:
help
your-cli-name help
# Displays help
your-cli-name start --help
# Displays help for start command
start
- Starts the service process in background. A custom name for the process instance can be provided as first argument, or using the--name
option.
// global cli
your-cli-name start fooName --logLevel=debug
// npm script
npm run your-cli-name start fooName -- --logLevel=debug
// bin execution
./bin/your-cli-name start fooName --logLevel=debug
All available options for the start
command are described in the options chapter of this documentation.
stop
- Stops a background service instance:
your-cli-name stop
## If name was provided when started, if must be provided to stop:
your-cli-name stop fooName
logs
- Displays logs of a background service instance:
your-cli-name logs
# Displays logs
your-cli-name logs --lines=300
# Displays 300 last lines of logs (30 by default, if option is not provided)
## If name was provided when started, if must be provided to stop:
your-cli-name logs fooName --lines=300
- Custom configuration
A
customConfig
property can be defined in initialization object in order to define the custom options of your service:
const path = require('path')
const domapic = require('domapic-base')
const customConfig = require('./customConfig')
domapic.cli({
script: path.resolve(__dirname, '..', 'server.js'),
customConfig: customConfig
})
Read more about how to define them in the Custom options chapter
-
Custom commands A
customCommands
property can be defined in initialization object in order to extend the CLI features. It must be an object, whose keys will be the names of the custom commands. Each command must have properties:processName
- String. A reference name for the core in order to save the command default configuration, etc... As examples:service
,logs
, etc...describe
- Description for the command. Used when displaying help.cli
- Command name and arguments expression. Yargs is used as underlayer to manage commands, so you can read its documentation for more details about how to define them.options
- Available options for the command. All commands will inherit the optionsname
,color
,logLevel
,path
andsaveConfig
. Read Yargs documentation for further info about defining your own options.command
- Handler function that will be executed when command is invoqued.- Arguments:
config
- An object containing default config, extended with stored config and extended with explicitly defined options in the command execution.cliUtils
- A set of methods:tracer
- Atracer
object, as described here.errors
- Anerrors
object, as described here.config
- Aconfig
object, as described here.utils
- Anutils
object, as described here.process
- An object that allows to manage the relatedscript
property pm2 process instance related to provided mandatory option--name
. It has methods:start
- Starts the process.stop
- Stops the process.logs
- Displays out logs while are being received.
- Arguments:
Example of custom command definition:
const path = require('path')
const domapic = require('domapic-base')
domapic.cli({
script: path.resolve(__dirname, '..', 'server.js'),
customCommands: {
restart: {
processName: 'stopCustom',
describe: 'Example of a custom command',
cli: 'stopCustom <fooOption1>',
options: {
fooOption2: {
type: 'boolean',
describe: 'Foo option for command example',
default: true
}
},
command: (config, cliUtils) => {
return cliUtils.tracer.info(JSON.stringify(config))
.then(() => {
cliUtils.process.stop()
})
}
}
}
})
Example of custom command usage:
your-cli-name stopCustom value1 --fooOption2=false --name=testing
# Will display configuration for the custom command, and then stop the process of script "server.js" with name "testing"
This package uses Narval as test suites runner.
Different test suites are included, categorized in "unit", "integration", and "end-to-end" tests. For further details, read the .narval.yml
, and, if needed, you can learn more about Narval configuration at its own documentation.
Run tests:
npm test