forked from Silverfish/proton-bridge
fix(GODT-2418): Ensure child folders are updated when parent is
This commit is contained in:
@ -611,6 +611,81 @@ func TestBridge_User_CreateDisabledAddress(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestBridge_User_HandleParentLabelRename(t *testing.T) {
|
||||
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) {
|
||||
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
|
||||
require.NoError(t, getErr(bridge.LoginFull(ctx, username, password, nil, nil)))
|
||||
|
||||
info, err := bridge.QueryUserInfo(username)
|
||||
require.NoError(t, err)
|
||||
|
||||
client, err := client.Dial(fmt.Sprintf("%v:%v", constants.Host, bridge.GetIMAPPort()))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, client.Login(info.Addresses[0], string(info.BridgePass)))
|
||||
defer func() { _ = client.Logout() }()
|
||||
|
||||
withClient(ctx, t, s, username, password, func(ctx context.Context, c *proton.Client) {
|
||||
parentName := uuid.NewString()
|
||||
childName := uuid.NewString()
|
||||
|
||||
// Create a folder.
|
||||
parentLabel, err := c.CreateLabel(ctx, proton.CreateLabelReq{
|
||||
Name: parentName,
|
||||
Type: proton.LabelTypeFolder,
|
||||
Color: "#f66",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait for the parent folder to be created.
|
||||
require.Eventually(t, func() bool {
|
||||
return xslices.IndexFunc(clientList(client), func(mailbox *imap.MailboxInfo) bool {
|
||||
return mailbox.Name == fmt.Sprintf("Folders/%v", parentName)
|
||||
}) >= 0
|
||||
}, 100*user.EventPeriod, user.EventPeriod)
|
||||
|
||||
// Create a subfolder.
|
||||
childLabel, err := c.CreateLabel(ctx, proton.CreateLabelReq{
|
||||
Name: childName,
|
||||
Type: proton.LabelTypeFolder,
|
||||
Color: "#f66",
|
||||
ParentID: parentLabel.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, parentLabel.ID, childLabel.ParentID)
|
||||
|
||||
// Wait for the parent folder to be created.
|
||||
require.Eventually(t, func() bool {
|
||||
return xslices.IndexFunc(clientList(client), func(mailbox *imap.MailboxInfo) bool {
|
||||
return mailbox.Name == fmt.Sprintf("Folders/%v/%v", parentName, childName)
|
||||
}) >= 0
|
||||
}, 100*user.EventPeriod, user.EventPeriod)
|
||||
|
||||
newParentName := uuid.NewString()
|
||||
|
||||
// Rename the parent folder.
|
||||
require.NoError(t, getErr(c.UpdateLabel(ctx, parentLabel.ID, proton.UpdateLabelReq{
|
||||
Color: "#f66",
|
||||
Name: newParentName,
|
||||
})))
|
||||
|
||||
// Wait for the parent folder to be renamed.
|
||||
require.Eventually(t, func() bool {
|
||||
return xslices.IndexFunc(clientList(client), func(mailbox *imap.MailboxInfo) bool {
|
||||
return mailbox.Name == fmt.Sprintf("Folders/%v", newParentName)
|
||||
}) >= 0
|
||||
}, 100*user.EventPeriod, user.EventPeriod)
|
||||
|
||||
// Wait for the child folder to be renamed.
|
||||
require.Eventually(t, func() bool {
|
||||
return xslices.IndexFunc(clientList(client), func(mailbox *imap.MailboxInfo) bool {
|
||||
return mailbox.Name == fmt.Sprintf("Folders/%v/%v", newParentName, childName)
|
||||
}) >= 0
|
||||
}, 100*user.EventPeriod, user.EventPeriod)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// userLoginAndSync logs in user and waits until user is fully synced.
|
||||
func userLoginAndSync(
|
||||
ctx context.Context,
|
||||
|
||||
@ -425,25 +425,51 @@ func (user *User) handleUpdateLabelEvent(ctx context.Context, event proton.Label
|
||||
"name": logging.Sensitive(event.Label.Name),
|
||||
}).Info("Handling label updated event")
|
||||
|
||||
// Only update the label if it exists; we don't want to create it as a client may have just deleted it.
|
||||
if _, ok := user.apiLabels[event.Label.ID]; ok {
|
||||
user.apiLabels[event.Label.ID] = event.Label
|
||||
}
|
||||
stack := []proton.Label{event.Label}
|
||||
|
||||
for _, updateCh := range xslices.Unique(maps.Values(user.updateCh)) {
|
||||
update := imap.NewMailboxUpdated(
|
||||
imap.MailboxID(event.ID),
|
||||
getMailboxName(event.Label),
|
||||
)
|
||||
updateCh.Enqueue(update)
|
||||
updates = append(updates, update)
|
||||
}
|
||||
for len(stack) > 0 {
|
||||
label := stack[0]
|
||||
stack = stack[1:]
|
||||
|
||||
user.eventCh.Enqueue(events.UserLabelUpdated{
|
||||
UserID: user.apiUser.ID,
|
||||
LabelID: event.Label.ID,
|
||||
Name: event.Label.Name,
|
||||
})
|
||||
// Only update the label if it exists; we don't want to create it as a client may have just deleted it.
|
||||
if _, ok := user.apiLabels[label.ID]; ok {
|
||||
user.apiLabels[label.ID] = event.Label
|
||||
}
|
||||
|
||||
// API doesn't notify us that the path has changed. We need to fetch it again.
|
||||
apiLabel, err := user.client.GetLabel(ctx, label.ID, label.Type)
|
||||
if apiErr := new(proton.APIError); errors.As(err, &apiErr) && apiErr.Status == http.StatusUnprocessableEntity {
|
||||
user.log.WithError(apiErr).Warn("Failed to get label: label does not exist")
|
||||
continue
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("failed to get label %q: %w", label.ID, err)
|
||||
}
|
||||
|
||||
// Update the label in the map.
|
||||
user.apiLabels[apiLabel.ID] = apiLabel
|
||||
|
||||
// Notify the IMAP clients.
|
||||
for _, updateCh := range xslices.Unique(maps.Values(user.updateCh)) {
|
||||
update := imap.NewMailboxUpdated(
|
||||
imap.MailboxID(apiLabel.ID),
|
||||
getMailboxName(apiLabel),
|
||||
)
|
||||
updateCh.Enqueue(update)
|
||||
updates = append(updates, update)
|
||||
}
|
||||
|
||||
user.eventCh.Enqueue(events.UserLabelUpdated{
|
||||
UserID: user.apiUser.ID,
|
||||
LabelID: apiLabel.ID,
|
||||
Name: apiLabel.Name,
|
||||
})
|
||||
|
||||
children := xslices.Filter(maps.Values(user.apiLabels), func(other proton.Label) bool {
|
||||
return other.ParentID == label.ID
|
||||
})
|
||||
|
||||
stack = append(stack, children...)
|
||||
}
|
||||
|
||||
return updates, nil
|
||||
}, user.apiLabelsLock, user.updateChLock)
|
||||
|
||||
Reference in New Issue
Block a user