-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Adding hyperlinked fields in search result details #4669
Conversation
@@ -54,6 +55,13 @@ const MessageFieldDescription = React.createClass({ | |||
|
|||
return termsMarkup; | |||
}, | |||
_getFieldValue() { | |||
if (this.props.linkedFields.includes(this.props.fieldName)) { | |||
return <a href={this.props.possiblyHighlight(this.props.fieldName)}>{this.props.possiblyHighlight(this.props.fieldName)}</a> |
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.
You probably don't want to use the highlighted version of the field name as hyperlink reference. 😉
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.
Good catch. Will make that fix.
@@ -54,6 +55,13 @@ const MessageFieldDescription = React.createClass({ | |||
|
|||
return termsMarkup; | |||
}, | |||
_getFieldValue() { | |||
if (this.props.linkedFields.includes(this.props.fieldName)) { | |||
return <a href={this.props.fieldName}>{this.props.fieldName}</a> |
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.
Ah, now you're missing the highlight in the content of the <a>
tag. 😉
return <a href={this.props.fieldName}>{this.props.possiblyHighlight(this.props.fieldName)}</a>
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.
Fixed this!
@@ -63,6 +63,8 @@ | |||
.add("file") | |||
.add("source_file") | |||
.build(); | |||
private static final Set<String> DEFAULT_LINKED_SEARCH_FIELDS = ImmutableSet.<String>builder() |
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.
You could simply write ImmutableSet.of()
or Collections.emptySet()
.
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.
Changed!
@@ -63,6 +63,7 @@ | |||
.add("file") | |||
.add("source_file") | |||
.build(); | |||
private static final Set<String> DEFAULT_LINKED_SEARCH_FIELDS = Collections.emptySet(); |
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.
graylog2-server/graylog2-server/src/main/java/org/graylog2/indexer/searches/SearchesClusterConfig.java:66: error: cannot find symbol
private static final Set<String> DEFAULT_LINKED_SEARCH_FIELDS = Collections.emptySet();
This won't work unless you import the Collections
class.
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.
Fixed!
Hi @pbr0ck3r, In first place, thank you very much for your contribution! 🙂 We would love to add this feature to the product, but we have been discussing a bit about the implementation and we think this would be a better fit for a frontend decorator. Would you be willing to rework the feature so it uses a decorator to convert a field into a link? Please also create future PRs based on the current master branch, as that contains the latest code under development, making it easier to integrate your changes into the product. We also do not add new features to version branches, they are kept to let people build a certain Graylog version exactly as it was. Thank you! |
Sorry I ddin't branch off master. I was using the gitlab-cli which had me by default on 2.4 branch. I will branch off master and push the new code to this PR when I have time to re-write it in the coming weeks. |
I am assuming you all want me to make a decorator plugin that I would maintain myself. If so then this PR can then be closed. |
@pbr0ck3r we are happy to merge the decorator in our code and maintain it (we already have a few decorators)! I'll keep the PR open if you want to work on it. You are of course free to create a plugin with the decorator if you want to. |
How would users specify fields they want decorated? Currently I have a list in the configuration page that allows users to specific fields. In a decorator, that list would have to be hard coded in the config. Since decorators don't have any UI components, how would a user change the fields? |
Never mind... forgot how decorators work. A user can specify the field they want decorated in the pipeline rule. I will work on refactoring this code. |
@edmundoa After starting to refactor my code into a Decorator. I noticed that even if I create a decorator to make a field a link. It does not render as a link. If you look at the current PR I had to modify the MessageField.jsx file in order to get fields to render as hyperlinks (anchor tags). From the decorator even if I pass back a anchor tag for the field, react does not render it as that. I just renders it as a string. |
@pbr0ck3r decorators are not related to pipeline rules (except for I would start by creating a new decorator in Java, i.e. Link Field Converter, that takes a single field as parameter and converts it into something we can use to render an anchor with the field value used as text and As you said, React will not set any strings as HTML for security reasons. Formatting the link as HTML in the backend and rendering it with I think it would be safer to use What do you think? |
@edmundoa before I go and refactor my frontend react code. Is it fine for to be modifying the MessageField.jsx to use React.createElement instead of just creating a anchor tag like I was doing? I see you all's point of making a decorator instead of the configuration list I had before. |
@pbr0ck3r I'm not sure if I understood what you meant, so please ask again if it's not clear :) Modifying |
@edmundoa.. I have pushed up the new |
@pbr0ck3r sorry it's taking so long for me to review this, I hope I can make by the end of this week. Thank you for your patience! |
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 was testing the changes and it looks really good, great job @pbr0ck3r! 👍
Inline there are a couple of improvements and linting errors I found that should be fixed before merging the changes.
} | ||
|
||
@Inject | ||
public LinkFieldConverter(@Assisted Decorator decorator, Engine templateEngine) { |
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 use the templateEngine
parameter and we can remove it from here :)
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
public class LinkFieldConverter implements SearchResponseDecorator { |
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.
Small nitpick: I would append Decorator
to the class name, making it easier to identify it, and also keeping consistency with other existing decorators.
final List<ResultMessageSummary> summaries = searchResponse.messages().stream() | ||
.map(summary -> { | ||
final Message message = new Message(ImmutableMap.copyOf(summary.message())); | ||
if (summary.message().containsKey(linkField)) { |
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 it would be better to revert this condition and short-circuit the map method to avoid doing unnecessary work. Something like:
.map(summary -> {
if (!summary.message().containsKey(linkField)) {
return summary;
}
...
})
In that way we avoid creating a new message and then add its fields into the summary object, since we didn't decorate the message in this case.
final Message message = new Message(ImmutableMap.copyOf(summary.message())); | ||
if (summary.message().containsKey(linkField)) { | ||
final String href = (String) summary.message().get(linkField); | ||
final LinkedHashMap object = new LinkedHashMap<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.
Here I see a couple of improvements:
- Use a regular
HashMap
, as we don't really care to maintain order in this case, and it's going to be a bit faster - Use generics in the local variable you are storing the
Map
, avoiding unchecked calls toput()
afterwards - The name
object
is too generic for my taste, I would go withdecoratedField
or something similar
In summary, I would change this line into:
final Map<String, String> decoratedField = new HashMap<>();
const link = this.props.fieldValue.href; | ||
return React.createElement('a', {href: link}, link); | ||
} else { | ||
return this.props.renderForDisplay(this.props.fieldName) |
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.
Here there's a missing semicolon at the end of the line.
@@ -50,6 +50,15 @@ class MessageFieldDescription extends React.Component { | |||
SearchStore.addSearchTerm(this.props.fieldName, this.props.fieldValue); | |||
}; | |||
|
|||
_getFieldValue() { |
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.
Please use an arrow function here, to ensure the method is bound to this
in any case.
_getFieldValue() { | ||
if (this.props.isDecorated && ('type' in this.props.fieldValue) && (this.props.fieldValue.type === 'a')) { | ||
const link = this.props.fieldValue.href; | ||
return React.createElement('a', {href: link}, link); |
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.
Please add spaces between the object braces: https://eslint.org/docs/rules/object-curly-spacing
if (this.props.isDecorated && ('type' in this.props.fieldValue) && (this.props.fieldValue.type === 'a')) { | ||
const link = this.props.fieldValue.href; | ||
return React.createElement('a', {href: link}, link); | ||
} else { |
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 else
case is not really needed (since we return in the if
branch) and can be deleted.
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.
Fixed all the requested changes.
|
||
public interface Factory extends SearchResponseDecorator.Factory { | ||
@Override | ||
LinkFieldConverter create(Decorator decorator); |
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.
There are a bunch of the old LinkFieldConverter
class name that must be renamed here and in DecoratorBindings
.
import org.graylog2.rest.resources.search.responses.SearchResponse; | ||
|
||
import javax.inject.Inject; | ||
import java.util.LinkedHashMap; |
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 import is not needed any longer.
@@ -30,9 +30,8 @@ | |||
import org.graylog2.rest.resources.search.responses.SearchResponse; | |||
|
|||
import javax.inject.Inject; | |||
import java.util.LinkedHashMap; | |||
import java.util.List; |
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.
List
is still used in apply()
, so this import should not be removed.
@edmundoa Thanks for making the time to work through this with me! Much appreciated! |
Thank you very much for your contribution @pbr0ck3r! 🎉 |
Description
This PR allows graylog to have hyperlinked search result fields. A user can create a decorator for a field that they want to allow to be a hyperlink.
Closes this issue/feature request: #1914
Motivation and Context
How Has This Been Tested?
I built graylog via
mvn compile
I then ran the frontend and backend servers.
Tested saving config with several fields, not fields, etc.
Made sure that the search page details rendered accordingly.
Screenshots (if appropriate):
Types of changes
Checklist: