Onboarding - API to retrieve the state of a project analytics stack
Problem to solve
Product analytics onboarding requires state management because it is an asynchronous process and it consists of many steps that need to be completed in order. This would usually be accomplished in the frontend using Vuex or apollo, however product analytics is a shared page on projects so multiple users can access it and one project should only have up to one product analytics stack. Thus the need for a single shared state amongst all users & clients. (Think of it as the MR page shared by many authors and reviewers)
For additional context see the discussion #381319 (comment 1167423683).
Suggested solution
In my experience shared provisioning flows are really easy to overcomplicate, especially on the frontend, but the simplest solution I've found a bit counterintuitive. Make the frontend app stateless. This sounds weird, but what this really means is just using the backend as the SSOT and like a statemachine. Here's a simple example to determine the product analytics state:
Given a project ID, we should be able to retrieve the following states:
def product_analytics_state
project_analytics_disabled? # check if feature is available if needed
return nil
no_project_instance? # check if Jitsu key exists
return 'create_instance'
instance_initializing? # check if project:{ID}:product_analytics_initializing exists
return 'loading_instance'
no_instance_data? # Check if cube has TrackedEvents.count > 0. Memoize the result for performance.
return 'waiting_for_events'
'show_dashboards'
end
As this example shows this statemachine could simply be a method with guard clauses. It doesn't need a be a fully fledged statemachine, but it could be if that's what's needed or best suited. I also suspect that the result could be memoized since it's unlikely to change which should be a nice performance improvement over checking all the possible states.
The frontend app can use this information to route to the correct page. And the backend could also use this to check user actions against the current state, for example ProductAnalytics::InitializeStackService.new
should only be called if the state is create_instance
.
Additional information
This builds on the work done in:
-
Initialize product analytics stack via GraphQL ... (#383050 - closed)
- Adds a redis key to track the initialization worker state
-
Onboarding - Add Jitsu key to project GraphQL type (#381332 - closed)
- Enables the frontend to determine whether a stack has been initialized.
Implementation plan
backend
- Add a new helper method for
product_analytics_state
which takes a project UID. - For
no_instance_data
replicate thehasAnalyticsData
query in the backend and memoize the result. - Add a new GraphQL resource for project > product analytics status which calls the helper.
- Update the
ProjectInitializeProductAnalytics
resource to also include the status.- The frontend will rely on this to know what the current state is after calling initialize.
frontend
- Remove the
hasAnalyticsData
call. - Create a renderless
analytics_state
component that can:- 1 - Initialise a stack and return the new state
- 2 - Get the current state of a stack
- This can then be reused for the shared listing page.
- Update
product_analytics_app.vue
to get the onboarding status from the new component.- Show loading while the status is being fetched.
- Update
onboarding_view.vue
to use the new component.- Replace the initialize call.
- Use the new component for polling.
Recommendation to split this into two separate implementation issues.