-
-
Notifications
You must be signed in to change notification settings - Fork 444
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
Fix: Apply user from the scope to transaction. #1424
Conversation
assertNotNull(result.user) { | ||
assertEquals("john.doe", it.username) | ||
assertEquals("user-id", it.id) | ||
assertEquals("192.168.0.1", it.ipAddress) | ||
assertEquals("john.doe@example.com", it.email) | ||
assertEquals(mapOf("key" to "value"), it.others) | ||
} |
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 block is duplicated across tests, maybe we extract it to a method called asserUser or something like that?
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 in case of this test class duplicated code is a good trade off. Having fixed string values in assertUser
method although resolves duplication, doesn't feel right to couple this method with test methods. Feel free to refactor it if you like.
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.
tbh I'm not a big fan of that, it's the same strings and same asserts across every test, every time you'd need to change either a value or adding/removing a field, you've to change it everywhere.
I know @philipphofmann has strong opinions about that too, it's not scalable when the number of tests increases.
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 100% with @marandaneto. Treat your test code as production code. A few duplications are fine, but in this case, I would extract them to a method.
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 will merge as it is because I am blocked by this PR, but lets please discuss and refactor this as soon as we touch this class again
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 willing to die on this hill so bare with me:
I believe a test should be as self-sufficient as possible. When a test calls out to a private method getUser
then assertUser
it isn't clear what user
is, and how assert
works, unless I peek into those methods. It also means that I can be setting the user now thinking of a particular test, but be negatively affecting another, without knowing, since multiple tests "change" by means of modifying these common functions.
It has the advantage, obviously, of having a single place to "fix" things, or even to add a new property to be set and asserted. It But that comes with the cost of potentially changing a test that wasn't supposed to be changed.
Perhaps what we're missing here is a library that validates two objects are "equivalent". It solves the problem of adding a new property to the field and also expecting that to be mapped through which is a common mistake when mapping objects by hand.
Both these principles/approaches are used in the .NET SDK. The assertion library (introduced by @Tyrrrz, who from what I noticed also follows the idea of self-sufficient tests) is called FluentAssertions
, is there something similar for Java?
All of that said, this class in particular does have a lot of duplication for basic object mapping, to the point that this diff was mainly changes to asserting user
. I'd be very tempted to at least create a assertUserEquals(User a, User b)
to get rid of this duplication.
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.
Kotlin generates equals
and hashCode
for data classes, but Java does not, we'd need to implement and bloat the SDK with that (which is gonna be used only in tests), so we could do an equality check.
another approach is having a test per field, instead of the whole user every time.
I do agree that's a trade-off, but I often spend more time scaling the tests other than "understanding" them.
if (sentryBaseEvent.getUser() == null) { | ||
sentryBaseEvent.setUser(scope.getUser()); | ||
} |
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.
what's about the combination with this check and MainEventProcessor
? do they conflict with each other when isSendDefaultPii
is enabled? I believe one of them will never be executed if it sets to a non-null value
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.
If user is not set on the event, SentryClient
sets it from the scope. Then in MainEventProcessor
if user is null it sets the user with a default ip address, if its not null but the ip address is null it sets the default ip address. What conflict do you mean?
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.
indeed, apply scope happens before event processors, that's why, the other way around would be problematic I believe.
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.
a problem that I see is, MainEventProcessor takes into consideration the isSendDefaultPii
and sets IP address with DEFAULT_IP_ADDRESS
, but that won't happen for transactions, this can be fixed later IMO when we run event processors for transactions 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.
We could start setting the default value even if a user is set, but the IpAddress is null.
I don't believe the "setting user semantics" dictates it's a all or nothing thing, or does it?
Can't we merge values instead?
If I do captureEvent(Event{ user = User(name))
while having sendDefaultPii=true
I'd still like the the {{auto}}
IP Address to be added to the user. I just set the name
because that's all I have.
@maciejwalkowiak this PR cannot be released alone though, if it reads from Scope but integrations don't set the user request to the Scope, Scope.user will be always null |
@marandaneto for anyone who is not using integrations but does manual instrumentation this PR does provide a value. As I understand no matter how we solve the integrations, scope should be applied to transactions anyways. |
I agree, but ideally this PR or another one should fix for our integrations too, see #1335 (comment) |
what we could do is: creating the Spring filter that sets the user to the Scope and add it to https://docs.sentry.io/platforms/java/guides/spring/performance/#enable-tracing wdyt @maciejwalkowiak @bruno-garcia ? |
@marandaneto yes I agree. Please note, that adding extra filter for Spring integration is not as big problem as it may look. Using Spring without Spring Boot, means that users must configure a lot of things manually, and while it is possible to configure some things that happen inside Spring context (like To simplify user experience we could have just a single filter that does everything, but then the performance measurement would not start as early as it is right now. |
Codecov Report
@@ Coverage Diff @@
## main #1424 +/- ##
=========================================
Coverage 75.79% 75.80%
- Complexity 1865 1866 +1
=========================================
Files 185 185
Lines 6387 6389 +2
Branches 636 637 +1
=========================================
+ Hits 4841 4843 +2
Misses 1258 1258
Partials 288 288
Continue to review full report at Codecov.
|
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.
👍
Though as Manoel pointed out, we'll miss the default IP address at times. Left a note on that
if (sentryBaseEvent.getUser() == null) { | ||
sentryBaseEvent.setUser(scope.getUser()); | ||
} |
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 could start setting the default value even if a user is set, but the IpAddress is null.
I don't believe the "setting user semantics" dictates it's a all or nothing thing, or does it?
Can't we merge values instead?
If I do captureEvent(Event{ user = User(name))
while having sendDefaultPii=true
I'd still like the the {{auto}}
IP Address to be added to the user. I just set the name
because that's all I have.
@bruno-garcia the question is should we lift setting the IP address from |
let's not block this PR because of that, here is the issue to follow up later when we execute event processors #1427 |
assertThat(event.user).isNotNull() | ||
assertThat(event.user.username).isEqualTo("user") | ||
assertThat(event.user.ipAddress).isEqualTo("169.128.0.1") | ||
assertThat(event.user!!.username).isEqualTo("user") | ||
assertThat(event.user!!.ipAddress).isEqualTo("169.128.0.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.
maybe use assertNotNull
with the lambda so we avoid !!
for all user asserts on this PR?
`Instructions and example for changelog`$
To the changelog entry, please add a link to this PR (consider a more descriptive message):` - Apply user from the scope to transaction(#1424)
|
📜 Description
Apply user from the scope to transaction.
💡 Motivation and Context
#1335 Note: this does not solve the issue of setting user on transaction in Spring/Spring Boot integrations.
💚 How did you test it?
Unit tests.
📝 Checklist
Adding nullability annotations to
user
makes it a breaking change for Kotlin users.🔮 Next steps
Fix setting user on transaction for Spring & Spring Boot.