Skip to content

Commit

Permalink
feat: add advanced configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
dominik-korsa committed Jul 18, 2020
1 parent 7553220 commit e716e64
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 99 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea
.vscode
node_modules
.env
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,31 @@ Install using:
npm i @open-website-status/provider -g
```

After installing you can use the terminal command:
After installing, you can use the terminal command:
```shell script
ows-provider
```
The arguments are:

| Argument | Description | Required |
|----------------------|----------------------|----------|
|**-t, --token TOKEN** | **Provider token** | **YES** |
| -s, --server URL | Backend server URL | no |
| -p, --path PATH | Provider socket path | no |
| -h, --help | Shows command help | no |

Example usage:
The configuration can be provider in the following ways:
- As a command parameter
```shell script
ows-provider -t MY_TOKEN -s http://example.com:1234
```
- Set in the config
```shell script
ows-provider -t MY_FANCY_TOKEN_1234
ows-provider config-set token MY_TOKEN
ows-provider config-set server http://example.com:1234
```
- Passed as an environmental variable
- Provided in a .env file (using the same key as the Environmental variable)
```.dotenv
OWS_TOKEN=MY_TOKEN
OWS_SERVER=http://example.com:1234
```

If the setting is provided in multiple ways (eg. as a command line parameter and as an environmental variable) the priority is the same as the order of the list above

| Command argument | Config key | Environmental variable | Description | Required |
|-------------------|------------|------------------------|----------------------|----------|
| -t, --token TOKEN | token | OWS_TOKEN | Provider token | **YES** |
| -s, --server URL | server | OWS_SERVER | Backend server URL | **YES** |
| -p, --path PATH | path | OWS_PATH | Provider socket path | no |
104 changes: 19 additions & 85 deletions bin/index.js
Original file line number Diff line number Diff line change
@@ -1,101 +1,35 @@
#!/usr/bin/env node

const updateNotifier = require('update-notifier');
const { ProviderSDK } = require('@open-website-status/provider-sdk');
const chalk = require('chalk');
const { program } = require('commander');
const got = require('got');
const pkg = require('../package.json');
const mainAction = require('../lib/actions/main');
const { configSetAction, configResetAction, configGetAction } = require('../lib/actions/config');

updateNotifier({
pkg,
updateCheckInterval: 1000 * 60 * 60 * 8, // 8 hours
}).notify();

program
.requiredOption('-t, --token <token>', 'Provider token')
.requiredOption('-s, --server <url>', 'Server URL')
.option('-p, --path <path>', 'Provider socket path');
.option('-t, --token <token>', 'Provider token')
.option('-s, --server <url>', 'Server URL')
.option('-p, --path <path>', 'Provider socket path')
.action(mainAction);

program.parse(process.argv);

const sdk = new ProviderSDK({
token: program.token,
server: program.server,
path: program.path,
});

sdk.on('connect', () => {
console.log();
console.log(chalk.red.green('Connected successfully!'));
});

sdk.on('connect-error', (error) => {
console.log(`${chalk.red.bold('Failed to connect:')} ${chalk.red(error.message)}`);
});

sdk.on('connect-timeout', () => {
console.log();
console.log(chalk.yellow('Connection timed out'));
});
program
.command('config-set <setting> <value>')
.description('Sets a new setting value')
.action(configSetAction);

sdk.on('error', (error) => {
console.log();
console.log(`${chalk.red.bold('An error occurred:')} ${chalk.red(error)}`);
process.exit(1);
});
program
.command('config-reset <setting>')
.description('Resets the value of a setting')
.action(configResetAction);

sdk.on('disconnect', () => {
console.log(chalk.red.bold('Disconnected'));
console.log();
});
program
.command('config-get <setting>')
.description('Gets the current value of a setting')
.action(configGetAction);

sdk.on('dispatch-job', async (job) => {
const url = `${job.protocol}//${job.hostname}${job.port === undefined ? '' : `:${job.port}`}${job.pathname}${job.search}`;
console.log(`${chalk.blueBright('New job:')} ${chalk.white.underline(url)}`);
try {
await sdk.accept(job.jobId);
try {
const response = await got(url, {
timeout: 30000,
retry: {
limit: 0,
},
throwHttpErrors: false,
});
try {
await sdk.complete(job.jobId, {
state: 'success',
executionTime: response.timings.phases.total,
httpCode: response.statusCode,
});
console.log(chalk.blueBright('Success'));
} catch (completeError) {
console.log(`${chalk.red.bold('Failed to complete job:')}`);
console.error(completeError);
}
} catch (error) {
try {
if (error.name === 'TimeoutError') {
await sdk.complete(job.jobId, {
state: 'timeout',
executionTime: error.timings.phases.total,
});
console.log(chalk.yellowBright('Timeout'));
} else {
await sdk.complete(job.jobId, {
state: 'error',
errorCode: error.code,
});
console.log(`${chalk.red('Error:')} ${error.code}`);
}
} catch (completeError) {
console.log(`${chalk.red.bold('Failed to complete job:')}`);
console.error(completeError);
}
}
} catch (acceptError) {
console.log(`${chalk.red.bold('Failed to accept job:')}`);
console.error(acceptError);
}
});
program.parse(process.argv);
47 changes: 47 additions & 0 deletions lib/actions/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const chalk = require('chalk');
const ConfigManager = require('../config-manager');

function findSetting(key) {
const setting = ConfigManager.settings.find((item) => item.key === key);

if (!setting) {
console.log(chalk.red(`Unknown setting: ${chalk.blue(key)}`));
process.exit(12);
}

return setting;
}

function configSetAction(key, value) {
const setting = findSetting(key);

const validationError = setting.validator(value.trim());
if (validationError !== null) {
console.log(chalk.red(`Value for setting ${chalk.blue(setting.name)} (${chalk.blue(setting.key)}): "${chalk.white(value.trim())}" is invalid`));
console.log(chalk.red(validationError));
process.exit(11);
}

ConfigManager.config.set(key, value.trim());
console.log(chalk.green(`Set ${chalk.blue(setting.name)} to ${chalk.white(value.trim())}`));
}

function configResetAction(key) {
const setting = findSetting(key);
ConfigManager.config.delete(key);
console.log(chalk.green(`Reset ${chalk.blue(setting.name)}`));
}

function configGetAction(key) {
const setting = findSetting(key);
const value = ConfigManager.config.get(key);
if (value === undefined) {
console.log(chalk.yellowBright(`Setting ${chalk.blue(setting.name)} isn't currently set`));
} else {
console.log(chalk.green(`Setting ${chalk.blue(setting.name)} is currently set to: ${chalk.white(value)}`));
}
}

module.exports.configSetAction = configSetAction;
module.exports.configResetAction = configResetAction;
module.exports.configGetAction = configGetAction;
89 changes: 89 additions & 0 deletions lib/actions/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
const { ProviderSDK } = require('@open-website-status/provider-sdk');
const chalk = require('chalk');
const got = require('got');
const ConfigManager = require('../config-manager');

module.exports = function mainAction(cmdObject) {
const config = ConfigManager.resolve(cmdObject);

const sdk = new ProviderSDK({
token: config.token,
server: config.server,
path: config.path,
});

sdk.on('connect', () => {
console.log();
console.log(chalk.red.green('Connected successfully!'));
});

sdk.on('connect-error', (error) => {
console.log(`${chalk.red.bold('Failed to connect:')} ${chalk.red(error.message)}`);
});

sdk.on('connect-timeout', () => {
console.log();
console.log(chalk.yellow('Connection timed out'));
});

sdk.on('error', (error) => {
console.log();
console.log(`${chalk.red.bold('An error occurred:')} ${chalk.red(error)}`);
process.exit(1);
});

sdk.on('disconnect', () => {
console.log(chalk.red.bold('Disconnected'));
console.log();
});

sdk.on('dispatch-job', async (job) => {
const url = `${job.protocol}//${job.hostname}${job.port === undefined ? '' : `:${job.port}`}${job.pathname}${job.search}`;
console.log(`${chalk.blueBright('New job:')} ${chalk.white.underline(url)}`);
try {
await sdk.accept(job.jobId);
try {
const response = await got(url, {
timeout: 30000,
retry: {
limit: 0,
},
throwHttpErrors: false,
});
try {
await sdk.complete(job.jobId, {
state: 'success',
executionTime: response.timings.phases.total,
httpCode: response.statusCode,
});
console.log(chalk.blueBright('Success'));
} catch (completeError) {
console.log(`${chalk.red.bold('Failed to complete job:')}`);
console.error(completeError);
}
} catch (error) {
try {
if (error.name === 'TimeoutError') {
await sdk.complete(job.jobId, {
state: 'timeout',
executionTime: error.timings.phases.total,
});
console.log(chalk.yellowBright('Timeout'));
} else {
await sdk.complete(job.jobId, {
state: 'error',
errorCode: error.code,
});
console.log(`${chalk.red('Error:')} ${error.code}`);
}
} catch (completeError) {
console.log(`${chalk.red.bold('Failed to complete job:')}`);
console.error(completeError);
}
}
} catch (acceptError) {
console.log(`${chalk.red.bold('Failed to accept job:')}`);
console.error(acceptError);
}
});
};
79 changes: 79 additions & 0 deletions lib/config-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
require('dotenv').config();
const Configstore = require('configstore');
const isUrl = require('is-url');
const chalk = require('chalk');
const pkg = require('../package.json');

const config = new Configstore(pkg.name);

const settings = [
{
name: 'Provider token',
key: 'token',
env: 'OWS_TOKEN',
required: true,
validator() {
return null;
},
},
{
name: 'Server URL',
key: 'server',
env: 'OWS_SERVER',
required: true,
validator(value) {
if (!isUrl(value)) return 'The provided value is not a valid URL';
return null;
},
},
{
name: 'Provider socket path',
key: 'path',
env: 'OWS_PATH',
required: false,
default: undefined,
validator() {
return null;
},
},
];

function resolve(commandArguments) {
const resolvedConfig = {};

settings.forEach((setting) => {
const argumentValue = commandArguments[setting.key];
const envValue = process.env[setting.env];
const configValue = config.get(setting.key);

let value = argumentValue || envValue || configValue || undefined;
if (typeof value === 'string') value = value.trim();

if (value === undefined || value === '') {
if (setting.required) {
console.log(chalk.red(`Setting ${chalk.blue(setting.name)} is required`));
console.log(chalk.red(
`You can provide it as a command parameter, set it in config using ${chalk.white(`ows-provider config set ${chalk.blue(`${setting.key} NEW_VALUE`)}`)} or pass it as an environmental variable ${chalk.blue(setting.env)}`,
));
process.exit(10);
return;
}
resolvedConfig[setting.key] = setting.default;
} else {
const validationError = setting.validator(value);
if (validationError !== null) {
console.log(chalk.red(`Value for setting ${chalk.blue(setting.name)} (${chalk.blue(setting.key)}): "${chalk.white(value)}" is invalid`));
console.log(chalk.red(validationError));
process.exit(11);
return;
}
resolvedConfig[setting.key] = value;
}
});

return resolvedConfig;
}

module.exports.config = config;
module.exports.resolve = resolve;
module.exports.settings = settings;
Loading

0 comments on commit e716e64

Please sign in to comment.