Skip to content

Commit

Permalink
support node 10, use promises
Browse files Browse the repository at this point in the history
  • Loading branch information
spike1292 committed Oct 17, 2020
1 parent 21accd1 commit d4826d9
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 128 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Behave Pro NodeJS Client

```
$ npm install behavepro -g
$ npm install @essent/behavepro -g
```

See [API Key setup](introduction.html) to retrieve the required credentials.
Expand Down Expand Up @@ -86,9 +86,9 @@ BehavePro({
"id": 10000,
"userId": "amlyYToyNDM0ZG.....ZiNzQwNGI=",
"apiKey": "44993b0481838e.....a246c723e8e"
}, function() {
}).then(() => {
// done
});
})
```

Available parameters:
Expand Down
23 changes: 15 additions & 8 deletions bin/cli.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/env node
"use strict";
var BehavePro = require("../lib/behavepro");
var args = require("minimist")(process.argv.slice(2));
var packageJson = require("../package.json");
var chalk = require("chalk");
const BehavePro = require("../lib/behavepro");
const args = require("minimist")(process.argv.slice(2));
const packageJson = require("../package.json");
const chalk = require("chalk");

if (args.help) {
console.log(
Expand All @@ -22,18 +22,25 @@ if (args.help) {
return;
}

var settings = {
const settings = {
host: args.host || "https://behave.pro",
id: args.key || args.project || args.id,
userId: args.user || args.userId,
apiKey: args.api || args.apiKey || args.password,
output: args.output || args.dir || args.directory || "features",
manual: args.manual || args.m || false,
config: args.config || "config.json"
config: args.config || "config.json",
};

const errorHandler = (err) => {
console.error("There was an error:", err);
process.exit(1); //mandatory (as per the Node.js docs)
};

if (settings.id && settings.userId && settings.apiKey) {
BehavePro.fetchFeatures(settings);
BehavePro.fetchFeatures(settings).catch(errorHandler);
} else {
BehavePro.fetchFeaturesFromConfig(settings);
BehavePro.fetchFeaturesFromConfig(settings).catch(errorHandler);
}

process.on("uncaughtException", errorHandler);
12 changes: 5 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
#!/usr/bin/env node
"use strict";
var BehavePro = require("./lib/behavepro");
var _ = require("lodash");
const BehavePro = require("./lib/behavepro");
const _ = require("lodash");

var defaultSettings = {
const defaultSettings = {
host: "https://behave.pro",
output: "features",
manual: false,
config: "config.json"
};

module.exports = function(settings, callback) {
module.exports = function(settings) {
_.defaults(settings, defaultSettings);
BehavePro.fetchFeatures(settings, function() {
if (callback) callback();
});
return BehavePro.fetchFeatures(settings)
};
245 changes: 135 additions & 110 deletions lib/behavepro.js
Original file line number Diff line number Diff line change
@@ -1,146 +1,171 @@
#!/usr/bin/env node
// @ts-check
"use strict";
const request = require("request");
const fetch = require("node-fetch");
const unzipper = require("unzipper");
const fs = require("fs");
const path = require("path");
const mkdir = require("mkdirp");
const _ = require("lodash");
const chalk = require("chalk");
const red = chalk.red;

const fetchFeaturesFromConfig = function(settings, callback) {
fs.exists(process.cwd() + "/" + settings.config, function findConfig(exists) {
if (exists) {
const configuration = require(process.cwd() + "/" + settings.config);
configuration.forEach(function(config) {
_.extend(settings, config);
fetchFeatures(settings, callback);
});
} else {
const err = new Error(
red("Could not find config at " + process.cwd() + "/" + settings.config)
);
throw err;
}
});
};

const fetchFeatures = function(settings, callback) {
/**
* @typedef {{
* host?: string,
* id?: string,
* userId?: string,
* apiKey?: string,
* output?:string,
* manual?: boolean,
* config?: string
* }} Settings
*/

/**
*
* @param {Settings} settings
*/
async function fetchFeaturesFromConfig(settings) {
const settingsPath = path.join(process.cwd(), settings.config);
try {
await fs.promises.access(
settingsPath,
fs.constants.F_OK | fs.constants.R_OK
);

const configuration = require(settingsPath);
const features = configuration.map(function (config) {
_.extend(settings, config);
return fetchFeatures(settings);
});
return await Promise.all(features);
} catch (error) {
throw new Error(
chalk.red(`Could not find config at: ${chalk.bold(settingsPath)}`)
);
}
}

/**
*
* @param {Settings} settings
*/
async function fetchFeatures(settings) {
const projectId = settings.id;
const userId = settings.userId;
const apiKey = settings.apiKey;

ensureSettingsExist(projectId, userId, apiKey, function() {
console.log(
chalk.cyan("Downloading features from JIRA project " + projectId + "...")
);
const url = [
settings.host,
"/rest/cucumber/1.0/project/",
projectId,
"/features?manual=",
settings.manual
].join("");

request(
{
url: url,
headers: {
Authorization:
"Basic " + Buffer.from(userId + ":" + apiKey).toString("base64")
},
encoding: null
},
function(error, response, body) {
if (error) throw error;
let err = null;
switch (response.statusCode) {
case 500:
err = new Error(red("Server error: check your host url"));
throw err;
case 401:
err = new Error(
red("Unauthorized: ensure userId and apiKey are both valid")
);
throw err;
case 200:
break;
default:
err = new Error(
red(response.statusCode + " http error when downloading")
);
throw err;
}

const path = settings.output + "/" + projectId;
mkdir(path, function(err) {
if (err) throw err;
writeFeatures(body, path, function() {
countFeatures(path, function(files) {
console.log(
chalk.green(
`Saved ${files.length} ${
files.length > 1 ? "features" : "feature"
} to ${process.cwd()}/${path}/`
)
);
});
if (callback) callback();
});
});
}
);
ensureSettingsExist(projectId, userId, apiKey);

console.log(
chalk.cyan(
`Downloading features from JIRA project ${chalk.bold(projectId)} ...`
)
);

const url = [
settings.host,
"/rest/cucumber/1.0/project/",
projectId,
"/features?manual=",
settings.manual,
].join("");

const response = await fetch(url, {
headers: {
Authorization: `Basic ${Buffer.from(`${userId}:${apiKey}`).toString(
"base64"
)}`,
},
});
};

let err = null;
switch (response.status) {
case 500:
err = new Error(chalk.red("Server error: check your host url"));
throw err;
case 401:
err = new Error(
chalk.red("Unauthorized: ensure userId and apiKey are both valid")
);
throw err;
case 200:
break;
default:
err = new Error(
chalk.red(response.status + " http error when downloading")
);
throw err;
}

const projectPath = path.join(settings.output, `${projectId}`);
await mkdir(projectPath);
const buffer = await response.buffer();
await writeFeatures(buffer, projectPath);
const filesCount = await countFeatures(projectPath);

console.log(
chalk.green(
`Saved ${filesCount} ${
filesCount > 1 ? "features" : "feature"
} to ${path.join(process.cwd(), projectPath)}/`
)
);
}

module.exports = {
fetchFeatures: fetchFeatures,
fetchFeaturesFromConfig: fetchFeaturesFromConfig
fetchFeaturesFromConfig: fetchFeaturesFromConfig,
};

function writeFeatures(body, path, callback) {
fs.writeFile(path + ".zip", body, function(err) {
if (err) throw err;
const stream = fs.createReadStream(path + ".zip").pipe(
/**
*
* @param {Buffer} buffer
* @param {string} featuresPath
*/
async function writeFeatures(buffer, featuresPath) {
const zipPath = featuresPath + ".zip";
await fs.promises.writeFile(zipPath, buffer);

await fs
.createReadStream(zipPath)
.pipe(
unzipper.Extract({
path: path
path: featuresPath,
})
);
)
.promise();

stream.on("close", function() {
removeZip(path + ".zip");
callback();
});
});
await await fs.promises.unlink(zipPath);
}

function ensureSettingsExist(projectId, userId, apiKey, callback) {
/**
*
* @param {string} projectId
* @param {string} userId
* @param {string} apiKey
*/
function ensureSettingsExist(projectId, userId, apiKey) {
let err = null;

if (!projectId) {
err = new Error(red("projectId is missing"));
err = new Error(chalk.red("projectId is missing"));
} else if (!userId) {
err = new Error(red("userId is missing"));
err = new Error(chalk.red("userId is missing"));
} else if (!apiKey) {
err = new Error(red("apiKey is missing"));
err = new Error(chalk.red("apiKey is missing"));
}

if (err) {
throw err;
}

return callback();
}

function countFeatures(path, callback) {
fs.readdir(path, function(err, files) {
callback(files);
});
}

function removeZip(file, callback) {
fs.unlink(file, function(err) {
if (err) throw err;
if (callback) callback();
});
/**
*
* @param {string} path
*/
async function countFeatures(path) {
const files = await fs.promises.readdir(path);
return files.length;
}

0 comments on commit d4826d9

Please sign in to comment.