-
Notifications
You must be signed in to change notification settings - Fork 8.5k
[Guided onboarding] Fix multiple API requests when fetching guide state #144160
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
Changes from all commits
ffb8929
bebcf04
a4d43ce
fff97e6
9e2eb4b
63db4ef
b1e9d59
f97eaab
9a290b2
649f19d
5bb5c84
ad7d94b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,7 @@ | |
| */ | ||
|
|
||
| import { HttpSetup } from '@kbn/core/public'; | ||
| import { BehaviorSubject, map, from, concatMap, of, Observable, firstValueFrom } from 'rxjs'; | ||
| import { BehaviorSubject, map, concatMap, of, Observable, firstValueFrom } from 'rxjs'; | ||
| import type { GuideState, GuideId, GuideStep, GuideStepIds } from '@kbn/guided-onboarding'; | ||
|
|
||
| import { GuidedOnboardingApi } from '../types'; | ||
|
|
@@ -26,37 +26,54 @@ import { API_BASE_PATH } from '../../common/constants'; | |
| export class ApiService implements GuidedOnboardingApi { | ||
| private client: HttpSetup | undefined; | ||
| private onboardingGuideState$!: BehaviorSubject<GuideState | undefined>; | ||
| private isGuideStateLoading: boolean | undefined; | ||
| public isGuidePanelOpen$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); | ||
|
|
||
| public setup(httpClient: HttpSetup): void { | ||
| this.client = httpClient; | ||
| this.onboardingGuideState$ = new BehaviorSubject<GuideState | undefined>(undefined); | ||
| } | ||
|
|
||
| private createGetStateObservable(): Observable<GuideState | undefined> { | ||
| return new Observable<GuideState | undefined>((observer) => { | ||
| const controller = new AbortController(); | ||
| const signal = controller.signal; | ||
| this.isGuideStateLoading = true; | ||
| this.client!.get<{ state: GuideState[] }>(`${API_BASE_PATH}/state`, { | ||
| query: { | ||
| active: true, | ||
| }, | ||
| signal, | ||
| }) | ||
| .then((response) => { | ||
| this.isGuideStateLoading = false; | ||
| // There should only be 1 active guide | ||
| const hasState = response.state.length === 1; | ||
| if (hasState) { | ||
| this.onboardingGuideState$.next(response.state[0]); | ||
| } | ||
| observer.complete(); | ||
| }) | ||
| .catch((error) => { | ||
| this.isGuideStateLoading = false; | ||
| observer.error(error); | ||
| }); | ||
| return () => { | ||
| this.isGuideStateLoading = false; | ||
| controller.abort(); | ||
| }; | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * An Observable with the active guide state. | ||
| * Initially the state is fetched from the backend. | ||
| * Subsequently, the observable is updated automatically, when the state changes. | ||
| */ | ||
| public fetchActiveGuideState$(): Observable<GuideState | undefined> { | ||
| // TODO add error handling if this.client has not been initialized or request fails | ||
| return this.onboardingGuideState$.pipe( | ||
| concatMap((state) => | ||
| state === undefined | ||
| ? from( | ||
| this.client!.get<{ state: GuideState[] }>(`${API_BASE_PATH}/state`, { | ||
| query: { | ||
| active: true, | ||
| }, | ||
| }) | ||
| ).pipe( | ||
| map((response) => { | ||
| // There should only be 1 active guide | ||
| const hasState = response.state.length === 1; | ||
| return hasState ? response.state[0] : undefined; | ||
| }) | ||
| ) | ||
| : of(state) | ||
| !state && !this.isGuideStateLoading ? this.createGetStateObservable() : of(state) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related to my comment above. I think we need a way to differentiate between state that is undefined because it hasn't been fetched yet vs. state that is undefined bc the active guide has been completed. Maybe we should rethink this line in Change to: Then, I think we'd need to update |
||
| ) | ||
| ); | ||
| } | ||
|
|
@@ -83,7 +100,7 @@ export class ApiService implements GuidedOnboardingApi { | |
| /** | ||
| * Updates the SO with the updated guide state and refreshes the observables | ||
| * This is largely used internally and for tests | ||
| * @param {GuideState} guideState the updated guide state | ||
| * @param {GuideState} newState the updated guide state | ||
| * @param {boolean} panelState boolean to determine whether the dropdown panel should open or not | ||
| * @return {Promise} a promise with the updated guide state | ||
| */ | ||
|
|
@@ -99,8 +116,8 @@ export class ApiService implements GuidedOnboardingApi { | |
| const response = await this.client.put<{ state: GuideState }>(`${API_BASE_PATH}/state`, { | ||
| body: JSON.stringify(newState), | ||
| }); | ||
| // If the guide has been deactivated, we return undefined | ||
| this.onboardingGuideState$.next(newState.isActive ? newState : undefined); | ||
| // broadcast the newState | ||
| this.onboardingGuideState$.next(newState); | ||
| this.isGuidePanelOpen$.next(panelState); | ||
| return response; | ||
| } catch (error) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will break the "complete" state; we need to updateI think this will be irrelevant if you follow my suggestion below.this.onboardingGuideState$once the user completes a guide so we know to hide the setup guide button.