New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

github-stats-cli

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

github-stats-cli - npm Package Compare versions

Comparing version 1.0.1 to 2.0.0

tests/src/dataTransformations.test.js

33

index.js

@@ -6,8 +6,11 @@ #!/usr/bin/env node

const app = require('./package.json');
const { normalizeStates } = require('./src/utils');
const { getFromDate, getToDate } = require('./src/utils');
const getStats = require('./src/main');
const [cmdString] = Object.keys(app.bin);
const defaults = {
num: 10,
states: 'OPEN',
state: 'merged',
fromDate: getFromDate(),
toDate: getToDate(),
};

@@ -29,13 +32,13 @@

\n
Minimal usage - get last 10 PRs from facebook/react that are currently open:
Minimal usage - get PRs from facebook/react repo merged in last 7 days, paginate 10 at a time and save to ./prdata.csv:
\n
$ ${app.name} -o facebook -r react
$ ${cmdString} -o facebook -r react
\n
Get last 20 PRs that are currently merged, closed or open:
Get PRs from facebook/react repo merged in last 7 days, paginate 20 at a time and save to ./prdata.csv:
\n
$ ${app.name} -o facebook -r react -n 20 -s MERGED,CLOSED,OPEN
$ ${cmdString} -o facebook -r react -n 20
\n
Get last 20 closed PRs and return only the ones that match user name bvaughn:
Get PRs from facebook/react repo created on or after 2018-07-01 merged on or before 2018-09-30, paginate 20 at a time and save to ./prdata.csv:
\n
$ ${app.name} -o facebook -r react -n 20 -s MERGED -u bvaughn
$ ${cmdString} -o facebook -r react -n 20 -u bvaughn -f 2018-07-01 -t 2018-09-30
\n

@@ -51,9 +54,5 @@ `)

.option('-u, --user <user>', 'optional author name', undefined)
.option('-n, --num <num>', 'optional number of pull requests to return', defaults.num)
.option(
'-s, --states <states>',
'comma separated MERGED|CLOSED|OPEN',
defaults.states,
normalizeStates
)
.option('-n, --num <num>', 'optional number of pull requests to return per page', defaults.num)
.option('-f, --from <from>', 'YYYY-MM-DD date, e.g. 2018-12-21', defaults.fromDate)
.option('-t, --to <to>', 'YYYY-MM-DD date, e.g. 2018-12-25', defaults.toDate)
.parse(process.argv);

@@ -75,5 +74,7 @@

num: Number(program.num),
states: normalizeStates(program.states),
state: defaults.state,
fromDate: program.from,
toDate: program.to,
};
getStats(queryParams);
{
"name": "github-stats-cli",
"version": "1.0.1",
"version": "2.0.0",
"description": "Github stats cli",

@@ -28,6 +28,7 @@ "keywords": [

"graphql-client": "^2.0.1",
"json2csv": "^4.2.1",
"moment": "^2.22.1"
},
"bin": {
"github-stats-cli": "index.js"
"ghs": "index.js"
},

@@ -34,0 +35,0 @@ "devDependencies": {

@@ -21,4 +21,5 @@ # github-stats-cli

## Usage
```
Usage: github-stats-cli [options]
Usage: ghs [options]

@@ -35,18 +36,18 @@ Options:

Environment variables:
GH_STATS_TOKEN set export GH_STATS_TOKEN=<your generated github token>
Examples:
Minimal usage - get last 10 PRs from facebook/react that are currently open:
$ github-stats-cli -o facebook -r react
Get last 20 PRs that are currently merged, closed or open:
$ github-stats-cli -o facebook -r react -n 20 -s MERGED,CLOSED,OPEN
Get last 20 closed PRs and return only the ones that match user name bvaughn:
$ github-stats-cli -o facebook -r react -n 20 -s MERGED -u bvaughn
Minimal usage - get PRs from facebook/react repo merged in last 7 days, paginate 10 at a time and save to ./prdata.csv:
$ ghs -o facebook -r react
Get PRs from facebook/react repo merged in last 7 days, paginate 20 at a time and save to ./prdata.csv:
$ ghs -o facebook -r react -n 20
Get PRs from facebook/react repo created on or after 2018-07-01 merged on or before 2018-09-30, paginate 20 at a time and save to ./prdata.csv:
$ ghs -o facebook -r react -n 20 -u bvaughn -f 2018-07-01 -t 2018-09-30
```

@@ -1,7 +0,5 @@

const { getRelativeDate, getDaysOpen } = require('./utils');
const { getRelativeDate, getHoursOpen } = require('./utils');
const getMappedPrData = ({
data: { repository: { pullRequests: { edges = [] } = {} } = {} } = {},
} = {}) =>
edges.map(({ node }) => {
const getMappedPrData = ({ data: { search: { pageInfo, nodes = [] } = {} } = {} } = {}) =>
nodes.map(node => {
const {

@@ -13,6 +11,7 @@ merged,

mergedAt,
resourcePath,
permalink,
} = node;
return Object.assign({}, node, {
return {
merged,
author: login,

@@ -22,13 +21,11 @@ createdAt: getRelativeDate(createdAt),

mergedAt: getRelativeDate(mergedAt),
resourcePath: `https://github.com${resourcePath}`,
daysOpen: !merged ? getDaysOpen(createdAt, Date.now()) : getDaysOpen(createdAt, mergedAt),
});
permalink,
hoursOpen: !merged
? getHoursOpen(createdAt, Date.now()).toFixed(2)
: getHoursOpen(createdAt, mergedAt).toFixed(2),
};
});
const filterByAuthor = (data, queriedAuthor) =>
data.filter(({ author }) => author === queriedAuthor);
module.exports = {
filterByAuthor,
getMappedPrData,
};

@@ -0,1 +1,2 @@

/* istanbul ignore file */
const gqlclient = require('graphql-client');

@@ -7,4 +8,4 @@ const { token } = require('../config');

headers: {
Authorization: `Bearer ${token}`
}
Authorization: `Bearer ${token}`,
},
});
const client = require('./graphqlClient');
const { getMappedPrData, filterByAuthor } = require('./dataTransformations');
const { printTable } = require('./utils');
const { getMappedPrData } = require('./dataTransformations');
const { appendToFile, constructSearchQueryString, printTable } = require('./utils');
const { pullRequestsQuery } = require('./queries');
const json2csv = require('json2csv').parse;
module.exports = (queryParams) => {
client
.query(pullRequestsQuery, queryParams, (req, res) => {
if (res.status === 401) {
throw new Error('Not authorized');
}
})
const getData = params => {
return client.query(pullRequestsQuery, params, (req, res) => {
if (res.status === 401) {
throw new Error('Not authorized');
}
});
};
const saveCsv = (data, options) => {
const fields = [
'number',
'permalink',
'createdAt',
'closedAt',
'mergedAt',
'merged',
'title',
'lastEditedAt',
'additions',
'deletions',
'changedFiles',
'commits.totalCount',
'comments.totalCount',
'author.login',
];
try {
const csv = json2csv(data, { fields, ...options });
appendToFile(csv);
} catch (err) {
console.error(err);
}
};
const request = queryParams => {
const searchQueryString = constructSearchQueryString(queryParams);
getData(searchQueryString)
.then(body => {
const mappedData = getMappedPrData(body);
const { author: queriedAuthor } = queryParams;
const { hasPreviousPage, hasNextPage, endCursor } = body.data.search.pageInfo;
if (queriedAuthor) {
printTable(filterByAuthor(mappedData, queriedAuthor));
saveCsv(body.data.search.nodes, { header: !hasPreviousPage });
printTable(mappedData);
if (hasNextPage) {
request({ ...queryParams, after: endCursor });
} else {
printTable(mappedData);
console.log('File saved to ./prdata.csv');
}

@@ -27,1 +63,3 @@ })

};
module.exports = request;

@@ -0,18 +1,33 @@

/* istanbul ignore file */
const pullRequestsQuery = `
query repo($org: String!, $repo: String!, $num: Int!, $states: [PullRequestState!]) {
repository(owner: $org, name: $repo) {
pullRequests(last: $num, states: $states) {
edges {
node {
author {
login
},
title,
createdAt,
lastEditedAt,
mergedAt,
merged,
mergeable,
resourcePath,
query searchMergedPrsQuery($after: String, $num: Int!, $query: String!) {
search (first: $num, type:ISSUE, after: $after, query: $query) {
pageInfo {
hasPreviousPage,
hasNextPage,
startCursor,
endCursor
}
nodes {
... on PullRequest {
number
permalink
createdAt
closedAt
mergedAt
merged
title
lastEditedAt
additions
deletions
changedFiles
commits {
totalCount
}
comments {
totalCount
}
author {
login
}
}

@@ -25,3 +40,3 @@ }

module.exports = {
pullRequestsQuery
pullRequestsQuery,
};
const Table = require('easy-table');
const moment = require('moment');
const fs = require('fs');
const getRelativeDate = date => (date ? moment(date).fromNow() : '-');
const getDaysOpen = (fromDate, toDate) => moment(toDate).diff(fromDate, 'days');
const getHoursOpen = (fromDate, toDate) => moment(toDate).diff(fromDate, 'hours', true);
const getFromDate = () =>
moment()
.subtract(7, 'd')
.format('YYYY-MM-DD');
const getToDate = () => moment().format('YYYY-MM-DD');
const printTable = tableData => {

@@ -12,11 +20,29 @@ console.log(Table.print(tableData));

const normalizeStates = value => {
return value.replace(/ /g, '').split(',');
const constructSearchQueryString = params => {
const { org, repo, state, fromDate, toDate, author } = params;
return {
...params,
query: `repo:${org}/${repo} type:pr is:${state} created:>=${fromDate} merged:<=${toDate}${
author ? ` author:${author}` : ''
}`,
};
};
const appendToFile = (fileContent, filePathWithName = './prdata.csv') => {
fs.appendFile(filePathWithName, fileContent, err => {
if (err) {
throw err;
}
});
};
module.exports = {
getRelativeDate,
getDaysOpen,
normalizeStates,
getHoursOpen,
getFromDate,
getToDate,
printTable,
constructSearchQueryString,
appendToFile,
};
const utils = require('../../src/utils');
const Table = require('easy-table');
const fs = require('fs');
jest.mock('fs', () => ({
appendFile: jest.fn(),
}));
describe('src/utils', () => {
beforeEach(() => {
window.Date.now = jest.fn(() => '2018-05-30T23:23:53Z');
});
describe('#getRelativeDate', () => {
test('returns number of days passed', () => {
window.Date.now = jest.fn(() => '2018-05-30T23:23:53Z');
expect(utils.getRelativeDate('2018-05-20T23:23:53Z')).toEqual('10 days ago');

@@ -17,5 +24,7 @@ });

describe('#getDaysOpen', () => {
test('returns number of days passed between 2 dates', () => {
expect(utils.getDaysOpen('2018-05-20T23:23:53Z', '2018-05-25T23:40:09Z')).toEqual(5);
describe('#getHoursOpen', () => {
test('returns number of hours passed between 2 dates', () => {
expect(utils.getHoursOpen('2018-05-20T23:23:53Z', '2018-05-25T23:40:09Z')).toEqual(
120.27111111111111
);
});

@@ -43,19 +52,52 @@ });

describe('#normalizeStates', () => {
test('returns single value', () => {
expect(utils.normalizeStates('HELLO')).toEqual(['HELLO']);
describe('#getFromDate', () => {
test('returns YYYY-MM-DD format of date 7 days ago', () => {
expect(utils.getFromDate()).toEqual('2018-05-23');
});
});
test('returns array from comma separated string and removes spaces', () => {
expect(utils.normalizeStates('HELLO, WORLD')).toEqual(['HELLO', 'WORLD']);
describe('#getToDate', () => {
test('returns YYYY-MM-DD format of present day', () => {
expect(utils.getToDate()).toEqual('2018-05-30');
});
});
test('returns array from comma separated string', () => {
expect(utils.normalizeStates('HELLO,WORLD')).toEqual(['HELLO', 'WORLD']);
describe('#constructSearchQueryString', () => {
const params = {
org: 'facebook',
repo: 'react',
state: 'merged',
fromDate: '2018-01-01',
toDate: '2018-01-02',
foo: 'bar',
};
test('returns query without author', () => {
expect(utils.constructSearchQueryString(params)).toMatchObject({
...params,
query: 'repo:facebook/react type:pr is:merged created:>=2018-01-01 merged:<=2018-01-02',
});
});
test('returns array from empty string', () => {
expect(utils.normalizeStates('')).toEqual(['']);
test('returns query with author', () => {
const paramsWithAuthor = { ...params, author: 'authorName' };
expect(utils.constructSearchQueryString({ ...paramsWithAuthor })).toMatchObject({
...paramsWithAuthor,
query:
'repo:facebook/react type:pr is:merged created:>=2018-01-01 merged:<=2018-01-02 author:authorName',
});
});
});
describe('#appendToFile', () => {
test('calls appendFile', () => {
const fileContent = 'fileContentText';
const fileName = './prdata.csv';
utils.appendToFile(fileContent, fileName);
expect(fs.appendFile).toHaveBeenCalledWith(fileName, fileContent, expect.any(Function));
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc