diff --git a/packages/analytics/analytics-config-store/src/stores/analytics-config.ts b/packages/analytics/analytics-config-store/src/stores/analytics-config.ts index f153f01d5d..f0bc5c6234 100644 --- a/packages/analytics/analytics-config-store/src/stores/analytics-config.ts +++ b/packages/analytics/analytics-config-store/src/stores/analytics-config.ts @@ -9,7 +9,6 @@ export type ConfigStoreState = null | AnalyticsConfigV2 export const useAnalyticsConfigStore = defineStore('analytics-config', () => { const analyticsConfig = ref(null) - const skuFeatureFlag = ref(false) const queryBridge: AnalyticsBridge | undefined = inject(INJECT_QUERY_PROVIDER) @@ -31,8 +30,6 @@ export const useAnalyticsConfigStore = defineStore('analytics-config', () => { console.warn('Error fetching analytics config') console.warn(err) }) - - skuFeatureFlag.value = queryBridge.evaluateFeatureFlagFn('MA-2527-analytics-sku-config-endpoint', false) } const longRetention = computed(() => { @@ -40,12 +37,8 @@ export const useAnalyticsConfigStore = defineStore('analytics-config', () => { return !!retentionMs && retentionMs >= THIRTY_DAYS_MS }) - const defaultQueryTimeForOrg = computed<'24h' | '7d' | '30d'>(() => { - if (skuFeatureFlag.value) { - return '7d' - } - - return longRetention.value ? '30d' : '24h' + const defaultQueryTimeForOrg = computed<'7d'>(() => { + return '7d' }) const loading = computed(() => !analyticsConfig.value) diff --git a/packages/analytics/analytics-metric-provider/src/components/MetricsProvider.cy.ts b/packages/analytics/analytics-metric-provider/src/components/MetricsProvider.cy.ts index 7ce2dab3d1..1aa80d93cf 100644 --- a/packages/analytics/analytics-metric-provider/src/components/MetricsProvider.cy.ts +++ b/packages/analytics/analytics-metric-provider/src/components/MetricsProvider.cy.ts @@ -13,11 +13,6 @@ import { mockExploreResponse } from '../mockExploreResponse' import { INJECT_QUERY_PROVIDER } from '../constants' import { createPinia, setActivePinia } from 'pinia' -interface MakeQueryBridgeOptions extends MockOptions { - hasTrendAccess?: boolean - skuFeatureFlag?: boolean -} - describe('', () => { // General note when working on these tests: SWRV tends to cache the results of queries between tests. @@ -29,7 +24,7 @@ describe('', () => { setActivePinia(createPinia()) }) - const makeQueryBridge = (opts?: MakeQueryBridgeOptions): AnalyticsBridge => { + const makeQueryBridge = (opts?: MockOptions): AnalyticsBridge => { const queryFn = (dsAwareQuery: DatasourceAwareQuery): Promise => { const { query } = dsAwareQuery as AdvancedDatasourceQuery @@ -62,19 +57,17 @@ describe('', () => { return Promise.resolve(result) } - const hasTrendAccess = opts?.hasTrendAccess ?? true - const configFn = (): Promise => Promise.resolve({ analytics: { percentiles: true, - retention_ms: hasTrendAccess ? 2592000000 : 86400000, // 30d | 1d + retention_ms: 2592000000, // 30d }, requests: { retention_ms: 86400000, }, }) - const evaluateFeatureFlagFn: AnalyticsBridge['evaluateFeatureFlagFn'] = () => (opts?.skuFeatureFlag ?? true) as any + const evaluateFeatureFlagFn: AnalyticsBridge['evaluateFeatureFlagFn'] = () => (true) as any return { queryFn: cy.spy(queryFn).as('fetcher'), @@ -168,36 +161,6 @@ describe('', () => { cy.get('.metricscard-title').eq(2).should('have.text', 'Average Latency') }) - it('renders percentiles if the feature flag is not set', () => { - const queryBridge = makeQueryBridge({ skuFeatureFlag: false }) - - cy.mount(MetricsTestHarness, { - props: { - render: 'global', - longCardTitles: true, - additionalFilter: [{ type: 'in', dimension: 'api_product', values: ['renders percentiles if the feature flag is not set'] } as ExploreFilter], - }, - global: { - provide: { - [INJECT_QUERY_PROVIDER]: queryBridge, - }, - }, - }) - - cy.get('@fetcher').should('have.been.calledTwice') - - cy.get('@fetcher').should('have.been.calledWithMatch', Cypress.sinon.match({ - datasource: 'advanced', - query: { metrics: ['response_latency_p99'] }, - })) - cy.get('@fetcher').should('always.have.not.been.calledWithMatch', Cypress.sinon.match({ query: { metrics: ['response_latency_average'] } })) - - cy.get('.metricscard').should('exist') - cy.get('.metricscard-title').eq(0).should('have.text', 'Number of Requests') - cy.get('.metricscard-title').eq(1).should('have.text', 'Average Error Rate') - cy.get('.metricscard-title').eq(2).should('have.text', 'P99 Latency') - }) - it('renders percentiles if the override is set', () => { const queryBridge = makeQueryBridge() @@ -246,46 +209,8 @@ describe('', () => { cy.get('.container-description').eq(0).should('contain', 'Lorem ipsum golden signal details') }) - it('displays "30 days" if trend access allows', () => { - const queryBridge = makeQueryBridge({ skuFeatureFlag: false }) - - cy.mount(MetricsTestHarness, { - props: { - render: 'global', - description: 'Lorem ipsum golden signal details', - }, - global: { - provide: { - [INJECT_QUERY_PROVIDER]: queryBridge, - }, - }, - }) - - cy.get('.metricscard').should('exist') - cy.get('.metricscard').eq(0).find('.metricscard-trend-range').should('contain', 'vs previous 30 days') - }) - - it('displays "24 hours" if trend access allows', () => { - const queryBridge = makeQueryBridge({ hasTrendAccess: false, skuFeatureFlag: false }) - - cy.mount(MetricsTestHarness, { - props: { - render: 'global', - description: 'Lorem ipsum golden signal details', - }, - global: { - provide: { - [INJECT_QUERY_PROVIDER]: queryBridge, - }, - }, - }) - - cy.get('.metricscard').should('exist') - cy.get('.metricscard').eq(0).find('.metricscard-trend-range').should('contain', 'vs previous 24 hours') - }) - - it('displays "7 days" if the feature flag is set', () => { - const queryBridge = makeQueryBridge({ hasTrendAccess: false, skuFeatureFlag: true }) + it('displays "7 days" by default', () => { + const queryBridge = makeQueryBridge() cy.mount(MetricsTestHarness, { props: { @@ -303,31 +228,6 @@ describe('', () => { cy.get('.metricscard').eq(0).find('.metricscard-trend-range').should('contain', 'vs previous 7 days') }) - it('handles no trend', () => { - const queryBridge = makeQueryBridge({ hasTrendAccess: false, skuFeatureFlag: false }) - - cy.mount(MetricsTestHarness, { - props: { - render: 'global', - }, - global: { - provide: { - [INJECT_QUERY_PROVIDER]: queryBridge, - }, - }, - }) - - cy.get('.metricscard').should('exist') - - cy.getTestId('metric-value').eq(0).should('have.text', '5K') - cy.getTestId('metric-value').eq(1).should('have.text', '40.00%') - cy.getTestId('metric-value').eq(2).should('have.text', '1001ms') - - cy.get('.metricscard-trend-change > div').eq(0).should('have.text', 'N/A') - cy.get('.metricscard-trend-change > div').eq(1).should('have.text', 'N/A') - cy.get('.metricscard-trend-change > div').eq(2).should('have.text', 'N/A') - }) - it('handles queryReady', () => { const queryBridge = makeQueryBridge({ hasTrendAccess: false }) @@ -454,37 +354,6 @@ describe('', () => { cy.get('.metricscard-trend-change > div').eq(2).should('have.text', '49.98%') }) - it('displays single-entity metrics with no trend', () => { - const queryBridge = makeQueryBridge({ - dimensionNames: ['blah'], - hasTrendAccess: false, - skuFeatureFlag: false, - }) - - cy.mount(MetricsTestHarness, { - props: { - render: 'single', - }, - global: { - provide: { - [INJECT_QUERY_PROVIDER]: queryBridge, - }, - }, - }) - - cy.get('@fetcher').should('have.been.calledTwice') - - cy.get('.metricscard').should('exist') - - cy.getTestId('metric-value').eq(0).should('have.text', '5K') - cy.getTestId('metric-value').eq(1).should('have.text', '40.00%') - cy.getTestId('metric-value').eq(2).should('have.text', '1001ms') - - cy.get('.metricscard-trend-change > div').eq(0).should('have.text', 'N/A') - cy.get('.metricscard-trend-change > div').eq(1).should('have.text', 'N/A') - cy.get('.metricscard-trend-change > div').eq(2).should('have.text', 'N/A') - }) - it('displays multi-entity metrics', () => { const queryBridge = makeQueryBridge({ dimensionNames: ['blah', 'arrgh'] }) diff --git a/packages/analytics/analytics-metric-provider/src/components/MetricsProvider.vue b/packages/analytics/analytics-metric-provider/src/components/MetricsProvider.vue index a9acc6dc0e..d023642f53 100644 --- a/packages/analytics/analytics-metric-provider/src/components/MetricsProvider.vue +++ b/packages/analytics/analytics-metric-provider/src/components/MetricsProvider.vue @@ -59,24 +59,20 @@ if (props.dimension && queryableExploreDimensions.findIndex(x => x === props.dim const queryBridge: AnalyticsBridge | undefined = inject(INJECT_QUERY_PROVIDER) let queryFn: AnalyticsBridge['queryFn'] -let evaluateFeatureFlagFn: AnalyticsBridge['evaluateFeatureFlagFn'] if (!queryBridge) { console.warn('Analytics dashboards require a query bridge supplied via provide / inject.') console.warn("Please ensure your application has a query bridge provided under the key 'analytics-query-provider', as described in") console.warn('https://github.com/Kong/public-ui-components/blob/main/packages/analytics/analytics-metric-provider/README.md#requirements') queryFn = () => Promise.reject(new Error('Query bridge required')) - evaluateFeatureFlagFn = (_key, defaultValue) => defaultValue } else { queryFn = queryBridge.queryFn - evaluateFeatureFlagFn = queryBridge.evaluateFeatureFlagFn } const analyticsConfigStore = useAnalyticsConfigStore() // Check if the current org has long enough retention to make a sane trend query. -// If the feature flag is set, trend access is always true. -const hasTrendAccess = computed(() => skuFeatureFlag.value || analyticsConfigStore.longRetention) +const hasTrendAccess = computed(() => true) // Don't attempt to issue a query until we know what we can query for. const queryReady = computed(() => !analyticsConfigStore.loading && props.queryReady) @@ -89,43 +85,21 @@ const tz = computed(() => { return (new Intl.DateTimeFormat()).resolvedOptions().timeZone }) -const skuFeatureFlag = computed(() => { - // The feature flag client is guaranteed to be initialized by the time the code gets to this place. - return evaluateFeatureFlagFn('MA-2527-analytics-sku-config-endpoint', false) -}) const resolvedDatasource = computed(() => { if (props.datasource) { return props.datasource } - return skuFeatureFlag.value ? 'basic' : 'advanced' + return 'basic' }) // Note: the component implicitly assumes the values it feeds to the composables aren't going to change. // If they do need to change, then the `useMetricCardGoldenSignals` composable, and dependencies, // needs to be reactive as well. Ideally, this would be the case; we don't have any guarantee that the // tier data has finished loading by the time the component mounts, for example. - const timeframe = computed(() => { - if (props.overrideTimeframe) { - // Trust that the host component calculated the timeframe for us. - return props.overrideTimeframe - } - - if (skuFeatureFlag.value) { - return TimePeriods.get(TimeframeKeys.SEVEN_DAY)! - } - - const retval = hasTrendAccess.value - ? TimePeriods.get(props.maxTimeframe) - : TimePeriods.get(TimeframeKeys.ONE_DAY) - - if (!retval) { - throw new Error('Metrics provider failed to resolve fallback timeframe.') - } - - return retval + return props.overrideTimeframe || TimePeriods.get(TimeframeKeys.SEVEN_DAY)! }) const averageLatencies = computed(() => { @@ -134,7 +108,7 @@ const averageLatencies = computed(() => { return false } - return skuFeatureFlag.value + return true }) const { diff --git a/packages/analytics/dashboard-renderer/src/components/DashboardRenderer.cy.ts b/packages/analytics/dashboard-renderer/src/components/DashboardRenderer.cy.ts index c9aa4d69e1..252c4a961e 100644 --- a/packages/analytics/dashboard-renderer/src/components/DashboardRenderer.cy.ts +++ b/packages/analytics/dashboard-renderer/src/components/DashboardRenderer.cy.ts @@ -30,7 +30,6 @@ import { createPinia, setActivePinia } from 'pinia' interface MockOptions { failToResolveConfig?: boolean shortRetention?: boolean - skuFeatureFlag?: boolean } describe('', () => { @@ -99,16 +98,13 @@ describe('', () => { }, } - console.log('Resolving config') + console.log('> Resolving config: ', config) + return Promise.resolve(config) } // @ts-ignore: TS doesn't infer things correctly. NoInfer may help. const evaluateFeatureFlagFn: AnalyticsBridge['evaluateFeatureFlagFn'] = (key) => { - if (key === 'MA-2527-analytics-sku-config-endpoint') { - return opts?.skuFeatureFlag ?? true - } - return true } @@ -436,42 +432,18 @@ describe('', () => { }) }) - it('picks a default timeSpec', () => { + it('picks a lower retention timeSpec if provided', () => { const props = { context: { // Use default timeframe for the org: don't provide one here. - filters: [], - }, - config: summaryDashboardConfig, - } - - cy.mount(DashboardRenderer, { - props, - global: { - provide: { - [INJECT_QUERY_PROVIDER]: mockQueryProvider({ skuFeatureFlag: false }), - }, - }, - }).then(() => { - // Extra calls may mean we mistakenly issued queries before knowing the timeSpec. - cy.get('@fetcher').should('have.callCount', 5) - cy.get('@fetcher').should('always.have.been.calledWithMatch', Cypress.sinon.match({ - datasource: 'advanced', - query: { - time_range: { time_range: '30d' }, - }, - })) - - // Check that it replaces the description token. - cy.get('.container-description').should('have.text', 'Last 30-Day Summary') - }) - }) - - it('picks a lower retention timeSpec', () => { - const props = { - context: { - // Use default timeframe for the org: don't provide one here. - filters: [], + filters: [ + { + dimension: 'api_product', + type: 'in', + values: ['lower retention'], + }, + ], + timeSpec: ((TimePeriods.get(TimeframeKeys.ONE_DAY)) as Timeframe).v4Query(), }, config: summaryDashboardConfig, } @@ -480,7 +452,7 @@ describe('', () => { props, global: { provide: { - [INJECT_QUERY_PROVIDER]: mockQueryProvider({ shortRetention: true, skuFeatureFlag: false }), + [INJECT_QUERY_PROVIDER]: mockQueryProvider({ shortRetention: true }), }, }, }).then(() => { @@ -495,11 +467,17 @@ describe('', () => { }) }) - it('picks 7 days and basic datasource if FF is enabled', () => { + it('picks 7 days and basic datasource by default', () => { const props = { context: { // Use default timeframe for the org: don't provide one here. - filters: [], + filters: [ + { + dimension: 'api_product', + type: 'in', + values: ['basic'], + }, + ], }, config: summaryDashboardConfig, } @@ -508,7 +486,7 @@ describe('', () => { props, global: { provide: { - [INJECT_QUERY_PROVIDER]: mockQueryProvider({ skuFeatureFlag: true }), + [INJECT_QUERY_PROVIDER]: mockQueryProvider(), }, }, }).then(() => { @@ -546,7 +524,7 @@ describe('', () => { props, global: { provide: { - [INJECT_QUERY_PROVIDER]: mockQueryProvider({ skuFeatureFlag: true }), + [INJECT_QUERY_PROVIDER]: mockQueryProvider(), }, }, }).then(() => { @@ -585,7 +563,7 @@ describe('', () => { props, global: { provide: { - [INJECT_QUERY_PROVIDER]: mockQueryProvider({ skuFeatureFlag: true }), + [INJECT_QUERY_PROVIDER]: mockQueryProvider(), }, }, }).then(() => { diff --git a/packages/analytics/dashboard-renderer/src/components/QueryDataProvider.vue b/packages/analytics/dashboard-renderer/src/components/QueryDataProvider.vue index d6ce0d3bbc..14e380e9ce 100644 --- a/packages/analytics/dashboard-renderer/src/components/QueryDataProvider.vue +++ b/packages/analytics/dashboard-renderer/src/components/QueryDataProvider.vue @@ -93,11 +93,8 @@ const { data: v4Data, error, isValidating } = useSWRV(queryKey, async () => { ...rest } = props.query - if (!datasource && queryBridge) { - // TODO(MA-2987): Remove this. - const analyticsSKU = queryBridge.evaluateFeatureFlagFn('MA-2527-analytics-sku-config-endpoint', false) - - datasource = analyticsSKU ? 'basic' : 'advanced' + if (!datasource) { + datasource = 'basic' } const mergedFilters = deriveFilters(datasource, props.query.filters, props.context.filters)