-
Notifications
You must be signed in to change notification settings - Fork 142
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
♻️ [RUM-8123] Introduce a hook to assemble events #3289
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #3289 +/- ##
==========================================
- Coverage 93.68% 93.67% -0.02%
==========================================
Files 288 289 +1
Lines 7617 7633 +16
Branches 1739 1741 +2
==========================================
+ Hits 7136 7150 +14
- Misses 481 483 +2 ☔ View full report in Codecov by Sentry. |
Bundles Sizes Evolution
🚀 CPU Performance
🧠 Memory Performance
|
d66a0b9
to
1192e1d
Compare
1192e1d
to
222c207
Compare
e1cef02
to
c70139a
Compare
): ReturnType<HookCallbackMap[K]> { | ||
const hookCallbacks = callbacks[hookName] || [] | ||
const results = hookCallbacks.map((callback) => callback(param)) | ||
return combine(...(results as [object, object])) as ReturnType<HookCallbackMap[K]> |
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.
💬 suggestion: The result looks nice. I'm just a bit concerned about debugging the Assembly Hook for overridden props if we connect many sources to it because we can't easily know the order of declarations. Maybe we could warn user when a prop is being overridden?
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.
Yeah, I remember we talked about this a bit too, @amortemousque. What do you think of the idea of requiring all hooks to be registered in a single place and specifying them with an array that makes the order clear?
It doesn't look like you actually use the unregister
callback returned from register()
anywhere, but if there's a need to dynamically add and remove these hooks individually, an alternative approach that might work would be to support enabling and disabling them. That way, they'd keep their initially-defined order, but you'd still be able to turn them on and off when needed.
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! We discussed this earlier with @sethfowler-datadog, and I experimented with centralized registration. However, this approach adds some boilerplate and coupling, as modules would need to expose their hooks. Self-registration offers better decoupling.
In my view, the assemble hook will be used by contexts
and collections
, which are explicitly called in startRum
. This makes the order relatively easy to visualize. For now, I suggest waiting to see if this becomes an issue before making improvements. Wdyt?
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 now, I suggest waiting to see if this becomes an issue before making improvements. Wdyt?
That'd be fine with me, if you prefer to take that direction!
... this approach adds some boilerplate and coupling, as modules would need to expose their hooks. Self-registration offers better decoupling.
FWIW, my perspective is that centralized registration does not add any additional coupling; it just turns implicit dependencies, which are hard to reason about, into explicit dependencies, which are easier to reason about. Another way to think about it is that this isn't coupling at all, but composition: we start with small pieces of functionality that can be reasoned about independently, and compose them together in an explicit way to form larger pieces of functionality. You end up with a hierarchical structure, which is nice for all the reasons that structured programming in general is nice.
That said, programming is all about tradeoffs, and there are pros and cons to every solution; I'm totally fine with a dynamic registration approach if you prefer it. As you say, we can always revisit the issue later.
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 love the direction this takes us in!
): ReturnType<HookCallbackMap[K]> { | ||
const hookCallbacks = callbacks[hookName] || [] | ||
const results = hookCallbacks.map((callback) => callback(param)) | ||
return combine(...(results as [object, object])) as ReturnType<HookCallbackMap[K]> |
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.
Yeah, I remember we talked about this a bit too, @amortemousque. What do you think of the idea of requiring all hooks to be registered in a single place and specifying them with an array that makes the order clear?
It doesn't look like you actually use the unregister
callback returned from register()
anywhere, but if there's a need to dynamically add and remove these hooks individually, an alternative approach that might work would be to support enabling and disabling them. That way, they'd keep their initially-defined order, but you'd still be able to turn them on and off when needed.
packages/rum-core/src/hooks.ts
Outdated
} | ||
export type Hooks = ReturnType<typeof startHooks> | ||
|
||
export function startHooks() { |
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.
💬 suggestion: I would rename it createHooks
as it doesn't start anything, it just creates a data structure
service: configuration.service, | ||
version: configuration.version, |
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.
💬 suggestion: it might be less confusing / more explicit to have service and version declared in a single place in trackViews.ts
:
const service = viewOptions?.service || configuration.service
const version = viewOptions?.version || configuration.version
so we can remove those two lines from assembly
@@ -33,11 +38,27 @@ export function startViewCollection( | |||
processViewUpdate(view, configuration, featureFlagContexts, recorderApi, pageStateHistory) | |||
) | |||
) | |||
|
|||
hooks.register(HookNames.Assemble, ({ startTime, eventType }): PartialRumEvent | 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.
🥜 nitpick: you can remove the return type
hooks.register(HookNames.Assemble, ({ startTime, eventType }): PartialRumEvent | undefined => { | |
hooks.register(HookNames.Assemble, ({ startTime, eventType }) => { |
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 know why but it is required for the typechecking to work properly.
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.
Weird. Do you have an example where there is an issue with the return type but not without the return type?
@@ -69,6 +72,18 @@ export function startUrlContexts( | |||
} | |||
} | |||
|
|||
hooks.register(HookNames.Assemble, ({ startTime, eventType }): PartialRumEvent | 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.
🥜 nitpick: same here
hooks.register(HookNames.Assemble, ({ startTime, eventType }): PartialRumEvent | undefined => { | |
hooks.register(HookNames.Assemble, ({ startTime, eventType }) => { |
02f9fb2
to
0eb4e78
Compare
Motivation
The overall goal is to create a Modular SDK to simplify contributions and enhance extensibility.
This PR introduces a hook system, initially used to
decouple event assembly
. Each collection module can now independently handle the assembly of its own properties.Changes
Testing
I have gone over the contributing documentation.