Skip to content

Commit b1825f0

Browse files
committed
v3.2.2: removing empty users and limiting slack messages
1 parent 312efe2 commit b1825f0

13 files changed

Lines changed: 174 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Changelog
22
All notable changes to this project will be documented in this file.
33

4+
## [3.2.2] - 2025-02-03
5+
### Fixed
6+
- Removes the users with empty stats.
7+
- Limits the number of stats displayed in the Slack message to 10.
8+
49
## [3.2.1] - 2025-01-19
510
### Fixed
611
- Makes the `include` and `exclude` options case insensitive (solves #101)

dist/index.js

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42800,6 +42800,7 @@ module.exports = ({
4280042800

4280142801
const calculateBests = __nccwpck_require__(8657);
4280242802
const getTableData = __nccwpck_require__(6026);
42803+
const removeEmpty = __nccwpck_require__(993);
4280342804
const sortByStats = __nccwpck_require__(3256);
4280442805

4280542806
const applyLimit = (data, limit) => (limit > 0 ? data.slice(0, limit) : data);
@@ -42814,7 +42815,8 @@ module.exports = ({
4281442815
}) => {
4281542816
const execute = () => {
4281642817
const sortByStat = sortBy || mainStats[0];
42817-
const sorted = applyLimit(sortByStats(entries, sortByStat), limit);
42818+
const filtered = removeEmpty(entries, mainStats);
42819+
const sorted = applyLimit(sortByStats(filtered, sortByStat), limit);
4281842820
const bests = calculateBests(sorted);
4281942821

4282042822
return getTableData({
@@ -42830,6 +42832,17 @@ module.exports = ({
4283042832
};
4283142833

4283242834

42835+
/***/ }),
42836+
42837+
/***/ 993:
42838+
/***/ ((module) => {
42839+
42840+
const removeEmpty = (entries, mainStats) => entries
42841+
.filter((entry) => mainStats.some((stat) => !!entry.stats[stat]));
42842+
42843+
module.exports = removeEmpty;
42844+
42845+
4283342846
/***/ }),
4283442847

4283542848
/***/ 3256:
@@ -43535,8 +43548,8 @@ const getUsername = ({ text, image, emoji }) => {
4353543548
};
4353643549
};
4353743550

43538-
const getStats = ({ row, statNames }) => {
43539-
const { stats } = row;
43551+
const getStats = ({ row, maxStats, statNames }) => {
43552+
const stats = maxStats > 0 ? row.stats.slice(0, maxStats) : row.stats;
4354043553
const fields = stats.map(({ text, link }, index) => {
4354143554
const value = link ? `<${link}|${text}>` : text;
4354243555
return {
@@ -43557,10 +43570,11 @@ const getDivider = () => ({
4355743570

4355843571
module.exports = ({
4355943572
row,
43573+
maxStats,
4356043574
statNames,
4356143575
}) => [
4356243576
getUsername(row.user),
43563-
getStats({ row, statNames }),
43577+
getStats({ row, maxStats, statNames }),
4356443578
getDivider(),
4356543579
];
4356643580

@@ -43621,6 +43635,7 @@ module.exports = ({
4362143635
table,
4362243636
pullRequest,
4362343637
periodLength,
43638+
maxStats,
4362443639
}) => ({
4362543640
blocks: [
4362643641
...buildSubtitle({
@@ -43634,7 +43649,12 @@ module.exports = ({
4363443649
...table.rows.reduce(
4363543650
(prev, row) => [
4363643651
...prev,
43637-
...buildRow({ row, statNames: getStatNames(table.headers) })],
43652+
...buildRow({
43653+
row,
43654+
maxStats,
43655+
statNames: getStatNames(table.headers),
43656+
}),
43657+
],
4363843658
[],
4363943659
),
4364043660
],
@@ -43651,6 +43671,8 @@ const { postToSlack } = __nccwpck_require__(5045);
4365143671
const { SlackSplitter } = __nccwpck_require__(7117);
4365243672
const buildMessage = __nccwpck_require__(6429);
4365343673

43674+
const MAX_STATS_PER_BLOCK = 10; // https://api.slack.com/reference/block-kit/blocks
43675+
4365443676
module.exports = async ({
4365543677
core,
4365643678
org,
@@ -43673,6 +43695,13 @@ module.exports = async ({
4367343695
return;
4367443696
}
4367543697

43698+
const statsCount = table.rows[0]?.stats?.length;
43699+
if (statsCount > MAX_STATS_PER_BLOCK) {
43700+
core.warning(t('integrations.slack.errors.statsLimitExceeded', {
43701+
statsLimit: MAX_STATS_PER_BLOCK,
43702+
}));
43703+
}
43704+
4367643705
const send = (message) => {
4367743706
const params = {
4367843707
webhook,
@@ -43693,6 +43722,7 @@ module.exports = async ({
4369343722
table,
4369443723
pullRequest,
4369543724
periodLength,
43725+
maxStats: MAX_STATS_PER_BLOCK,
4369643726
});
4369743727

4369843728
const { chunks } = new SlackSplitter({ message: fullMessage });
@@ -49640,7 +49670,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"name":"mixpanel","description":"A si
4964049670
/***/ ((module) => {
4964149671

4964249672
"use strict";
49643-
module.exports = /*#__PURE__*/JSON.parse('{"name":"pull-request-stats","version":"3.2.1","description":"Github action to print relevant stats about Pull Request reviewers","main":"dist/index.js","type":"commonjs","scripts":{"build":"eslint src && ncc build src/index.js -o dist -a","test":"jest","lint":"eslint ./"},"keywords":[],"author":"Manuel de la Torre","license":"MIT","jest":{"testEnvironment":"node","testMatch":["**/?(*.)+(spec|test).[jt]s?(x)"]},"dependencies":{"@actions/core":"^1.11.1","@actions/github":"^6.0.0","axios":"^1.7.9","humanize-duration":"^3.32.1","i18n-js":"^3.9.2","jsurl":"^0.1.5","lodash.get":"^4.4.2","markdown-table":"^2.0.0","mixpanel":"^0.18.0"},"devDependencies":{"@eslint/eslintrc":"^3.2.0","@eslint/js":"^9.16.0","@vercel/ncc":"^0.38.3","eslint":"^9.16.0","eslint-config-airbnb-base":"^15.0.0","eslint-plugin-import":"^2.31.0","eslint-plugin-jest":"^28.9.0","globals":"^15.13.0","jest":"^29.7.0"},"funding":"https://github.com/sponsors/manuelmhtr","packageManager":"yarn@4.1.0"}');
49673+
module.exports = /*#__PURE__*/JSON.parse('{"name":"pull-request-stats","version":"3.2.2","description":"Github action to print relevant stats about Pull Request reviewers","main":"dist/index.js","type":"commonjs","scripts":{"build":"eslint src && ncc build src/index.js -o dist -a","test":"jest","lint":"eslint ./"},"keywords":[],"author":"Manuel de la Torre","license":"MIT","jest":{"testEnvironment":"node","testMatch":["**/?(*.)+(spec|test).[jt]s?(x)"]},"dependencies":{"@actions/core":"^1.11.1","@actions/github":"^6.0.0","axios":"^1.7.9","humanize-duration":"^3.32.1","i18n-js":"^3.9.2","jsurl":"^0.1.5","lodash.get":"^4.4.2","markdown-table":"^2.0.0","mixpanel":"^0.18.0"},"devDependencies":{"@eslint/eslintrc":"^3.2.0","@eslint/js":"^9.16.0","@vercel/ncc":"^0.38.3","eslint":"^9.16.0","eslint-config-airbnb-base":"^15.0.0","eslint-plugin-import":"^2.31.0","eslint-plugin-jest":"^28.9.0","globals":"^15.13.0","jest":"^29.7.0"},"funding":"https://github.com/sponsors/manuelmhtr","packageManager":"yarn@4.1.0"}');
4964449674

4964549675
/***/ }),
4964649676

@@ -49656,7 +49686,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"logs":{"success":"Action successfull
4965649686
/***/ ((module) => {
4965749687

4965849688
"use strict";
49659-
module.exports = /*#__PURE__*/JSON.parse('{"slack":{"logs":{"notConfigured":"Slack integration is disabled. No webhook or channel configured.","posting":"Post a Slack message with params: {{params}}","success":"Successfully posted to slack"},"errors":{"notSponsor":"Slack integration is a premium feature, available to sponsors.\\n(If you are already an sponsor, please make sure it is configured as public).","requestFailed":"Error posting Slack message: {{error}}"}},"teams":{"logs":{"notConfigured":"Microsoft Teams integration is disabled. No webhook configured.","posting":"Post a MS Teams message with params: {{params}}","success":"Successfully posted to MS Teams"},"errors":{"notSponsor":"Microsoft Teams integration is a premium feature, available to sponsors.\\n(If you are already an sponsor, please make sure it is configured as public).","requestFailed":"Error posting MS Teams message: {{error}}"}},"webhook":{"logs":{"notConfigured":"Webhook integration is disabled.","posting":"Post a Slack message with params: {{params}}","success":"Successfully posted to slack"},"errors":{"requestFailed":"Error posting Webhook: {{error}}"}},"summary":{"logs":{"posting":"Post action summary: {{content}}","success":"Successfully posted to action summary"},"errors":{"writeFailed":"Error posting action summary: {{error}}"}}}');
49689+
module.exports = /*#__PURE__*/JSON.parse('{"slack":{"logs":{"notConfigured":"Slack integration is disabled. No webhook or channel configured.","posting":"Post a Slack message with params: {{params}}","success":"Successfully posted to slack"},"errors":{"notSponsor":"Slack integration is a premium feature, available to sponsors.\\n(If you are already an sponsor, please make sure it is configured as public).","requestFailed":"Error posting Slack message: {{error}}"}},"teams":{"logs":{"notConfigured":"Microsoft Teams integration is disabled. No webhook configured.","posting":"Post a MS Teams message with params: {{params}}","success":"Successfully posted to MS Teams"},"errors":{"notSponsor":"Microsoft Teams integration is a premium feature, available to sponsors.\\n(If you are already an sponsor, please make sure it is configured as public).","requestFailed":"Error posting MS Teams message: {{error}}"}},"webhook":{"logs":{"notConfigured":"Webhook integration is disabled.","posting":"Post a Slack message with params: {{params}}","success":"Successfully posted to slack"},"errors":{"requestFailed":"Error posting Webhook: {{error}}","statsLimitExceeded":"Slack integration cannot post more than {{statsLimit}} stats due to API limits. Reduce the number of stats to post in the \'stats\' parameter to avoid this error."}},"summary":{"logs":{"posting":"Post action summary: {{content}}","success":"Successfully posted to action summary"},"errors":{"writeFailed":"Error posting action summary: {{error}}"}}}');
4966049690

4966149691
/***/ }),
4966249692

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pull-request-stats",
3-
"version": "3.2.1",
3+
"version": "3.2.2",
44
"description": "Github action to print relevant stats about Pull Request reviewers",
55
"main": "dist/index.js",
66
"type": "commonjs",

src/i18n/locales/en-US/integrations.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
"success": "Successfully posted to slack"
2929
},
3030
"errors": {
31-
"requestFailed": "Error posting Webhook: {{error}}"
31+
"requestFailed": "Error posting Webhook: {{error}}",
32+
"statsLimitExceeded": "Slack integration cannot post more than {{statsLimit}} stats due to API limits. Reduce the number of stats to post in the 'stats' parameter to avoid this error."
3233
}
3334
},
3435
"summary": {

src/interactors/buildTable/__tests__/index.test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,12 @@ describe('Interactors | .buildTable', () => {
4747
displayCharts: defaultParams.displayCharts,
4848
}));
4949
});
50+
51+
it('removes the entries with empty stats', () => {
52+
const response = buildTable({
53+
...defaultParams,
54+
mainStats: [],
55+
});
56+
expect(response.rows.length).toEqual(0);
57+
});
5058
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const { entries } = require('../../../../tests/mocks');
2+
const removeEmpty = require('../removeEmpty');
3+
4+
describe('Interactors | .buildTable | .removeEmpty', () => {
5+
const mainStats = ['timeToReview', 'totalReviews', 'totalComments'];
6+
7+
it('keeps all the entry when no stats are empty', () => {
8+
const response = removeEmpty(entries, mainStats);
9+
expect(response).toHaveLength(entries.length);
10+
expect(response).toMatchObject(entries);
11+
});
12+
13+
it('removes the entries if they have no requested stats', () => {
14+
const input = entries.map((entry) => ({
15+
...entry,
16+
stats: {
17+
...entry.stats,
18+
timeToReview: null,
19+
totalReviews: null,
20+
totalComments: null,
21+
},
22+
}));
23+
const response = removeEmpty(input, mainStats);
24+
expect(response).toEqual([]);
25+
});
26+
27+
it('keeps the entries if they have some requested stats', () => {
28+
const input = entries.map((entry) => ({
29+
...entry,
30+
stats: {
31+
...entry.stats,
32+
totalComments: 0,
33+
},
34+
}));
35+
const response = removeEmpty(input, mainStats);
36+
expect(response).toHaveLength(entries.length);
37+
expect(response).toMatchObject(input);
38+
});
39+
40+
it('removes all if no stats are requested', () => {
41+
const response = removeEmpty(entries, []);
42+
expect(response).toEqual([]);
43+
});
44+
});

src/interactors/buildTable/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const calculateBests = require('./calculateBests');
22
const getTableData = require('./getTableData');
3+
const removeEmpty = require('./removeEmpty');
34
const sortByStats = require('./sortByStats');
45

56
const applyLimit = (data, limit) => (limit > 0 ? data.slice(0, limit) : data);
@@ -14,7 +15,8 @@ module.exports = ({
1415
}) => {
1516
const execute = () => {
1617
const sortByStat = sortBy || mainStats[0];
17-
const sorted = applyLimit(sortByStats(entries, sortByStat), limit);
18+
const filtered = removeEmpty(entries, mainStats);
19+
const sorted = applyLimit(sortByStats(filtered, sortByStat), limit);
1820
const bests = calculateBests(sorted);
1921

2022
return getTableData({
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const removeEmpty = (entries, mainStats) => entries
2+
.filter((entry) => mainStats.some((stat) => !!entry.stats[stat]));
3+
4+
module.exports = removeEmpty;

src/interactors/postSlackMessage/__tests__/index.test.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const { t } = require('../../../i18n');
33
const buildMessage = require('../buildMessage');
44
const postSlackMessage = require('../index');
55

6+
const MAX_STATS_PER_BLOCK = 10;
67
const MESSAGE = {
78
blocks: [
89
{ type: 'section', text: 'MESSAGE' },
@@ -15,18 +16,25 @@ jest.mock('../buildMessage', () => jest.fn());
1516
describe('Interactors | .postSlackMessage', () => {
1617
const debug = jest.fn();
1718
const error = jest.fn();
19+
const warning = jest.fn();
1820
const setFailed = jest.fn();
21+
const table = {
22+
rows: [
23+
{ stats: [] },
24+
],
25+
};
1926

2027
const core = {
2128
debug,
2229
error,
30+
warning,
2331
setFailed,
2432
};
2533

2634
const defaultOptions = {
2735
core,
36+
table,
2837
isSponsor: true,
29-
table: 'TABLE',
3038
pullRequest: 'PULl REQUEST',
3139
periodLength: 'PERIOD LENGTH',
3240
slack: {
@@ -74,11 +82,29 @@ describe('Interactors | .postSlackMessage', () => {
7482
});
7583
});
7684

85+
describe('when there are more than the permitted stats', () => {
86+
it('warns the user and posts successfully to Slack', async () => {
87+
const inputTable = {
88+
...table,
89+
rows: [
90+
{ stats: new Array(MAX_STATS_PER_BLOCK + 1) },
91+
],
92+
};
93+
const expectedWarning = t('integrations.slack.errors.statsLimitExceeded', {
94+
statsLimit: MAX_STATS_PER_BLOCK,
95+
});
96+
await postSlackMessage({ ...defaultOptions, table: inputTable });
97+
expect(warning).toHaveBeenCalledWith(expectedWarning);
98+
expect(Fetchers.postToSlack).toHaveBeenCalled();
99+
});
100+
});
101+
77102
describe('when integration is enabled', () => {
78103
it('posts successfully to Slack', async () => {
79104
await postSlackMessage({ ...defaultOptions });
80105
expect(error).not.toHaveBeenCalled();
81106
expect(buildMessage).toBeCalledWith({
107+
maxStats: MAX_STATS_PER_BLOCK,
82108
table: defaultOptions.table,
83109
pullRequest: defaultOptions.pullRequest,
84110
periodLength: defaultOptions.periodLength,

src/interactors/postSlackMessage/buildMessage/__tests__/buildRow.test.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const buildRow = require('../buildRow');
44
const [row] = table.rows;
55
const defaultParams = {
66
row,
7+
maxStats: 0,
78
statNames: table.headers.slice(1).map(({ text }) => text),
89
};
910

@@ -67,8 +68,13 @@ describe('Interactors | postSlackMessage | .buildRow', () => {
6768

6869
describe('when the user has no emoji', () => {
6970
it('adds no medal to the username', () => {
70-
const rowCopy = { ...row };
71-
rowCopy.user.emoji = null;
71+
const rowCopy = {
72+
...row,
73+
user: {
74+
...row.user,
75+
emoji: null,
76+
},
77+
};
7278
const response = buildRow({ ...defaultParams, row: rowCopy });
7379
expect(response).toEqual([
7480
{
@@ -87,4 +93,18 @@ describe('Interactors | postSlackMessage | .buildRow', () => {
8793
]);
8894
});
8995
});
96+
97+
describe('when limiting the number of stats', () => {
98+
it('shows the correct number of stats', () => {
99+
const response = buildRow({ ...defaultParams, maxStats: 2 });
100+
expect(response).toEqual([
101+
USERNAME,
102+
{
103+
...STATS,
104+
fields: STATS.fields.slice(0, 2),
105+
},
106+
DIVIDER,
107+
]);
108+
});
109+
});
90110
});

0 commit comments

Comments
 (0)