Skip to content

Commit 31791f1

Browse files
committed
refactor(orderdesk): move message composition to FoxyWebhook class
1 parent 6c51dd2 commit 31791f1

File tree

6 files changed

+136
-45
lines changed

6 files changed

+136
-45
lines changed

src/foxy/FoxyWebhook.js

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,35 @@ function response(details="", code=200) {
9696
}
9797
}
9898

99+
/**
100+
* Creates a details message about insufficient inventory.
101+
*
102+
* @param {Array} pairs with insufficient inventory.
103+
* @returns {string} a configurable message.
104+
*/
105+
function messageInsufficientInventory(pairs) {
106+
if (!pairs.length) return '';
107+
const message = config.datastore.error.insufficientInventory ||
108+
'Insufficient inventory for these items';
109+
return message + ' ' + pairs
110+
.map(p => `${p[1].name}: only ${p[1].inventory} available`).join(';')
111+
}
112+
113+
/**
114+
* Creates a details message about invalid price.
115+
*
116+
* @param {Array} pairs with invalid price.
117+
* @returns {string} a configurable message.
118+
*/
119+
function messagePriceMismatch(pairs) {
120+
if (!pairs.length) return '';
121+
const message = config.datastore.error.priceMismatch ||
122+
'Prices do not match:';
123+
return message + ' ' + pairs
124+
.map(p => p[0].name)
125+
.join(', ');
126+
}
127+
99128
/**
100129
* Verifies a Foxy Signature in a Webhook.
101130
*
@@ -135,9 +164,10 @@ function verifyWebhookSignature(req) {
135164
/**
136165
* Validates a Foxy request.
137166
*
138-
* It must be a Signed POST request with content-type application/json
139-
* @param request
140-
* @returns {boolean}
167+
* It must be a Signed POST request with content-type application/json.
168+
*
169+
* @param {Request} requestEvent to be evaluated as valid.
170+
* @returns {string|boolean} the error with this request.
141171
*/
142172
function validFoxyRequest(requestEvent) {
143173
let err = false;
@@ -157,9 +187,11 @@ function validFoxyRequest(requestEvent) {
157187

158188
module.exports = {
159189
getItems,
190+
messageInsufficientInventory,
191+
messagePriceMismatch,
160192
response,
161-
verifyWebhookSignature,
193+
validFoxyRequest,
162194
validSignature,
163-
validFoxyRequest
195+
verifyWebhookSignature,
164196
}
165197

src/functions/datastore-integration-orderdesk/webhook.js

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,10 @@ async function prePayment(body) {
1818
cartValidator.skipFromEnv();
1919
const pairs = await buildPairs(body, FoxyWebhook, datastore);
2020
// Check Prices
21-
const invalidPrice = pairs.filter(
22-
p => !cartValidator.validPrice(...p)
23-
);
24-
const priceDetail = invalidPrice.length ? `Invalid items: ${
25-
invalidPrice.map(p => p[0].name).join(',')
26-
}` : "";
27-
const invalidInventory = pairs.filter(
28-
p => !cartValidator.validInventory(...p)
29-
);
30-
const inventoryDetail = invalidInventory.length ? `Insufficient inventory for these items: ${
31-
invalidInventory.map(p => `${p[1].name}: only ${p[1].inventory} available`).join(';')
32-
}` : ""
21+
const invalidPrice = pairs.filter(p => !cartValidator.validPrice(...p));
22+
const priceDetail = FoxyWebhook.messagePriceMismatch(invalidPrice);
23+
const invalidInventory = pairs.filter(p => !cartValidator.validInventory(...p));
24+
const inventoryDetail = FoxyWebhook.messageInsufficientInventory(invalidInventory);
3325
if (invalidInventory.length || invalidPrice.length) {
3426
return response([inventoryDetail, priceDetail].filter(m => m.length > 0).join('\n'));
3527
} else {

test/foxy/FoxyWebhook.test.js

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
11
const crypto = require("crypto");
22
const FoxyWebhook = require('../../src/foxy/FoxyWebhook.js');
3+
const sinon = require("sinon");
34

45
const { expect } = require("chai");
5-
const { describe, it } = require("mocha");
6+
const { after, before, describe, it } = require("mocha");
67

8+
function silenceLog() {
9+
log = sinon.stub(console, 'log');
10+
logError = sinon.stub(console, 'error');
11+
}
712

13+
function restoreLog() {
14+
log.restore();
15+
logError.restore();
16+
}
817

918
describe("Handles Foxy Webhook Requests", function() {
19+
let log;
20+
let logError;
21+
22+
before(silenceLog);
23+
after(restoreLog);
1024

1125
describe("Should retrieve items from a Webhook Request", function() {
1226
it("Should retrieve items when they are present.", function() {
@@ -22,6 +36,11 @@ describe("Handles Foxy Webhook Requests", function() {
2236
});
2337

2438
describe("Builds Foxy Webhook Responses", function() {
39+
let log;
40+
let logError;
41+
42+
before(silenceLog);
43+
after(restoreLog);
2544

2645
it ("Should not accept error responses without details", function () {
2746
expect(() => FoxyWebhook.response("", 500)).to.throw(Error, /An error response needs to specify details/);
@@ -31,6 +50,11 @@ describe("Builds Foxy Webhook Responses", function() {
3150
});
3251

3352
describe("Verifies Foxy Webhook Signatures", function () {
53+
let log;
54+
let logError;
55+
56+
before(silenceLog);
57+
after(restoreLog);
3458
it("Accepts the correct signature",function () {
3559
const foxySignature = crypto.createHmac('sha256', 'foo').update('bar').digest('hex');
3660
expect(FoxyWebhook.validSignature('bar', foxySignature, 'foo')).to.be.true;
@@ -48,3 +72,31 @@ describe("Verifies Foxy Webhook Signatures", function () {
4872
}
4973
});
5074
});
75+
76+
describe("Builds useful error messages", function() {
77+
let log;
78+
let logError;
79+
80+
before(silenceLog);
81+
after(restoreLog);
82+
83+
describe("Responds useful messages", function () {
84+
it("Informs the invalid items when the price is wrong.", async function () {
85+
const message = FoxyWebhook.messagePriceMismatch([
86+
[{name: 'foo'}, {name: 'foo'}],
87+
[{name: 'bar'}, {name: 'bar'}],
88+
])
89+
expect(message).to.match(/foo/);
90+
expect(message).to.match(/bar/);
91+
});
92+
93+
it("Informs the items with insufficient inventory and the current available inventory.", async function () {
94+
const message = FoxyWebhook.messageInsufficientInventory([
95+
[{name: 'foo', quantity:3}, {inventory: 2, name: 'foo'}],
96+
[{name: 'bar', quantity:3}, {inventory: 1, name: 'bar'}],
97+
])
98+
expect(message).to.match(/2 available/);
99+
expect(message).to.match(/1 available/);
100+
});
101+
});
102+
});

test/functions/datastore-integration-orderdesk/DataStore.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,6 @@ describe("OrderDesk Datastore", function() {
178178
[{price: 0, code: '1'}, {name: undefined, price: 0, inventory: undefined, code: '1', update_source: 'Foxy-OrderDesk-Webhook'}],
179179
];
180180
for (let c of cases) {
181-
console.debug(c);
182181
expect(odClient.convertToCanonical(c[0]))
183182
.to.deep.equal(c[1]);
184183
}

test/functions/datastore-integration-orderdesk/index.test.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
1-
const {afterEach, beforeEach, describe, it } = require("mocha");
1+
const {after, afterEach, before, beforeEach, describe, it } = require("mocha");
22
const {expect} = require("chai");
33
const MockFoxyRequest = require("../../MockFoxyRequests.js");
4+
const sinon = require("sinon");
45

56
const crypto = require("crypto");
67
const rewire = require("rewire");
78

89
const odHandler = rewire("../../../src/functions/datastore-integration-orderdesk/index.js");
910
const config = odHandler.__get__('config');
1011

12+
function silenceLog() {
13+
log = sinon.stub(console, 'log');
14+
logError = sinon.stub(console, 'error');
15+
}
16+
17+
function restoreLog() {
18+
log.restore();
19+
logError.restore();
20+
}
21+
1122
describe("Order Desk Pre-payment Webhook", function() {
23+
let log;
24+
let logError;
25+
26+
before(silenceLog);
27+
after(restoreLog);
1228
beforeEach(
1329
function () {
1430
config.datastore.provider.orderDesk.storeId = 'foo';

test/functions/datastore-integration-orderdesk/webhook.test.js

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
const rewire = require("rewire");
22
const { expect } = require("chai");
3-
const { describe, it } = require("mocha");
3+
const { describe, it, before, after } = require("mocha");
4+
const sinon = require("sinon");
45
const webhook = rewire("../../../src/functions/datastore-integration-orderdesk/webhook.js");
56

67
const oldFoxyWebhook = webhook.__get__('FoxyWebhook');
78
const MockFoxyWebhook = {
9+
getItems: function() {return [this.item];},
810
item: {},
9-
getItems: function() {
10-
return [
11-
this.item
12-
];
13-
},
11+
messageInsufficientInventory: () => 'insufficientInventory',
12+
messagePriceMismatch: () => 'priceMismatch',
1413
response: oldFoxyWebhook.response
1514
}
1615

@@ -100,6 +99,23 @@ async function prePaymentExpectInvalid(reg) {
10099
}
101100

102101
describe("OrderDesk Pre-payment Webhook", function() {
102+
let log;
103+
let logError;
104+
105+
before(
106+
function() {
107+
log = sinon.stub(console, 'log');
108+
logError = sinon.stub(console, 'error');
109+
}
110+
);
111+
112+
after(
113+
function() {
114+
log.restore();
115+
logError.restore();
116+
}
117+
);
118+
103119
describe("Validates the cart items prices against a datastore", function() {
104120
it("Accepts if the prices are the same", async function () {
105121
resetMocks();
@@ -126,13 +142,13 @@ describe("OrderDesk Pre-payment Webhook", function() {
126142
expect(result.statusCode).to.equal(200);
127143
expect(result.body).to.exist;
128144
expect(JSON.parse(result.body).ok).to.be.false;
129-
expect(JSON.parse(result.body).details).to.match(/Invalid items/);
145+
expect(JSON.parse(result.body).details).to.match(/priceMismatch/);
130146
});
131147

132148
it("Rejects if the cart has no price and the datastore does", async function() {
133149
resetMocks();
134150
MockFoxyWebhook.item.price = undefined;
135-
await prePaymentExpectInvalid(/Invalid items/);
151+
await prePaymentExpectInvalid(/priceMismatch/);
136152
});
137153
});
138154

@@ -162,26 +178,10 @@ describe("OrderDesk Pre-payment Webhook", function() {
162178
it("Rejects if the quantity is higher", async function () {
163179
resetMocks();
164180
MockFoxyWebhook.item.quantity = 10;
165-
await prePaymentExpectInvalid(/Insufficient inventory/);
181+
await prePaymentExpectInvalid(/insufficientInventory/);
166182
});
167183

168184
});
169-
170-
describe("Responds useful messages", function () {
171-
it("Informs the invalid items when the price is wrong.", async function () {
172-
resetMocks();
173-
MockDatastore.item.price = 100;
174-
const body = await prePaymentExpectInvalid(/Invalid items/);
175-
expect(body.details).match(/Invalid items: foo/);
176-
});
177-
178-
it("Informs the items with insufficient inventory and the current available inventory.", async function () {
179-
resetMocks();
180-
MockDatastore.item.inventory = 0;
181-
const body = await prePaymentExpectInvalid(/Insufficient inventory/);
182-
expect(body.details).match(/Insufficient inventory for these items: foo/);
183-
});
184-
});
185185
});
186186

187187
describe("Transaction Created Webhook", function() {

0 commit comments

Comments
 (0)