-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtransact.js
More file actions
358 lines (321 loc) · 12 KB
/
transact.js
File metadata and controls
358 lines (321 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
// javascript method to execute transactions, retry if they fail according
// to retryPolicy, and rollback if the entire transaction cannot succeed.
/**
* @param generator {function*} - yield generator of transactions to execute
*/
function executeTransaction(generator) {
let isSuccess = true; // flag success/fail to return
const executed = []; // table of transactions in case rollback is needed
for (const operation of generator) {
if (isSuccess == false) break; // stop if last transaction failed
const numRetries = operation.retryPolicy.numRetries;
try {
var count = 0; // count of retries (not including first try)
let execute = operation.execute.bind(operation);
execute( count==numRetries ); // parameter indicating if last retry
executed.unshift(operation); // push to front of the list
} catch (e) {
// execute failed, begin retry mechanism
logMsg(e.message);
const retry = operation.retry.bind(operation);
while (count++ < numRetries) { // repeat until policy is exhausted
try {
retry( count==numRetries ); // parameter indicating if last retry
executed.unshift(operation); // push to front of the list
break; // stop if retry succeeded
} catch (e) {
// retry failed, check if this was last retry
logMsg(e.message);
if (count==numRetries ) { // rollback everything if last retry
for (const operation of executed) {
operation.rollback();
}
isSuccess = false; // transaction aborted
break;
}
}
}
}
}
return isSuccess; // return to calling function whether transaction succeeded or failed
}
// javascript function to return object (consisting of methods execute,
// retry, rollback, calculate, and another object retryPolicy) necessary to
// calculate the order total.
/**
* @param orderId {number} id of the order for which total is being calculated
*/
function calculateTotal(orderId) {
return {
// javascript function to execute total calculation.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
execute(isLast) {
logMsg(`Attempting to calculate total for order ${orderId}... `);
this.calculate(isLast);
},
// javascript function to retry the total calculation.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
retry(isLast) {
pause(this.retryPolicy.delay);
this.calculate(isLast);
},
// javascript function to rollback the total calculation.
rollback() {
logMsg(`Rolled back order ${orderId} total calculation.`);
},
// javascript object including numRetries and delay for retry mechanism.
/**
* @param numRetries {number} max number of retries allowed to be attempted
* @param delay {number} delay in milliseconds between retries
*/
retryPolicy: {
numRetries: 2,
delay: 1000,
},
// javascript function to actually perform the total calculation
// called by both execute() and retry() functions above.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
calculate(isLast) {
if ( module.exports.randomSuccess() == false ) {
if (isLast) throw new Error('Failed.'); // final retry
throw new Error('Failed... ');
}
logMsg('Succeeded.');
}
}
}
// javascript function to return object (consisting of methods execute,
// retry, rollback, calculate, and another object retryPolicy) necessary to
// update a coupon.
/**
* @param couponNumber {number} id of the coupon being updated
*/
function updateCoupon(couponNumber) {
return {
// javascript function to execute coupon update.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
execute(isLast) {
logMsg(`Attempting to update coupon ${couponNumber}... `);
this.update(isLast);
},
// javascript function to retry the coupon update.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
retry(isLast) {
pause(this.retryPolicy.delay);
this.update(isLast);
},
// javascript function to rollback the coupon update.
rollback: () => {
logMsg(`Rolled back coupon ${couponNumber} update.`);
},
// javascript object including numRetries and delay for retry mechanism.
/**
* @param numRetries {number} max number of retries allowed to be attempted
* @param delay {number} delay in milliseconds between retries
*/
retryPolicy: {
numRetries: 2,
delay: 500,
},
// javascript function to actually perform the coupon update
// called by both execute() and retry() functions above.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
update(isLast) {
if ( module.exports.randomSuccess() == false ) {
if (isLast) throw new Error('Failed.'); // final retry
throw new Error('Failed... ');
}
logMsg('Succeeded.');
}
}
}
// javascript function to return object (consisting of methods execute,
// retry, rollback, calculate, and another object retryPolicy) necessary to
// charge the vendor fee.
function chargeVendorFee() {
return {
// javascript function to execute vendor fee charge.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
execute(isLast) {
logMsg('Attempting to charge vendor fee... ');
this.chargeVendor(isLast);
},
// javascript function to retry the vendor fee charge.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
retry(isLast) {
pause(this.retryPolicy.delay);
this.chargeVendor(isLast);
},
// javascript function to rollback the vendor fee charge.
rollback() {
logMsg('Rolled back vendor charge.');
},
// javascript object including numRetries and delay for retry mechanism.
/**
* @param numRetries {number} max number of retries allowed to be attempted
* @param delay {number} delay in milliseconds between retries
*/
retryPolicy: {
numRetries: 2,
delay: 500,
},
// javascript function to actually perform the vendor fee charge
// called by both execute() and retry() functions above.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
chargeVendor(isLast) {
if ( module.exports.randomSuccess() == false ) {
if (isLast) throw new Error('Failed.'); // final retry
throw new Error('Failed... ');
}
logMsg('Succeeded.');
}
}
}
// javascript function to return object (consisting of methods execute,
// retry, rollback, calculate, and another object retryPolicy) necessary to
// charge the customer's credit card.
/**
* @param ccNumber {number} id of the credit card being charged
*/
function chargeCreditCard(ccNumber) {
return {
// javascript function to execute credit card charge.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
execute(isLast) {
logMsg(`Charging credit card with ccNumber ${ccNumber}... `);
this.chargeCreditCard(isLast);
},
// javascript function to retry the credit card charge.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
retry(isLast) {
pause(this.retryPolicy.delay);
this.chargeCreditCard(isLast);
},
// javascript function to rollback the credit card charge.
rollback() {
logMsg(`Rolled back credit card charge for ${ccNumber}.`);
},
// javascript object including numRetries and delay for retry mechanism.
/**
* @param numRetries {number} max number of retries allowed to be attempted
* @param delay {number} delay in milliseconds between retries
*/
retryPolicy: {
numRetries: 2,
delay: 500,
},
// javascript function to actually perform the credit card charge
// called by both execute() and retry() functions above.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
chargeCreditCard(isLast) {
if ( module.exports.randomSuccess() == false ) {
if (isLast) throw new Error('Failed.'); // final retry
throw new Error('Failed... ');
}
logMsg('Succeeded.');
}
}
}
// javascript function to return object (consisting of methods execute,
// retry, rollback, calculate, and another object retryPolicy) necessary to
// save the order.
/**
* @param orderId {number} id of the order for which total is being calculated
*/
function saveOrder(orderId) {
return {
// javascript function to execute saving the order.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
execute(isLast) {
logMsg(`Saving order ${orderId}... `);
this.saveOrder(isLast);
},
// javascript function to retry saving the order.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
retry(isLast) {
pause(this.retryPolicy.delay);
this.saveOrder(isLast);
},
// javascript function to rollback saving the order.
rollback() {
logMsg(`Removing order ${orderId}.`);
},
// javascript object including numRetries and delay for retry mechanism.
/**
* @param numRetries {number} max number of retries allowed to be attempted
* @param delay {number} delay in milliseconds between retries
*/
retryPolicy: {
numRetries: 2,
delay: 500,
},
// javascript function to actually perform saving the order
// called by both execute() and retry() functions above.
/**
* @param isLast {boolean} indicating if this is the last retry
*/
saveOrder(isLast) {
if ( module.exports.randomSuccess() == false ) {
if (isLast) throw new Error('Failed.'); // final retry
throw new Error('Failed... ');
}
logMsg('Succeeded.');
}
}
}
// javascript method to log a message to the console.
/**
* @param msg {string} content of the message to be logged
*/
function logMsg(msg) {
if ( (msg || "") == "") { console.log(); return; }
if (msg.slice(-1) == " ") process.stdout.write(msg);
else console.log(msg);
}
// javascript method to pause for a given period of milliseconds.
/**
* @param milliseconds {number} - number of milliseconds to pause
*/
function pause(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
// javascript method to return an equal chance of true or false,
// representing success or failure respectively.
function randomSuccess() {
return (Math.random() < .5);
}
module.exports = {executeTransaction, calculateTotal, updateCoupon,
chargeVendorFee, chargeCreditCard, saveOrder,
logMsg, randomSuccess};