Skip to content
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

feat: improve unified-cost feature #5038

Merged
merged 21 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import RegionVariableModel from '@/lib/variable-models/managed-model/resource-mo
import RoleVariableModel from '@/lib/variable-models/managed-model/resource-model/role-variable-model';
import SecretVariableModel from '@/lib/variable-models/managed-model/resource-model/secret-variable-model';
import ServiceAccountVariableModel from '@/lib/variable-models/managed-model/resource-model/service-account-variable-model';
import UnifiedCostVariableModel from '@/lib/variable-models/managed-model/resource-model/unified-cost-variable-model';
import UserVariableModel from '@/lib/variable-models/managed-model/resource-model/user-variable-model';
import WebhookVariableModel from '@/lib/variable-models/managed-model/resource-model/webhook-variable-model';
import WorkspaceGroupVariableModel from '@/lib/variable-models/managed-model/resource-model/workspace-group-variable-model';
Expand Down Expand Up @@ -50,6 +51,7 @@ const RESOURCE_VARIABLE_MODELS = {
workspace: WorkspaceVariableModel,
workspace_group: WorkspaceGroupVariableModel,
cost: CostVariableModel,
unified_cost: UnifiedCostVariableModel,
metric_data: MetricDataVariableModel,
role: RoleVariableModel,
} as const;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { UnifiedCostModel } from '@/schema/cost-analysis/unified-cost/model';

import ResourceVariableModel from '@/lib/variable-models/_base/resource-variable-model';
import type { VariableModelConstructorConfig } from '@/lib/variable-models/_base/types';


export default class UnifiedCostVariableModel extends ResourceVariableModel<UnifiedCostModel> {
unified_cost_id = this.generateProperty({ key: 'unified_cost_id', name: 'Unified Cost ID' });

product = this.generateProperty({ key: 'product', name: 'Product' });

usage_type = this.generateProperty({ key: 'usage_type', name: 'Usage Type (Unified Cost)' });

provider = this.generateProperty({ key: 'provider', name: 'Provider' });

region = this.generateProperty({ key: 'region_code', name: 'Region' });

static meta = {
key: 'unified_cost',
name: 'Unified Cost',
resourceType: 'cost_analysis.UnifiedCost',
idKey: 'unified_cost_id',
nameKey: 'name',
};

constructor(config: VariableModelConstructorConfig = {}) {
super(config);
this._meta = UnifiedCostVariableModel.meta;
}
}
14 changes: 10 additions & 4 deletions apps/web/src/services/cost-explorer/CostExplorerContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import { SpaceConnector } from '@cloudforet/core-lib/space-connector';
import { getCancellableFetcher } from '@cloudforet/core-lib/space-connector/cancellable-fetcher';


import { useAppContextStore } from '@/store/app-context/app-context-store';

import ErrorHandler from '@/common/composables/error/errorHandler';
import { useGrantScopeGuard } from '@/common/composables/grant-scope-guard';
import CenteredPageLayout from '@/common/modules/page-layouts/CenteredPageLayout.vue';
import GeneralPageLayout from '@/common/modules/page-layouts/GeneralPageLayout.vue';
import VerticalPageLayout from '@/common/modules/page-layouts/VerticalPageLayout.vue';

import { UNIFIED_COST_KEY } from '@/services/cost-explorer/constants/cost-explorer-constant';
import CostExplorerLSB from '@/services/cost-explorer/CostExplorerLSB.vue';
import { useCostExplorerSettingsStore } from '@/services/cost-explorer/stores/cost-explorer-settings-store';
import { useCostQuerySetStore } from '@/services/cost-explorer/stores/cost-query-set-store';
Expand All @@ -22,6 +25,7 @@ import { useCostQuerySetStore } from '@/services/cost-explorer/stores/cost-query
const costQuerySetStore = useCostQuerySetStore();
const costQuerySetState = costQuerySetStore.state;
const costExplorerSettingsStore = useCostExplorerSettingsStore();
const appContextStore = useAppContextStore();

