Skip to content
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-8652] Allow definition of custom implementations of specific SR methods #2516

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

cdn34dd
Copy link

@cdn34dd cdn34dd commented Feb 27, 2025

What does this PR do?

  • Exposes a new SessionReplayConfigutation option to allow clients to pass their own implementations of specific methods used in the Session Replay SDK.

  • Updates the SDK code to use the overriden function if provided and falling back to the original implementation otherwise.
    ( Currently only getCurrentWIndows is allowed to be redefined, but there's a high likelihood that more will be needed )

Motivation

What inspired you to submit this pull request?

Certain parts of the session replay SDK don't work as intended when ran in a react-native environment, leading to broken replays. In order to keep platform specific code out of the android SDK, the goal is to allow each client to add their own implementations of the parts that don't work.

Additional Notes

We keep the setCustomCallbacks only accessible through a internal proxy in order to not expose internal config options. In practice it would look something like this:

        val customCallbacks = SessionReplayCustomCallbacks(
            getCurrentWindows = ::getCurrentWindows
            /* Other potential methods */
        )
        
        val configuration = SessionReplayConfiguration.Builder(replaySampleRate.toFloat())
            /* .... */
            .setImagePrivacy(/* .... */)
            .setTouchPrivacy(/* .... */)
            .setTextAndInputPrivacy(/* .... */)
            .let { builder ->
                _SessionReplayInternalProxy.setCustomCallbacks(builder, customCallbacks)
            }

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

Comment on lines 11 to 13
data class SessionReplayCustomCallbacks(
val getCurrentWindows: (() -> List<Window>)? = null
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Why did you chose to make it a data class instead of an interface?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general idea was to make a simple configuration object we could add to the configuration builder that holds optional callbacks, allowing clients to set only the ones they needed. But here both would work.

@cdn34dd cdn34dd marked this pull request as ready for review March 3, 2025 07:12
@cdn34dd cdn34dd requested review from a team as code owners March 3, 2025 07:12
@cdn34dd cdn34dd force-pushed the carlosnogueira/RUM-8652/add-config-option-to-override-specific-sr-methods branch from 8d1bb14 to fdb1b74 Compare March 3, 2025 08:00
@0xnm
Copy link
Member

0xnm commented Mar 3, 2025

I think we shouldn't go this way (by exposing it in the public API along with other methods), it should be an internal API accessible from the proxy.

*/
@InternalApi
@Suppress(
"UndocumentedPublicClass",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class has been documented above, why do we still need this Suppression here?

import android.view.Window

data class SessionReplayCustomCallbacks(
val getCurrentWindows: (() -> List<Window>)? = null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think instead of having a data class with only one callback inside, you can just make it as an interface

@NoOpImplementation
interface SessionReplayCustomCallback{
      fun getCurrentWindows() : List<Window>
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not sure how many callbacks there will be for sure, for now it's just one, if it turns out to be just this one, we will likely not even need this and I'll change this then.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having several callbacks feature, is that possible to have only one callback but with more functions in it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what that means, can you explain it a bit more or give an example ? Just for context, all these functions we need to override from RN or other project can all be unrelated to each other and can affect different parts of the sdk.

Copy link
Member

@ambushwork ambushwork Mar 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, what you intent to do should be like:

data class SessionReplayCustomCallbacks(
    val getCurrentWindows: (() -> List<Window>)? = null
    val doSomethingElseA: ((SomeInputA) -> SomeOutputA)? = null
    val doSomethingElseB: ((SomeInputB) -> List<SomeOutputB>)? = null
)

Instead of doing that, you can do :

@NoOpImplementation
interface SessionReplayCustomCallback{

     fun getCurrentWindows() : List<Window>

     fun doSomethingElseA(input: SomeInputA) : SomeOutputA

     fun doSomethingElseA(input: SomeInputB) : SomeOutputB
}

In that case you will have only one callback but can be called in different placesto serve for your need.

/**
* Specifies callbacks functions to override standard behaviours in Session Replay
* that need to behave differently depending on the platform.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While reading this documentation, I would expect to understand what's the exact behaviours it will override. can you elaborate it?

@@ -73,6 +75,7 @@ data class SessionReplayConfiguration internal constructor(
private var extensionSupportSet: MutableSet<ExtensionSupport> = mutableSetOf()
private var dynamicOptimizationEnabled = true
private var systemRequirementsConfiguration = SystemRequirementsConfiguration.NONE
private var customCallbacks: SessionReplayCustomCallbacks = SessionReplayCustomCallbacks()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it expected that the class name and field name are all pluriel?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say in this case yes, but what would you suggest ?

Copy link
Member

@ambushwork ambushwork Mar 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usually we keep class name singular to represent a single instance of an object, unless the class itself presents a pluriel collection such as data class Users(val users: List<User>), and for the field name it should be like:

private var customCallback : SessionReplayCustomCallback = SessionReplayCustomCallback()

// or 

private var customCallbacks : List<SessionReplayCustomCallback> = SessionReplayCustomCallback()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind changing the name, but having something called SessionReplayCustomCallback, will be incorrect as soon as another callback is added no ?

@0xnm
Copy link
Member

0xnm commented Mar 3, 2025

Please ignore my comment above, I had an old version of this PR opened. I will review a new one.

0xnm
0xnm previously approved these changes Mar 3, 2025
Copy link
Member

@0xnm 0xnm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left few suggestions, but nothing blocking. LGTM.

import android.view.Window

data class SessionReplayCustomCallbacks(
val getCurrentWindows: (() -> List<Window>)? = null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: there is no need to use get prefix for val property, it can be just val currentWindows

Comment on lines 172 to 175
logger = internalLogger,
resourceResolver = resourceResolver,
logger = internalLogger, resourceResolver = resourceResolver,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be reverted

Comment on lines 11 to 12
data class SessionReplayCustomCallbacks(
val getCurrentWindows: (() -> List<Window>)? = null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe some explanation/docs here will be useful

"ClassNaming",
"VariableNaming"
)
class _SessionReplayInternalProxy {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_SessionReplayInternalProxy.setCustomCallbacks(builder, customCallbacks)

_SessionReplayInternalProxy(builder).setCustomCallbacks(customCallbacks)

… builder

- have the option to override `getCurrentWindows` in order to improve
cross-platform support
- make `setInternalCallback`to be available only through internal proxy
@cdn34dd cdn34dd force-pushed the carlosnogueira/RUM-8652/add-config-option-to-override-specific-sr-methods branch from 504b964 to 83f6f78 Compare March 3, 2025 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants