Skip to content

notif: Support migration of Android notification channels #981

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

Merged
merged 3 commits into from
Oct 8, 2024

Conversation

rajveermalviya
Copy link
Member

(Split from #717)

Needed for #340, when updating notification channels to use a custom notification sound.

Copy link
Collaborator

@chrisbobbe chrisbobbe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Small comments below, and marking for Greg's review.

Comment on lines 557 to 558
/// Consumes the log of sequence of calls made to [getNotificationChannels],
/// [deleteNotificationChannel] and [createNotificationChannel].
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"log of sequence of calls" -> "log of calls"?

if (channel!.id == kChannelId) {
found = true;
} else {
await _androidHost.deleteNotificationChannel(channel.id);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like it would be helpful to mention this in the dartdoc; something like:

  /// Create our notification channel, if it doesn't already exist.
  /// 
  /// Deletes obsolete channels, if present, from old versions of the app.

Comment on lines 30 to 31
// TODO(?) at launch make sure this channel-id doesn't collide with the one
// present in zulip-mobile.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More accurately, I think, the collision to avoid is with a channel ID that's active in the installed app the user will be upgrading from. In theory, that could be different from the one zulip-mobile has in main, right, because the user could be upgrading from a very old zulip-mobile straight to zulip-flutter?

I see in main zulip-mobile has "messages-3". There's a handy list of previous values in a code comment on it:

/** The channel ID we use for our one notification channel, which we use for all notifications. */
// Previous values: "default", "messages-1", (alpha-only: "messages-2")
val CHANNEL_ID = "messages-3"

which…hmm, that list does include "messages-1". I'm not sure how old that one is, and how many of our users will still have that as their active channel.

Rather than tracking that down, maybe simplest to be conservative and just say something like

  // TODO(launch) check this doesn't match zulip-mobile's current or previous channel IDs

@rajveermalviya rajveermalviya force-pushed the pr-notif-channel-migrations branch from 8afb983 to c85adc9 Compare October 5, 2024 03:28
@rajveermalviya
Copy link
Member Author

Thanks for the review @chrisbobbe! Pushed a new revision PTAL.

@chrisbobbe
Copy link
Collaborator

Thanks! LGTM, over to @gnprice.

@chrisbobbe chrisbobbe added integration review Added by maintainers when PR may be ready for integration and removed maintainer review PR ready for review by Zulip maintainers labels Oct 7, 2024
@chrisbobbe chrisbobbe requested a review from gnprice October 7, 2024 21:31
Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @rajveermalviya, and thanks @chrisbobbe for the previous review!

Generally this looks great. Various comments below.

}

@override
Future<void> deleteNotificationChannel(String channelId) async {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: avoid the intermediate state with

    // TODO: implement deleteNotificationChannel
    throw UnimplementedError();

by adding the implementation here in the same commit that adds the binding

Comment on lines 49 to 53
.map { NotificationChannel(
it.id,
it.importance.toLong(),
it.name?.toString(),
it.shouldShowLights()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use named parameters for these — otherwise it's impossible to tell locally if some of the parameters have been accidentally swapped

Comment on lines 569 to 571
Future<List<NotificationChannel?>> getNotificationChannels() async {
_channelMethodCallLogs.add('getNotificationChannels');
return _createdChannels.toList(growable: false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This simulation leaves out one important aspect of how getNotificationChannels can behave: it can return channels that weren't created by this run of the app, but by some previous run (possibly years in the past).

If the tests you're currently writing don't need that feature, then it's fine to leave it out. But let's leave a comment here mentioning that gap. (Partly that serves as an invitation to a future developer to add that feature, if they find themself needing it.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, what the tests effectively do is call createNotificationChannel in order to simulate a channel having been created in the distant past.

That works fine. But this approach changes the semantics of _createdChannels — particularly because deleteNotificationChannel now removes channels from that list. Instead of a log of what was created, it's really serving now as a list of the channels that currently exist. The name, and the "take"-based API which is suited for a log, are no longer a fit.

So let's have a prep commit that replaces takeCreatedChannels with a getter like:

  Iterable<NotificationChannel> get createdChannels => _createdChannels;

Then that's well adapted for the direction it gets extended in the rest of this PR.

/// [deleteNotificationChannel] and [createNotificationChannel].
///
/// Returns a list of function names in the order they were invoked.
List<String> takeChannelMethodCallLogs() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "log" singular, since this is really just one log

Comment on lines 30 to 31
// TODO(launch) check this doesn't match zulip-mobile's current or previous
// channel IDs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: in a TODO comment (or other labeled comment), indent any continuation lines to keep them grouped under the heading:

Suggested change
// TODO(launch) check this doesn't match zulip-mobile's current or previous
// channel IDs
// TODO(launch) check this doesn't match zulip-mobile's current or previous
// channel IDs

Comment on lines 135 to 152
test('channel is not recreated if one with same id already exists', () async {
await NotificationChannelManager.ensureChannel();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like these tests affect the state in the binding, and then leak that state. To fix that, they should start with addTearDown(testBinding.reset) (like the existing smoke test does via init).

Comment on lines 136 to 140
await NotificationChannelManager.ensureChannel();
check(testBinding.androidNotificationHost.takeChannelMethodCallLogs())
.deepEquals(['getNotificationChannels', 'createNotificationChannel']);

await NotificationChannelManager.ensureChannel();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the first ensureChannel here is to simulate a channel having gotten created in the past, and then the second is the call that we're testing the behavior of.

I think it'd be cleaner to do the setup more directly, without ensureChannel. Using ensureChannel will only produce a channel that's exactly the way the current code sets it up; this breaks down in the next test case, where we want to simulate some previous version of the app having set it up, and it also breaks down with the possibility that the user may have altered the settings on the channel. (If they have, we should leave the existing channel in place; only the ID counts for saying it's the same channel.)

Comment on lines 141 to 142
check(testBinding.androidNotificationHost.takeChannelMethodCallLogs())
.deepEquals(['getNotificationChannels']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm generally not a fan of having "get" calls in a log that a test checks like this — it feels too far into testing the implementation details (cf https://zulip.readthedocs.io/en/stable/testing/philosophy.html#integration-testing-or-unit-testing ).

(We do routinely include the equivalent in our API tests — we check what HTTP requests the app made to the simulated server. But because these queries are local to the device, I'd like to try to treat them more as internal.)

Can we leave these out of the log, and have the tests just look at what the code does with the information? I.e., the create and delete calls.

@rajveermalviya rajveermalviya force-pushed the pr-notif-channel-migrations branch from c85adc9 to b3a5b2a Compare October 8, 2024 09:50
@rajveermalviya rajveermalviya force-pushed the pr-notif-channel-migrations branch from b3a5b2a to 00deaa6 Compare October 8, 2024 12:33
@rajveermalviya
Copy link
Member Author

Thanks for the reviews @chrisbobbe and @gnprice!
@gnprice pushed a new revision with update test bindings. PTAL.

@rajveermalviya rajveermalviya requested a review from gnprice October 8, 2024 13:01
@gnprice gnprice merged commit 00deaa6 into zulip:main Oct 8, 2024
1 check passed
@gnprice
Copy link
Member

gnprice commented Oct 8, 2024

Thanks for the revision! All looks good — merging.

@rajveermalviya rajveermalviya deleted the pr-notif-channel-migrations branch October 9, 2024 04:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integration review Added by maintainers when PR may be ready for integration
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants