Skip to content

Commit ec6dfce

Browse files
authored
Fix CWL event sorting (#2744)
1 parent be45ebf commit ec6dfce

File tree

5 files changed

+131
-12
lines changed

5 files changed

+131
-12
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "bugfix",
3+
"description" : "Fix CWL last event sorting (#2737)"
4+
}

jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudwatch/logs/editor/TableUtils.kt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import software.aws.toolkits.jetbrains.utils.ui.setSelectionHighlighting
1717
import software.aws.toolkits.resources.message
1818
import java.awt.Component
1919
import java.text.SimpleDateFormat
20+
import java.util.Comparator
2021
import javax.swing.JTable
2122
import javax.swing.SortOrder
2223
import javax.swing.table.TableCellRenderer
@@ -44,12 +45,17 @@ class LogStreamsStreamColumnRenderer() : TableCellRenderer {
4445
}
4546
}
4647

47-
class LogStreamsDateColumn : ColumnInfo<LogStream, String>(message("cloudwatch.logs.last_event_time")) {
48+
class LogStreamsDateColumn(private val format: SyncDateFormat? = null) : ColumnInfo<LogStream, String>(message("cloudwatch.logs.last_event_time")) {
4849
private val renderer = ResizingTextColumnRenderer()
49-
override fun valueOf(item: LogStream?): String? = TimeFormatConversion.convertEpochTimeToStringDateTime(item?.lastEventTimestamp(), showSeconds = false)
50+
override fun valueOf(item: LogStream?): String? = TimeFormatConversion.convertEpochTimeToStringDateTime(
51+
item?.lastEventTimestamp(),
52+
showSeconds = false,
53+
format = format
54+
)
5055

5156
override fun isCellEditable(item: LogStream?): Boolean = false
5257
override fun getRenderer(item: LogStream?): TableCellRenderer? = renderer
58+
override fun getComparator(): Comparator<LogStream> = Comparator.comparing { it.lastEventTimestamp() }
5359
}
5460

5561
class LogGroupTableSorter(model: ListTableModel<LogStream>) : TableRowSorter<ListTableModel<LogStream>>(model) {
@@ -68,12 +74,17 @@ class LogGroupFilterTableSorter(model: ListTableModel<LogStream>) : TableRowSort
6874
}
6975
}
7076

