[stats-collector] introduced DeserializationResult to fail early

This commit is contained in:
Yaroslav Lepenkin
2017-05-17 16:03:46 +03:00
parent e62cfa8bce
commit 5f5989bcf5
3 changed files with 76 additions and 15 deletions
@@ -34,7 +34,9 @@ open class SessionsInputSeparator(input: InputStream,
var line: String? = inputReader.readLine()
while (line != null) {
val event: LogEvent? = LogEventSerializer.fromString(line)
val event: LogEvent? = LogEventSerializer.fromString(line)?.event
//if there is any unknown or absent fields we should fail here
if (event == null) {
handleNullEvent(line)
continue
@@ -46,7 +48,7 @@ open class SessionsInputSeparator(input: InputStream,
currentSessionUid = event.sessionUid
}
session.add(com.intellij.stats.events.completion.EventLine(event, line))
session.add(EventLine(event, line))
line = inputReader.readLine()
}
@@ -63,7 +65,7 @@ open class SessionsInputSeparator(input: InputStream,
val initial = session.first()
if (initial.event is CompletionStartedEvent) {
val state = com.intellij.stats.events.completion.CompletionValidationState(initial.event)
val state = CompletionValidationState(initial.event)
session.drop(1).forEach { state.accept(it.event) }
isValidSession = state.isFinished && state.isValid
}
@@ -16,16 +16,32 @@
package com.intellij.stats.events.completion
import com.google.gson.Gson
import com.google.gson.internal.LinkedTreeMap
object JsonSerializer {
private val gson = Gson()
fun toJson(obj: Any) = gson.toJson(obj)
fun <T> fromJson(json: String, clazz: Class<T>) = gson.fromJson(json, clazz)
fun toJson(obj: Any): String = gson.toJson(obj)
fun <T> fromJson(json: String, clazz: Class<T>): DeserializationResult<T> {
val declaredFields = clazz.declaredFields.map { it.name }.toSet()
val jsonFields = gson.fromJson(json, LinkedTreeMap::class.java).keys.map { it.toString() }.toSet()
val value = gson.fromJson(json, clazz)
val unknownFields = jsonFields.subtract(declaredFields)
val absentFields = declaredFields.subtract(jsonFields)
return DeserializationResult(value, unknownFields, absentFields)
}
}
class DeserializationResult<out T>(val value: T, val unknownFields: Set<String>, val absentFields: Set<String>)
object LogEventSerializer {
private val actionClassMap: Map<Action, Class<out LogEvent>> = mapOf(
Action.COMPLETION_STARTED to CompletionStartedEvent::class.java,
Action.TYPE to TypeEvent::class.java,
@@ -38,11 +54,13 @@ object LogEventSerializer {
Action.CUSTOM to CustomMessageEvent::class.java
)
fun toString(event: LogEvent): String {
return "${event.timestamp}\t${event.recorderId}\t${event.userUid}\t${event.sessionUid}\t${event.actionType}\t${JsonSerializer.toJson(event)}"
}
fun fromString(line: String): LogEvent? {
fun fromString(line: String): DeserializedLogEvent? {
val items = mutableListOf<String>()
var start = -1
@@ -62,15 +80,29 @@ object LogEventSerializer {
val clazz = actionClassMap[actionType] ?: return null
val json = line.substring(start + 1)
val obj = JsonSerializer.fromJson(json, clazz)
val result = JsonSerializer.fromJson(json, clazz)
obj.userUid = userUid
obj.timestamp = timestamp
obj.recorderId = recorderId
obj.sessionUid = sessionUid
obj.actionType = actionType
val event = result.value
return obj
event.userUid = userUid
event.timestamp = timestamp
event.recorderId = recorderId
event.sessionUid = sessionUid
event.actionType = actionType
return DeserializedLogEvent(event, result.unknownFields, result.absentFields)
}
}
class DeserializedLogEvent(
val event: LogEvent,
val unknownEventFields: Set<String>,
val absentEventFields: Set<String>
) {
val isOk: Boolean
get() = unknownEventFields.isEmpty() || absentEventFields.isEmpty()
}
@@ -1,6 +1,7 @@
package com.intellij.stats.events.completion
import junit.framework.Assert.assertEquals
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.util.*
@@ -23,8 +24,8 @@ class EventSerializeDeserializeTest {
private fun serializeDeserializeAndCheck(event: LogEvent) {
val logLine = LogEventSerializer.toString(event)
val eventFromString = LogEventSerializer.fromString(logLine)
assertEquals(logLine, LogEventSerializer.toString(eventFromString!!))
val eventFromString = LogEventSerializer.fromString(logLine)!!.event
assertEquals(logLine, LogEventSerializer.toString(eventFromString))
}
@Test
@@ -84,5 +85,31 @@ class EventSerializeDeserializeTest {
val event = TypeEvent(Fixtures.userId, "xx", listOf(1,2,3), Fixtures.lookupList, 1)
serializeDeserializeAndCheck(event)
}
@Test
fun `deserialization with info`() {
val json = JsonSerializer.toJson(First())
val obj: DeserializationResult<Second> = JsonSerializer.fromJson(json, Second::class.java)
assertThat(obj.absentFields).hasSize(2)
assertThat(obj.absentFields).contains("absent_field0").contains("absent_field1")
assertThat(obj.unknownFields).hasSize(1)
assertThat(obj.unknownFields).contains("unknown_field")
}
}
private class First {
val just_field: String = ""
val unknown_field: Int = 0
}
class Second {
val just_field: String = ""
val absent_field0: Double = 1.0
val absent_field1: Double = 1.0
}