Skip to content

Fix up out-of-office annotations#259

Open
kroeschl wants to merge 14 commits into
alejandro5042:masterfrom
kroeschl:ooo-annotation
Open

Fix up out-of-office annotations#259
kroeschl wants to merge 14 commits into
alejandro5042:masterfrom
kroeschl:ooo-annotation

Conversation

@kroeschl

@kroeschl kroeschl commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Justification

Out-of-office annotations have been broken for a good long while due to IT policy changes after Emerson acquired NI. Additionally, this feature only ever worked for NI users because we fetched OOO user data from an internal source.

Implementation

  • Switch from reading a cached of OOO data to querying Microsoft Graph for each reviewer's presence info.
  • Presence info includes a boolean out-of-office value as well as a status message (if present), but no start/end time, so drop start/end handling.
  • Update verbiage from "auto-reply" to "out-of-office" since that's more correct for the new data.
  • The Microsoft Graph API requires a registered application ID, which is baked into the script. Non-NI users can configure their own client ID via a new menu item if they want to use this feature.
  • We need to authenticate as the current user for Microsoft Graph, so implement an OAuth flow. Note that this will briefly pop-up the users default login redirect page, which is mildly annoying. We're forced to re-auth every 24 hours by Microsoft policies.

Testing

I tested this in my browser and now see annotations for OOO data. I also tweaked to script to test token refresh and saw that succeed as expected (without popup).
ooo-fixed

@kroeschl kroeschl force-pushed the ooo-annotation branch 2 times, most recently from c91e9ec to 8c4de0c Compare July 15, 2026 14:52
kroeschl added 14 commits July 15, 2026 11:18
Assigning untrusted HTML to innerHTML (even on a detached node) parses it
and fires resource-loading events (e.g. <img onerror>). DOMParser gives the
same tag-stripping without ever creating live elements.
Each reviewer on a PR calls getOooGraphAccessToken concurrently. The old
boolean flag was set before the token resolved, so only the first reviewer
triggered auth while the rest got null and showed no annotation until a
reload found the cached token. Caching the in-flight promise lets every
reviewer resolve from a single attempt, while still limiting it to one
attempt per page session (a resolved-null promise does not retry).
Separate the concurrency-coalescing from the once-per-session popup guard:
_oooAuthPromise now only coalesces concurrent callers and resets when the
acquisition settles (via .finally), while a dedicated _oooSilentAuthAttempted
boolean keeps the fallback popup to one attempt per page session. This stops
the shared promise from pinning the first resolved token for the whole page
session and makes room for alternative acquisition paths.
Request the offline_access scope so AAD returns a refresh token, then mint new
access tokens via a background POST (grant_type=refresh_token) instead of a
popup. Because the redirect URIs are SPA-type, refresh tokens expire 24h after
the original interactive login and that expiry carries over across rotations,
so a silent popup is still needed at most once per day rather than ~hourly.

- _oooStoreTokens centralizes access/refresh token persistence and stamps the
  24h refresh-token expiry only on a fresh login (not on rotation).
- _oooRefreshAccessToken does the no-popup refresh and drops the stored refresh
  token if AAD rejects it, falling back to the silent popup.
- The coalesced acquisition now tries the refresh token before the popup.
- Reconfiguring the client ID clears the stored refresh token (different app).
@kroeschl kroeschl marked this pull request as ready for review July 15, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant