From cc17366c1cf1c09a00a9e95569d4b6d33c4fecc3 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Fri, 30 Jun 2023 13:02:38 +0200 Subject: [PATCH 1/2] fix(GODT-2578): Refresh literals appended to Sent folder Whenever a message gets moved to the sent folder we should retrieve the new literal in order to guarantee that, if another client modifies and sends the message, we always see the latest version of the message and not a previous state stored in the Gluon cache. Includes the following Gluon MRs: * https://github.com/ProtonMail/gluon/pull/374 * https://github.com/ProtonMail/gluon/pull/376 Includes the followin gpa MR: https://github.com/ProtonMail/go-proton-api/pull/88 --- go.mod | 4 +- go.sum | 13 +-- internal/bridge/draft_test.go | 175 ++++++++++++++++++++++++++++++++++ internal/bridge/sync_test.go | 13 ++- internal/user/events.go | 13 +-- 5 files changed, 200 insertions(+), 18 deletions(-) create mode 100644 internal/bridge/draft_test.go diff --git a/go.mod b/go.mod index 806608cf..49e78f2b 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.20 require ( github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557 github.com/Masterminds/semver/v3 v3.2.0 - github.com/ProtonMail/gluon v0.16.1-0.20230628130101-52391e3cbf12 + github.com/ProtonMail/gluon v0.16.1-0.20230704083024-d901d16834de github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a - github.com/ProtonMail/go-proton-api v0.4.1-0.20230628092916-81cb3f87f184 + github.com/ProtonMail/go-proton-api v0.4.1-0.20230704060229-a77a437ec052 github.com/ProtonMail/gopenpgp/v2 v2.7.1-proton github.com/PuerkitoBio/goquery v1.8.1 github.com/abiosoft/ishell v2.0.0+incompatible diff --git a/go.sum b/go.sum index 642c975b..c230fe19 100644 --- a/go.sum +++ b/go.sum @@ -23,12 +23,8 @@ github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo= github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk= github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g= -github.com/ProtonMail/gluon v0.16.1-0.20230628083422-e2cfbe8c5823 h1:mOaTSgPEUu0KZjH1SN7ZxqWIljlZFvyST48coZlvC1o= -github.com/ProtonMail/gluon v0.16.1-0.20230628083422-e2cfbe8c5823/go.mod h1:Og5/Dz1MiGpCJn51XujZwxiLG7WzvvjE5PRpZBQmAHo= -github.com/ProtonMail/gluon v0.16.1-0.20230628112102-80c05f878b53 h1:38aw1TGmZlmY8l7uo+RRHnm97nZJz8xkEG//yPUS+9k= -github.com/ProtonMail/gluon v0.16.1-0.20230628112102-80c05f878b53/go.mod h1:Og5/Dz1MiGpCJn51XujZwxiLG7WzvvjE5PRpZBQmAHo= -github.com/ProtonMail/gluon v0.16.1-0.20230628130101-52391e3cbf12 h1:/3S2RdUUASLW/JD+17yeJ+tanU6yWD4yzQSEXdilMMk= -github.com/ProtonMail/gluon v0.16.1-0.20230628130101-52391e3cbf12/go.mod h1:Og5/Dz1MiGpCJn51XujZwxiLG7WzvvjE5PRpZBQmAHo= +github.com/ProtonMail/gluon v0.16.1-0.20230704083024-d901d16834de h1:th289W4w6aE6IAdHwZgfHIgtK1ew9ZvAdXnie0BtVuw= +github.com/ProtonMail/gluon v0.16.1-0.20230704083024-d901d16834de/go.mod h1:Og5/Dz1MiGpCJn51XujZwxiLG7WzvvjE5PRpZBQmAHo= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4= github.com/ProtonMail/go-crypto v0.0.0-20230321155629-9a39f2531310/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= @@ -39,8 +35,8 @@ github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7 h1:+j+Kd/ github.com/ProtonMail/go-message v0.13.1-0.20230526094639-b62c999c85b7/go.mod h1:NBAn21zgCJ/52WLDyed18YvYFm5tEoeDauubFqLokM4= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= -github.com/ProtonMail/go-proton-api v0.4.1-0.20230628092916-81cb3f87f184 h1:gw8sgQMCIDS/lw5xbF2iqlTfvY0HhuafjlGsKcN3VsE= -github.com/ProtonMail/go-proton-api v0.4.1-0.20230628092916-81cb3f87f184/go.mod h1:+aTJoYu8bqzGECXL2DOdiZTZ64bGn3w0NC8VcFpJrFM= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230704060229-a77a437ec052 h1:uIq0RX4gU9PSZ9x5b2LmJUXNOuBXRRVSOkM1RGnSy68= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230704060229-a77a437ec052/go.mod h1:+aTJoYu8bqzGECXL2DOdiZTZ64bGn3w0NC8VcFpJrFM= github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI= github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk= github.com/ProtonMail/gopenpgp/v2 v2.7.1-proton h1:YS6M20yvjCJPR1r4ADW5TPn6rahs4iAyZaACei86bEc= @@ -270,7 +266,6 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= diff --git a/internal/bridge/draft_test.go b/internal/bridge/draft_test.go new file mode 100644 index 00000000..85ccf3c3 --- /dev/null +++ b/internal/bridge/draft_test.go @@ -0,0 +1,175 @@ +// Copyright (c) 2023 Proton AG +// +// This file is part of Proton Mail Bridge. +// +// Proton Mail Bridge is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Proton Mail Bridge is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Proton Mail Bridge. If not, see . + +package bridge_test + +import ( + "bytes" + "context" + "crypto/tls" + "fmt" + "io" + "net" + "strings" + "testing" + "time" + + "github.com/ProtonMail/gluon/rfc822" + "github.com/ProtonMail/go-proton-api" + "github.com/ProtonMail/go-proton-api/server" + "github.com/ProtonMail/proton-bridge/v3/internal/bridge" + "github.com/ProtonMail/proton-bridge/v3/internal/constants" + "github.com/ProtonMail/proton-bridge/v3/internal/events" + go_imap "github.com/emersion/go-imap" + "github.com/emersion/go-sasl" + "github.com/emersion/go-smtp" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" +) + +func TestBridge_HandleDraftsSendFromOtherClient(t *testing.T) { + getGluonHeaderID := func(literal []byte) (string, string) { + h, err := rfc822.NewHeader(literal) + require.NoError(t, err) + + gluonID, ok := h.GetChecked("X-Pm-Gluon-Id") + require.True(t, ok) + + externalID, ok := h.GetChecked("Message-Id") + require.True(t, ok) + + return gluonID, externalID + } + + withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) { + _, _, err := s.CreateUser("imap", password) + require.NoError(t, err) + + _, _, err = s.CreateUser("bar", password) + require.NoError(t, err) + + // The initial user should be fully synced. + withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(b *bridge.Bridge, _ *bridge.Mocks) { + waiter := waitForIMAPServerReady(b) + defer waiter.Done() + + syncCh, done := chToType[events.Event, events.SyncFinished](b.GetEvents(events.SyncFinished{})) + defer done() + + userID, err := b.LoginFull(ctx, "imap", password, nil, nil) + require.NoError(t, err) + + require.Equal(t, userID, (<-syncCh).UserID) + waiter.Wait() + + info, err := b.GetUserInfo(userID) + require.NoError(t, err) + require.True(t, info.State == bridge.Connected) + + client, err := eventuallyDial(fmt.Sprintf("%v:%v", constants.Host, b.GetIMAPPort())) + require.NoError(t, err) + require.NoError(t, client.Login(info.Addresses[0], string(info.BridgePass))) + defer func() { _ = client.Logout() }() + + // Create first draft in client. + literal := fmt.Sprintf(`From: %v +To: %v +Date: Fri, 3 Feb 2023 01:04:32 +0100 +Subject: Foo + +Hello +`, info.Addresses[0], "bar@proton.local") + + require.NoError(t, client.Append("Drafts", nil, time.Now(), strings.NewReader(literal))) + // Verify the draft is available in client. + require.Eventually(t, func() bool { + status, err := client.Status("Drafts", []go_imap.StatusItem{go_imap.StatusMessages}) + require.NoError(t, err) + return status.Messages == 1 + }, 2*time.Second, time.Second) + + // Retrieve the new literal so we can have the Proton Message ID. + messages, err := clientFetch(client, "Drafts") + require.NoError(t, err) + require.Equal(t, 1, len(messages)) + + newLiteral, err := io.ReadAll(messages[0].GetBody(must(go_imap.ParseBodySectionName("BODY[]")))) + require.NoError(t, err) + logrus.Info(string(newLiteral)) + + newLiteralID, newLiteralExternID := getGluonHeaderID(newLiteral) + + // Modify new literal. + newLiteralModified := append(newLiteral, []byte(" world from client2")...) //nolint:gocritic + + func() { + smtpClient, err := smtp.Dial(net.JoinHostPort(constants.Host, fmt.Sprint(b.GetSMTPPort()))) + require.NoError(t, err) + defer func() { _ = smtpClient.Close() }() + + // Upgrade to TLS. + require.NoError(t, smtpClient.StartTLS(&tls.Config{InsecureSkipVerify: true})) + + // Authorize with SASL PLAIN. + require.NoError(t, smtpClient.Auth(sasl.NewPlainClient( + info.Addresses[0], + info.Addresses[0], + string(info.BridgePass)), + )) + + // Send the message. + require.NoError(t, smtpClient.SendMail( + info.Addresses[0], + []string{"bar@proton.local"}, + bytes.NewReader(newLiteralModified), + )) + }() + + // Append message to Sent as the imap client would. + require.NoError(t, client.Append("Sent", nil, time.Now(), strings.NewReader(literal))) + + // Verify the sent message gets updated with the new literal. + require.Eventually(t, func() bool { + // Check if sent message matches the latest draft. + messagesClient1, err := clientFetch(client, "Sent", "BODY[TEXT]", "BODY[]") + require.NoError(t, err) + + if len(messagesClient1) != 1 { + return false + } + + sentLiteral, err := io.ReadAll(messagesClient1[0].GetBody(must(go_imap.ParseBodySectionName("BODY[]")))) + require.NoError(t, err) + + sentLiteralID, sentLiteralExternID := getGluonHeaderID(sentLiteral) + + sentLiteralText, err := io.ReadAll(messagesClient1[0].GetBody(must(go_imap.ParseBodySectionName("BODY[TEXT]")))) + require.NoError(t, err) + + sentLiteralStr := string(sentLiteralText) + + literalMatches := sentLiteralStr == "Hello\r\n world from client2\r\n" + + idIsDifferent := sentLiteralID != newLiteralID + + externIDMatches := sentLiteralExternID == newLiteralExternID + + return literalMatches && idIsDifferent && externIDMatches + }, 2*time.Second, time.Second) + }) + }, server.WithMessageDedup()) +} diff --git a/internal/bridge/sync_test.go b/internal/bridge/sync_test.go index 00a0105e..6b330d4c 100644 --- a/internal/bridge/sync_test.go +++ b/internal/bridge/sync_test.go @@ -399,6 +399,10 @@ func createNumMessages(ctx context.Context, t *testing.T, c *proton.Client, addr } func createMessages(ctx context.Context, t *testing.T, c *proton.Client, addrID, labelID string, messages ...[]byte) []string { + return createMessagesWithFlags(ctx, t, c, addrID, labelID, 0, messages...) +} + +func createMessagesWithFlags(ctx context.Context, t *testing.T, c *proton.Client, addrID, labelID string, flags proton.MessageFlag, messages ...[]byte) []string { user, err := c.GetUser(ctx) require.NoError(t, err) @@ -417,6 +421,13 @@ func createMessages(ctx context.Context, t *testing.T, c *proton.Client, addrID, _, ok := addrKRs[addrID] require.True(t, ok) + var msgFlags proton.MessageFlag + if flags == 0 { + msgFlags = proton.MessageFlagReceived + } else { + msgFlags = flags + } + str, err := c.ImportMessages( ctx, addrKRs[addrID], @@ -427,7 +438,7 @@ func createMessages(ctx context.Context, t *testing.T, c *proton.Client, addrID, Metadata: proton.ImportMetadata{ AddressID: addrID, LabelIDs: []string{labelID}, - Flags: proton.MessageFlagReceived, + Flags: msgFlags, }, Message: message, } diff --git a/internal/user/events.go b/internal/user/events.go index 1a7067fd..277abbe2 100644 --- a/internal/user/events.go +++ b/internal/user/events.go @@ -514,9 +514,9 @@ func (user *User) handleMessageEvents(ctx context.Context, messageEvents []proto case proton.EventUpdate, proton.EventUpdateFlags: // Draft update means to completely remove old message and upload the new data again, but we should // only do this if the event is of type EventUpdate otherwise label switch operations will not work. - if event.Message.IsDraft() && event.Action == proton.EventUpdate { - updates, err := user.handleUpdateDraftEvent( - logging.WithLogrusField(ctx, "action", "update draft"), + if (event.Message.IsDraft() || (event.Message.Flags&proton.MessageFlagSent != 0)) && event.Action == proton.EventUpdate { + updates, err := user.handleUpdateDraftOrSentMessage( + logging.WithLogrusField(ctx, "action", "update draft or sent message"), event, ) if err != nil { @@ -701,18 +701,19 @@ func (user *User) handleDeleteMessageEvent(_ context.Context, event proton.Messa }, user.updateChLock) } -func (user *User) handleUpdateDraftEvent(ctx context.Context, event proton.MessageEvent) ([]imap.Update, error) { +func (user *User) handleUpdateDraftOrSentMessage(ctx context.Context, event proton.MessageEvent) ([]imap.Update, error) { return safe.RLockRetErr(func() ([]imap.Update, error) { user.log.WithFields(logrus.Fields{ "messageID": event.ID, "subject": logging.Sensitive(event.Message.Subject), - }).Info("Handling draft updated event") + "isDraft": event.Message.IsDraft(), + }).Info("Handling draft or sent updated event") full, err := user.client.GetFullMessage(ctx, event.Message.ID, newProtonAPIScheduler(user.panicHandler), proton.NewDefaultAttachmentAllocator()) if err != nil { // If the message is not found, it means that it has been deleted before we could fetch it. if apiErr := new(proton.APIError); errors.As(err, &apiErr) && apiErr.Status == http.StatusUnprocessableEntity { - user.log.WithField("messageID", event.Message.ID).Warn("Cannot update draft: full message is missing on API") + user.log.WithField("messageID", event.Message.ID).Warn("Cannot update message: full message is missing on API") return nil, nil } From eaa673c4e465aacf05075adb53ee3497f86b940f Mon Sep 17 00:00:00 2001 From: Romain Le Jeune Date: Tue, 4 Jul 2023 09:59:43 +0000 Subject: [PATCH 2/2] fix(GODT-2708): fix dimensions event format + handling of ReportClicked event. --- internal/bridge/config_status.go | 2 +- internal/configstatus/config_status.go | 26 +++++++++++++++++-- internal/configstatus/configuration_abort.go | 17 +++++++----- .../configstatus/configuration_abort_test.go | 12 ++++----- .../configstatus/configuration_progress.go | 2 +- .../configuration_progress_test.go | 2 +- .../configstatus/configuration_recovery.go | 17 ++++++------ .../configuration_recovery_test.go | 12 ++++----- .../configstatus/configuration_success.go | 15 ++++++----- .../configuration_success_test.go | 12 ++++----- internal/user/config_status.go | 6 +++-- 11 files changed, 76 insertions(+), 47 deletions(-) diff --git a/internal/bridge/config_status.go b/internal/bridge/config_status.go index 05b801e0..755e1627 100644 --- a/internal/bridge/config_status.go +++ b/internal/bridge/config_status.go @@ -24,7 +24,7 @@ import ( func (bridge *Bridge) ReportBugClicked() { safe.Lock(func() { for _, user := range bridge.users { - user.ReportBugSent() + user.ReportBugClicked() } }, bridge.usersLock) } diff --git a/internal/configstatus/config_status.go b/internal/configstatus/config_status.go index 6f590936..429a10a6 100644 --- a/internal/configstatus/config_status.go +++ b/internal/configstatus/config_status.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "os" + "strconv" "time" "github.com/ProtonMail/proton-bridge/v3/internal/safe" @@ -74,8 +75,9 @@ func (status *ConfigurationStatus) Save() error { if err != nil { return err } - - err = json.NewEncoder(f).Encode(status.Data) + enc := json.NewEncoder(f) + enc.SetIndent("", " ") + err = enc.Encode(status.Data) if err := f.Close(); err != nil { logrus.WithError(err).Error("Error while closing configstatus file.") } @@ -197,3 +199,23 @@ func (data *ConfigurationStatusData) hasLinkClicked(pos uint) bool { val := data.DataV1.ClickedLink & (1 << pos) return val > 0 } + +func (data *ConfigurationStatusData) clickedLinkToString() string { + var str = "" + var first = true + for i := 0; i < 64; i++ { + if data.hasLinkClicked(uint(i)) { + if !first { + str += "," + } else { + first = false + str += "[" + } + str += strconv.Itoa(i) + } + } + if str != "" { + str += "]" + } + return str +} diff --git a/internal/configstatus/configuration_abort.go b/internal/configstatus/configuration_abort.go index 29e5a147..36ad8f5c 100644 --- a/internal/configstatus/configuration_abort.go +++ b/internal/configstatus/configuration_abort.go @@ -17,16 +17,19 @@ package configstatus -import "time" +import ( + "strconv" + "time" +) type ConfigAbortValues struct { Duration int `json:"duration"` } type ConfigAbortDimensions struct { - ReportClick interface{} `json:"report_click"` - ReportSent interface{} `json:"report_sent"` - ClickedLink uint64 `json:"clicked_link"` + ReportClick string `json:"report_click"` + ReportSent string `json:"report_sent"` + ClickedLink string `json:"clicked_link"` } type ConfigAbortData struct { @@ -46,9 +49,9 @@ func (*ConfigAbortBuilder) New(data *ConfigurationStatusData) ConfigAbortData { Duration: int(time.Since(data.DataV1.PendingSince).Minutes()), }, Dimensions: ConfigSuccessDimensions{ - ReportClick: data.DataV1.ReportClick, - ReportSent: data.DataV1.ReportSent, - ClickedLink: data.DataV1.ClickedLink, + ReportClick: strconv.FormatBool(data.DataV1.ReportClick), + ReportSent: strconv.FormatBool(data.DataV1.ReportSent), + ClickedLink: data.clickedLinkToString(), }, } } diff --git a/internal/configstatus/configuration_abort_test.go b/internal/configstatus/configuration_abort_test.go index e076952a..c9851b2a 100644 --- a/internal/configstatus/configuration_abort_test.go +++ b/internal/configstatus/configuration_abort_test.go @@ -38,9 +38,9 @@ func TestConfigurationAbort_default(t *testing.T) { require.Equal(t, "bridge.any.configuration", req.MeasurementGroup) require.Equal(t, "bridge_config_abort", req.Event) require.Equal(t, 0, req.Values.Duration) - require.Equal(t, false, req.Dimensions.ReportClick) - require.Equal(t, false, req.Dimensions.ReportSent) - require.Equal(t, uint64(0), req.Dimensions.ClickedLink) + require.Equal(t, "false", req.Dimensions.ReportClick) + require.Equal(t, "false", req.Dimensions.ReportSent) + require.Equal(t, "", req.Dimensions.ClickedLink) } func TestConfigurationAbort_fed(t *testing.T) { @@ -69,7 +69,7 @@ func TestConfigurationAbort_fed(t *testing.T) { require.Equal(t, "bridge.any.configuration", req.MeasurementGroup) require.Equal(t, "bridge_config_abort", req.Event) require.Equal(t, 10, req.Values.Duration) - require.Equal(t, true, req.Dimensions.ReportClick) - require.Equal(t, false, req.Dimensions.ReportSent) - require.Equal(t, uint64(42), req.Dimensions.ClickedLink) + require.Equal(t, "true", req.Dimensions.ReportClick) + require.Equal(t, "false", req.Dimensions.ReportSent) + require.Equal(t, "[1,3,5]", req.Dimensions.ClickedLink) } diff --git a/internal/configstatus/configuration_progress.go b/internal/configstatus/configuration_progress.go index 8046e689..d25a6019 100644 --- a/internal/configstatus/configuration_progress.go +++ b/internal/configstatus/configuration_progress.go @@ -46,7 +46,7 @@ func (*ConfigProgressBuilder) New(data *ConfigurationStatusData) ConfigProgressD func numberOfDay(now, prev time.Time) int { if now.IsZero() || prev.IsZero() { - return 0 + return 1 } if now.Year() > prev.Year() { if now.YearDay() > prev.YearDay() { diff --git a/internal/configstatus/configuration_progress_test.go b/internal/configstatus/configuration_progress_test.go index ff0cd88c..e772af01 100644 --- a/internal/configstatus/configuration_progress_test.go +++ b/internal/configstatus/configuration_progress_test.go @@ -38,7 +38,7 @@ func TestConfigurationProgress_default(t *testing.T) { require.Equal(t, "bridge.any.configuration", req.MeasurementGroup) require.Equal(t, "bridge_config_progress", req.Event) require.Equal(t, 0, req.Values.NbDay) - require.Equal(t, 0, req.Values.NbDaySinceLast) + require.Equal(t, 1, req.Values.NbDaySinceLast) } func TestConfigurationProgress_fed(t *testing.T) { diff --git a/internal/configstatus/configuration_recovery.go b/internal/configstatus/configuration_recovery.go index dae2b96e..850a7dfc 100644 --- a/internal/configstatus/configuration_recovery.go +++ b/internal/configstatus/configuration_recovery.go @@ -18,6 +18,7 @@ package configstatus import ( + "strconv" "time" ) @@ -26,11 +27,11 @@ type ConfigRecoveryValues struct { } type ConfigRecoveryDimensions struct { - Autoconf string `json:"autoconf"` - ReportClick interface{} `json:"report_click"` - ReportSent interface{} `json:"report_sent"` - ClickedLink uint64 `json:"clicked_link"` - FailureDetails string `json:"failure_details"` + Autoconf string `json:"autoconf"` + ReportClick string `json:"report_click"` + ReportSent string `json:"report_sent"` + ClickedLink string `json:"clicked_link"` + FailureDetails string `json:"failure_details"` } type ConfigRecoveryData struct { @@ -51,9 +52,9 @@ func (*ConfigRecoveryBuilder) New(data *ConfigurationStatusData) ConfigRecoveryD }, Dimensions: ConfigRecoveryDimensions{ Autoconf: data.DataV1.Autoconf, - ReportClick: data.DataV1.ReportClick, - ReportSent: data.DataV1.ReportSent, - ClickedLink: data.DataV1.ClickedLink, + ReportClick: strconv.FormatBool(data.DataV1.ReportClick), + ReportSent: strconv.FormatBool(data.DataV1.ReportSent), + ClickedLink: data.clickedLinkToString(), FailureDetails: data.DataV1.FailureDetails, }, } diff --git a/internal/configstatus/configuration_recovery_test.go b/internal/configstatus/configuration_recovery_test.go index 722e5245..c05ac32a 100644 --- a/internal/configstatus/configuration_recovery_test.go +++ b/internal/configstatus/configuration_recovery_test.go @@ -39,9 +39,9 @@ func TestConfigurationRecovery_default(t *testing.T) { require.Equal(t, "bridge_config_recovery", req.Event) require.Equal(t, 0, req.Values.Duration) require.Equal(t, "", req.Dimensions.Autoconf) - require.Equal(t, false, req.Dimensions.ReportClick) - require.Equal(t, false, req.Dimensions.ReportSent) - require.Equal(t, uint64(0), req.Dimensions.ClickedLink) + require.Equal(t, "false", req.Dimensions.ReportClick) + require.Equal(t, "false", req.Dimensions.ReportSent) + require.Equal(t, "", req.Dimensions.ClickedLink) require.Equal(t, "", req.Dimensions.FailureDetails) } @@ -72,8 +72,8 @@ func TestConfigurationRecovery_fed(t *testing.T) { require.Equal(t, "bridge_config_recovery", req.Event) require.Equal(t, 10, req.Values.Duration) require.Equal(t, "Mr TBird", req.Dimensions.Autoconf) - require.Equal(t, true, req.Dimensions.ReportClick) - require.Equal(t, false, req.Dimensions.ReportSent) - require.Equal(t, uint64(42), req.Dimensions.ClickedLink) + require.Equal(t, "true", req.Dimensions.ReportClick) + require.Equal(t, "false", req.Dimensions.ReportSent) + require.Equal(t, "[1,3,5]", req.Dimensions.ClickedLink) require.Equal(t, "Not an error", req.Dimensions.FailureDetails) } diff --git a/internal/configstatus/configuration_success.go b/internal/configstatus/configuration_success.go index 2089a16e..84c421e1 100644 --- a/internal/configstatus/configuration_success.go +++ b/internal/configstatus/configuration_success.go @@ -18,6 +18,7 @@ package configstatus import ( + "strconv" "time" ) @@ -26,10 +27,10 @@ type ConfigSuccessValues struct { } type ConfigSuccessDimensions struct { - Autoconf string `json:"autoconf"` - ReportClick interface{} `json:"report_click"` - ReportSent interface{} `json:"report_sent"` - ClickedLink uint64 `json:"clicked_link"` + Autoconf string `json:"autoconf"` + ReportClick string `json:"report_click"` + ReportSent string `json:"report_sent"` + ClickedLink string `json:"clicked_link"` } type ConfigSuccessData struct { @@ -50,9 +51,9 @@ func (*ConfigSuccessBuilder) New(data *ConfigurationStatusData) ConfigSuccessDat }, Dimensions: ConfigSuccessDimensions{ Autoconf: data.DataV1.Autoconf, - ReportClick: data.DataV1.ReportClick, - ReportSent: data.DataV1.ReportSent, - ClickedLink: data.DataV1.ClickedLink, + ReportClick: strconv.FormatBool(data.DataV1.ReportClick), + ReportSent: strconv.FormatBool(data.DataV1.ReportSent), + ClickedLink: data.clickedLinkToString(), }, } } diff --git a/internal/configstatus/configuration_success_test.go b/internal/configstatus/configuration_success_test.go index 83b73f24..9f98990f 100644 --- a/internal/configstatus/configuration_success_test.go +++ b/internal/configstatus/configuration_success_test.go @@ -39,9 +39,9 @@ func TestConfigurationSuccess_default(t *testing.T) { require.Equal(t, "bridge_config_success", req.Event) require.Equal(t, 0, req.Values.Duration) require.Equal(t, "", req.Dimensions.Autoconf) - require.Equal(t, false, req.Dimensions.ReportClick) - require.Equal(t, false, req.Dimensions.ReportSent) - require.Equal(t, uint64(0), req.Dimensions.ClickedLink) + require.Equal(t, "false", req.Dimensions.ReportClick) + require.Equal(t, "false", req.Dimensions.ReportSent) + require.Equal(t, "", req.Dimensions.ClickedLink) } func TestConfigurationSuccess_fed(t *testing.T) { @@ -71,7 +71,7 @@ func TestConfigurationSuccess_fed(t *testing.T) { require.Equal(t, "bridge_config_success", req.Event) require.Equal(t, 10, req.Values.Duration) require.Equal(t, "Mr TBird", req.Dimensions.Autoconf) - require.Equal(t, true, req.Dimensions.ReportClick) - require.Equal(t, false, req.Dimensions.ReportSent) - require.Equal(t, uint64(42), req.Dimensions.ClickedLink) + require.Equal(t, "true", req.Dimensions.ReportClick) + require.Equal(t, "false", req.Dimensions.ReportSent) + require.Equal(t, "[1,3,5]", req.Dimensions.ClickedLink) } diff --git a/internal/user/config_status.go b/internal/user/config_status.go index b973989b..8a93c8f3 100644 --- a/internal/user/config_status.go +++ b/internal/user/config_status.go @@ -124,10 +124,12 @@ func (user *User) SendConfigStatusProgress() { if !user.configStatus.IsPending() { return } - var builder configstatus.ConfigProgressBuilder progress := builder.New(user.configStatus.Data) - if progress.Values.NbDaySinceLast == 0 || progress.Values.NbDay == 0 { + if progress.Values.NbDay == 0 { + return + } + if progress.Values.NbDaySinceLast == 0 { return }