-
Notifications
You must be signed in to change notification settings - Fork 141
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
Create rc of analytics node #587
Conversation
🦋 Changeset detectedLatest commit: 8b2cf19 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
6293183
to
06bc149
Compare
) | ||
} | ||
|
||
function remove(loc: Storage, 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.
Not necessary to be in core (the tests weren’t as brittle as I thought), but I refactored this entire file to no longer create any import side effects (takes loc as an argument -- the cache is only created when PersistedPriorityQueue is instantiated).
027c99f
to
21106d6
Compare
Started going through this PR, wanted to know about this point:
Can you share what was refactored vs what was just copied? That'll just help with knowing what to focus extra attention on. |
2d40546
to
862c993
Compare
@@ -0,0 +1,136 @@ | |||
import { PriorityQueue } from '.' |
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.
File refactored to have no import-time side effects -- it's not relevant to node, but I went down that path and thought I would sneak it in as a "core refactor".
| 'destination' | ||
| 'enrichment' | ||
| 'utility' | ||
|
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 file is basically basically unchanged, other than generics
All?: boolean | ||
[integration: string]: boolean | JSONObject | undefined | ||
} | ||
|
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.
Other than the word "core", materially unchanged
@@ -0,0 +1,32 @@ | |||
/** | |||
* @jest-environment jsdom | |||
*/ |
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.
Declaring tests using this magic comment allows us to easily identify where our dom dependencies are
import { isFunction, isPlainObject, isString } from '../validation' | ||
import { JSONObject, EventProperties, CoreOptions } from '../events' | ||
import { Callback } from '../events/interfaces' | ||
|
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.
Basically unchanged, except for moving the arguments resolver to its own "page" file -- it's the only resolver we need to bring in (because of the overloads)
internal/config/src/jest/index.js
Outdated
@@ -0,0 +1,18 @@ | |||
const getPackages = require('get-monorepo-packages') | |||
|
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 module allows ts-jest to pick up changes in other packages without having to run build on every external change.
} | ||
} | ||
|
||
export class CoreContext<Event extends CoreSegmentEvent = CoreSegmentEvent> { |
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.
refactored from the browser Context class to make this new "base" class (meant to be used by node and browser) -- adds a generic, improves decoupling by taking Stats and Logger via constructor injection, removes extraneous access modifiers like the word "public" to make a bit prettier.
@@ -1,12 +1,12 @@ | |||
export class Emitter { | |||
private callbacks: Record<string, Function[]> = {} | |||
export class Emitter<EventName extends string = string> { |
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.
Change to allow one to specific events
options: {}, | ||
} | ||
|
||
if (!this.user) return base |
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.
the only material change to this file IIRC
export async function dispatch( | ||
event: CoreSegmentEvent, | ||
queue: EventQueue, | ||
emitter: Emitter, |
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 argument is new -- will allow us to decouple this function from listeners like segment inspector
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.
Does it make sense for the emitter to ever not be analytics? Wonder if naming the argument something like analytics
would make it more clear what should be passed/what's emitting events.
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 see your point, but IMO it's cleaner overall and will encourage better unit testing if the name / arg type reflects only the minimal required interface, per "interface segregation principal". I think it's best to be clear about the actual required semantics of the things that's being passed -- it always ends up makes refactoring easier later on so you don't end up accidentally relying on behavior that you don't use.
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.
To be clear - I was expecting the type to still be Emitter
, just the name of the property to be analytics
. Can we have a doc string then so someone looking at the function understands we expect it to be analytics (even if unit tests will pass anything)
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.
Sure, we can have a doc string.
I think ISP can still be a useful concept when it comes to variable naming (see vanilla JS / dynamic languages), as it's about not exposing unnecessary "stuff" to a consumer, which can include ideas as well as actual type information -- if I was to call dispatch with new Emitter()
but the arg name part of type signature said analytics
, I think that would be a bit confusing for a new dev, even if it just for a test -- they might think it's a mistake, and seek to refactor it to take in an Analytics interface.
I want to be extra explicit (in both the variable name and type) that we don't need anything like an Analytics instance and all the baggage it implies, that they are fully decoupled. In the future, we may limit it further to only accept an interface with the emit
function.
Good call out. I added PR comments to the relevant files to review! |
try { | ||
ctx = await this.flushOne(ctx) | ||
const done = Date.now() - start | ||
this.emit('delivery_success', done) |
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.
new event emitted for inspector host to listen to -- I could see us also moving the stats stuff outside of this function altogether and just rely on emitter events.
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.
/cc @zikaari
|
||
this.emit('message_delivered', ctx) | ||
ctx.stats?.increment('message_delivered') | ||
// should emit to: TODO: inspectorHost.delivered?.(ctx as any, ['segment.io']) -- can we move all this inspector stuff to a plugin that listens to these events? |
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 don't want inspectorHost tightly coupled -- it can use the event emitter to listen to events. I can see it as a plugin.
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.
Sounds like a utility
plugin - /cc @zikaari
this.integrations = options?.integrations ?? {} | ||
this.options = options ?? {} | ||
this.queue = queue | ||
bindAll(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.
new
const registrations = plugins.map((xt) => | ||
this.queue.register(ctx, xt, this) | ||
) | ||
await Promise.all(registrations) |
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 might want to handle these errors, but left as is. Weirdly, no lint complaints.
@@ -0,0 +1,20 @@ | |||
{ |
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.
new package!
"postpublish-run-all": "yarn workspaces foreach -vpt --no-private run postpublish", | ||
"version-run-all": "yarn workspaces foreach -vpt --no-private run version", | ||
"core": "yarn workspace @segment/analytics-core", | ||
"core+deps": "turbo run --filter='analytics-core'", | ||
"core+deps": "turbo run --filter=@segment/analytics-core...", |
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.
the "..." syntax is how turborepo expresses "include all dependencies".
packages/core/jest.config.js
Outdated
setupFilesAfterEnv: ['./jest.setup.js'], | ||
projects: [ | ||
"<rootDir>", | ||
"<rootDir>/../core-integration-tests" |
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.
running "yarn test" and so forth in core automatically runs integration tests (yes, jest has its own support for monorepos!)
c879f56
to
6dcd98c
Compare
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.
Overall I think this looks good - thanks for commenting on what was changed vs copied.
it('populates anonymousId if provided', async () => { | ||
const [analytics] = await load({ writeKey }) | ||
|
||
const ctx = await analytics.alias('chris radek', 'chris', { |
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.
My test cases live on!
try { | ||
ctx = await this.flushOne(ctx) | ||
const done = Date.now() - start | ||
this.emit('delivery_success', done) |
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.
/cc @zikaari
|
||
this.emit('message_delivered', ctx) | ||
ctx.stats?.increment('message_delivered') | ||
// should emit to: TODO: inspectorHost.delivered?.(ctx as any, ['segment.io']) -- can we move all this inspector stuff to a plugin that listens to these events? |
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.
Sounds like a utility
plugin - /cc @zikaari
ef8f63b
to
4f3ca6f
Compare
76f0322
to
133da09
Compare
133da09
to
3880fa0
Compare
ec57ee9
to
d34892a
Compare
d34892a
to
081045b
Compare
0a6bf55
to
cd689d2
Compare
cd689d2
to
8b2cf19
Compare
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 going through the feedback and making updates! Looks good to me!
Sorry for this huge PR
Changelog:
Fast follow: