Skip to content

Commit fb97e4d

Browse files
Merge pull request #1982 from Infomaniak/attachments-sentry-breadcrumbs
Add more breadcrumbs to better understand Attachments issues
2 parents 30816cb + 0d4e6bd commit fb97e4d

File tree

7 files changed

+89
-41
lines changed

7 files changed

+89
-41
lines changed

app/src/main/java/com/infomaniak/mail/data/cache/mailboxContent/DraftController.kt

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import com.infomaniak.mail.data.models.draft.Draft
2626
import com.infomaniak.mail.data.models.draft.Draft.DraftMode
2727
import com.infomaniak.mail.data.models.message.Message
2828
import com.infomaniak.mail.utils.AccountUtils
29+
import com.infomaniak.mail.utils.SentryDebug
2930
import io.realm.kotlin.MutableRealm
3031
import io.realm.kotlin.Realm
3132
import io.realm.kotlin.TypedRealm
@@ -100,6 +101,7 @@ class DraftController @Inject constructor(
100101
resource = previousMessage.attachments.find { it.name == name }?.resource
101102
setUploadStatus(UploadStatus.FINISHED)
102103
}
104+
SentryDebug.addAttachmentsBreadcrumb(draft, step = "set previousMessage when reply/replyAll/Forward")
103105
}
104106

105107
draft.uiQuote = replyForwardFooterManager.createForwardFooter(previousMessage, draft.attachments)

app/src/main/java/com/infomaniak/mail/data/models/Attachment.kt

+6-3
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import android.content.Context
2121
import androidx.core.net.toFile
2222
import androidx.core.net.toUri
2323
import com.infomaniak.lib.core.utils.Utils.enumValueOfOrNull
24+
import com.infomaniak.mail.data.models.draft.Draft
2425
import com.infomaniak.mail.utils.AttachableMimeTypeUtils
2526
import com.infomaniak.mail.utils.LocalStorageUtils
27+
import com.infomaniak.mail.utils.SentryDebug
2628
import io.realm.kotlin.types.EmbeddedRealmObject
2729
import kotlinx.serialization.SerialName
2830
import kotlinx.serialization.Serializable
@@ -80,13 +82,14 @@ class Attachment : EmbeddedRealmObject, Attachable {
8082
* After uploading an Attachment, we replace the local version with the remote one.
8183
* The remote one doesn't know about local data, so we have to backup them.
8284
*/
83-
fun backupLocalData(oldAttachment: Attachment, uploadStatus: UploadStatus) {
85+
fun backupLocalData(oldAttachment: Attachment, uploadStatus: UploadStatus, draft: Draft) {
8486
localUuid = oldAttachment.localUuid
8587
uploadLocalUri = oldAttachment.uploadLocalUri
86-
setUploadStatus(uploadStatus)
88+
setUploadStatus(uploadStatus, draft, "backupLocalData -> setUploadStatus")
8789
}
8890

89-
fun setUploadStatus(uploadStatus: UploadStatus) {
91+
fun setUploadStatus(uploadStatus: UploadStatus, draft: Draft? = null, step: String = "") {
92+
draft?.let { SentryDebug.addAttachmentsBreadcrumb(it, step) }
9093
_uploadStatus = uploadStatus.name
9194
}
9295

app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt

+22-5
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,16 @@ import com.infomaniak.mail.di.MainDispatcher
6868
import com.infomaniak.mail.ui.main.SnackbarManager
6969
import com.infomaniak.mail.ui.newMessage.NewMessageEditorManager.EditorAction
7070
import com.infomaniak.mail.ui.newMessage.NewMessageRecipientFieldsManager.FieldType
71-
import com.infomaniak.mail.ui.newMessage.NewMessageViewModel.SignatureScore.*
71+
import com.infomaniak.mail.ui.newMessage.NewMessageViewModel.SignatureScore.EXACT_MATCH
72+
import com.infomaniak.mail.ui.newMessage.NewMessageViewModel.SignatureScore.EXACT_MATCH_AND_IS_DEFAULT
73+
import com.infomaniak.mail.ui.newMessage.NewMessageViewModel.SignatureScore.NO_MATCH
74+
import com.infomaniak.mail.ui.newMessage.NewMessageViewModel.SignatureScore.ONLY_EMAIL_MATCH
75+
import com.infomaniak.mail.ui.newMessage.NewMessageViewModel.SignatureScore.ONLY_EMAIL_MATCH_AND_IS_DEFAULT
7276
import com.infomaniak.mail.utils.AccountUtils
7377
import com.infomaniak.mail.utils.ContactUtils.arrangeMergedContacts
7478
import com.infomaniak.mail.utils.LocalStorageUtils
7579
import com.infomaniak.mail.utils.MessageBodyUtils
80+
import com.infomaniak.mail.utils.SentryDebug
7681
import com.infomaniak.mail.utils.SharedUtils
7782
import com.infomaniak.mail.utils.SignatureUtils
7883
import com.infomaniak.mail.utils.Utils
@@ -93,14 +98,14 @@ import io.realm.kotlin.ext.realmListOf
9398
import io.realm.kotlin.ext.toRealmList
9499
import io.realm.kotlin.types.RealmList
95100
import io.sentry.Sentry
101+
import javax.inject.Inject
96102
import kotlinx.coroutines.CoroutineDispatcher
97103
import kotlinx.coroutines.CoroutineScope
98104
import kotlinx.coroutines.flow.map
99105
import kotlinx.coroutines.launch
100106
import kotlinx.coroutines.withContext
101107
import org.jsoup.Jsoup
102108
import org.jsoup.nodes.Document
103-
import javax.inject.Inject
104109

105110
@HiltViewModel
106111
class NewMessageViewModel @Inject constructor(
@@ -339,6 +344,8 @@ class NewMessageViewModel @Inject constructor(
339344
}
340345

341346
if (mailToUri != null) handleMailTo(draft, mailToUri)
347+
348+
SentryDebug.addAttachmentsBreadcrumb(draft, step = "populate Draft with external mail data")
342349
}
343350

344351
private fun Draft.flagRecipientsAsAutomaticallyEntered() {
@@ -747,7 +754,12 @@ class NewMessageViewModel @Inject constructor(
747754
fun uploadAttachmentsToServer(uiAttachments: List<Attachment>) = viewModelScope.launch(ioDispatcher) {
748755
val localUuid = draftLocalUuid ?: return@launch
749756
val localDraft = mailboxContentRealm().writeBlocking {
750-
DraftController.getDraft(localUuid, realm = this)?.also { it.updateDraftAttachmentsWithLiveData(uiAttachments) }
757+
DraftController.getDraft(localUuid, realm = this)?.also {
758+
it.updateDraftAttachmentsWithLiveData(
759+
uiAttachments = uiAttachments,
760+
step = "observeAttachments -> uploadAttachmentsToServer",
761+
)
762+
}
751763
} ?: return@launch
752764

753765
runCatching {
@@ -813,7 +825,10 @@ class NewMessageViewModel @Inject constructor(
813825
cc = ccLiveData.valueOrEmpty().toRealmList()
814826
bcc = bccLiveData.valueOrEmpty().toRealmList()
815827

816-
updateDraftAttachmentsWithLiveData(attachmentsLiveData.valueOrEmpty())
828+
updateDraftAttachmentsWithLiveData(
829+
uiAttachments = attachmentsLiveData.valueOrEmpty(),
830+
step = "executeDraftActionWhenStopping (action = ${draftAction.name}) -> updateDraftFromLiveData",
831+
)
817832

818833
subject = subjectValue
819834

@@ -848,7 +863,7 @@ class NewMessageViewModel @Inject constructor(
848863
}
849864
}
850865

851-
private fun Draft.updateDraftAttachmentsWithLiveData(uiAttachments: List<Attachment>) {
866+
private fun Draft.updateDraftAttachmentsWithLiveData(uiAttachments: List<Attachment>, step: String) {
852867

853868
/**
854869
* If :
@@ -882,6 +897,8 @@ class NewMessageViewModel @Inject constructor(
882897
clear()
883898
addAll(updatedAttachments)
884899
}
900+
901+
SentryDebug.addAttachmentsBreadcrumb(draft = this, step)
885902
}
886903

887904
private fun Draft.getWholeBody(): String = uiBody.textToHtml() + (uiSignature ?: "") + (uiQuote ?: "")

app/src/main/java/com/infomaniak/mail/utils/DraftUtils.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ private suspend fun Draft.uploadAttachments(mailbox: Mailbox, draftController: D
4949

5050
fun getAwaitingAttachments(): List<Attachment> = attachments.filter { it.uploadStatus == UploadStatus.AWAITING }
5151

52-
fun setUploadStatus(attachment: Attachment, uploadStatus: UploadStatus) {
52+
fun setUploadStatus(attachment: Attachment, uploadStatus: UploadStatus, step: String) {
5353
realm.writeBlocking {
5454
draftController.updateDraft(localUuid, realm = this) {
55-
it.attachments.findSpecificAttachment(attachment)?.setUploadStatus(uploadStatus)
55+
it.attachments.findSpecificAttachment(attachment)?.setUploadStatus(uploadStatus, draft = it, step)
5656
}
5757
}
5858
}
@@ -69,10 +69,10 @@ private suspend fun Draft.uploadAttachments(mailbox: Mailbox, draftController: D
6969

7070
attachmentsToUpload.forEach { attachment ->
7171
runCatching {
72-
setUploadStatus(attachment, UploadStatus.ONGOING)
72+
setUploadStatus(attachment, UploadStatus.ONGOING, step = "before starting upload")
7373
attachment.startUpload(localUuid, mailbox, draftController, realm)
7474
}.onFailure { exception ->
75-
setUploadStatus(attachment, UploadStatus.AWAITING)
75+
setUploadStatus(attachment, UploadStatus.AWAITING, step = "after failing upload")
7676
SentryLog.d(ATTACHMENT_TAG, "${exception.message}", exception)
7777
if ((exception as Exception).isNetworkException()) throw ApiController.NetworkException()
7878
throw exception

app/src/main/java/com/infomaniak/mail/utils/SentryDebug.kt

+34-1
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,44 @@ object SentryDebug {
104104
)
105105
}
106106

107+
fun addAttachmentsBreadcrumb(draft: Draft, step: String) = with(draft) {
108+
109+
var count = 1
110+
val data = mutableMapOf<String, Any>()
111+
112+
fun String.keyPad(): String = padStart(length = 15)
113+
fun Int.countPad(): String = toString().padStart(length = 2, '0')
114+
fun count(): String = "${count.countPad().also { count++ }}."
115+
fun format(index: Int): String = (index + 1).countPad()
116+
117+
data[count() + "step".keyPad()] = step
118+
data[count() + "email".keyPad()] = AccountUtils.currentMailboxEmail.toString()
119+
120+
data[count() + "draft".keyPad() + " - localUuid"] = localUuid
121+
data[count() + "draft".keyPad() + " - remoteUuid"] = remoteUuid.toString()
122+
data[count() + "draft".keyPad() + " - action"] = action?.name.toString()
123+
data[count() + "draft".keyPad() + " - mode"] = when {
124+
inReplyToUid != null -> "REPLY or REPLY_ALL"
125+
forwardedUid != null -> "FORWARD"
126+
else -> "NEW_MAIL"
127+
}
128+
129+
data[count() + "attachments".keyPad() + " - count"] = attachments.count()
130+
131+
attachments.forEachIndexed { index, it ->
132+
data[count() + "attachment #${format(index)}".keyPad()] =
133+
"localUuid: ${it.localUuid} | uuid: ${it.uuid} | uploadLocalUri: ${it.uploadLocalUri}"
134+
data[count() + "attachment #${format(index)}".keyPad()] = "uploadStatus: ${it.uploadStatus.name} | size: ${it.size}"
135+
}
136+
137+
addInfoBreadcrumb(category = "Attachments_Situation", data = data)
138+
}
139+
107140
private fun addInfoBreadcrumb(category: String, message: String? = null, data: Map<String, Any>? = null) {
108141
Breadcrumb().apply {
109142
this.category = category
110143
this.message = message
111-
data?.let { it.forEach { (key, value) -> this.data[key] = value } }
144+
data?.let { it.forEach { (key, value) -> this.setData(key, value) } }
112145
this.level = SentryLevel.INFO
113146
}.also(Sentry::addBreadcrumb)
114147
}

app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import com.infomaniak.mail.data.models.mailbox.Mailbox
3939
import com.infomaniak.mail.ui.main.SnackbarManager
4040
import com.infomaniak.mail.ui.main.thread.actions.DownloadAttachmentProgressDialogArgs
4141
import com.infomaniak.mail.utils.AccountUtils
42+
import com.infomaniak.mail.utils.SentryDebug
4243
import com.infomaniak.mail.utils.WorkerUtils.UploadMissingLocalFileException
4344
import com.infomaniak.mail.utils.extensions.AttachmentExtensions.AttachmentIntentType.OPEN_WITH
4445
import com.infomaniak.mail.utils.extensions.AttachmentExtensions.AttachmentIntentType.SAVE_TO_DRIVE
@@ -189,7 +190,7 @@ object AttachmentExtensions {
189190
SentryLog.d(ATTACHMENT_TAG, "When removing uploaded attachment, we found (uuids to localUris): $uuidToLocalUri")
190191
SentryLog.d(ATTACHMENT_TAG, "Target uploadLocalUri is: $uploadLocalUri")
191192

192-
remoteAttachment.backupLocalData(oldAttachment = this@updateLocalAttachment, UploadStatus.FINISHED)
193+
remoteAttachment.backupLocalData(oldAttachment = this@updateLocalAttachment, UploadStatus.FINISHED, draft)
193194

194195
SentryLog.d(ATTACHMENT_TAG, "Uploaded attachment uuid: ${remoteAttachment.uuid}")
195196
SentryLog.d(ATTACHMENT_TAG, "Uploaded attachment localUuid: ${remoteAttachment.localUuid}")
@@ -199,6 +200,8 @@ object AttachmentExtensions {
199200
delete(findSpecificAttachment(attachment = this@updateLocalAttachment)!!)
200201
add(remoteAttachment)
201202
}
203+
204+
SentryDebug.addAttachmentsBreadcrumb(draft, step = "update local Attachment after success upload")
202205
}
203206
}
204207
}

app/src/main/java/com/infomaniak/mail/workers/DraftsActionsWorker.kt

+17-27
Original file line numberDiff line numberDiff line change
@@ -302,34 +302,24 @@ class DraftsActionsWorker @AssistedInject constructor(
302302
var scheduledDate: String? = null
303303
var savedDraftUuid: String? = null
304304

305-
// TODO: Remove this whole `draft.attachments.forEach { … }` when the Attachment issue is fixed.
306-
draft.attachments.forEach { attachment ->
307-
if (attachment.uploadStatus != UploadStatus.FINISHED) {
308-
309-
Sentry.withScope { scope ->
310-
scope.setExtra("attachmentUuid", attachment.uuid)
311-
scope.setExtra("attachmentsCount", "${draft.attachments.count()}")
312-
scope.setExtra(
313-
"attachmentsUuids to attachmentsLocalUuid",
314-
"${draft.attachments.map { it.uuid to it.localUuid }}",
315-
)
316-
scope.setExtra("draftUuid", "${draft.remoteUuid}")
317-
scope.setExtra("draftLocalUuid", draft.localUuid)
318-
scope.setExtra("email", AccountUtils.currentMailboxEmail.toString())
319-
Sentry.captureMessage(
320-
"We tried to [${draft.action?.name}] a Draft, but an Attachment wasn't uploaded.",
321-
SentryLevel.ERROR,
322-
)
323-
}
305+
SentryDebug.addAttachmentsBreadcrumb(draft, step = "executeDraftAction (action = ${draft.action?.name.toString()})")
324306

325-
return DraftActionResult(
326-
realmActionOnDraft = null,
327-
scheduledDate = null,
328-
errorMessageResId = R.string.errorCorruptAttachment,
329-
savedDraftUuid = null,
330-
isSuccess = false,
331-
)
332-
}
307+
// TODO: Remove this whole `draft.attachments.any { … }` + `addAttachmentsBreadcrumb()`
308+
// when the Attachments issue is fixed.
309+
if (draft.attachments.any { it.uploadStatus != UploadStatus.FINISHED }) {
310+
311+
Sentry.captureMessage(
312+
"We tried to [${draft.action?.name}] a Draft, but an Attachment wasn't uploaded.",
313+
SentryLevel.ERROR,
314+
)
315+
316+
return DraftActionResult(
317+
realmActionOnDraft = null,
318+
scheduledDate = null,
319+
errorMessageResId = R.string.errorCorruptAttachment,
320+
savedDraftUuid = null,
321+
isSuccess = false,
322+
)
333323
}
334324

335325
fun executeSaveAction() = with(ApiRepository.saveDraft(mailboxUuid, draft, okHttpClient)) {

0 commit comments

Comments
 (0)