Skip to content

Commit 688ed4e

Browse files
author
MineMe0812
committed
fix bugs in notifications
1 parent a7dd00e commit 688ed4e

File tree

13 files changed

+207
-23
lines changed

13 files changed

+207
-23
lines changed

config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ prod:
1212
COGNITO_POOL_ARN: arn:aws:cognito-idp:us-east-1:501132611696:userpool/us-east-1_HSo98n6V8
1313
COGNITO_POOL_ID: us-east-1_HSo98n6V8
1414
ERROR_LEVEL: CRITICAL
15-
SNS_NOTI_TRIGGER_ARN: arn:aws:sns:us-east-1:501132611696:pennyboxapp-dev-noti_trigger-pipe
15+
SNS_NOTI_TRIGGER_ARN: arn:aws:sns:us-east-1:501132611696:pennyboxapp-prod-noti_trigger-pipe
1616
SNS_PUSH_APN_ARN: arn:aws:sns:us-east-1:501132611696:app/APNS/PennyboxApp-Prod
1717
CUSTOM_DOMAIN_NAME: api-prod.pennybox.com
1818
test:

fixtures/offline-seeds/job.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[{
1+
[{
22
"id": "c796d733-9779-45c5-a130-20fd1fd0b652",
33
"familyId": "287ef3f0-d79f-11e7-a2eb-bb9bea49fda2",
44
"childUserId": "a2f5acbc-6e3e-4acf-b13f-9ea98e474237",
@@ -19,6 +19,22 @@
1919
"issuedAt": "1506423488524",
2020
"issuedBy": "c796d733-9779-45c5-a130-20fd1fd0b652",
2121
"meta": {}
22+
}, {
23+
"status": "SUMMARY_UPDATED",
24+
"issuedAt": "1506423488524",
25+
"issuedBy": "c796d733-9779-45c5-a130-20fd1fd0b652",
26+
"meta": {
27+
"before": {
28+
"title": "Walk the dog before",
29+
"price": 4,
30+
"backdropResource": "old url"
31+
},
32+
"after": {
33+
"title": "Walk the dog",
34+
"price": 3,
35+
"backdropResource": "image or video url here"
36+
}
37+
}
2238
}
2339
]
2440
}, {

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"eslint-import-resolver-webpack": "^0.8.3",
4040
"eslint-plugin-import": "^2.7.0",
4141
"mocha": "^4.0.1",
42+
"noop-webpack-plugin": "^1.0.1",
4243
"serverless-domain-manager": "^2.0.1",
4344
"serverless-dynamodb-autoscaling": "^0.6.2",
4445
"serverless-dynamodb-local": "^0.2.25",
@@ -49,7 +50,8 @@
4950
"serverless-webpack": "^4.1.0",
5051
"uglifyjs-webpack-plugin": "^1.1.4",
5152
"webpack": "^3.10.0",
52-
"webpack-node-externals": "^1.6.0"
53+
"webpack-node-externals": "^1.6.0",
54+
"yargs": "^11.0.0"
5355
},
5456
"dependencies": {
5557
"aws-sdk": "^2.122.0",

serverless.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,16 @@ functions:
196196
authorizer:
197197
name: authorizer
198198
arn: ${file(./config.yml):${self:custom.stage}.COGNITO_POOL_ARN}
199+
updateJobSummary:
200+
handler: src/handlers/job.updateSummary
201+
events:
202+
- http:
203+
method: PUT
204+
path: job/{jobId}/summary
205+
cors: true
206+
authorizer:
207+
name: authorizer
208+
arn: ${file(./config.yml):${self:custom.stage}.COGNITO_POOL_ARN}
199209
listJobsByFamilyMember:
200210
handler: src/handlers/job.listByFamilyMember
201211
events:

src/controllers/Job.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import TransactionModel from 'models/Transaction';
66
import { notifyJob } from 'utils/noti/index';
77

88
import {
9-
checkAllowedJobStatusSafeUpdate as checkSafeStatus
9+
checkAllowedJobStatusSafeUpdate as checkSafeStatus,
10+
checkAllowedJobSummarySafeUpdate as checkSafeSummary
1011
} from 'utils/validation';
1112

1213
export default class JobController {
@@ -86,11 +87,24 @@ export default class JobController {
8687

8788
return updatedJob;
8889
}
89-
}
9090

91-
function test(updatedJobStatus, currentUserType) {
92-
if (updatedJobStatus !== 'REMOVED' ||
93-
(updatedJobStatus === 'REMOVED' && currentUserType === 'PARENT')) {
94-
console.log('Send notification');
91+
async safeUpdateSummary(currentUser, jobId, reqParam) {
92+
const jobData = await this.job.fetchById(jobId);
93+
if (!jobData) {
94+
throw Boom.notFound('Not existing job');
95+
}
96+
97+
// TODO: child user can only update the job that created for him.
98+
// TODO: parent user can only update the job that created for his family.
99+
const safetyError = checkSafeSummary(currentUser.type, jobData.status);
100+
if (safetyError) {
101+
throw Boom.badRequest(safetyError);
102+
}
103+
104+
const updatedJob = await this.job.updateSummary(currentUser.userId, reqParam, jobData);
105+
106+
await notifyJob(updatedJob);
107+
108+
return updatedJob;
95109
}
96110
}

src/controllers/Notification.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,26 @@ export default class NotiController {
107107

108108
async notifyJob(jobId, snsOriginMessage) {
109109
const job = await this.job.fetchById(jobId);
110+
if (!job) {
111+
throw Boom.notFound(`Not existing job with id ${jobId}`);
112+
}
110113

111114
let targetUsers;
112115
const { status } = job;
113-
const lastHistory = R.last(job.history);
114-
const prevLastHistory = R.pipe(R.takeLast(2), R.first)(job.history);
116+
const jobHistory = R.filter(h => h.status, job.history);
117+
const lastHistory = R.last(jobHistory);
118+
const prevLastHistory = R.pipe(R.takeLast(2), R.head)(jobHistory);
115119
const { username, type } = await this.user.fetchById(lastHistory.issuedBy);
116120

117121
console.log('RECEIVED ACTION: ', snsOriginMessage.content);
118122
console.log('CURRENT JOB STATUS: ', status);
119-
120-
if (['CREATED_BY_CHILD', 'FINISHED', 'STARTED'].includes(status)) {
123+
if (snsOriginMessage.content === 'child.job.SUMMARY_UPDATED') {
124+
if (type === 'parent' && !['CREATED_BY_CHILD', 'START_DECLINED'].includes(prevLastHistory.status)) {
125+
targetUsers = [await this.user.fetchById(job.childUserId)];
126+
} else {
127+
throw Boom.teapot('No need to notify for the premature jobs');
128+
}
129+
} else if (['CREATED_BY_CHILD', 'FINISHED', 'STARTED'].includes(status)) {
121130
targetUsers = await this._getUsersFromFamily(job.familyId, 'parent');
122131
} else if (['START_DECLINED', 'START_APPROVED', 'FINISH_DECLINED', 'PAID'].includes(status)) {
123132
targetUsers = [await this.user.fetchById(job.childUserId)];
@@ -144,6 +153,9 @@ export default class NotiController {
144153

145154
async notifyWithdrawal(withdrawalId, snsOriginMessage) {
146155
const withdrawal = await this.withdrawal.fetchById(withdrawalId);
156+
if (!withdrawal) {
157+
throw Boom.notFound(`Not existing withdrawal request with id ${withdrawalId}`);
158+
}
147159

148160
let targetUsers;
149161
const { status } = withdrawal;

src/handlers/job.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import JobController from 'controllers/Job';
55
import {
66
checkCreateJobDataSchema,
77
checkUpdateJobStatusSchema,
8+
checkUpdateJobSummarySchema,
89
} from 'utils/validation';
910

1011
const job = new JobController();
@@ -73,6 +74,26 @@ export async function updateStatus(event, context, callback) {
7374
callback(null, response);
7475
}
7576

77+
export async function updateSummary(event, context, callback) {
78+
let response;
79+
80+
try {
81+
const { currentUser, body, params } = await parseAPIGatewayEvent(event);
82+
83+
const validationError = checkUpdateJobSummarySchema(body);
84+
if (validationError) {
85+
throw Boom.preconditionFailed(validationError);
86+
}
87+
88+
const updated = await job.safeUpdateSummary(currentUser, params.jobId, body);
89+
response = success(updated);
90+
} catch (e) {
91+
response = failure(e, event);
92+
}
93+
94+
callback(null, response);
95+
}
96+
7697
export async function listByFamily(event, context, callback) {
7798
let response;
7899

src/models/Job.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { omit, pick } from 'ramda';
1+
import { omit, pick, merge } from 'ramda';
22
import uuidv1 from 'uuid/v1';
33
import dbClient from 'utils/db-client';
44

@@ -145,7 +145,8 @@ export default class JobModel {
145145
':h': [{
146146
...newData,
147147
issuedAt: now.getTime(),
148-
issuedBy: currentUserId
148+
issuedBy: currentUserId,
149+
description: 'STATUS UPDATED'
149150
}],
150151
':s': newData.status,
151152
':m': now.toISOString(),
@@ -157,4 +158,40 @@ export default class JobModel {
157158

158159
return this.dbClient('update', params).then(data => data.Attributes);
159160
}
161+
162+
updateSummary(currentUserId, summaryUpdate, currentJob) {
163+
const now = new Date();
164+
const primaryKeys = ['familyId', 'childUserId__createdTimestamp'];
165+
const newSummary = merge(currentJob.jobSummary, summaryUpdate);
166+
167+
const params = {
168+
TableName: JOB_TABLENAME,
169+
Key: pick(primaryKeys, currentJob),
170+
UpdateExpression: [
171+
'SET history = list_append(history, :h)',
172+
'jobSummary = :summary',
173+
'modified = :m',
174+
'childUserId__modifiedTimestamp = :cm',
175+
'modifiedTimestamp__familyId = :mf'
176+
].join(', '),
177+
ExpressionAttributeValues: {
178+
':h': [{
179+
issuedAt: now.getTime(),
180+
issuedBy: currentUserId,
181+
meta: {
182+
before: currentJob.jobSummary,
183+
after: newSummary
184+
},
185+
description: 'SUMMARY UPDATED'
186+
}],
187+
':summary': newSummary,
188+
':m': now.toISOString(),
189+
':cm': `${currentJob.childUserId}__${now.getTime()}`,
190+
':mf': `${now.getTime()}__${currentJob.familyId}`
191+
},
192+
ReturnValues: 'ALL_NEW'
193+
};
194+
195+
return this.dbClient('update', params).then(data => data.Attributes);
196+
}
160197
}

src/utils/noti/available_notifications.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
"push": "{username} has deleted this job\n{title}",
3636
"inapp": "Job deleted\n{title}"
3737
},
38+
"child.job.SUMMARY_UPDATED": {
39+
"push": "{username} has edited this job\n{title} for {amount}",
40+
"inapp": "Job detail's changed\n{title}"
41+
},
3842
"parent.withdrawal.CREATED_BY_CHILD": {
3943
"push": "{username} has requested a cash out for {amount}.\nPay it now?",
4044
"inapp": "Cash out requested\nPay it now?"

src/utils/noti/trigger.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import AWSXRay from 'aws-xray-sdk-core';
2+
import * as R from 'ramda';
23
import { checkValidNotiTriggerMessage } from 'utils/validation';
34

45
const AWS = AWSXRay.captureAWS(require('aws-sdk'));
@@ -27,6 +28,12 @@ export function trigger (message) { // eslint-disable-line
2728

2829
export const notifyJob = (job) => {
2930
let performedAction;
31+
if (R.last(job.history).status === 'SUMMARY_UPDATED') {
32+
return trigger({
33+
content: 'child.job.SUMMARY_UPDATED',
34+
jobId: job.id
35+
});
36+
}
3037

3138
switch (job.status) {
3239
default: case 'CREATED_BY_PARENT':

0 commit comments

Comments
 (0)