71-
class LogStreamDateColumn : ColumnInfo<LogStreamEntry, String>(message("general.time")) {
77+
class LogStreamDateColumn(private val format: SyncDateFormat? = null) : ColumnInfo<LogStreamEntry, String>(message("general.time")) {
7278
private val renderer = ResizingTextColumnRenderer()
73-
override fun valueOf(item: LogStreamEntry?): String? = TimeFormatConversion.convertEpochTimeToStringDateTime(item?.timestamp, showSeconds = true)
79+
override fun valueOf(item: LogStreamEntry?): String? = TimeFormatConversion.convertEpochTimeToStringDateTime(
80+
item?.timestamp,
81+
showSeconds = true,
82+
format = format
83+
)
7484

7585
override fun isCellEditable(item: LogStreamEntry?): Boolean = false
7686
override fun getRenderer(item: LogStreamEntry?): TableCellRenderer? = renderer
87+
override fun getComparator(): Comparator<LogStreamEntry> = Comparator.comparing { it.timestamp }
7788
}
7889

7990
class LogStreamMessageColumn : ColumnInfo<LogStreamEntry, String>(message("general.message")) {
@@ -92,8 +103,8 @@ class LogStreamMessageColumn : ColumnInfo<LogStreamEntry, String>(message("gener
92103
}
93104

94105
object TimeFormatConversion {
95-
fun convertEpochTimeToStringDateTime(epochTime: Long?, showSeconds: Boolean): String? {
96-
val formatter: SyncDateFormat = if (showSeconds) {
106+
fun convertEpochTimeToStringDateTime(epochTime: Long?, showSeconds: Boolean, format: SyncDateFormat? = null): String? {
107+
val formatter = format ?: if (showSeconds) {
97108
SyncDateFormat(SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"))
98109
} else {
99110
DateFormatUtil.getDateTimeFormat()

jetbrains-core/src/software/aws/toolkits/jetbrains/utils/ui/UiUtils.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ class WrappingCellRenderer(
231231
}
232232
}
233233

234+
// TODO: figure out why this has weird hysteresis during rendering causing no text
234235
class ResizingDateColumnRenderer(showSeconds: Boolean) : ResizingColumnRenderer() {
235236
private val formatter: SyncDateFormat = if (showSeconds) {
236237
SyncDateFormat(SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"))

jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/TableUtilsTest.kt

Lines changed: 104 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,127 @@
33

44
package software.aws.toolkits.jetbrains.services.cloudwatch.logs
55

6+
import com.intellij.ui.table.JBTable
67
import com.intellij.util.text.DateFormatUtil
8+
import com.intellij.util.text.SyncDateFormat
9+
import com.intellij.util.ui.ListTableModel
710
import org.assertj.core.api.Assertions.assertThat
811
import org.junit.Test
12+
import org.mockito.kotlin.mock
13+
import org.mockito.kotlin.whenever
14+
import software.amazon.awssdk.services.cloudwatchlogs.model.LogStream
15+
import software.aws.toolkits.jetbrains.services.cloudwatch.logs.editor.LogStreamsDateColumn
916
import software.aws.toolkits.jetbrains.services.cloudwatch.logs.editor.TimeFormatConversion
1017
import java.text.SimpleDateFormat
18+
import java.time.LocalDate
19+
import java.time.LocalTime
20+
import java.time.ZoneOffset
21+
import java.time.format.DateTimeFormatter
22+
import java.util.Locale
23+
import javax.swing.RowSorter
24+
import javax.swing.SortOrder
1125

1226
class TableUtilsTest {
13-
private val sampleTime: Long = 1621173813000
27+
@Test
28+
fun `test short MDY sorting`() {
29+
val dateFormat = SimpleDateFormat("M/d/yy", Locale.ENGLISH)
30+
31+
val model = ListTableModel(
32+
arrayOf(LogStreamsDateColumn(SyncDateFormat(dateFormat))),
33+
mutableListOf(
34+
mockLogStream(stringToEpoch("6/11/21", dateFormat)),
35+
mockLogStream(stringToEpoch("2/1/21", dateFormat)),
36+
mockLogStream(stringToEpoch("7/11/21", dateFormat)),
37+
)
38+
)
39+
40+
val table = JBTable(model).also {
41+
it.rowSorter.sortKeys = listOf(RowSorter.SortKey(0, SortOrder.DESCENDING))
42+
}
43+
44+
assertThat(table).satisfies {
45+
assertThat(it.getValueAt(0, 0)).isEqualTo("7/11/21")
46+
assertThat(it.getValueAt(1, 0)).isEqualTo("6/11/21")
47+
assertThat(it.getValueAt(2, 0)).isEqualTo("2/1/21")
48+
}
49+
}
50+
51+
@Test
52+
fun `test medium MDY sorting`() {
53+
val dateFormat = SimpleDateFormat("MMM d, y", Locale.ENGLISH)
54+
55+
val model = ListTableModel(
56+
arrayOf(LogStreamsDateColumn(SyncDateFormat(dateFormat))),
57+
mutableListOf(
58+
mockLogStream(stringToEpoch("Jun 11, 2021", dateFormat)),
59+
mockLogStream(stringToEpoch("Feb 1, 2021", dateFormat)),
60+
mockLogStream(stringToEpoch("Jul 11, 2021", dateFormat)),
61+
)
62+
)
63+
64+
val table = JBTable(model).also {
65+
it.rowSorter.sortKeys = listOf(RowSorter.SortKey(0, SortOrder.DESCENDING))
66+
}
67+
68+
assertThat(table).satisfies {
69+
assertThat(it.getValueAt(0, 0)).isEqualTo("Jul 11, 2021")
70+
assertThat(it.getValueAt(1, 0)).isEqualTo("Jun 11, 2021")
71+
assertThat(it.getValueAt(2, 0)).isEqualTo("Feb 1, 2021")
72+
}
73+
}
74+
75+
@Test
76+
fun `test medium DMY sorting`() {
77+
val dateFormat = SimpleDateFormat("d MMM y", Locale.ENGLISH)
78+
79+
val model = ListTableModel(
80+
arrayOf(LogStreamsDateColumn(SyncDateFormat(dateFormat))),
81+
mutableListOf(
82+
mockLogStream(stringToEpoch("11 Jun 2021", dateFormat)),
83+
mockLogStream(stringToEpoch("1 Feb 2021", dateFormat)),
84+
mockLogStream(stringToEpoch("11 Jul 2021", dateFormat)),
85+
)
86+
)
87+
88+
val table = JBTable(model).also {
89+
it.rowSorter.sortKeys = listOf(RowSorter.SortKey(0, SortOrder.DESCENDING))
90+
}
91+
92+
assertThat(table).satisfies {
93+
assertThat(it.getValueAt(0, 0)).isEqualTo("11 Jul 2021")
94+
assertThat(it.getValueAt(1, 0)).isEqualTo("11 Jun 2021")
95+
assertThat(it.getValueAt(2, 0)).isEqualTo("1 Feb 2021")
96+
}
97+
}
1498

1599
@Test
16100
fun `convert epoch time to string date time with seconds included`() {
101+
val epochTime = 1621173813000
17102
val showSeconds = true
18-
val correctTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(sampleTime)
19-
val time = TimeFormatConversion.convertEpochTimeToStringDateTime(sampleTime, showSeconds)
103+
val correctTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(epochTime)
104+
val time = TimeFormatConversion.convertEpochTimeToStringDateTime(epochTime, showSeconds)
20105
assertThat(time).isEqualTo(correctTime)
21106
}
22107

23108
@Test
24109
fun `convert epoch time to string date time with seconds excluded`() {
110+
val epochTime = 1621173813000
25111
val showSeconds = false
26-
val correctTime = DateFormatUtil.getDateTimeFormat().format(sampleTime)
27-
val time = TimeFormatConversion.convertEpochTimeToStringDateTime(sampleTime, showSeconds)
112+
val correctTime = DateFormatUtil.getDateTimeFormat().format(epochTime)
113+
val time = TimeFormatConversion.convertEpochTimeToStringDateTime(epochTime, showSeconds)
28114
assertThat(time).isEqualTo(correctTime)
29115
}
116+
117+
private fun stringToEpoch(string: String, formatter: SimpleDateFormat) =
118+
LocalDate.parse(string, DateTimeFormatter.ofPattern(formatter.toPattern(), Locale.ENGLISH))
119+
.atTime(LocalTime.NOON.atOffset(ZoneOffset.UTC))
120+
.toInstant()
121+
.toEpochMilli()
122+
123+
private fun mockLogStream(epoch: Long): LogStream {
124+
val stream: LogStream = mock()
125+
whenever(stream.lastEventTimestamp()).thenReturn(epoch)
126+
127+
return stream
128+
}
30129
}

jetbrains-core/tst/software/aws/toolkits/jetbrains/services/cloudwatch/logs/WrapLogsActionTest.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.intellij.util.ui.ListTableModel
1111
import org.assertj.core.api.Assertions.assertThat
1212
import org.junit.Rule
1313
import org.junit.Test
14+
import org.mockito.kotlin.mock
1415
import software.aws.toolkits.jetbrains.services.cloudwatch.logs.actions.WrapLogsAction
1516
import software.aws.toolkits.jetbrains.services.cloudwatch.logs.editor.LogStreamDateColumn
1617
import software.aws.toolkits.jetbrains.services.cloudwatch.logs.editor.LogStreamMessageColumn
@@ -23,7 +24,10 @@ class WrapLogsActionTest {
2324

2425
@Test
2526
fun wrappingChangesModel() {
26-
val model = ListTableModel<LogStreamEntry>(LogStreamDateColumn(), LogStreamMessageColumn())
27+
val model = ListTableModel<LogStreamEntry>(
28+
arrayOf(LogStreamDateColumn(), LogStreamMessageColumn()),
29+
listOf(mock())
30+
)
2731
val table = TableView(model)
2832
val wrapLogsAction = WrapLogsAction(projectRule.project) { table }
2933
wrapLogsAction.setSelected(TestActionEvent(), true)

0 commit comments

Comments
 (0)