shell: Add notification boards, replacing page_status - #23314
Conversation
Venefilyn
left a comment
There was a problem hiding this comment.
Mostly went over the new notification structure in the first commit, WDYT?
| * - page_status: legacy per-page status keyed by [host, page]. Used | ||
| * for shell navigation icons. |
There was a problem hiding this comment.
If it's legacy and not recommended I'd say deprecated instead
| * | ||
| * The details are all still experimental and subject to change. |
There was a problem hiding this comment.
Once merged it shouldn't be experimental anymore IMO
| export const page_status = new PageStatus(); | ||
|
|
||
| export const CHANNELS_KEY = "cockpit:notification-channels"; | ||
| export const OVERVIEW_HEALTH_CHANNEL = "overview:health"; |
There was a problem hiding this comment.
Seems to only be used as a fallback/default. Can be renamed to reflect that. E.g. CHANNEL_DEFAULT
|
|
||
| publish(notification: ChannelNotification): void { | ||
| if (!notification || typeof notification.id !== "string" || notification.id === "") | ||
| throw new Error("NotificationChannel.publish: notification.id is required"); |
| if (dequal(prev, notification)) | ||
| return; |
There was a problem hiding this comment.
Could be faster with quick undefined check
| if (dequal(prev, notification)) | |
| return; | |
| if (prev && dequal(prev, notification)) | |
| return; |
| #init_notification_channels() { | ||
| sessionStorage.removeItem(CHANNELS_KEY); | ||
| } |
There was a problem hiding this comment.
Clearing notifications channel should be done with a function in notifications.ts I think. Or we move all sessionStorage stuff out of notifications
| #notify_channel(host: string, page: string, channel: string, id: string, | ||
| notification: ChannelNotification | null) { |
There was a problem hiding this comment.
This is enough params that I think we can use an object for it instead. {host, page, channel, id, notification}
| return ch; | ||
| } | ||
|
|
||
| /* - publish_page_health(page_id, status, opts?) |
There was a problem hiding this comment.
Can add in @deprecated. Though I wonder if we should even include it at all
|
@mvollmer pinging you as well if you can do review |
|
Nice! I think the original idea to have some generic mechanism where any page can tell the Shell to put an icon and a tooltip into its navigation entry was good, and we should of course keep this functionality. But driving the health card via that same mechanism was not a good idea, and we (I) probably only did that because it was convenient, implementation wise. You channel idea is much better. Since the health card currently has a hard coded list of pages that it listens to, we can make some breaking changes to the existing API (since we know all places that will be affected). So what about going further here in this PR already:
I think we should make dedicated little APIs for specific channels, maybe even in their own files:
How does that sound? |
|
|
||
| const t = status.type; | ||
| ch.publish({ | ||
| id: page_id, |
There was a problem hiding this comment.
I'd say it should not be necessary for a page to know its own id. The old page_status.set_own doesn't need that and the Shell will figure it out because it knows where the message comes from. Why do we need an id for channels?
There was a problem hiding this comment.
it was there so one publisher could hold multiple entries and clear them independently, but nothing uses that, every caller publishes one entry with id = page name
| * - page_status: legacy per-page status keyed by [host, page]. Used | ||
| * for shell navigation icons. | ||
| * | ||
| * - channel() / NotificationChannel: consumer-managed named channels |
There was a problem hiding this comment.
I think we need a different name here instead of "channel", or at least qualify it.
A "channel" is a very fundamental concept in the Cockpit architecture, see doc/protocol.md, and we need to make sure these notification channels are not confused with them in the mind of a code reader.
Hmm. What about "notification_board"? But if we export functions like "set_health_status" from pkg/lib, maybe the name "channel" here isn't actually part of the API that most people use.
There was a problem hiding this comment.
- Notification board
- Notification box
- Notification tap
There was a problem hiding this comment.
I vote for "board", to emphasize the permanent nature of the notifications, and de-emphasize the transport mechanism.
There was a problem hiding this comment.
From Matrix
mvollmer: If we go with keeping notifications in _internal and nobody uses the "channel" name in client code, then I am fine with using "channel". Or just "notification_channel" to distinguish it enough from cockpit.channel.
mvollmer: the localStorage key name also doesn't matter a whole lot, but since it is the storage, it should probably not be called "channel", but rather "cockpit:notifications" (which then include a channel identifier as a attribute)
mvollmer: or something else that conveys "this is the current set of things received over the notification channels" as opposed to "this is a channel", which it isn't.
mvollmer: but if we want something else than "channel"... I vote for "notification board"
Which SGTM. Board works
|
|
||
| export const page_status = new PageStatus(); | ||
|
|
||
| export const CHANNELS_KEY = "cockpit:notification-channels"; |
There was a problem hiding this comment.
"cockpit:notification-boards"? It kinda makes sense to me....
|
So, thinking a tiny bit more about this: We should have a minimal public API for exactly the things that we need (page_status, health_status), and not commit to anything else for now. Thus:
That would keep the "channel" name out of the public API, which would make me happy. It would also make it clear that the Shell is the one defining what a "page status" is, and the Overview what a "health status" is,, and that there is currently nothing else. (I can see how we add show_notification to pkg/lib/shell later for showing actual toast notifications, or other things related to controlling the Shell from a page, including moving cockpit.jump() there and other Shelly things currently part of the "cockpit" module.) |
| const handler = () => { fired++ }; | ||
| ch.addEventListener("changed", handler); | ||
|
|
||
| window.dispatchEvent(new StorageEvent("storage", { key: "cockpit:page_status" })); |
| ch.addEventListener("changed", handler); | ||
|
|
||
| window.dispatchEvent(new StorageEvent("storage", { key: "cockpit:page_status" })); | ||
| window.dispatchEvent(new StorageEvent("storage", { key: "unrelated" })); |
|
|
||
| window.dispatchEvent(new StorageEvent("storage", { key: "cockpit:page_status" })); | ||
| window.dispatchEvent(new StorageEvent("storage", { key: "unrelated" })); | ||
| window.dispatchEvent(new StorageEvent("storage", { key: CHANNELS_KEY })); |
| if (typeof data.channel === "string" && typeof data.id === "string") { | ||
| const n = data.notification; | ||
| const valid = n === null || n === undefined || | ||
| (typeof n === "object" && n !== null && !Array.isArray(n)); |
|
About compatibility: We should keep the nav icons working with any combination of shell and page versions. But we can assume that the health card stuff is all updated at the same time. The Shell should keep processing "notify" events with a "page_status" field and should decorate the navbar with icons and popups announced with them. A call to page_status.publish() should send a "notify" message with bothe "page_status" and the new "channel" stuff in it. |
sounds good to me, I can make those changes
removing it is probably cleaner yea |
85a8501 to
5471ee5
Compare
| export interface Notification { | ||
| type?: string | null; | ||
| title?: string; | ||
| [key: string]: JsonValue | undefined; | ||
| } |
There was a problem hiding this comment.
Why is the notification data itself part of the notification message? I feel like this should be within a sub-field "details" like before. That way it is more flexible and easier to make changes in the future
I also think we should add a generic as mentioned in my previous review #23314 (comment)
| export interface NotificationControlMessage { | ||
| page_status?: Notification | null; | ||
| board?: unknown; | ||
| notification?: unknown; | ||
| } |
There was a problem hiding this comment.
What's the difference between page_status and notification? Why can page_status accept both null and undefined?
What would board and notification style be? Can we make this easier without unknown?
| // Distinct from null so a fresh instance after a frame reload still sends its | ||
| // first publish/clear, even if it matches the stale registry entry. | ||
| const UNSENT = Symbol("unsent"); |
There was a problem hiding this comment.
This is also mentioned in notifications.ts. Consolidation?
| // "type" carries "info"/"warning"/"error" or a PatternFly icon name | ||
| // accepted by get_pficon() in pkg/systemd/page-status.jsx (e.g. | ||
| // "security", "bug", "spinner", "check", "enhancement"). | ||
|
|
| @@ -0,0 +1,45 @@ | |||
| /* | |||
| * Copyright (C) 2019 Red Hat, Inc. | |||
| @@ -0,0 +1,29 @@ | |||
| /* | |||
| * Copyright (C) 2019 Red Hat, Inc. | |||
|
Marius is out for 3 weeks so will try to go through this. Ping me on Matrix on Slack when you have time and we could go over it faster - either by chat or meeting |
Introduce a generic notification board (pkg/lib/_internal/notifications) that any page can post a single entry to and that the Shell aggregates. Expose two small typed facades on top, page_status (pkg/lib/shell) and health_status (pkg/lib/overview), so the generic "board" name stays internal and is not confused with protocol channels (doc/protocol.md). Signed-off-by: Josephine Pfeiffer <hi@josie.lol>
Aggregate board posts by (board, host, page) and persist them under cockpit:notifications. Drive nav icons from the shell:page-status board, folding the legacy page_status notify field from older pages into it so icons keep working across shell and page versions. Signed-off-by: Josephine Pfeiffer <hi@josie.lol>
Publish updates and services health through the page_status and health_status facades. The Overview health card now lists the overview:health board instead of polling a hard-coded page list. Signed-off-by: Josephine Pfeiffer <hi@josie.lol>
Signed-off-by: Josephine Pfeiffer <hi@josie.lol>
Cover the board mechanism with qunit tests and remove the obsolete page_status module now that every consumer uses the facades. Signed-off-by: Josephine Pfeiffer <hi@josie.lol>
5471ee5 to
fc47d09
Compare
the hardcoded allowlist in PageStatusNotifications is replaced by a generic notification board mechanism that any page can post a single entry to and that the Shell aggregates
closes #23221 (alternative to #23127)