Skip to content

Commit 83ffb2d

Browse files
authored
Revert back to new version with bugfix (#44)
* Revert "Revert "Also do created objectives (#42)" (#43)" This reverts commit aab080f. * Update index.js postfix on new dynamo entries * fix dynamo insertRecord for postfix * fix tests
1 parent aab080f commit 83ffb2d

14 files changed

+232
-83
lines changed

it/integration.test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ const index = require('../src/index');
33
describe('integration', () => {
44
const event = {
55
time: '2022-06-16T00:00:00Z'
6-
}
6+
};
77

88
test('process event', async () => {
9-
const result = await index.handler(event);
10-
})
11-
})
9+
await index.handler(event);
10+
});
11+
});

src/dynamodb.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ async function getRecord(key) {
2121
return response.Items;
2222
}
2323

24-
function insertRecord(activity) {
24+
function insertRecord(activity, postfix) {
2525
const timeToLive = activity.occurredAt / 1000 + sevenDaysInSeconds;
2626
const params = {
2727
TableName: tableName,
2828
Item: {
29-
ID: { S: activity.content.objective.id },
29+
ID: { S: activity.content.objective.id + postfix },
3030
TIMESTAMP: { N: activity.occurredAt.toString() },
3131
TTL: { N: timeToLive.toString() }
3232
}

src/filter.js

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,35 @@ const threeDaysInMillis = 3 * 24 * 60 * 60 * 1000;
55
Partially achieved status == 103
66
*/
77
function filterActivities(activities, eventDate) {
8-
const publicObjectiveStatusChanges = activities.items.flatMap(i => i.items || [])
9-
.flatMap(i => i.activities || [])
10-
.filter(a => a.type === 'OBJECTIVE_STATUS_CHANGED')
8+
const allActivities = activities.items.flatMap(i => i.items || [])
9+
.flatMap(i => i.activities || []);
10+
const recentPublicActivitesCreatedOrChanged = allActivities.filter(a => a.type === 'OBJECTIVE_STATUS_CHANGED' || a.type === 'OBJECTIVE_CREATED')
1111
.filter(a => a.content.objective.visibility === 'PUBLIC')
1212
.filter(a => a.occurredAt >= eventDate.getTime() - threeDaysInMillis);
13-
14-
const groupedObjectiveStatusChanges = publicObjectiveStatusChanges.reduce((acc, activity) => {
13+
const reducer = (acc, activity) => {
1514
acc[activity.content.objective.id] = acc[activity.content.objective.id] || [];
1615
acc[activity.content.objective.id].push(activity);
1716
return acc;
18-
}, {});
17+
};
18+
const groupedObjectivesCreated = recentPublicActivitesCreatedOrChanged.filter(a => a.type === 'OBJECTIVE_CREATED')
19+
.reduce(reducer, {});
20+
const groupedObjectiveStatusChanges = recentPublicActivitesCreatedOrChanged.filter(a => a.type === 'OBJECTIVE_STATUS_CHANGED')
21+
.reduce(reducer, {});
1922

20-
return Object.entries(groupedObjectiveStatusChanges)
21-
.flatMap(([objectiveId, activities]) => {
22-
const mostRecentObjectiveChange = activities.sort((a, b) => b.occurredAt - a.occurredAt)[0];
23-
if (mostRecentObjectiveChange.change.newStatus.status === 100 || mostRecentObjectiveChange.change.newStatus.status === 103) {
24-
return [mostRecentObjectiveChange];
25-
}
26-
return [];
27-
});
23+
return {
24+
completed: Object.entries(groupedObjectiveStatusChanges)
25+
.flatMap(([objectiveId, activities]) => {
26+
const mostRecentObjectiveChange = activities.sort((a, b) => b.occurredAt - a.occurredAt)[0];
27+
if (mostRecentObjectiveChange.change.newStatus.status === 100 || mostRecentObjectiveChange.change.newStatus.status === 103) {
28+
return [mostRecentObjectiveChange];
29+
}
30+
return [];
31+
}),
32+
created: Object.entries(groupedObjectivesCreated)
33+
.flatMap(([objectiveId, activities]) => {
34+
return [activities.sort((a, b) => b.occurredAt - a.occurredAt)[0]];
35+
})
36+
};
2837
}
2938

3039
exports.filterActivities = filterActivities;

src/index.js

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,48 @@ async function main(event, context) {
1919
*/
2020
const slackChannel = process.env.SlackChannel;
2121
const secrets = await secretsClient.getSecret();
22-
const objectiveActivities = await smallImprovementsClient.getObjectives(secrets.SIToken);
23-
const recentlyCompletedObjectives = filter.filterActivities(objectiveActivities, new Date(event.time));
24-
console.log(`Found ${recentlyCompletedObjectives.length} recently completed objectives.`);
25-
const results = await Promise.allSettled(
26-
recentlyCompletedObjectives.map(async (activity) => {
22+
const objectiveActivities = await smallImprovementsClient.GetObjectives(secrets.SIToken);
23+
const { completed, created } = filter.filterActivities(objectiveActivities, new Date(event.time));
24+
console.log(`Found ${completed.length} recently completed and ${created.length} recently created objectives.`);
25+
const completedResults = await Promise.allSettled(
26+
completed.map(async (activity) => {
2727
const exisingEntry = await dynamodbClient.getRecord(activity.content.objective.id);
2828
if (!exisingEntry?.length) {
29-
const SIEmail = await smallImprovementsClient.getEmail(activity.content.objective.owner.id, secrets.SIToken);
30-
await slackService.postObjective(
29+
const SIEmail = await smallImprovementsClient.GetEmail(activity.content.objective.owner.id, secrets.SIToken);
30+
await slackService.PostCompletedObjective(
3131
secrets.SlackToken,
3232
slackChannel,
3333
activity.content,
3434
activity.change.newStatus.description,
3535
SIEmail
3636
);
37-
await dynamodbClient.insertRecord(activity);
37+
await dynamodbClient.insertRecord(activity, '');
3838
return activity.content.objective;
3939
}
4040
return undefined;
4141
})
4242
);
43-
const successfulPosts = results.filter(x => x.value);
44-
const failedPosts = results.filter(x => x.status === 'rejected');
43+
const createdResults = await Promise.allSettled(
44+
created.map(async (activity) => {
45+
const exisingEntry = await dynamodbClient.getRecord(activity.content.objective.id + 'CREATED');
46+
if (!exisingEntry?.length) {
47+
const SIEmail = await smallImprovementsClient.GetEmail(activity.content.objective.owner.id, secrets.SIToken);
48+
await slackService.PostCreatedObjective(
49+
secrets.SlackToken,
50+
slackChannel,
51+
activity.content,
52+
SIEmail
53+
);
54+
await dynamodbClient.insertRecord(activity, 'CREATED');
55+
return activity.content.objective;
56+
}
57+
return undefined;
58+
})
59+
);
60+
const allPostResults = completedResults.concat(createdResults);
61+
const successfulPosts = allPostResults.filter(x => x.value);
62+
const failedPosts = allPostResults.filter(x => x.status === 'rejected');
63+
4564
failedPosts.forEach(fail => console.log(fail.reason));
4665
const message = `Finished ${successfulPosts.length} successfully. Failed ${failedPosts.length}`;
4766
console.log(message);

src/slack-service.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
const slackClient = require('./slack');
22

33
// Get Slack ID, Format Message, Post
4-
async function postObjective(token, channelName, content, newStatus, email) {
4+
async function PostCompletedObjective(token, channelName, content, newStatus, email) {
55
const slackID = await slackClient.getSlackID(email, token);
6-
const formattedMessage = await slackClient.formatSlackMessage(content.objective, newStatus, slackID, content.cycle.id);
7-
const postResp = await slackClient.slackPost(token, channelName, formattedMessage);
8-
return postResp;
6+
const formattedMessage = await slackClient.formatSlackMessageForCompleted(content.objective, newStatus, slackID, content.cycle.id);
7+
return await slackClient.slackPost(token, channelName, formattedMessage);
8+
}
9+
async function PostCreatedObjective(token, channelName, content, email) {
10+
const slackID = await slackClient.getSlackID(email, token);
11+
const formattedMessage = await slackClient.formatSlackMessageForCreated(content.objective, slackID, content.cycle.id);
12+
return await slackClient.slackPost(token, channelName, formattedMessage);
913
}
1014

11-
exports.postObjective = postObjective;
15+
exports.PostCompletedObjective = PostCompletedObjective;
16+
exports.PostCreatedObjective = PostCreatedObjective;

src/slack.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ async function slackPost(authToken, channelName, formattedMessage) { // postData
4141
});
4242
}
4343

44-
async function formatSlackMessage(objectiveItem, newStatus, slackUID, cycleId) {
44+
async function formatSlackMessageForCompleted(objectiveItem, newStatus, slackUID, cycleId) {
4545
const toSend = messageVariables;
4646
toSend.text = `<@${slackUID}> has ${newStatus.toLowerCase()} their goal!\n*${objectiveItem.title}*\n`;
4747
if (objectiveItem.description) {
@@ -51,6 +51,16 @@ async function formatSlackMessage(objectiveItem, newStatus, slackUID, cycleId) {
5151
return toSend;// return JSON format
5252
}
5353

54+
async function formatSlackMessageForCreated(objectiveItem, slackUID, cycleId) {
55+
const toSend = messageVariables;
56+
toSend.text = `<@${slackUID}> has created a new goal!\n*${objectiveItem.title}*\n`;
57+
if (objectiveItem.description) {
58+
toSend.text += `${formatDescription(objectiveItem.description)}\n`;
59+
}
60+
toSend.text += `<https://allies.small-improvements.com/app/objectives/${cycleId}/${objectiveItem.id}|Open in Small Improvements>`;
61+
return toSend;// return JSON format
62+
}
63+
5464
function formatDescription(description) {
5565
if (description.includes('<!--MARKUP_VERSION:v3-->')) {
5666
const bold = /<\/?strong>/gi;
@@ -119,7 +129,8 @@ function getSlackID(email, token) {
119129
});
120130
}
121131

122-
exports.formatSlackMessage = formatSlackMessage;
132+
exports.formatSlackMessageForCompleted = formatSlackMessageForCompleted;
133+
exports.formatSlackMessageForCreated = formatSlackMessageForCreated;
123134
exports.formatDescription = formatDescription;
124135
exports.slackPost = slackPost;
125136
exports.getSlackID = getSlackID;

src/small-improvements.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,5 @@ function getEmail(SIUID, token) { // SIUID (Small Improvements User ID) is in th
7070
});
7171
}
7272

73-
exports.getObjectives = getObjectives;
74-
exports.getEmail = getEmail;
73+
exports.GetObjectives = getObjectives;
74+
exports.GetEmail = getEmail;

test/data-factory.js

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const defaultNewStatus = {
44
status: 100
55
};
66

7-
exports.createActivity = (activityProps, objectiveProps, newStatus = defaultNewStatus) => {
7+
exports.createCompletedActivity = (activityProps, objectiveProps, newStatus = defaultNewStatus) => {
88
return {
99
actor: {
1010
firstName: 'Developer'
@@ -49,3 +49,42 @@ exports.createActivity = (activityProps, objectiveProps, newStatus = defaultNewS
4949
...activityProps
5050
};
5151
};
52+
53+
exports.createCreatedActivity = (activityProps, objectiveProps) => {
54+
return {
55+
actor: {
56+
firstName: 'Developer'
57+
},
58+
occurredAt: 1651856682326,
59+
change: {},
60+
id: 'w02bdXSdzFNre*n3plJQgQ',
61+
type: 'OBJECTIVE_CREATED',
62+
targets: [
63+
{
64+
firstName: 'Developer'
65+
}
66+
],
67+
content: {
68+
cycle: {
69+
id: 'E0hlMiEuRi7T7Md8PWyQuQ',
70+
name: 'Objective Cycle 2022'
71+
},
72+
objective: {
73+
id: 'w02bdXSdzFNre*n3plJQll',
74+
icon: 'o_3goldstars',
75+
title: 'Objective Title',
76+
description: '<!--MARKUP_VERSION:v3--><p>Description</p>',
77+
dueDate: '2022-06-30T05:00:00.000Z',
78+
owner: {
79+
firstName: 'Developer',
80+
id: 'V*jArA9pQbaK0U7grc9Qrw',
81+
email: 'rappling@sourceallies.com'
82+
},
83+
visibility: 'PUBLIC',
84+
visibleTo: [],
85+
...objectiveProps
86+
}
87+
},
88+
...activityProps
89+
};
90+
};

test/dynamodb.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ describe('dynamodb', () => {
9090
});
9191
putItemPromise.mockResolvedValue({});
9292

93-
const result = await dynamodbClient.insertRecord(activity);
93+
const result = await dynamodbClient.insertRecord(activity, '');
9494

9595
expect(result).toStrictEqual({});
9696
expect(mockPutItem).toBeCalledWith({

test/filter.test.js

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,20 @@ describe('filter activities', () => {
1919
{
2020
occurredAt: 1651856682326,
2121
activities: [
22-
dataFactory.createActivity(
22+
dataFactory.createCompletedActivity(
23+
{ occurredAt: new Date(eventDateString).getTime() - (60 * 1000) },
24+
{ id: objectiveId }
25+
),
26+
dataFactory.createCreatedActivity(
2327
{ occurredAt: new Date(eventDateString).getTime() - (60 * 1000) },
2428
{ id: objectiveId }
2529
)
2630
]
2731
}
2832
]
33+
},
34+
{
35+
2936
}
3037
]
3138
};
@@ -36,7 +43,8 @@ describe('filter activities', () => {
3643

3744
const result = filter.filterActivities(activities, new Date(eventDateString));
3845

39-
expect(result).toHaveLength(1);
46+
expect(result.completed).toHaveLength(1);
47+
expect(result.created).toHaveLength(1);
4048
});
4149

4250
test('should ignore items with no activities', () => {
@@ -46,7 +54,8 @@ describe('filter activities', () => {
4654

4755
const result = filter.filterActivities(activities, new Date(eventDateString));
4856

49-
expect(result).toHaveLength(1);
57+
expect(result.completed).toHaveLength(1);
58+
expect(result.created).toHaveLength(1);
5059
});
5160

5261
test('should remove activity with status change to in progress', () => {
@@ -58,23 +67,28 @@ describe('filter activities', () => {
5867

5968
const result = filter.filterActivities(activities, new Date(eventDateString));
6069

61-
expect(result).toHaveLength(0);
70+
expect(result.completed).toHaveLength(0);
71+
expect(result.created).toHaveLength(1);
6272
});
6373

6474
test('should remove private objectives', () => {
6575
activities.items[0].items[0].activities[0].content.objective.visibility = 'PRIVATE';
76+
activities.items[0].items[0].activities[1].content.objective.visibility = 'PRIVATE';
6677

6778
const result = filter.filterActivities(activities, new Date(eventDateString));
6879

69-
expect(result).toHaveLength(0);
80+
expect(result.completed).toHaveLength(0);
81+
expect(result.created).toHaveLength(0);
7082
});
7183

7284
test('should remove old objective changes', () => {
7385
activities.items[0].items[0].activities[0].occurredAt = new Date(eventDateString).getTime() - (3 * 24 * 60 * 60 * 1000) - 1;
86+
activities.items[0].items[0].activities[1].occurredAt = new Date(eventDateString).getTime() - (3 * 24 * 60 * 60 * 1000) - 1;
7487

7588
const result = filter.filterActivities(activities, new Date(eventDateString));
7689

77-
expect(result).toHaveLength(0);
90+
expect(result.completed).toHaveLength(0);
91+
expect(result.created).toHaveLength(0);
7892
});
7993

8094
test('should keep objective when most recent activity is achieved', () => {
@@ -87,7 +101,7 @@ describe('filter activities', () => {
87101
{
88102
occurredAt: 1651856682326,
89103
activities: [
90-
dataFactory.createActivity(
104+
dataFactory.createCompletedActivity(
91105
{ occurredAt: pastTime },
92106
{ id: objectiveId },
93107
{
@@ -103,7 +117,7 @@ describe('filter activities', () => {
103117

104118
const result = filter.filterActivities(activities, new Date(eventDateString));
105119

106-
expect(result).toHaveLength(1);
120+
expect(result.completed).toHaveLength(1);
107121
});
108122

109123
test('should remove objective when most recent activity is not achieved or partially achieved', () => {
@@ -116,7 +130,7 @@ describe('filter activities', () => {
116130
{
117131
occurredAt: 1651856682326,
118132
activities: [
119-
dataFactory.createActivity(
133+
dataFactory.createCompletedActivity(
120134
{ occurredAt: futureTime },
121135
{ id: objectiveId },
122136
{
@@ -132,6 +146,6 @@ describe('filter activities', () => {
132146

133147
const result = filter.filterActivities(activities, new Date(eventDateString));
134148

135-
expect(result).toHaveLength(0);
149+
expect(result.completed).toHaveLength(0);
136150
});
137151
});

0 commit comments

Comments
 (0)