Implement feature flag for workflow evaluation experiment

- Add feature flag check
- Update UI to show/hide evaluation tab
- Fetch tags for test definitions
- Handle routing when feature is disabled
- Cleanup console logs
This commit is contained in:
Oleg Ivaniv 2024-11-12 16:42:21 +01:00
parent 7adfbd236c
commit 44b8fab493
No known key found for this signature in database
4 changed files with 53 additions and 30 deletions

View file

@ -9,6 +9,7 @@ import {
PLACEHOLDER_EMPTY_WORKFLOW_ID,
STICKY_NODE_TYPE,
VIEWS,
WORKFLOW_EVALUATION_EXPERIMENT,
} from '@/constants';
import { useI18n } from '@/composables/useI18n';
import { useNDVStore } from '@/stores/ndv.store';
@ -17,6 +18,7 @@ import { useUIStore } from '@/stores/ui.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useExecutionsStore } from '@/stores/executions.store';
import { usePushConnection } from '@/composables/usePushConnection';
import { usePostHog } from '@/stores/posthog.store';
const router = useRouter();
const route = useRoute();
@ -27,17 +29,24 @@ const uiStore = useUIStore();
const sourceControlStore = useSourceControlStore();
const workflowsStore = useWorkflowsStore();
const executionsStore = useExecutionsStore();
const posthogStore = usePostHog();
const activeHeaderTab = ref(MAIN_HEADER_TABS.WORKFLOW);
const workflowToReturnTo = ref('');
const executionToReturnTo = ref('');
const dirtyState = ref(false);
const tabBarItems = computed(() => [
const tabBarItems = computed(() => {
const items = [
{ value: MAIN_HEADER_TABS.WORKFLOW, label: locale.baseText('generic.editor') },
{ value: MAIN_HEADER_TABS.EXECUTIONS, label: locale.baseText('generic.executions') },
{ value: MAIN_HEADER_TABS.EVALUATION, label: locale.baseText('generic.tests') },
]);
];
if (posthogStore.isFeatureEnabled(WORKFLOW_EVALUATION_EXPERIMENT)) {
items.push({ value: MAIN_HEADER_TABS.EVALUATION, label: locale.baseText('generic.tests') });
}
return items;
});
const activeNode = computed(() => ndvStore.activeNode);
const hideMenuBar = computed(() =>
@ -68,7 +77,6 @@ onMounted(async () => {
});
function syncTabsWithRoute(to: RouteLocation, from?: RouteLocation): void {
console.log('🚀 ~ syncTabsWithRoute ~ to.name:', to.name);
if (to.name === VIEWS.WORKFLOW_EVALUATION) {
activeHeaderTab.value = MAIN_HEADER_TABS.EVALUATION;
}

View file

@ -702,6 +702,8 @@ export const EXPERIMENTS_TO_TRACK = [
CREDENTIAL_DOCS_EXPERIMENT.name,
];
export const WORKFLOW_EVALUATION_EXPERIMENT = '025_workflow_evaluation';
export const MFA_FORM = {
MFA_TOKEN: 'MFA_TOKEN',
MFA_RECOVERY_CODE: 'MFA_RECOVERY_CODE',

View file

@ -3,6 +3,8 @@ import { computed, ref } from 'vue';
import { useRootStore } from './root.store';
import { createTestDefinitionsApi } from '@/api/evaluations.ee';
import type { ITestDefinition } from '@/api/evaluations.ee';
import { usePostHog } from './posthog.store';
import { WORKFLOW_EVALUATION_EXPERIMENT } from '@/constants';
export const useEvaluationsStore = defineStore(
'evaluations',
@ -13,6 +15,7 @@ export const useEvaluationsStore = defineStore(
const fetchedAll = ref(false);
// Store instances
const posthogStore = usePostHog();
const rootStore = useRootStore();
const testDefinitionsApi = createTestDefinitionsApi();
@ -21,13 +24,17 @@ export const useEvaluationsStore = defineStore(
return Object.values(testDefinitionsById.value).sort((a, b) => a.name.localeCompare(b.name));
});
// Enable with `window.featureFlags.override('025_workflow_evaluation', true)`
const isFeatureEnabled = computed(() =>
posthogStore.isFeatureEnabled(WORKFLOW_EVALUATION_EXPERIMENT),
);
const isLoading = computed(() => loading.value);
const hasTestDefinitions = computed(() => Object.keys(testDefinitionsById.value).length > 0);
// Methods
const setAllTestDefinitions = (definitions: ITestDefinition[]) => {
console.log('🚀 ~ setAllTestDefinitions ~ definitions:', definitions);
testDefinitionsById.value = definitions.reduce(
(acc: Record<number, ITestDefinition>, def: ITestDefinition) => {
acc[def.id] = def;
@ -76,7 +83,7 @@ export const useEvaluationsStore = defineStore(
rootStore.restApiContext,
{ includeScopes },
);
console.log('🚀 ~ fetchAll ~ retrievedDefinitions:', retrievedDefinitions);
setAllTestDefinitions(retrievedDefinitions.testDefinitions);
return retrievedDefinitions;
} finally {
@ -131,6 +138,7 @@ export const useEvaluationsStore = defineStore(
allTestDefinitions,
isLoading,
hasTestDefinitions,
isFeatureEnabled,
// Methods
fetchAll,

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed } from 'vue';
import { ref, computed, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { VIEWS } from '@/constants';
import { useEvaluationsStore } from '@/stores/evaluations.store.ee';
@ -8,8 +8,10 @@ import { useI18n } from '@/composables/useI18n';
import EmptyState from '@/components/WorkflowEvaluation/ListEvaluation/EmptyState.vue';
import TestsList from '@/components/WorkflowEvaluation/ListEvaluation/TestsList.vue';
import type { TestExecution, TestListItem } from '@/components/WorkflowEvaluation/types';
import { useAnnotationTagsStore } from '@/stores/tags.store';
const router = useRouter();
const tagsStore = useAnnotationTagsStore();
const evaluationsStore = useEvaluationsStore();
const isLoading = ref(false);
const toast = useToast();
@ -20,25 +22,21 @@ const tests = computed<TestListItem[]>(() => {
id: test.id,
name: test.name,
tagName: test.annotationTagId ? getTagName(test.annotationTagId) : '',
testCases: 0, // This should come from the API
testCases: 0, // TODO: This should come from the API
execution: getTestExecution(test.id),
}));
});
const hasTests = computed(() => tests.value.length > 0);
const allTags = computed(() => tagsStore.allTags);
// Mock function to get tag name - replace with actual tag lookup
function getTagName(tagId: string) {
const tags = {
tag1: 'marketing',
tag2: 'SupportOps',
};
return tags[tagId] || '';
const matchingTag = allTags.value.find((t) => t.id === tagId);
return matchingTag?.name ?? '';
}
// Mock function to get test execution data - replace with actual API call
function getTestExecution(testId: number): TestExecution {
console.log('🚀 ~ getTestExecution ~ testId:', testId);
// Mock data - replace with actual data from your API
// TODO: Replace with actual API call once implemented
function getTestExecution(_testId: number): TestExecution {
const mockExecutions = {
12: {
lastRun: 'an hour ago',
@ -63,24 +61,18 @@ function onCreateTest() {
function onRunTest(testId: number) {
console.log('Running test:', testId);
// Implement test run logic
// TODO: Implement test run logic
}
function onViewDetails(testId: number) {
console.log('Viewing details for test:', testId);
void router.push({ name: VIEWS.WORKFLOW_EVALUATION_EDIT, params: { testId } });
// Implement navigation to test details
}
function onEditTest(testId: number) {
console.log('Editing test:', testId);
void router.push({ name: VIEWS.WORKFLOW_EVALUATION_EDIT, params: { testId } });
// Implement edit navigation
}
async function onDeleteTest(testId: number) {
console.log('Deleting test:', testId);
// Implement delete logic
await evaluationsStore.deleteById(testId);
toast.showMessage({
@ -90,17 +82,30 @@ async function onDeleteTest(testId: number) {
}
// Load initial data
async function loadTests() {
async function loadInitialData() {
isLoading.value = true;
try {
await tagsStore.fetchAll();
await evaluationsStore.fetchAll();
} finally {
isLoading.value = false;
}
}
// Load tests on mount
void loadTests();
onMounted(() => {
if (!evaluationsStore.isFeatureEnabled) {
toast.showMessage({
// message: "Feature not enabled",
title: 'Feature not enabled',
type: 'error',
});
void router.push({
name: VIEWS.WORKFLOW,
params: { name: router.currentRoute.value.params.name },
});
}
void loadInitialData();
});
</script>
<template>