-
-
Notifications
You must be signed in to change notification settings - Fork 731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: be explicit when specifying time & replace moment with date-fns #1072
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrote some comments to help the poor reader of this all-over-the-place diff.
|
||
const secureHeaders: (config: IUnleashConfig) => RequestHandler = (config) => { | ||
if (config.secureHeaders) { | ||
return helmet({ | ||
hsts: { | ||
maxAge: 63072000, | ||
maxAge: hoursToSeconds(24 * 365 * 2), // 2 non-leap years |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$ node
> require('date-fns').hoursToSeconds(24 * 365 * 2)
63072000
(There is no way to safely translate hours to days, due to daylight savings time. The same goes for days to years, due to leap years. Therefore (I believe), date-fns offers no utilities to make those conversions, and I just kept the original value. I think this is the only inline code comment I wrote.)
@@ -88,7 +88,7 @@ class StateController extends Controller { | |||
includeTags, | |||
includeEnvironments, | |||
}); | |||
const timestamp = moment().format('YYYY-MM-DD_HH-mm-ss'); | |||
const timestamp = formatDate(Date.now(), 'yyyy-MM-dd_HH-mm-ss'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$ node
> require('moment')().format('YYYY-MM-DD_HH-mm-ss')
'2021-10-27_15-06-43'
> require('date-fns').format(Date.now(), 'yyyy-MM-dd_HH-mm-ss')
'2021-10-27_15-07-01'
(The minute and seconds are different because I ran the commands 18 seconds apart.)
src/lib/services/addon-service.ts
Outdated
@@ -75,7 +74,7 @@ export default class AddonService { | |||
async () => addonStore.getAll({ enabled: true }), | |||
{ | |||
promise: true, | |||
maxAge: ADDONS_CACHE_TIME, | |||
maxAge: millisecondsInMinute, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the named constant adds any value, and I haven't seen this "pattern" anywhere else in the code, so I just inlined it since there's no need for the // 60s
comment anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer minutesToMilliseconds(1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree! I'll switch to that everywhere so it's consistent.
await db.stores.clientInstanceStore.insert({ | ||
appName: 'demo-app-1', | ||
instanceId: 'test-1', | ||
strategies: ['default'], | ||
started: 1516026938494, | ||
started: clientStartedDate, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$ node
> new Date(1516026938494)
2018-01-15T14:35:38.494Z
> require('date-fns').parseISO('2018-01-15T14:35:38.494Z');
2018-01-15T14:35:38.494Z
I think it's nice to not have gigantic numbers just hanging out in the code, so I prefer it like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed.
@@ -72,7 +73,7 @@ export default class SessionStore implements ISessionStore { | |||
.insert({ | |||
sid: data.sid, | |||
sess: JSON.stringify(data.sess), | |||
expired: data.expired || new Date(Date.now() + 86400000), | |||
expired: data.expired || addDays(Date.now(), 1), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$ node
> require('date-fns').hoursToMilliseconds(24)
86400000
originalMaxAge: 2880000, | ||
expires: new Date(Date.now() + 86400000).toDateString(), | ||
originalMaxAge: minutesToMilliseconds(48), | ||
expires: addDays(Date.now(), 1).toDateString(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$ node
> require('date-fns').hoursToMilliseconds(24)
86400000
@@ -141,8 +142,8 @@ test("deleting a user should delete the user's sessions", async () => { | |||
sid: 'xyz321', | |||
sess: { | |||
cookie: { | |||
originalMaxAge: 2880000, | |||
expires: new Date(Date.now() + 86400000).toDateString(), | |||
originalMaxAge: minutesToMilliseconds(48), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$ node
> require('date-fns').minutesToMilliseconds(48)
2880000
I have no idea why this is 48 minutes, but it is, so I kept it. Let me know if I missed some kind of significance about this number 🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't see any good reason other than a small mistake when the test was written. I would assume 48 hours make more sense, as this is our default session length.
Maybe @chriswk remembers this part?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the code could just be confused about seconds and milliseconds. Even though the Max-Age and Expiry parts of a set-cookie header use seconds, express-session
uses milliseconds (for compatibility with the JS Date
, I guess). Multiplying the number by 1000 doesn't get us to 48 hours though 😅 I'm happy to change it to 48 hours if you prefer that – it is just a test, after all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think that's needed.
d1.setHours(10, 10, 11); | ||
d2.setHours(11, 10, 11); | ||
const d1 = set(Date.now(), { hours: 10, minutes: 10, seconds: 11 }); | ||
const d2 = addHours(d1, 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the spirit of d2
is to be d1
+ 1 hour, so I changed it a bit. The values are the same, though
this.startTimer(); | ||
} | ||
|
||
destroy() { | ||
// https://github.com/nodejs/node/issues/9561 | ||
// clearTimeout(this.timer); | ||
// this.timer = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe nodejs/node#9561 was fixed in 2016 and was patched into both Node 4 and 6, so this code should be safe to run 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ttl-list will be removed in Unleash v4.3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alright, should I just remove this particular change? I still need some diff in the file to avoid depending on moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, good to remove the moment dependency. Feel free to fix the destroy to, its good to not leave timers hanging around if we do not have too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, I'll leave my original change in, then 😄
subSeconds, | ||
} from 'date-fns'; | ||
|
||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went a little bit wild explaining this, because it took me a while to figure out what was happening. Let me know if you don't want this long explanation, or if this utility belongs somewhere else in the project!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining this, I learned something new, so good to keep it here!
We might reuse it later, but let's keep it here for now.
@@ -36,26 +36,22 @@ module.exports = class TTLList extends EventEmitter { | |||
} | |||
|
|||
add(value, timestamp = new Date()) { | |||
const ttl = moment(timestamp).add(this.expireAmount, this.expireType); | |||
if (moment().isBefore(ttl)) { | |||
const ttl = add(timestamp, { [this.expireType]: this.expireAmount }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found a small issue with this: it doesn't support milliseconds. It shouldn't affect the production code (which only uses hours and minutes), but it does affect the tests. I have no idea why they pass… Pushing a fix in a sec
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 442c0b9. maybe it would be better to have the list not support milliseconds? I wanted to keep the the functional changes to a minimum
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks really good. just a few minor comments
src/lib/services/addon-service.ts
Outdated
@@ -75,7 +74,7 @@ export default class AddonService { | |||
async () => addonStore.getAll({ enabled: true }), | |||
{ | |||
promise: true, | |||
maxAge: ADDONS_CACHE_TIME, | |||
maxAge: millisecondsInMinute, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer minutesToMilliseconds(1)
subSeconds, | ||
} from 'date-fns'; | ||
|
||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining this, I learned something new, so good to keep it here!
We might reuse it later, but let's keep it here for now.
this.startTimer(); | ||
} | ||
|
||
destroy() { | ||
// https://github.com/nodejs/node/issues/9561 | ||
// clearTimeout(this.timer); | ||
// this.timer = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, good to remove the moment dependency. Feel free to fix the destroy to, its good to not leave timers hanging around if we do not have too.
await db.stores.clientInstanceStore.insert({ | ||
appName: 'demo-app-1', | ||
instanceId: 'test-1', | ||
strategies: ['default'], | ||
started: 1516026938494, | ||
started: clientStartedDate, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed.
@@ -141,8 +142,8 @@ test("deleting a user should delete the user's sessions", async () => { | |||
sid: 'xyz321', | |||
sess: { | |||
cookie: { | |||
originalMaxAge: 2880000, | |||
expires: new Date(Date.now() + 86400000).toDateString(), | |||
originalMaxAge: minutesToMilliseconds(48), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't see any good reason other than a small mistake when the test was written. I would assume 48 hours make more sense, as this is our default session length.
Maybe @chriswk remembers this part?
@@ -252,11 +295,11 @@ test('should have correct values for lastMinute', () => { | |||
let c = metrics.getTogglesMetrics(); | |||
expect(c.lastMinute.toggle).toEqual({ yes: 20, no: 20 }); | |||
|
|||
jest.advanceTimersByTime(10 * 1000); | |||
jest.advanceTimersByTime(10_000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, having played with it a bit, I think anything below 1 minute is better written as a constant with underscores than either 10 * 1000
or secondsToMilliseconds(10)
in the case of jest.advanceTimersByTime
. When reading the test, you already know that this function takes milliseconds, so it's better to avoid the noise of helper functions or maths
c = metrics.getTogglesMetrics(); | ||
expect(c.lastHour.toggle).toEqual({ yes: 41, no: 41 }); | ||
|
||
// at 30 | ||
jest.advanceTimersByTime(30 * 60 * 1000); | ||
jest.advanceTimersByTime(minutesToMilliseconds(30)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
… but when we're dealing with minutes, the numbers are less easy to grok, so I prefer the helper functions over either a constant number of milliseconds or inline arithmetic
|
||
list.destroy(); | ||
jest.useRealTimers(); | ||
}); | ||
|
||
test('should add item created in the past but expiring in the future', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this to ensure it both works with milliseconds and to catch a bug that I almost introduced and the other tests didn't catch
Okay, I think this is ready for a final look 😄 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
This uses
date-fns
for all (I hope) fiddling with time and durations.I think this makes it way easier to understand which units are used (for example, milliseconds or seconds). It also removes the need for things like hard coded millisecond representations of 24 hours.
I took the liberty of replacing all usage of
moment
withdate-fns
, and removing the dependency. (I did not, however, update the docs page that uses moment in an example (website/docs/advanced/custom-activation-strategy.md)).I'll write some inline PR comments in the places where I think the changes are less than obvious – but feel free to ask questions 😄
A small note: I've not been super strict about using just one of
Date.now()
andnew Date()
, since it doesn't matter.date-fns
happily consumes both.Do you think this makes sense?