diff --git a/playwright.config.ts b/playwright.config.ts index 6dd8f42aa..9032e5d35 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -22,7 +22,7 @@ export default defineConfig({ /* Retry on CI only */ retries: process.env.CI ? 2 : 0, /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, + workers: process.env.CI ? 1 : 2, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: 'html', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ diff --git a/src/Components/ServiceScene/ActionBarScene.tsx b/src/Components/ServiceScene/ActionBarScene.tsx index bdc219f10..6ef7d28ec 100644 --- a/src/Components/ServiceScene/ActionBarScene.tsx +++ b/src/Components/ServiceScene/ActionBarScene.tsx @@ -34,6 +34,7 @@ export class ActionBarScene extends SceneObjectBase { const serviceScene = sceneGraph.getAncestor(model, ServiceScene); const { loading, $data, ...state } = serviceScene.useState(); + const loadingStates = state.loadingStates; return ( @@ -51,8 +52,8 @@ export class ActionBarScene extends SceneObjectBase { key={index} label={tab.displayName} active={currentBreakdownViewSlug === tab.value} - counter={loading ? undefined : getCounter(tab, { ...state, $data })} - icon={loading ? 'spinner' : undefined} + counter={loadingStates[tab.displayName] ? undefined : getCounter(tab, { ...state, $data })} + icon={loadingStates[tab.displayName] ? 'spinner' : undefined} onChangeTab={() => { if ((tab.value && tab.value !== currentBreakdownViewSlug) || allowNavToParent) { reportAppInteraction( diff --git a/src/Components/ServiceScene/BreakdownViews.ts b/src/Components/ServiceScene/BreakdownViews.ts index c9f4d1808..9d94b90e8 100644 --- a/src/Components/ServiceScene/BreakdownViews.ts +++ b/src/Components/ServiceScene/BreakdownViews.ts @@ -14,8 +14,14 @@ interface ValueBreakdownViewDefinition { getScene: (value: string) => SceneObject; } +export enum TabNames { + logs = 'Logs', + labels = 'Labels', + fields = 'Fields', + patterns = 'Patterns', +} export interface BreakdownViewDefinition { - displayName: string; + displayName: TabNames; value: PageSlugs; testId: string; getScene: (changeFields: (f: string[]) => void) => SceneObject; @@ -23,25 +29,25 @@ export interface BreakdownViewDefinition { export const breakdownViewsDefinitions: BreakdownViewDefinition[] = [ { - displayName: 'Logs', + displayName: TabNames.logs, value: PageSlugs.logs, getScene: () => buildLogsListScene(), testId: testIds.exploreServiceDetails.tabLogs, }, { - displayName: 'Labels', + displayName: TabNames.labels, value: PageSlugs.labels, getScene: () => buildLabelBreakdownActionScene(), testId: testIds.exploreServiceDetails.tabLabels, }, { - displayName: 'Fields', + displayName: TabNames.fields, value: PageSlugs.fields, getScene: (f) => buildFieldsBreakdownActionScene(f), testId: testIds.exploreServiceDetails.tabFields, }, { - displayName: 'Patterns', + displayName: TabNames.patterns, value: PageSlugs.patterns, getScene: () => buildPatternsScene(), testId: testIds.exploreServiceDetails.tabPatterns, diff --git a/src/Components/ServiceScene/ServiceScene.tsx b/src/Components/ServiceScene/ServiceScene.tsx index 4673bf6ce..6ace0fdbe 100644 --- a/src/Components/ServiceScene/ServiceScene.tsx +++ b/src/Components/ServiceScene/ServiceScene.tsx @@ -5,6 +5,7 @@ import { QueryRunnerState, SceneComponentProps, SceneDataProvider, + SceneDataState, SceneFlexItem, SceneFlexLayout, sceneGraph, @@ -37,7 +38,7 @@ import { getMetadataService } from '../../services/metadata'; import { navigateToIndex } from '../../services/navigate'; import { areArraysEqual } from '../../services/comparison'; import { ActionBarScene } from './ActionBarScene'; -import { breakdownViewsDefinitions, valueBreakdownViews } from './BreakdownViews'; +import { breakdownViewsDefinitions, TabNames, valueBreakdownViews } from './BreakdownViews'; const LOGS_PANEL_QUERY_REFID = 'logsPanelQuery'; const PATTERNS_QUERY_REFID = 'patterns'; @@ -47,9 +48,7 @@ const DETECTED_FIELDS_QUERY_REFID = 'detectedFields'; type MakeOptional = Pick, K> & Omit; type ServiceSceneLoadingStates = { - patterns: boolean; - labels: boolean; - fields: boolean; + [name in TabNames]: boolean; }; export interface ServiceSceneCustomState { @@ -106,7 +105,12 @@ export class ServiceScene extends SceneObjectBase { > ) { super({ - loadingStates: { patterns: false, labels: false, fields: false }, + loadingStates: { + [TabNames.patterns]: false, + [TabNames.labels]: false, + [TabNames.fields]: false, + [TabNames.logs]: false, + }, loading: true, body: state.body ?? buildGraphScene(), $data: getServiceSceneQueryRunner(), @@ -212,6 +216,7 @@ export class ServiceScene extends SceneObjectBase { this._subs.add(this.subscribeToPatternsQuery()); this._subs.add(this.subscribeToDetectedLabelsQuery()); this._subs.add(this.subscribeToDetectedFieldsQuery()); + this._subs.add(this.subscribeToLogsQuery()); // Variable subscriptions this._subs.add(this.subscribeToLabelsVariable()); @@ -272,7 +277,7 @@ export class ServiceScene extends SceneObjectBase { private subscribeToPatternsQuery() { return this.state.$patternsData?.subscribeToState((newState) => { - this.updateLoadingState(newState, 'patterns'); + this.updateLoadingState(newState, TabNames.patterns); if (newState.data?.state === LoadingState.Done) { const patternsResponse = newState.data.series; if (patternsResponse?.length !== undefined) { @@ -288,7 +293,7 @@ export class ServiceScene extends SceneObjectBase { private subscribeToDetectedLabelsQuery() { return this.state.$detectedLabelsData?.subscribeToState((newState) => { - this.updateLoadingState(newState, 'labels'); + this.updateLoadingState(newState, TabNames.labels); if (newState.data?.state === LoadingState.Done) { const detectedLabelsResponse = newState.data; // Detected labels API call always returns a single frame, with a field for each label @@ -303,7 +308,7 @@ export class ServiceScene extends SceneObjectBase { }); } - private updateLoadingState(newState: QueryRunnerState, key: keyof ServiceSceneLoadingStates) { + private updateLoadingState(newState: SceneDataState, key: keyof ServiceSceneLoadingStates) { const loadingStates = this.state.loadingStates; loadingStates[key] = newState.data?.state === LoadingState.Loading; // set loading state to true if any of the queries are loading @@ -311,9 +316,15 @@ export class ServiceScene extends SceneObjectBase { this.setState({ loading, loadingStates }); } + private subscribeToLogsQuery() { + return this.state.$data.subscribeToState((newState) => { + this.updateLoadingState(newState, TabNames.logs); + }); + } + private subscribeToDetectedFieldsQuery() { return this.state.$detectedFieldsData?.subscribeToState((newState) => { - this.updateLoadingState(newState, 'fields'); + this.updateLoadingState(newState, TabNames.fields); if (newState.data?.state === LoadingState.Done) { const detectedFieldsResponse = newState.data; const detectedFieldsFields = detectedFieldsResponse.series[0]; diff --git a/src/services/datasource.ts b/src/services/datasource.ts index 3f5ece974..15afa98a3 100644 --- a/src/services/datasource.ts +++ b/src/services/datasource.ts @@ -151,6 +151,7 @@ export class WrappedLokiDatasource extends RuntimeDataSource { throw new Error('Patterns query can only have a single target!'); } const { interpolatedTarget, expression } = this.interpolate(ds, targets, request); + subscriber.next({ data: [], state: LoadingState.Loading }); try { const dsResponse = ds.getResource( @@ -259,6 +260,8 @@ export class WrappedLokiDatasource extends RuntimeDataSource { const { interpolatedTarget, expression } = this.interpolate(ds, targets, request); + subscriber.next({ data: [], state: LoadingState.Loading }); + try { const response = await ds.getResource( 'detected_labels', @@ -311,6 +314,8 @@ export class WrappedLokiDatasource extends RuntimeDataSource { throw new Error('Detected fields query can only have a single target!'); } + subscriber.next({ data: [], state: LoadingState.Loading }); + const { interpolatedTarget, expression } = this.interpolate(ds, targets, request); try { @@ -373,6 +378,7 @@ export class WrappedLokiDatasource extends RuntimeDataSource { const targetsInterpolated = ds.interpolateVariablesInQueries(request.targets, request.scopedVars); const expression = targetsInterpolated[0].expr.replace('.*.*', '.+'); + subscriber.next({ data: [], state: LoadingState.Loading }); try { const volumeResponse: IndexVolumeResponse = await ds.getResource(