Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
Guard reserved tags field against incorrect use #14822
Guard reserved tags field against incorrect use #14822
Changes from 18 commits
58ac5c0
410c8b9
461a34d
97dce2e
a528472
9cf92f2
8348a04
f89d765
fa1ee92
d37d453
1e1a951
c356d9f
4d7c080
f3ab6ed
9fa7993
94607c0
713923c
909526b
409b634
4e322d1
3ffa3cc
61de3d0
ad4259d
fce4f4a
cd27ed8
b312284
32a7d8b
05ae484
11bf4aa
ff21c0d
5df8219
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
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 this is doing more than it needs to, and that we should merely rename the root edge of the
field
fromtags
to_tags
and let the setter work as it usually doesThe semantics of
Event#setField(FieldReference, Object)
— the only caller of this method — are typically to overwrite the value in the target field with the provided value, although in attempting to validate that I have found an edge-case where if thefield
references a nesting beneath an existing non-map object we silently fail to set the value:Suppose we have an event with a top-level field
a
that contains an array of stringsx
,y
, andz
:And then we try to set the field with reference
[a][b]
to the valueok
:This silently fails, and has since at least 7.0 😩
The existing value of top-level field
a
doesn't have an item at indexb
, so we try to create one, but fail to parseb
as an int and silently abort.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 reason of not doing a simple rename of the root of the field is the same as the edge-case sample.
_tags
can fail silently by copying a map to an existing array, so to solve it and give a better user experience, I initialize_tags
as a list and preserve all values.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.
Adding special-case logic to handle this one circumstance is going to set us up for surprises. We don't do similar for any of our other reserved fields, and don't handle this type of collision when a user is explicitly setting a particular field, so it is surprising to me that we do so here.
I would prefer to solve the underlying bug, so that
Event#set(FieldReference, Object)
sets the field at the specified address fromFieldReference
to the specified value fromObject
, overwriting intermediates as necessary.This is likely a discussion for the larger team since I am sure that we have users in-the-wild who are accidentally relying on this silent failure.
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 referred to the first review that we simply rename the first part of FieldReference [tags][nested][field] to [_tags][nested][field] could experience the same exception that happened in
tags
field. Cleaning up the field byevent.remove()
and then copying the value should avoid type collision. I don't think we need to handle explicitly setting an incompatible type to particular field.Currently, a normal use case is appending every illegal
tags
value to_tags
list. If user setevent.set('_tags', ['x','y','z'])
andevent.set("[_tags][b]", "not_ok")
, it will throw exception. To address this issue, it is better to track a new issue, not necessary to fix in this PR.To the next reviewer: here we need to decide what preserving strategy is preferred
_tags
_tags
which aligns with other reserved fields.Both cannot handle collision when a user is explicitly setting a particular field.
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.
Iterating from 1 is correct but could easily be interpreted as a mistake. My IDE suggested something 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.
subList
from 1 is correct as this method handles Map which must contain at least one path, otherwisesubList(1, path)
throw index out of bound exception.for 1..length
guarantees eligible index. I prefer to keep it.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 that we can add a
FieldReference#rebaseOnto(FieldReference)
method that will handle all of this in a more testable way that tolerates fragments that can't legally be represented without escapes:[EDIT: my initial suggestion missed adding
newbase.key
🤦🏼 ]Doing so requires that we break
FieldReference.parse
into two methods: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.
@yaauie The rebase idea works well when a single root path is the rebase target.
eg.
newBase.path == [_tags]
However, when newBase path is more than one,
newBase.path == [_tags, 0]
, it again needs to construct a string[_tags][0]
to create newBasenewBase = FieldReference.from("[_tags][0]")
Can we make
rebaseOnto()
accept newBase.path?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 primary concern with using
FieldReference#from
with a manually-constructed string is that if any component of the path contains reserved characters, the result cannot be parsed and an exception will be thrown, which means including arbitrary field names from the input can lead to runtime exceptions.When
index
is anint
,FieldReference#from(String.format("[_tags][%s]", index))
cannot possibly be invalid.And
FieldReference.from(String)
is cached, so it is super cheap especially in cases where we use the same input multiple times which we are likely to do with the only variant being numeric indices.I started with a
List<String>
, but there are weird edge-cases of how@metadata
fields work that made me suggest taking aFieldReference
to rebase onto:[deeply][nested][field]
["deeply", "nested"]
"field"
DATA_CHILD
[@metadata][deeply][nested][field]
["deeply", "nested"]
"field"
METADATA_CHILD
[@metadata]
[]
"@metadata"
METADATA_PARENT
Even though we are only using this new
FieldReference#rebaseOnto
with known base that is not@metadata
, I didn't want to introduce a new API as a trap that fails weirdly when someone starts using it in new ways.HOWEVER my suggestion provides no way to rebase onto the data-root of the event, which can be done by providing an empty list with your implementation.
I think something like the following satisfies the
@metadata
weirdness by extracting the complexity of reconstructing the list of tokens, and also gives us the freedom moving forward to use this for other purposes: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.
@metadata
is indeed weird.rebaseOnto()
accepting path gives much better flexibility 👍