Skip to content

Commit

Permalink
v3.2.2: removing empty users and limiting slack messages
Browse files Browse the repository at this point in the history
  • Loading branch information
manuelmhtr committed Feb 3, 2025
1 parent 312efe2 commit b1825f0
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 17 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Changelog
All notable changes to this project will be documented in this file.

## [3.2.2] - 2025-02-03
### Fixed
- Removes the users with empty stats.
- Limits the number of stats displayed in the Slack message to 10.

## [3.2.1] - 2025-01-19
### Fixed
- Makes the `include` and `exclude` options case insensitive (solves #101)
Expand Down
44 changes: 37 additions & 7 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42800,6 +42800,7 @@ module.exports = ({

const calculateBests = __nccwpck_require__(8657);
const getTableData = __nccwpck_require__(6026);
const removeEmpty = __nccwpck_require__(993);
const sortByStats = __nccwpck_require__(3256);

const applyLimit = (data, limit) => (limit > 0 ? data.slice(0, limit) : data);
Expand All @@ -42814,7 +42815,8 @@ module.exports = ({
}) => {
const execute = () => {
const sortByStat = sortBy || mainStats[0];
const sorted = applyLimit(sortByStats(entries, sortByStat), limit);
const filtered = removeEmpty(entries, mainStats);
const sorted = applyLimit(sortByStats(filtered, sortByStat), limit);
const bests = calculateBests(sorted);

return getTableData({
Expand All @@ -42830,6 +42832,17 @@ module.exports = ({
};


/***/ }),

/***/ 993:
/***/ ((module) => {

const removeEmpty = (entries, mainStats) => entries
.filter((entry) => mainStats.some((stat) => !!entry.stats[stat]));

module.exports = removeEmpty;


/***/ }),

/***/ 3256:
Expand Down Expand Up @@ -43535,8 +43548,8 @@ const getUsername = ({ text, image, emoji }) => {
};
};

const getStats = ({ row, statNames }) => {
const { stats } = row;
const getStats = ({ row, maxStats, statNames }) => {
const stats = maxStats > 0 ? row.stats.slice(0, maxStats) : row.stats;
const fields = stats.map(({ text, link }, index) => {
const value = link ? `<${link}|${text}>` : text;
return {
Expand All @@ -43557,10 +43570,11 @@ const getDivider = () => ({

module.exports = ({
row,
maxStats,
statNames,
}) => [
getUsername(row.user),
getStats({ row, statNames }),
getStats({ row, maxStats, statNames }),
getDivider(),
];

Expand Down Expand Up @@ -43621,6 +43635,7 @@ module.exports = ({
table,
pullRequest,
periodLength,
maxStats,
}) => ({
blocks: [
...buildSubtitle({
Expand All @@ -43634,7 +43649,12 @@ module.exports = ({
...table.rows.reduce(
(prev, row) => [
...prev,
...buildRow({ row, statNames: getStatNames(table.headers) })],
...buildRow({
row,
maxStats,
statNames: getStatNames(table.headers),
}),
],
[],
),
],
Expand All @@ -43651,6 +43671,8 @@ const { postToSlack } = __nccwpck_require__(5045);
const { SlackSplitter } = __nccwpck_require__(7117);
const buildMessage = __nccwpck_require__(6429);

const MAX_STATS_PER_BLOCK = 10; // https://api.slack.com/reference/block-kit/blocks

module.exports = async ({
core,
org,
Expand All @@ -43673,6 +43695,13 @@ module.exports = async ({
return;
}

const statsCount = table.rows[0]?.stats?.length;
if (statsCount > MAX_STATS_PER_BLOCK) {
core.warning(t('integrations.slack.errors.statsLimitExceeded', {
statsLimit: MAX_STATS_PER_BLOCK,
}));
}

const send = (message) => {
const params = {
webhook,
Expand All @@ -43693,6 +43722,7 @@ module.exports = async ({
table,
pullRequest,
periodLength,
maxStats: MAX_STATS_PER_BLOCK,
});

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

"use strict";
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"}');
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"}');

/***/ }),

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

"use strict";
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}}"}}}');
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}}"}}}');

/***/ }),

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pull-request-stats",
"version": "3.2.1",
"version": "3.2.2",
"description": "Github action to print relevant stats about Pull Request reviewers",
"main": "dist/index.js",
"type": "commonjs",
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/locales/en-US/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"success": "Successfully posted to slack"
},
"errors": {
"requestFailed": "Error posting Webhook: {{error}}"
"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": {
Expand Down
8 changes: 8 additions & 0 deletions src/interactors/buildTable/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,12 @@ describe('Interactors | .buildTable', () => {
displayCharts: defaultParams.displayCharts,
}));
});

it('removes the entries with empty stats', () => {
const response = buildTable({
...defaultParams,
mainStats: [],
});
expect(response.rows.length).toEqual(0);
});
});
44 changes: 44 additions & 0 deletions src/interactors/buildTable/__tests__/removeEmpty.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const { entries } = require('../../../../tests/mocks');
const removeEmpty = require('../removeEmpty');

describe('Interactors | .buildTable | .removeEmpty', () => {
const mainStats = ['timeToReview', 'totalReviews', 'totalComments'];

it('keeps all the entry when no stats are empty', () => {
const response = removeEmpty(entries, mainStats);
expect(response).toHaveLength(entries.length);
expect(response).toMatchObject(entries);
});

it('removes the entries if they have no requested stats', () => {
const input = entries.map((entry) => ({
...entry,
stats: {
...entry.stats,
timeToReview: null,
totalReviews: null,
totalComments: null,
},
}));
const response = removeEmpty(input, mainStats);
expect(response).toEqual([]);
});

it('keeps the entries if they have some requested stats', () => {
const input = entries.map((entry) => ({
...entry,
stats: {
...entry.stats,
totalComments: 0,
},
}));
const response = removeEmpty(input, mainStats);
expect(response).toHaveLength(entries.length);
expect(response).toMatchObject(input);
});

it('removes all if no stats are requested', () => {
const response = removeEmpty(entries, []);
expect(response).toEqual([]);
});
});
4 changes: 3 additions & 1 deletion src/interactors/buildTable/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const calculateBests = require('./calculateBests');
const getTableData = require('./getTableData');
const removeEmpty = require('./removeEmpty');
const sortByStats = require('./sortByStats');

const applyLimit = (data, limit) => (limit > 0 ? data.slice(0, limit) : data);
Expand All @@ -14,7 +15,8 @@ module.exports = ({
}) => {
const execute = () => {
const sortByStat = sortBy || mainStats[0];
const sorted = applyLimit(sortByStats(entries, sortByStat), limit);
const filtered = removeEmpty(entries, mainStats);
const sorted = applyLimit(sortByStats(filtered, sortByStat), limit);
const bests = calculateBests(sorted);

return getTableData({
Expand Down
4 changes: 4 additions & 0 deletions src/interactors/buildTable/removeEmpty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const removeEmpty = (entries, mainStats) => entries
.filter((entry) => mainStats.some((stat) => !!entry.stats[stat]));

module.exports = removeEmpty;
28 changes: 27 additions & 1 deletion src/interactors/postSlackMessage/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { t } = require('../../../i18n');
const buildMessage = require('../buildMessage');
const postSlackMessage = require('../index');

const MAX_STATS_PER_BLOCK = 10;
const MESSAGE = {
blocks: [
{ type: 'section', text: 'MESSAGE' },
Expand All @@ -15,18 +16,25 @@ jest.mock('../buildMessage', () => jest.fn());
describe('Interactors | .postSlackMessage', () => {
const debug = jest.fn();
const error = jest.fn();
const warning = jest.fn();
const setFailed = jest.fn();
const table = {
rows: [
{ stats: [] },
],
};

const core = {
debug,
error,
warning,
setFailed,
};

const defaultOptions = {
core,
table,
isSponsor: true,
table: 'TABLE',
pullRequest: 'PULl REQUEST',
periodLength: 'PERIOD LENGTH',
slack: {
Expand Down Expand Up @@ -74,11 +82,29 @@ describe('Interactors | .postSlackMessage', () => {
});
});

describe('when there are more than the permitted stats', () => {
it('warns the user and posts successfully to Slack', async () => {
const inputTable = {
...table,
rows: [
{ stats: new Array(MAX_STATS_PER_BLOCK + 1) },
],
};
const expectedWarning = t('integrations.slack.errors.statsLimitExceeded', {
statsLimit: MAX_STATS_PER_BLOCK,
});
await postSlackMessage({ ...defaultOptions, table: inputTable });
expect(warning).toHaveBeenCalledWith(expectedWarning);
expect(Fetchers.postToSlack).toHaveBeenCalled();
});
});

describe('when integration is enabled', () => {
it('posts successfully to Slack', async () => {
await postSlackMessage({ ...defaultOptions });
expect(error).not.toHaveBeenCalled();
expect(buildMessage).toBeCalledWith({
maxStats: MAX_STATS_PER_BLOCK,
table: defaultOptions.table,
pullRequest: defaultOptions.pullRequest,
periodLength: defaultOptions.periodLength,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const buildRow = require('../buildRow');
const [row] = table.rows;
const defaultParams = {
row,
maxStats: 0,
statNames: table.headers.slice(1).map(({ text }) => text),
};

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

describe('when the user has no emoji', () => {
it('adds no medal to the username', () => {
const rowCopy = { ...row };
rowCopy.user.emoji = null;
const rowCopy = {
...row,
user: {
...row.user,
emoji: null,
},
};
const response = buildRow({ ...defaultParams, row: rowCopy });
expect(response).toEqual([
{
Expand All @@ -87,4 +93,18 @@ describe('Interactors | postSlackMessage | .buildRow', () => {
]);
});
});

describe('when limiting the number of stats', () => {
it('shows the correct number of stats', () => {
const response = buildRow({ ...defaultParams, maxStats: 2 });
expect(response).toEqual([
USERNAME,
{
...STATS,
fields: STATS.fields.slice(0, 2),
},
DIVIDER,
]);
});
});
});
7 changes: 4 additions & 3 deletions src/interactors/postSlackMessage/buildMessage/buildRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ const getUsername = ({ text, image, emoji }) => {
};
};

const getStats = ({ row, statNames }) => {
const { stats } = row;
const getStats = ({ row, maxStats, statNames }) => {
const stats = maxStats > 0 ? row.stats.slice(0, maxStats) : row.stats;
const fields = stats.map(({ text, link }, index) => {
const value = link ? `<${link}|${text}>` : text;
return {
Expand All @@ -47,9 +47,10 @@ const getDivider = () => ({

module.exports = ({
row,
maxStats,
statNames,
}) => [
getUsername(row.user),
getStats({ row, statNames }),
getStats({ row, maxStats, statNames }),
getDivider(),
];
8 changes: 7 additions & 1 deletion src/interactors/postSlackMessage/buildMessage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = ({
table,
pullRequest,
periodLength,
maxStats,
}) => ({
blocks: [
...buildSubtitle({
Expand All @@ -23,7 +24,12 @@ module.exports = ({
...table.rows.reduce(
(prev, row) => [
...prev,
...buildRow({ row, statNames: getStatNames(table.headers) })],
...buildRow({
row,
maxStats,
statNames: getStatNames(table.headers),
}),
],
[],
),
],
Expand Down
Loading

0 comments on commit b1825f0

Please sign in to comment.