const route = useRoute();
const setCostParams = async () => {
Expand All @@ -32,8 +36,10 @@ const setCostParams = async () => {
const { status, response } = await fetcher({
query: {
only: ['data_source_id'],
sort: [{ key: 'workspace_id', desc: appContextStore.getters.isAdminMode }],
},
});

if (status === 'succeed') {
const dataSourceId = response.results[0].data_source_id;
costQuerySetStore.setSelectedDataSourceId(dataSourceId);
Expand All @@ -42,13 +48,13 @@ const setCostParams = async () => {
ErrorHandler.handleError(e);
}
}

const { dataSourceId, costQuerySetId } = route.params;
/*
* Both parameters are set in the route. (beforeEnter navigation guard in routes.ts)
* */
if (route.params.dataSourceId && route.params.costQuerySetId) {
costQuerySetStore.setSelectedDataSourceId(route.params.dataSourceId);
costQuerySetStore.setSelectedQuerySetId(route.params.costQuerySetId);
if (dataSourceId && costQuerySetId) {
if (dataSourceId !== UNIFIED_COST_KEY) costQuerySetStore.setSelectedDataSourceId(dataSourceId);
costQuerySetStore.setSelectedQuerySetId(costQuerySetId);
}
await costQuerySetStore.listCostQuerySets();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
} from '@/services/cost-explorer/constants/cost-explorer-constant';
import { useCostAnalysisPageStore } from '@/services/cost-explorer/stores/cost-analysis-page-store';
import type { CostAnalyzeRawData } from '@/services/cost-explorer/types/cost-analyze-type';
import type { XYChartData } from '@/services/cost-explorer/types/cost-explorer-chart-type';
import type { CostXYChartData } from '@/services/cost-explorer/types/cost-explorer-chart-type';
import type {
Period,
} from '@/services/cost-explorer/types/cost-explorer-query-type';
Expand All @@ -41,7 +41,7 @@ const state = reactive({
loading: true,
legend: {} as Record<string, boolean>,
data: {} as AnalyzeResponse<CostAnalyzeRawData>,
chartData: [] as XYChartData[],
chartData: [] as CostXYChartData[],
chart: null as XYChart | null,
groupByMenuItems: computed<SelectDropdownMenuItem[]>(() => costAnalysisPageState.groupBy.map((d) => {
if (GROUP_BY_ITEM_MAP[d]) return GROUP_BY_ITEM_MAP[d];
Expand Down Expand Up @@ -138,8 +138,9 @@ watch([
() => costAnalysisPageState,
() => costAnalysisPageGetters.selectedDataSourceId,
() => costAnalysisPageGetters.selectedQueryId,
], ([, selectedDataSourceId]) => {
if (costAnalysisPageState.period && selectedDataSourceId) setChartData(costAnalysisPageState.period);
() => costAnalysisPageGetters.isUnifiedCost,
], ([, selectedDataSourceId, , isUnifiedCost]) => {
if (costAnalysisPageState.period && (selectedDataSourceId || isUnifiedCost)) setChartData(costAnalysisPageState.period);
}, { immediate: true, deep: true });
watch(() => state.groupByMenuItems, (after) => {
if (!after.length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ watch(
() => costAnalysisPageState,
() => costAnalysisPageGetters.selectedDataSourceId,
() => costAnalysisPageGetters.selectedQueryId,
() => costAnalysisPageGetters.isUnifiedCost,
],
async ([, selectedDataSourceId]) => {
if (!selectedDataSourceId) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const storeState = reactive({
const state = reactive({
targetCostDataSource: computed<CostDataSourceItems|undefined>(() => {
if (!costAnalysisPageGetters.selectedDataSourceId) return undefined;
return storeState.costDataSource[costAnalysisPageGetters.selectedDataSourceId];
return costAnalysisPageGetters.isUnifiedCost ? undefined : storeState.costDataSource[costAnalysisPageGetters.selectedDataSourceId];
}),
additionalMenuItems: computed<MenuItem[]>(() => {
if (state.targetCostDataSource) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import CostTagKeyVariableModel from '@/lib/variable-models/managed-model/custom-

import ErrorHandler from '@/common/composables/error/errorHandler';

import { UNIFIED_COST_KEY } from '@/services/cost-explorer/constants/cost-explorer-constant';
import { useCostAnalysisPageStore } from '@/services/cost-explorer/stores/cost-analysis-page-store';


Expand Down Expand Up @@ -42,7 +43,7 @@ const state = reactive({
tagsMenuItems: [] as MenuItem[],
selectedItems: [] as MenuItem[],
searchText: '',
dataSourceId: computed<string>(() => costAnalysisPageGetters.selectedDataSourceId ?? ''),
dataSourceId: computed<string>(() => (costAnalysisPageGetters.isUnifiedCost ? UNIFIED_COST_KEY : (costAnalysisPageGetters.selectedDataSourceId ?? ''))),
});

const containerRef = ref<HTMLElement|null>(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { WorkspaceModel } from '@/schema/identity/workspace/model';
import { useAppContextStore } from '@/store/app-context/app-context-store';
import { useUserWorkspaceStore } from '@/store/app-context/workspace/user-workspace-store';

import getRandomId from '@/lib/random-id-generator';
import { VariableModelFactory } from '@/lib/variable-models';
import type {
ManagedVariableModelKey,
Expand Down Expand Up @@ -62,8 +63,18 @@ const GROUP_BY_TO_VAR_MODELS: Record<string, VariableOption> = {
[GROUP_BY.USAGE_TYPE]: { key: MANAGED_VARIABLE_MODEL_KEY_MAP.cost, dataKey: 'usage_type' },
};

const getInitialSelectedItemsMap = (): Record<string, SelectDropdownMenuItem[]> => ({
});
const GROUP_BY_TO_VAR_MODELS_FOR_UNIFIED_COST: Record<string, VariableOption> = {
[GROUP_BY.WORKSPACE]: { key: MANAGED_VARIABLE_MODEL_KEY_MAP.workspace },
[GROUP_BY.PROJECT]: { key: MANAGED_VARIABLE_MODEL_KEY_MAP.project },
[GROUP_BY.PROJECT_GROUP]: { key: MANAGED_VARIABLE_MODEL_KEY_MAP.project_group },
[GROUP_BY.PRODUCT]: { key: MANAGED_VARIABLE_MODEL_KEY_MAP.unified_cost, dataKey: 'product' },
[GROUP_BY.PROVIDER]: { key: MANAGED_VARIABLE_MODEL_KEY_MAP.provider },
[GROUP_BY.SERVICE_ACCOUNT]: { key: MANAGED_VARIABLE_MODEL_KEY_MAP.service_account },
[GROUP_BY.REGION]: { key: MANAGED_VARIABLE_MODEL_KEY_MAP.unified_cost, dataKey: 'region' },
[GROUP_BY.USAGE_TYPE]: { key: MANAGED_VARIABLE_MODEL_KEY_MAP.unified_cost, dataKey: 'usage_type' },
};

const getInitialSelectedItemsMap = (): Record<string, SelectDropdownMenuItem[]> => ({});

const props = defineProps<{
visible: boolean;
Expand All @@ -74,6 +85,7 @@ const storeState = reactive({
isAdminMode: computed(() => appContextStore.getters.isAdminMode),
});
const state = reactive({
randomId: '',
loading: true,
enabledFilters: computed<SelectDropdownMenuItem[]>(() => {
if (!costAnalysisPageState.enabledFiltersProperties) return [];
Expand All @@ -85,8 +97,9 @@ const state = reactive({
return { name: d, label: d };
});
}),
isUnifiedCost: computed(() => costAnalysisPageGetters.isUnifiedCost),
primaryCostOptions: computed<Record<string, any>>(() => ({
data_source_id: costAnalysisPageGetters.selectedDataSourceId,
...(!state.isUnifiedCost && { data_source_id: costAnalysisPageGetters.selectedDataSourceId }),
})),
selectedItemsMap: {} as Record<string, SelectDropdownMenuItem[]>,
handlerMap: computed(() => {
Expand All @@ -102,7 +115,7 @@ const state = reactive({
const getMenuHandler = (groupBy: string, modelOptions: Record<string, any>, primaryQueryOptions: Record<string, any>): AutocompleteHandler => {
try {
let variableModelInfo: VariableModelMenuHandlerInfo;
const _variableOption = GROUP_BY_TO_VAR_MODELS[groupBy];
const _variableOption = state.isUnifiedCost ? GROUP_BY_TO_VAR_MODELS_FOR_UNIFIED_COST[groupBy] : GROUP_BY_TO_VAR_MODELS[groupBy];
let _queryOptions: Record<string, any> = {};
if (groupBy === MANAGED_VARIABLE_MODELS.workspace.meta.idKey) {
_queryOptions.is_dormant = false;
Expand Down Expand Up @@ -142,16 +155,17 @@ const getMenuHandler = (groupBy: string, modelOptions: Record<string, any>, prim
return async () => ({ results: [] });
}
};
const initSelectedFilters = () => {
const _filters = costAnalysisPageState.filters;
const initSelectedFilters = (isReset = false) => {
const _filters = isReset ? costAnalysisPageGetters.convertedOriginFilter : costAnalysisPageState.filters;
const _selectedItemsMap = {};
Object.keys(_filters ?? {}).forEach((groupBy) => {
if (storeState.isAdminMode && !costAnalysisPageState.isAllWorkspaceSelected && groupBy === GROUP_BY.WORKSPACE) {
_selectedItemsMap[groupBy] = [];
return;
}
_selectedItemsMap[groupBy] = _filters?.[groupBy].map((d) => ({ name: d, label: d })) ?? [];
if (costAnalysisPageState.enabledFiltersProperties?.indexOf(groupBy) === -1) {
const isGroupByExist = costAnalysisPageState.enabledFiltersProperties?.indexOf(groupBy) === -1;
if (isGroupByExist) {
costAnalysisPageStore.setEnabledFiltersProperties([
...(costAnalysisPageState.enabledFiltersProperties ?? []),
groupBy,
Expand Down Expand Up @@ -183,8 +197,7 @@ const handleDisabledFilters = (all?: boolean, disabledFilter?: string) => {
}
};
const handleClickResetFilters = () => {
state.selectedItemsMap = getInitialSelectedItemsMap();

initSelectedFilters(true);
const _originConsoleFilters: ConsoleFilter[]|undefined = costAnalysisPageGetters.selectedQuerySet?.options?.filters;
const _originFilters: Record<string, string[]> = {};
if (_originConsoleFilters?.length) {
Expand All @@ -193,10 +206,10 @@ const handleClickResetFilters = () => {
});
}
costAnalysisPageStore.setFilters(_originFilters);
state.randomId = getRandomId();
};

watch(() => props.visible, (visible) => {
if (!visible) return;
watch([() => costAnalysisPageGetters.selectedQueryId, () => costAnalysisPageGetters.isUnifiedCost, () => costAnalysisPageGetters.selectedDataSourceId], () => {
initSelectedFilters();
}, { immediate: true });

Expand All @@ -206,7 +219,7 @@ watch(() => props.visible, (visible) => {
<div class="cost-analysis-filters-popper">
<p-select-dropdown
v-for="groupBy in state.enabledFilters"
:key="`filters-dropdown-${groupBy.name}`"
:key="`filters-dropdown-${groupBy.name}-${state.randomId}`"
class="filters-popper-dropdown"
is-filterable
:handler="state.handlerMap[groupBy.name]"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const costAnalysisPageGetters = costAnalysisPageStore.getters;
const costAnalysisPageState = costAnalysisPageStore.state;

const state = reactive({
granularityItems: computed<MenuItem[]>(() => ([
isUnifiedCost: computed(() => costAnalysisPageGetters.isUnifiedCost),
granularityItems: computed<MenuItem[]>(() => (([
{
type: 'item',
name: GRANULARITY.DAILY,
Expand All @@ -43,7 +44,7 @@ const state = reactive({
name: GRANULARITY.YEARLY,
label: i18n.t('BILLING.COST_MANAGEMENT.COST_ANALYSIS.YEARLY'),
},
])),
] as MenuItem[]).filter((item) => !(state.isUnifiedCost && item.name === GRANULARITY.DAILY)))),
showPeriodBadge: computed<boolean>(() => costAnalysisPageGetters.selectedQueryId === DYNAMIC_COST_QUERY_SET_PARAMS || !costAnalysisPageState.relativePeriod),
periodBadgeText: computed<string>(() => {
if (!costAnalysisPageState.period) return '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ watch(() => costAnalysisPageState.groupBy, (groupBy) => {
{{ defaultGroupByItem.label }}
</p-select-button>
<div class="tags-button-wrapper">
<p-select-dropdown :handler="tagsMenuHandler"
<p-select-dropdown v-if="!costAnalysisPageGetters.isUnifiedCost"
:handler="tagsMenuHandler"
:selected.sync="state.selectedTagsMenu"
selection-label="Tags"
appearance-type="badge"
Expand Down
Loading
Loading