-
Notifications
You must be signed in to change notification settings - Fork 3
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: Accumulate Events #65
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.
I know it's in progress but I looked anyway since you checked mine out
src/types/countly.d.ts
Outdated
export interface CountlyEventData { | ||
key: string | ||
count: number | ||
sum: number | ||
segmentation: Record<string, string | number> | ||
dur: number | ||
segmentation: Segments | ||
} | ||
interface CountlyEvent { | ||
export interface CountlyEvent { |
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.
👍
src/MetricsProvider.ts
Outdated
import { COUNTLY_SETUP_DEFAULTS } from './config.js' | ||
import { EventAccumulator } from './EventAccumulator.js' | ||
|
||
import type { metricFeatures, CountlyWebSdk } from 'countly-sdk-web' | ||
import type { CountlyNodeSdk } from 'countly-sdk-nodejs' | ||
import type { CountlyWebSdk, metricFeatures } from 'countly-sdk-web' | ||
import type { consentTypes, consentTypesExceptAll } from './types/index.js' |
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.
you should update eslint so it's organizing imports instead of depending on your editor for that! otherwise they'll keep getting out of order ;)
speaking of ordering imports. Can we group the imports? we've currently got two relative imports at the top, then 2 3p imports (countly-sdk-*), then another relative.
* | ||
* @param {string} key - event key | ||
*/ | ||
flush (key: string): void { |
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.
should we have a flush_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.
added with tests
src/EventAccumulator.ts
Outdated
key: '', | ||
count: 1, | ||
sum: 1, | ||
dur: Date.now(), |
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.
duration should be an amount of time, not a timestamp. This duration is currently going to be the full time since 1970.
We should probably default this to 0
or even leave it unset entirely.
see
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.
this gets reset before the event gets flushed. setting default to 0.
src/EventAccumulator.ts
Outdated
timeout: setTimeout(() => { | ||
this.flush(key) | ||
}, this.flushInterval) | ||
} |
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.
} | |
}) |
src/EventAccumulator.ts
Outdated
} | ||
} else { | ||
// if event is in the store, update the event data. | ||
const { eventData } = this.events[key] |
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.
const { eventData } = this.events[key] | |
const { eventData } = this.events.get(key) as eventStore |
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.
src/EventAccumulator.ts
Outdated
const { eventData } = this.events[key] | ||
eventData.count += count | ||
eventData.sum += 1 | ||
eventData.segmentation = { ...eventData.segmentation, ...newEvent.segmentation } |
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.
eventData.segmentation = { ...eventData.segmentation, ...newEvent.segmentation } | |
eventData.segmentation = { ...eventData.segmentation, ...newEvent.segmentation } | |
this.events.set(key, eventData) |
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.
why do I need to set it back? Map.get should get me reference to the object, modifying the object updates the referenced object.
src/EventAccumulator.ts
Outdated
*/ | ||
export class EventAccumulator implements IEventAccumulator { | ||
private readonly metricsService: CountlyWebSdk | ||
private readonly events: Record<string, eventStore> |
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.
private readonly events: Record<string, eventStore> | |
private readonly events: Map<string, eventStore> |
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.
explainers
src/EventAccumulator.ts
Outdated
key: '', | ||
count: 1, | ||
sum: 1, | ||
dur: Date.now(), |
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.
this gets reset before the event gets flushed. setting default to 0.
src/EventAccumulator.ts
Outdated
this.metricsService.q.push(['add_event', eventData]) | ||
clearTimeout(timeout) | ||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete | ||
delete this.events[key] |
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
* | ||
* @param {string} key - event key | ||
*/ | ||
flush (key: string): void { |
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.
added with tests
* main: chore(release): 1.2.0 [skip ci] feat: use localStorage to cache consent in browsers (#64)
{ | ||
"extensions": ["ts"], | ||
"spec": ["test/**/*.spec.*"], | ||
"require": ["ts-node/register"], | ||
"node-option": [ | ||
"experimental-specifier-resolution=node", | ||
"loader=ts-node/esm" | ||
] | ||
} |
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.
this change for running the tests without build should probably be in a separate PR
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.
yes, but I started when we did not have any testing in place, apologies, if you want I can spend sometime splitting these, but I hope it's fine for this time?
"spec": ["test/**/*.spec.*"], | ||
"require": ["ts-node/register"], | ||
"node-option": [ | ||
"experimental-specifier-resolution=node", |
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.
do we need this if we're using ts-node/esm loader?
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.
for some reason, it wants both.
test/unit/EventAccumulator.spec.ts
Outdated
|
||
accumulator.addEvent(event1) | ||
// adding the second event after 50ms so that the first event is flushed first | ||
await sleep(50) |
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.
await sleep(50) | |
clock.tick(50) |
test/unit/EventAccumulator.spec.ts
Outdated
// adding the second event after 50ms so that the first event is flushed first | ||
await sleep(50) | ||
accumulator.addEvent(event2) | ||
await sleep(50) |
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.
await sleep(50) | |
clock.tick(50) |
test/unit/EventAccumulator.spec.ts
Outdated
expect(test2Results.sum).to.be.equal(1) | ||
expect(test2Results.segmentation).to.be.deep.equals({ bar: 'baz' }) | ||
|
||
expect(countlyStub.add_event.callCount).to.be.equal(2) |
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.
check before getting args
expect(countlyStub.add_event.callCount).to.be.equal(2) |
test/unit/EventAccumulator.spec.ts
Outdated
} | ||
|
||
accumulator.addEvent(event1) | ||
// adding the second event after 50ms so that the first event is flushed first |
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'm not sure I understand this. If the event names are different, why does a 50ms delay ensure the first event is flushed first?
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.
because right now events are isolated per key type, i.e. they are flushed per event key and not all of those are flushed together. We can make all events flush at once, but that would break the use case where we only want to flush click
events, that would not clear the right timeout and would need to be handled separately for other event types. I can change this to flush everything if you like that better.
package.json
Outdated
"test": "run-s test:*", | ||
"test:node": "aegir test -t node -f 'dist/test/node.spec.js'", | ||
"test:node": "aegir test --target node --files 'test/**/*.spec.ts'", |
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.
should be a separate PR.
we will need a way to separate node and browser tests because of the countly import. if you're proposing we just find all tests in node/
then we need to update this
"test:node": "aegir test --target node --files 'test/**/*.spec.ts'", | |
"test:node": "aegir test --target node --files 'test/node/**/*.spec.ts'", |
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.
tsconfig can have both node and browser types, just wondering if it still be a problem, moving for now.
@SgtPooki thanks for a thorough review, I fixed most of the issues: 👍🏽 = fixed. Discussions:
Ready for a re-review. |
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 the updates, looks great. A few non-blocking comments
this.accumulate = new EventAccumulator(metricsService) | ||
|
||
this.storageProvider = storageProvider ?? null | ||
this.metricsService.init(serviceConfig) |
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.
yea we should. we can handle in a separate PR though.
@@ -1,3 +1,4 @@ | |||
/* eslint-disable @typescript-eslint/no-unused-expressions */ |
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.
we shouldn't need this one here if we're ignoring this rule in tests? but we probably shouldn't disable it completely in all tests.. I'm good with it either way.. eslint can be painful in test code.
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.
expect(telemetry).to.have.property('storageProvider').that.is.not.null
seems to drive eslint crazy, I can look at this later.
🎉 This PR is included in version 1.3.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Closes: #61
In this PR:
beforeunload
event to prevent event loss.