From f3713985ed33877186adfa17fae843e7faccf500 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 1 Oct 2022 22:57:29 -0700 Subject: [PATCH] fix(order): Refactored to completed some to-do items --- package-lock.json | 99 +++++++++++++++++++++- package.json | 1 + src/entities/order.js | 18 ++-- src/use-cases/order.js | 17 ++-- test/unit/entities/order.entity.unit.js | 18 ++-- test/unit/use-cases/order.use-case.unit.js | 2 +- 6 files changed, 126 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index aea59f9..e4c24c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { "name": "bch-dex", - "version": "1.0.0", + "version": "2.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bch-dex", - "version": "1.0.0", + "version": "2.0.2", "license": "MIT", "dependencies": { + "@chris.troutner/retry-queue": "1.0.5", "axios": "0.27.2", "bch-message-lib": "2.2.1", "bcryptjs": "2.4.3", @@ -169,6 +170,50 @@ "node": ">=10.15.1" } }, + "node_modules/@chris.troutner/retry-queue": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@chris.troutner/retry-queue/-/retry-queue-1.0.5.tgz", + "integrity": "sha512-autE5GRCXR0xgPqq6qIq1sbUw8j2btxCWlezSvpRJmPVQQZwGhYbeF7euxRbpMmi31kj47iHgG7sb45bD0HY7Q==", + "dependencies": { + "p-queue": "7.3.0", + "p-retry": "5.1.1" + } + }, + "node_modules/@chris.troutner/retry-queue/node_modules/@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" + }, + "node_modules/@chris.troutner/retry-queue/node_modules/p-queue": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-7.3.0.tgz", + "integrity": "sha512-5fP+yVQ0qp0rEfZoDTlP2c3RYBgxvRsw30qO+VtPPc95lyvSG+x6USSh1TuLB4n96IO6I8/oXQGsTgtna4q2nQ==", + "dependencies": { + "eventemitter3": "^4.0.7", + "p-timeout": "^5.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@chris.troutner/retry-queue/node_modules/p-retry": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-5.1.1.tgz", + "integrity": "sha512-i69WkEU5ZAL8mrmdmVviWwU+DN+IUF8f4sSJThoJ3z5A7Nn5iuO5ROX3Boye0u+uYQLOSfgFl7SuFZCjlAVbQA==", + "dependencies": { + "@types/retry": "0.12.1", + "retry": "^0.13.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "license": "MIT", @@ -11702,6 +11747,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-timeout": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", + "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "1.0.0", "dev": true, @@ -15628,6 +15684,40 @@ "tape": "*" } }, + "@chris.troutner/retry-queue": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@chris.troutner/retry-queue/-/retry-queue-1.0.5.tgz", + "integrity": "sha512-autE5GRCXR0xgPqq6qIq1sbUw8j2btxCWlezSvpRJmPVQQZwGhYbeF7euxRbpMmi31kj47iHgG7sb45bD0HY7Q==", + "requires": { + "p-queue": "7.3.0", + "p-retry": "5.1.1" + }, + "dependencies": { + "@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" + }, + "p-queue": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-7.3.0.tgz", + "integrity": "sha512-5fP+yVQ0qp0rEfZoDTlP2c3RYBgxvRsw30qO+VtPPc95lyvSG+x6USSh1TuLB4n96IO6I8/oXQGsTgtna4q2nQ==", + "requires": { + "eventemitter3": "^4.0.7", + "p-timeout": "^5.0.2" + } + }, + "p-retry": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-5.1.1.tgz", + "integrity": "sha512-i69WkEU5ZAL8mrmdmVviWwU+DN+IUF8f4sSJThoJ3z5A7Nn5iuO5ROX3Boye0u+uYQLOSfgFl7SuFZCjlAVbQA==", + "requires": { + "@types/retry": "0.12.1", + "retry": "^0.13.1" + } + } + } + }, "@colors/colors": { "version": "1.5.0" }, @@ -23348,6 +23438,11 @@ "p-cancelable": "^2.0.0" } }, + "p-timeout": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", + "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==" + }, "p-try": { "version": "1.0.0", "dev": true diff --git a/package.json b/package.json index 688177d..7aba394 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ }, "repository": "Permissionless-Software-Foundation/bch-dex", "dependencies": { + "@chris.troutner/retry-queue": "1.0.5", "axios": "0.27.2", "bch-message-lib": "2.2.1", "bcryptjs": "2.4.3", diff --git a/src/entities/order.js b/src/entities/order.js index b62c3c0..8dd60c4 100644 --- a/src/entities/order.js +++ b/src/entities/order.js @@ -7,7 +7,10 @@ The Order tracks the hdIndex address used to hold tokens or BCH for sale. */ class Order { - validate (data) { + // TODO: Create a fullValidate() function that validates a fully-hydrated + // order model. + + inputValidate (data) { const { messageType, messageClass, @@ -16,8 +19,7 @@ class Order { rateInBaseUnit, minUnitsToExchange, numTokens, - makerAddr, - ticker + makerAddr } = data // Input Validation @@ -45,9 +47,9 @@ class Order { if (!makerAddr || typeof makerAddr !== 'string') { throw new Error("Property 'makerAddr' must be a string.") } - if (!ticker || typeof ticker !== 'string') { - throw new Error("Property 'ticker' must be a string.") - } + // if (!ticker || typeof ticker !== 'string') { + // throw new Error("Property 'ticker' must be a string.") + // } const offerData = { messageType, @@ -57,8 +59,8 @@ class Order { rateInBaseUnit, minUnitsToExchange, numTokens, - makerAddr, - ticker + makerAddr + // ticker } return offerData diff --git a/src/use-cases/order.js b/src/use-cases/order.js index 8cdde6b..d334e7f 100644 --- a/src/use-cases/order.js +++ b/src/use-cases/order.js @@ -2,9 +2,11 @@ Order use-case library. */ +// Global npm libraries +import RetryQueue from '@chris.troutner/retry-queue' + // Local libraries import wlogger from '../adapters/wlogger.js' - import OrderEntity from '../entities/order.js' import config from '../../config/index.js' @@ -23,6 +25,7 @@ class OrderLib { this.OrderModel = this.adapters.localdb.Order this.bch = this.adapters.bch this.config = config + this.retryQueue = new RetryQueue({ retryPeriod: 1000, attempts: 3 }) } // Create a new order model and add it to the Mongo database. @@ -37,19 +40,15 @@ class OrderLib { entryObj.makerAddr = this.adapters.wallet.bchWallet.walletInfo.cashAddress console.log('entryObj.makerAddr: ', entryObj.makerAddr) + // Input Validation + const orderEntity = this.orderEntity.inputValidate(entryObj) + console.log('orderEntity: ', orderEntity) + // Get Ticker for token ID. - // TODO: Move this below the orderEntity.validate() call. const tokenData = await this.adapters.wallet.bchWallet.getTxData([entryObj.tokenId]) // console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`) entryObj.ticker = tokenData[0].tokenTicker - // Input Validation - // TODO: Remove ticker from validation. - // TODO: Rename validate() to inputValidate(). Create fullValidate() that - // validates a fully hydrated Order entity. - const orderEntity = this.orderEntity.validate(entryObj) - console.log('orderEntity: ', orderEntity) - // Ensure sufficient tokens exist to create the order. await this.ensureFunds(orderEntity) diff --git a/test/unit/entities/order.entity.unit.js b/test/unit/entities/order.entity.unit.js index 9322377..9a80d4b 100644 --- a/test/unit/entities/order.entity.unit.js +++ b/test/unit/entities/order.entity.unit.js @@ -21,10 +21,10 @@ describe('#Order-Entity', () => { afterEach(() => sandbox.restore()) - describe('#validate', () => { + describe('#inputValidate', () => { it('should throw an error if data is not provided', () => { try { - uut.validate() + uut.inputValidate() } catch (err) { // console.log(err) assert.include(err.message, 'Cannot destructure property') @@ -34,7 +34,7 @@ describe('#Order-Entity', () => { it('should throw an error if messageType is not included', () => { try { const data = {} - uut.validate(data) + uut.inputValidate(data) } catch (err) { // console.log(err) assert.include( @@ -47,7 +47,7 @@ describe('#Order-Entity', () => { it('should throw an error if messageClass is not included', () => { try { const data = { messageType: 1 } - uut.validate(data) + uut.inputValidate(data) } catch (err) { // console.log(err) assert.include( @@ -60,7 +60,7 @@ describe('#Order-Entity', () => { it('should throw an error if tokenId is not included', () => { try { const data = { messageType: 1, messageClass: 1 } - uut.validate(data) + uut.inputValidate(data) } catch (err) { // console.log(err) assert.include(err.message, "Property 'tokenId' must be a string.") @@ -70,7 +70,7 @@ describe('#Order-Entity', () => { it('should throw an error if buyOrSell is not included', () => { try { const data = { messageType: 1, messageClass: 1, tokenId: 'fakeId' } - uut.validate(data) + uut.inputValidate(data) } catch (err) { // console.log(err) assert.include(err.message, "Property 'buyOrSell' must be a string.") @@ -85,7 +85,7 @@ describe('#Order-Entity', () => { tokenId: 'fakeId', buyOrSell: 'buy' } - uut.validate(data) + uut.inputValidate(data) } catch (err) { // console.log(err) assert.include( @@ -104,7 +104,7 @@ describe('#Order-Entity', () => { buyOrSell: 'buy', rateInBaseUnit: 1000 } - uut.validate(data) + uut.inputValidate(data) } catch (err) { // console.log(err) assert.include( @@ -124,7 +124,7 @@ describe('#Order-Entity', () => { rateInBaseUnit: 1000, minUnitsToExchange: 350 } - uut.validate(data) + uut.inputValidate(data) } catch (err) { // console.log(err) assert.include(err.message, "Property 'numTokens' must be a number.") diff --git a/test/unit/use-cases/order.use-case.unit.js b/test/unit/use-cases/order.use-case.unit.js index c0ea860..2dfd7c9 100644 --- a/test/unit/use-cases/order.use-case.unit.js +++ b/test/unit/use-cases/order.use-case.unit.js @@ -109,7 +109,7 @@ describe('#order-use-case', () => { // Mock dependencies // sandbox.stub(uut.adapters.wallet, 'burnPsf').resolves('fakeTxid') // sandbox.stub(uut.adapters.wallet.bchWallet, 'getTxData').resolves({ tokenTicker: 'TROUT' }) - sandbox.stub(uut.orderEntity, 'validate').returns(entryObj) + sandbox.stub(uut.orderEntity, 'inputValidate').returns(entryObj) sandbox.stub(uut, 'ensureFunds').resolves() sandbox.stub(uut.adapters.wallet.bchWallet.bchjs.Util, 'sleep').resolves() sandbox.stub(uut.adapters.wallet, 'moveTokens').resolves({ txid: 'fakeTxid', vout: 0, hdIndex: 1 })