From 4793dc9afbe6c1d04e3799ea01a1f5020db08595 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Fri, 13 Dec 2019 12:24:13 -0800 Subject: [PATCH] Replace custom parse-duration implementation with an existing lib --- ui/app/components/drain-popover.js | 6 +- ui/app/utils/parse-duration.js | 88 -------------------- ui/package.json | 1 + ui/tests/unit/utils/parse-duration-test.js | 93 ---------------------- ui/yarn.lock | 5 ++ 5 files changed, 9 insertions(+), 184 deletions(-) delete mode 100644 ui/app/utils/parse-duration.js delete mode 100644 ui/tests/unit/utils/parse-duration-test.js diff --git a/ui/app/components/drain-popover.js b/ui/app/components/drain-popover.js index f1fb5680dcc4..b5741d302209 100644 --- a/ui/app/components/drain-popover.js +++ b/ui/app/components/drain-popover.js @@ -3,7 +3,7 @@ import { computed } from '@ember/object'; import { equal } from '@ember/object/computed'; import { computed as overridable } from 'ember-overridable-computed'; import { task } from 'ember-concurrency'; -import parseDuration from 'nomad-ui/utils/parse-duration'; +import Duration from 'duration-js'; export default Component.extend({ tagName: '', @@ -53,9 +53,9 @@ export default Component.extend({ let deadline; try { - deadline = parseDuration(this.deadline); + deadline = new Duration(this.deadline).nanoseconds(); } catch (err) { - this.set('parseError', 'Failed to parse duration'); + this.set('parseError', err.message); return; } diff --git a/ui/app/utils/parse-duration.js b/ui/app/utils/parse-duration.js deleted file mode 100644 index 52a130447325..000000000000 --- a/ui/app/utils/parse-duration.js +++ /dev/null @@ -1,88 +0,0 @@ -const unitToMs = { - ms: 1, - s: 1000, - m: 1000 * 60, - h: 1000 * 60 * 60, - d: 1000 * 60 * 60 * 24, -}; -const durationUnits = Object.keys(unitToMs); - -const isNumeric = char => char >= 0 && char < 10; - -const encodeUnit = (str, token) => { - // Convert it to a string and validate the unit type. - let newToken = token.join(''); - if (!durationUnits.includes(newToken)) { - throw new Error(`ParseError: [${str}] Unallowed duration unit "${newToken}"`); - } - return newToken; -}; - -const encodeQuantity = (str, token) => { - return parseInt(token.join('')); -}; - -export default str => { - if (typeof str === 'number') return str; - - // Split the string into characters to make iteration easier - const chars = str.split(''); - - // Bail early if the duration doesn't start with a number - if (!isNumeric(chars[0])) { - throw new Error(`ParseError: [${str}] Durations must start with a numeric quantity`); - } - - // Collect tokens - const tokens = []; - - // A token can be multi-character, so collect characters - let token = []; - - // Alternate between numeric "quantity" tokens and non-numeric "unit" tokens - let unitMode = false; - - while (chars.length) { - let finishToken = false; - let next = chars.shift(); - - // First identify if the next character is the first - // character of the next token. - if (isNumeric(next) && unitMode) { - unitMode = false; - finishToken = true; - } else if (!isNumeric(next) && !unitMode) { - unitMode = true; - finishToken = true; - } - - // When a token is finished, validate it, encode it, and add it to the tokens list - if (finishToken) { - tokens.push(unitMode ? encodeQuantity(str, token) : encodeUnit(str, token)); - token = []; - } - - // Always add the next character to the token buffer. - token.push(next); - } - - // Once the loop finishes, flush the token buffer one more time. - if (unitMode) { - tokens.push(encodeUnit(str, token)); - } else { - throw new Error(`ParseError: [${str}] Unmatched quantities and units`); - } - - // Loop over the tokens array, two at a time, converting unit and quanties into milliseconds - let duration = 0; - while (tokens.length) { - const quantity = tokens.shift(); - const unit = tokens.shift(); - duration += quantity * unitToMs[unit]; - } - - // Convert from Milliseconds to Nanoseconds - duration *= 1000000; - - return duration; -}; diff --git a/ui/package.json b/ui/package.json index e7e8932eb617..9e6cc886ec1e 100644 --- a/ui/package.json +++ b/ui/package.json @@ -50,6 +50,7 @@ "d3-shape": "^1.2.0", "d3-time-format": "^2.1.0", "d3-transition": "^1.1.0", + "duration-js": "^4.0.0", "ember-ajax": "^5.0.0", "ember-auto-import": "^1.2.21", "ember-can": "^2.0.0", diff --git a/ui/tests/unit/utils/parse-duration-test.js b/ui/tests/unit/utils/parse-duration-test.js deleted file mode 100644 index a131a7c78abb..000000000000 --- a/ui/tests/unit/utils/parse-duration-test.js +++ /dev/null @@ -1,93 +0,0 @@ -import { module, test } from 'qunit'; -import parseDuration from 'nomad-ui/utils/parse-duration'; - -const testCases = [ - { - name: 'Only milliseconds', - in: '100ms', - out: 100 * 1000000, - }, - { - name: 'Only seconds', - in: '5s', - out: 5 * 1000 * 1000000, - }, - { - name: 'Only minutes', - in: '30m', - out: 30 * 60 * 1000 * 1000000, - }, - { - name: 'Only hours', - in: '8h', - out: 8 * 60 * 60 * 1000 * 1000000, - }, - { - name: 'Only days', - in: '2d', - out: 2 * 24 * 60 * 60 * 1000 * 1000000, - }, - { - name: 'Composite', - in: '1d8h15m30s', - out: (((1 * 24 + 8) * 60 + 15) * 60 + 30) * 1000 * 1000000, - }, - { - name: 'Zeroes', - in: '0d0h0m0s', - out: 0, - }, - { - name: 'Improper durations', - in: '90m', - out: 90 * 60 * 1000 * 1000000, - }, - { - name: 'Already parsed', - in: 1000000, - out: 1000000, - }, -]; - -const errorCases = [ - { - name: 'Empty string', - in: '', - error: /Durations must start with a numeric quantity/, - }, - { - name: 'No quantity', - in: 'h', - error: /Durations must start with a numeric quantity/, - }, - { - name: 'Unallowed unit', - in: '15M', - error: /Unallowed duration unit "M"/, - }, - { - name: 'Float quantities', - in: '1.5m', - error: /Unallowed duration unit "\."/, - }, -]; - -module('Unit | Util | parseDuration', function() { - testCases.forEach(testCase => { - test(testCase.name, function(assert) { - assert.equal(parseDuration(testCase.in), testCase.out, `'${testCase.in}' => ${testCase.out}`); - }); - }); - - errorCases.forEach(testCase => { - test(`Error Case: ${testCase.name}`, function(assert) { - assert.throws( - () => { - parseDuration(testCase.in); - }, - testCase.error, - `'${testCase.in}' throws ${testCase.error}` - ); - }); - }); -}); diff --git a/ui/yarn.lock b/ui/yarn.lock index 4ebb0d560287..0032c5119e40 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -6375,6 +6375,11 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" +duration-js@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/duration-js/-/duration-js-4.0.0.tgz#ab91575a4f1a6b096034685cfc6ea9aca99cd63f" + integrity sha1-q5FXWk8aawlgNGhc/G6prKmc1j8= + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"