-
Notifications
You must be signed in to change notification settings - Fork 578
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
Add SessionEvent data class #4763
Changes from all commits
4d273db
3c53a66
4cc494c
17e59c0
94908c2
b3fe273
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/* | ||
* Copyright 2023 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.google.firebase.sessions | ||
|
||
/** | ||
* Contains the relevant information around a Firebase Session Event. | ||
* | ||
* See go/app-quality-unified-session-definition for more details. Keep in sync with | ||
* https://github.com/firebase/firebase-ios-sdk/blob/master/FirebaseSessions/ProtoSupport/Protos/sessions.proto | ||
*/ | ||
// TODO(mrober): Add and populate all fields from sessions.proto | ||
internal data class SessionEvent( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just confirming that this class will eventually do more things than what it is today: Add more App related fields to the session event? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup this will eventually have all the fields from the proto. |
||
/** The type of event being reported. */ | ||
val eventType: EventType, | ||
Comment on lines
+27
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to remember this state? It is already a part of SessionStartEvent, can we use it from there? I'm trying to avoid multiple source of truth here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is there to match the proto 1:1, this data class will be serialized and sent to firelog. |
||
|
||
/** Information about the session triggering the event. */ | ||
val sessionData: SessionInfo, | ||
) { | ||
companion object { | ||
fun sessionStart(sessionDetails: SessionDetails) = | ||
SessionEvent( | ||
eventType = EventType.SESSION_START, | ||
sessionData = | ||
SessionInfo( | ||
sessionDetails.sessionId, | ||
sessionDetails.firstSessionId, | ||
sessionDetails.sessionIndex, | ||
), | ||
) | ||
} | ||
} | ||
|
||
/** Enum denoting all possible session event types. */ | ||
internal enum class EventType(val number: Int) { | ||
EVENT_TYPE_UNKNOWN(0), | ||
|
||
/** This event type is fired as soon as a new session begins. */ | ||
SESSION_START(1), | ||
} | ||
|
||
/** Contains session-specific information relating to the event being uploaded. */ | ||
internal data class SessionInfo( | ||
/** A globally unique identifier for the session. */ | ||
val sessionId: String, | ||
|
||
/** | ||
* Will point to the first Session for the run of the app. | ||
* | ||
* For the first session, this will be the same as session_id. | ||
*/ | ||
val firstSessionId: String, | ||
|
||
/** What order this Session came in this run of the app. For the first Session this will be 0. */ | ||
val sessionIndex: Int, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,11 +19,11 @@ package com.google.firebase.sessions | |
import java.util.UUID | ||
|
||
/** | ||
* [SessionInfo] is a data class responsible for storing information about the current Session. | ||
* [SessionDetails] is a data class responsible for storing information about the current Session. | ||
* | ||
* @hide | ||
*/ | ||
internal data class SessionInfo( | ||
internal data class SessionDetails( | ||
val sessionId: String, | ||
val firstSessionId: String, | ||
val collectEvents: Boolean, | ||
|
@@ -32,43 +32,39 @@ internal data class SessionInfo( | |
|
||
/** | ||
* The [SessionGenerator] is responsible for generating the Session ID, and keeping the | ||
* [SessionInfo] up to date with the latest values. | ||
* [SessionDetails] up to date with the latest values. | ||
* | ||
* @hide | ||
*/ | ||
internal class SessionGenerator(collectEvents: Boolean) { | ||
internal class SessionGenerator(private var collectEvents: Boolean) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wow! We can call parameters with access protectors? |
||
private var firstSessionId = "" | ||
private var sessionIndex: Int = -1 | ||
private var collectEvents = collectEvents | ||
|
||
private var thisSession: SessionInfo = | ||
SessionInfo( | ||
private var thisSession: SessionDetails = | ||
SessionDetails( | ||
sessionId = "", | ||
firstSessionId = "", | ||
collectEvents = collectEvents, | ||
sessionIndex = sessionIndex | ||
collectEvents, | ||
sessionIndex, | ||
) | ||
|
||
// Generates a new Session ID. If there was already a generated Session ID | ||
// from the last session during the app's lifecycle, it will also set the last Session ID | ||
fun generateNewSession() { | ||
fun generateNewSession(): SessionDetails { | ||
val newSessionId = UUID.randomUUID().toString().replace("-", "").lowercase() | ||
|
||
// If firstSessionId is set, use it. Otherwise set it to the | ||
// first generated Session ID | ||
firstSessionId = if (firstSessionId.isEmpty()) newSessionId else firstSessionId | ||
firstSessionId = firstSessionId.ifEmpty { newSessionId } | ||
|
||
sessionIndex += 1 | ||
|
||
thisSession = | ||
SessionInfo( | ||
sessionId = newSessionId, | ||
firstSessionId = firstSessionId, | ||
collectEvents = collectEvents, | ||
sessionIndex = sessionIndex | ||
) | ||
SessionDetails(sessionId = newSessionId, firstSessionId, collectEvents, sessionIndex) | ||
|
||
return thisSession | ||
} | ||
|
||
val currentSession: SessionInfo | ||
val currentSession: SessionDetails | ||
get() = thisSession | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* Copyright 2023 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.google.firebase.sessions | ||
|
||
import com.google.common.truth.Truth.assertThat | ||
import org.junit.Test | ||
|
||
class SessionEventTest { | ||
@Test | ||
fun sessionStart_populatesSessionDetailsCorrectly() { | ||
val sessionDetails = | ||
SessionDetails( | ||
sessionId = "a1b2c3", | ||
firstSessionId = "a1a1a1", | ||
collectEvents = true, | ||
sessionIndex = 3, | ||
) | ||
|
||
val sessionEvent = SessionEvent.sessionStart(sessionDetails) | ||
|
||
assertThat(sessionEvent) | ||
.isEqualTo( | ||
SessionEvent( | ||
eventType = EventType.SESSION_START, | ||
sessionData = | ||
SessionInfo( | ||
sessionId = "a1b2c3", | ||
firstSessionId = "a1a1a1", | ||
sessionIndex = 3, | ||
) | ||
) | ||
) | ||
} | ||
} |
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 very close to the iOS implementation. Just to confirm, here is how I think the current piece of code would evolve for event dispatch. Please let me know if you think otherwise.
2.SessionEvent class takes those details to fill in more details
Curious as to where would the config layer fit in here and how would sampling be done to either capture or skip an event?
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.
Yup, and for sampling we'll handle that when we do Settings. We will pass in something like
collectEvents = shouldCollectEvents(...)
to the SessionGenerator.