Skip to content
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

feat(pwa): track online status [LIBS-315] #718

Merged
merged 41 commits into from
Mar 3, 2023

Conversation

KaiVandivier
Copy link
Contributor

@KaiVandivier KaiVandivier commented Jun 6, 2022

Adds the service worker, offline interface, and app adapter foundation for tracking the DHIS2 connection status alongside incidental requests.

Part of LIBS-315

The spec

  • We want to detect connection to the DHIS2 server.
  • We want to minimize the network and server resources used to do so
  • The service worker watches app network traffic and uses the success or failure of responses to determine connectivity status, then send messages to the client to broadcast the updates
    • Only requests to the DHIS2 server (based on the Base URL) are used to determine the connection status
  • If there is no app network traffic to the DHIS2 server, the client will send pings
    • The service worker is configured to only handle pings over the network, i.e. not to cache ping requests/responses, so the result observed on the client will reflect the server connection status
    • If the connection status detected by the ping is the same as the previous status, then the time to the next ping will increase. This will continue to create an exponential back-off, which will max at out at a value, for example 5 minutes.
    • If the connection status has changed (either by ping or by app network traffic), the exponential back-off will be reset and the timeout to the next ping will be reduced back to the initial timeout.
    • If the app loses focus, a “standby” procedure is followed to stop pinging when it’s unnecessary
      • Nothing happens initially. If the user refocuses the app before the next ping is scheduled to be sent, the behavior will not change.
      • If the next-scheduled ping would take place while the app is defocused, instead the hook enters a “standby” mode.
      • When the app is refocused, if the hook is in “standby”, a ping will be sent immediately to detect the current connection status, following other rules for back-off
      • If the app is opened while defocused initially, it will also start running in standby and will not ping until refocused.
    • If the app detects an ‘offline’ event from the browser, it will ping immediately to check the connection status, since that should be a low-cost action
    • If the app detects an ‘offline’ event from the browser and is defocused, a special thing happens: it enters standby to send a ping imediately when the app is refocused again
      • If the app is refocused before the next regularly-scheduled ping, the ping timeout will be restarted but not incremented.
      • Otherwise, if the regularly scheduled ping is ready before the app is refocused, the normal standby procedure is followed (e.g. the next timeout can be incremented if the connection status hasn’t changed)

Notes for this PR

  • The service worker is extended to track requests to the DHIS2 base URL and send updates to the clients based on the successes or failures of those requests
    • This is accomplished by a plugin that’s added to all the Workbox route handlers
    • Note that the SW handles ‘ping’ requests differently: it uses only the network to avoid caching, and doesn’t use the connection status plugin, since it assumes the ping is being used by the client to determine connection status (in which case the message broadcast from the SW would be redundant)
  • To avoid sending a ton of messages from the SW to every client (one for every request to the DHIS2 base url it handles), the message sending function is throttled to send max 1 message a second. Messages should still be relatively frequent though in order to inform the client to delay unnecessary pings.
  • Base URLs are now stored in IndexedDB on a per-app basis
    • IndexedDB is used because it is asynchronous, so the service worker can read the base URL and detect if a request is headed for the DHIS2 server or not to determine connectivity
    • Base URLs are saved on a per-app basis because it’s technically possible to configure an app to target a DHIS2 isntance that it is not hosted on as its base URL, and after speaking with Austin, we want to support that case
    • Much of this handling happens in the App Adapter, in the ServerVersionProvider and LoginModal components
    • The @dhis2/pwa package exports some utils for performing the IndexedDB operations for base URLs

To test locally:

update: some of these tools are not available any more, since much of the testing has passed

  • Check out the app-runtime branch and build it
  • In the PWA app’s RequestTester.js file, follow the comments to use the useDhis2ConnectionStatus hook instead of the dummy placeholder. Note that this will be imported from app-runtime when it’s released
  • Run yarn build:pwa && yarn build:adapter
  • then run yarn temp:start to copy the built app-runtime libs into the app-platform node_modules and start the PWA app
  • The console logs and network tab can give you some cues about what’s going on under the hood

Todos:

  • remove console logs after testing
  • remove temp scripts

Copy link
Contributor

@HendrikThePendric HendrikThePendric left a comment

Choose a reason for hiding this comment

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

I've left some comments relating to code style. You have also provided me with in depth explanation about various parts of this PR on Slack and during meetings. After receiving these, I think the changes you've implemented / the approach you took here make a lot of sense.

There are still quite a few todo comments in the code and the PR is still draft, so I'll just leave this review as a "comment". I can have another look once you take this PR out of draft mode.

adapter/src/components/ServerVersionProvider.js Outdated Show resolved Hide resolved
adapter/src/components/ServerVersionProvider.js Outdated Show resolved Hide resolved
adapter/src/components/ServerVersionProvider.js Outdated Show resolved Hide resolved
pwa/src/service-worker/recording-mode.js Outdated Show resolved Hide resolved
@KaiVandivier KaiVandivier marked this pull request as ready for review December 14, 2022 14:30
Comment on lines -80 to +92
export function handleRecordedRequest({ request, event }) {
const recordingState = self.clientRecordingStates[event.clientId]
export class RecordingMode extends Strategy {
_handle(request, handler) {
const { event } = handler
const recordingState = self.clientRecordingStates[event.clientId]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This handler is refactored to a Workbox route handling strategy to take advantage of the 'plugin' functionality

Comment on lines -12 to +10
window.localStorage.DHIS2_BASE_URL,
url: process.env.REACT_APP_DHIS2_BASE_URL,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

localStorage is now checked in ServerVersionProvider.js

Comment on lines 60 to 81
/**
* A plugin to hook into lifecycle events in workbox strategies
* https://developer.chrome.com/docs/workbox/using-plugins/
*/
export const dhis2ConnectionStatusPlugin = {
// todo: remove console logs
fetchDidFail: async ({ request, error }) => {
console.log('fetch did FAIL', { request, error })

if (await isRequestToDhis2Server(request)) {
broadcastDhis2ConnectionStatus(false)
}
},
fetchDidSucceed: async ({ request, response }) => {
console.log('fetch did SUCCEED', { request, response })

if (await isRequestToDhis2Server(request)) {
broadcastDhis2ConnectionStatus(true)
}
return response
},
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This plugin is used in set-up-service-worker.js

@HendrikThePendric
Copy link
Contributor

I had a quick look at this PR again, and while the code is looking good to me, there are still a lot of todo comments in the newly added code. Since it's not obvious to me if these are just ideas for potential future improvements, known issues that will be addressed later, or things that should actually get fixed before the PR is merged, I stopped reviewing and didn't functionally test either.

@KaiVandivier
Copy link
Contributor Author

Todos have been clarified

@HendrikThePendric
Copy link
Contributor

Just to note: I decided to approve this PR and its counterpart dhis2/app-runtime#1203 because the code is looking good. But before merging, please take care to either remove the console.log statements or replace them with something that only logs in development mode, see dhis2/app-runtime#1203 (comment).

@KaiVandivier KaiVandivier enabled auto-merge (squash) March 3, 2023 10:14
@KaiVandivier KaiVandivier merged commit 1dfd1e6 into master Mar 3, 2023
@KaiVandivier KaiVandivier deleted the feat-track-online-status branch March 3, 2023 10:22
dhis2-bot added a commit that referenced this pull request Mar 3, 2023
# [10.3.0](v10.2.3...v10.3.0) (2023-03-03)

### Features

* **pwa:** track online status [LIBS-315] ([#718](#718)) ([1dfd1e6](1dfd1e6))
@dhis2-bot
Copy link
Contributor

🎉 This PR is included in version 10.3.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

None yet

3 participants