Skip to content

Commit 57396dc

Browse files
author
Vladimir Kotal
authored
Messages class rework (#3011)
reflect Message level in the UI make Message level first class citizen fixes #2974
1 parent 1da0f1e commit 57396dc

File tree

17 files changed

+334
-90
lines changed

17 files changed

+334
-90
lines changed

apiary.apib

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Besides `/suggester` and `/search` endpoints, everything is accessible from `loc
7171

7272
### adds message to a system [POST]
7373

74-
Usable values for `cssClass` are: `info`, `warning`, `error`.
74+
Usable values for `messageLevel` are: `success`, `info`, `warning`, `error`.
7575
This will affect CSS class of the displayed message, leading to different coloring.
7676

7777
The values in the `tags` list can be: `main` (special value for top level message in the whole web application),
@@ -84,7 +84,7 @@ see https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-java
8484

8585
{
8686
"tags": ["main"],
87-
"cssClass": "class",
87+
"messageLevel": "info",
8888
"text":"test message",
8989
"duration":"PT10M"
9090
}

opengrok-indexer/src/main/java/org/opengrok/indexer/search/Results.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ public static void prettyPrint(Writer out, SearchHelper sh, int start,
196196
String messages = MessagesUtils.messagesToJson(p, MESSAGES_MAIN_PAGE_TAG);
197197
if (p != null && !messages.isEmpty()) {
198198
out.write(" <a href=\"" + xrefPrefix + "/" + p.getName() + "\">");
199-
out.write("<span class=\"important-note important-note-rounded\" data-messages='" + messages + "'>!</span>");
199+
out.write("<span class=\"note-" + MessagesUtils.getMessageLevel(p.getName(), MESSAGES_MAIN_PAGE_TAG) +
200+
" important-note important-note-rounded\" data-messages='" + messages + "'>!</span>");
200201
out.write("</a>");
201202
}
202203

opengrok-indexer/src/main/java/org/opengrok/indexer/web/Scripts.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ public String toHtml() {
111111
SCRIPTS.put("jquery-tablesorter", new FileScript("js/jquery-tablesorter-2.26.6.min.js", 12));
112112
SCRIPTS.put("tablesorter-parsers", new FileScript("js/tablesorter-parsers-0.0.2.min.js", 13));
113113
SCRIPTS.put("tablesorter-parsers" + DEBUG_SUFFIX, new FileScript("js/tablesorter-parsers-0.0.2.js", 13));
114-
SCRIPTS.put("searchable-option-list", new FileScript("js/searchable-option-list-2.0.7.min.js", 14));
115-
SCRIPTS.put("utils", new FileScript("js/utils-0.0.33.min.js", 15));
116-
SCRIPTS.put("utils" + DEBUG_SUFFIX, new FileScript("js/utils-0.0.33.js", 15));
114+
SCRIPTS.put("searchable-option-list", new FileScript("js/searchable-option-list-2.0.8.min.js", 14));
115+
SCRIPTS.put("utils", new FileScript("js/utils-0.0.34.min.js", 15));
116+
SCRIPTS.put("utils" + DEBUG_SUFFIX, new FileScript("js/utils-0.0.34.js", 15));
117117
SCRIPTS.put("repos", new FileScript("js/repos-0.0.2.min.js", 20));
118118
SCRIPTS.put("repos" + DEBUG_SUFFIX, new FileScript("js/repos-0.0.2.js", 20));
119119
SCRIPTS.put("diff", new FileScript("js/diff-0.0.4.min.js", 20));

opengrok-indexer/src/main/java/org/opengrok/indexer/web/messages/Message.java

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import java.time.Duration;
4040
import java.time.format.DateTimeParseException;
4141
import java.util.Collections;
42+
import java.util.Comparator;
43+
import java.util.Locale;
4244
import java.util.Objects;
4345
import java.util.Set;
4446
import java.util.TreeSet;
@@ -50,7 +52,40 @@ public class Message implements Comparable<Message>, JSONable {
5052
@NotEmpty(message = "tags cannot be empty")
5153
private Set<String> tags = Collections.singleton(MESSAGES_MAIN_PAGE_TAG);
5254

53-
private String cssClass = "info";
55+
public enum MessageLevel {
56+
/**
57+
* Known values: {@code SUCCESS}, {@code INFO}, {@code WARNING}, {@code ERROR}.
58+
* The values are sorted according to their level. Higher numeric value of the level (i.e. the enum ordinal)
59+
* means higher priority.
60+
*/
61+
SUCCESS("success"), INFO("info"), WARNING("warning"), ERROR("error");
62+
63+
private final String messageLevelString;
64+
65+
MessageLevel(String str) {
66+
messageLevelString = str;
67+
}
68+
69+
public static MessageLevel fromString(String val) throws IllegalArgumentException {
70+
for (MessageLevel v : MessageLevel.values()) {
71+
if (v.toString().equals(val.toLowerCase(Locale.ROOT))) {
72+
return v;
73+
}
74+
}
75+
throw new IllegalArgumentException("class type does not match any known value");
76+
}
77+
78+
@Override
79+
public String toString() {
80+
return messageLevelString;
81+
}
82+
83+
public static final Comparator<MessageLevel> VALUE_COMPARATOR = Comparator.comparingInt(Enum::ordinal);
84+
}
85+
86+
@JsonDeserialize(using = MessageLevelDeserializer.class)
87+
@JsonSerialize(using = MessageLevelSerializer.class)
88+
private MessageLevel messageLevel = MessageLevel.INFO;
5489

5590
@NotBlank(message = "text cannot be empty")
5691
@JsonSerialize(using = HTMLSerializer.class)
@@ -67,7 +102,7 @@ private Message() { // needed for deserialization
67102
public Message(
68103
final String text,
69104
final Set<String> tags,
70-
final String cssClass,
105+
final MessageLevel messageLevel,
71106
final Duration duration
72107
) {
73108
if (text == null || text.isEmpty()) {
@@ -82,16 +117,16 @@ public Message(
82117

83118
this.text = text;
84119
this.tags = tags;
85-
this.cssClass = cssClass;
120+
this.messageLevel = messageLevel;
86121
this.duration = duration;
87122
}
88123

89124
public Set<String> getTags() {
90125
return tags;
91126
}
92127

93-
public String getCssClass() {
94-
return cssClass;
128+
public MessageLevel getMessageLevel() {
129+
return messageLevel;
95130
}
96131

97132
public String getText() {
@@ -135,14 +170,14 @@ public boolean equals(Object o) {
135170
}
136171
Message message = (Message) o;
137172
return Objects.equals(tags, message.tags) &&
138-
Objects.equals(cssClass, message.cssClass) &&
173+
Objects.equals(messageLevel, message.messageLevel) &&
139174
Objects.equals(text, message.text) &&
140175
Objects.equals(duration, message.duration);
141176
}
142177

143178
@Override
144179
public int hashCode() {
145-
return Objects.hash(tags, cssClass, text, duration);
180+
return Objects.hash(tags, messageLevel, text, duration);
146181
}
147182

148183
@Override
@@ -157,6 +192,47 @@ public int compareTo(final Message o) {
157192
return tags.size() - o.tags.size();
158193
}
159194

195+
static class MessageLevelSerializer extends StdSerializer<MessageLevel> {
196+
private static final long serialVersionUID = 928540953227342817L;
197+
198+
MessageLevelSerializer() {
199+
this(null);
200+
}
201+
202+
MessageLevelSerializer(Class<MessageLevel> vc) {
203+
super(vc);
204+
}
205+
206+
@Override
207+
public void serialize(final MessageLevel messageLevel,
208+
final JsonGenerator jsonGenerator,
209+
final SerializerProvider serializerProvider) throws IOException {
210+
jsonGenerator.writeString(messageLevel.toString().toLowerCase(Locale.ROOT));
211+
}
212+
}
213+
214+
private static class MessageLevelDeserializer extends StdDeserializer<MessageLevel> {
215+
private static final long serialVersionUID = 928540953227342817L;
216+
217+
MessageLevelDeserializer() {
218+
this(null);
219+
}
220+
221+
MessageLevelDeserializer(Class<?> vc) {
222+
super(vc);
223+
}
224+
225+
@Override
226+
public MessageLevel deserialize(final JsonParser parser, final DeserializationContext context)
227+
throws IOException {
228+
try {
229+
return MessageLevel.fromString(context.readValue(parser, String.class));
230+
} catch (DateTimeParseException e) {
231+
throw new IOException(e);
232+
}
233+
}
234+
}
235+
160236
private static class DurationSerializer extends StdSerializer<Duration> {
161237

162238
private static final long serialVersionUID = 5275434375701446542L;

opengrok-indexer/src/main/java/org/opengrok/indexer/web/messages/MessagesContainer.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,10 @@ public String getText() {
250250
return message.getText();
251251
}
252252

253-
@JsonProperty("cssClass")
254-
public String getCssClass() {
255-
return message.getCssClass();
253+
@JsonProperty("messageLevel")
254+
@JsonSerialize(using = Message.MessageLevelSerializer.class)
255+
public Message.MessageLevel getMessageLevel() {
256+
return message.getMessageLevel();
256257
}
257258

258259
private AcceptedMessage(final Message message) {

opengrok-indexer/src/main/java/org/opengrok/indexer/web/messages/MessagesUtils.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@
3737
import java.text.SimpleDateFormat;
3838
import java.util.ArrayList;
3939
import java.util.Arrays;
40+
import java.util.Collection;
4041
import java.util.HashSet;
4142
import java.util.List;
4243
import java.util.Set;
4344
import java.util.SortedSet;
4445
import java.util.logging.Level;
4546
import java.util.logging.Logger;
47+
import java.util.stream.Collectors;
4648

4749
public final class MessagesUtils {
4850

@@ -98,7 +100,7 @@ private static void printMessages(Writer out, SortedSet<MessagesContainer.Accept
98100
out.write("\">\n");
99101
for (MessagesContainer.AcceptedMessage m : set) {
100102
out.write("<li class=\"message-group-item ");
101-
out.write(Util.encode(m.getMessage().getCssClass()));
103+
out.write(Util.encode(m.getMessage().getMessageLevel().toString()));
102104
out.write("\" title=\"Expires on ");
103105
out.write(Util.encode(df.format(Date.from(m.getExpirationTime()))));
104106
out.write("\">");
@@ -230,4 +232,32 @@ private static String messagesToJson(Group group, String... additionalTags) {
230232
public static String messagesToJson(Group group) {
231233
return messagesToJson(group, new String[0]);
232234
}
235+
236+
/**
237+
* @return name of highest cssClass of messages present in the system or null.
238+
*/
239+
static String getHighestMessageLevel(Collection<MessagesContainer.AcceptedMessage> messages) {
240+
return messages.
241+
stream().
242+
map(MessagesContainer.AcceptedMessage::getMessageLevel).
243+
max(Message.MessageLevel.VALUE_COMPARATOR).
244+
map(Message.MessageLevel::toString).
245+
orElse(null);
246+
}
247+
248+
/**
249+
* @param tags message tags
250+
* @return name of highest cssClass of messages present in the system or null.
251+
*/
252+
public static String getMessageLevel(String... tags) {
253+
Set<MessagesContainer.AcceptedMessage> messages;
254+
RuntimeEnvironment env = RuntimeEnvironment.getInstance();
255+
256+
messages = Arrays.stream(tags).
257+
map(env::getMessages).
258+
flatMap(Collection::stream).
259+
collect(Collectors.toSet());
260+
261+
return getHighestMessageLevel(messages);
262+
}
233263
}

opengrok-indexer/src/test/java/org/opengrok/indexer/web/messages/MessageTest.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,12 @@ public void createBadMessageTest6() {
6767

6868
@Test
6969
public void messageToJSON() throws IOException {
70-
Message m = new Message("test", Collections.singleton("test"), "foo", Duration.ofMinutes(1));
70+
Message m = new Message("test",
71+
Collections.singleton("test"),
72+
Message.MessageLevel.INFO,
73+
Duration.ofMinutes(1));
7174
String jsonString = m.toJSON();
72-
assertEquals(new HashSet<>(Arrays.asList("cssClass", "duration", "text", "tags")),
75+
assertEquals(new HashSet<>(Arrays.asList("messageLevel", "duration", "text", "tags")),
7376
getTopLevelJSONFields(jsonString));
7477
}
7578
}

opengrok-indexer/src/test/java/org/opengrok/indexer/web/messages/MessagesContainerTest.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@
3737
import java.util.concurrent.TimeUnit;
3838

3939
import static org.awaitility.Awaitility.await;
40-
import static org.junit.Assert.assertEquals;
41-
import static org.junit.Assert.assertTrue;
40+
import static org.junit.Assert.*;
4241
import static org.opengrok.indexer.web.messages.JSONUtils.getTopLevelJSONFields;
4342

4443
public class MessagesContainerTest {
@@ -58,7 +57,10 @@ public void tearDown() {
5857

5958
@Test
6059
public void addAndGetTest() {
61-
Message m = new Message("test", Collections.singleton("test"), "info", Duration.ofMinutes(10));
60+
Message m = new Message("test",
61+
Collections.singleton("test"),
62+
Message.MessageLevel.INFO,
63+
Duration.ofMinutes(10));
6264

6365
container.addMessage(m);
6466

@@ -93,7 +95,9 @@ private void parallelAddMessages() throws Exception {
9395
final int index = i;
9496
Thread t = new Thread(() -> {
9597
for (int j = 0; j < 100; j++) {
96-
Message m = new Message("test" + index + j, Collections.singleton("test"), "info",
98+
Message m = new Message("test" + index + j,
99+
Collections.singleton("test"),
100+
Message.MessageLevel.INFO,
97101
Duration.ofMinutes(10));
98102
container.addMessage(m);
99103
}
@@ -125,16 +129,22 @@ public void parallelAddLimitTest() throws Exception {
125129

126130
@Test
127131
public void expirationTest() {
128-
Message m = new Message("test", Collections.singleton("test"), "info", Duration.ofMillis(10));
132+
Message m = new Message("test",
133+
Collections.singleton("test"),
134+
Message.MessageLevel.INFO,
135+
Duration.ofMillis(10));
129136

130137
container.addMessage(m);
131138

132-
await().atMost(2, TimeUnit.SECONDS).until(() -> container.getMessages("info").isEmpty());
139+
await().atMost(2, TimeUnit.SECONDS).until(() -> container.getMessages(Message.MessageLevel.INFO.toString()).isEmpty());
133140
}
134141

135142
@Test
136143
public void removeTest() {
137-
Message m = new Message("test", Collections.singleton("test"), "info", Duration.ofMillis(10));
144+
Message m = new Message("test",
145+
Collections.singleton("test"),
146+
Message.MessageLevel.INFO,
147+
Duration.ofMillis(10));
138148

139149
container.addMessage(m);
140150

@@ -153,13 +163,16 @@ public void getMessagesNullTest() {
153163
*/
154164
@Test
155165
public void testJSON() throws IOException {
156-
Message m = new Message("testJSON", Collections.singleton("testJSON"), "info", Duration.ofMinutes(10));
166+
Message m = new Message("testJSON",
167+
Collections.singleton("testJSON"),
168+
Message.MessageLevel.INFO,
169+
Duration.ofMinutes(10));
157170

158171
container.addMessage(m);
159172

160173
MessagesContainer.AcceptedMessage am = container.getMessages("testJSON").first();
161174
String jsonString = am.toJSON();
162-
assertEquals(new HashSet<>(Arrays.asList("tags", "expired", "created", "expiration", "cssClass", "text")),
175+
assertEquals(new HashSet<>(Arrays.asList("tags", "expired", "created", "expiration", "messageLevel", "text")),
163176
getTopLevelJSONFields(jsonString));
164177
}
165178
}

0 commit comments

Comments
 (0)