-
{projName}
+
,
);
@@ -299,7 +223,119 @@ class Dashboard extends React.Component {
}
}
+async function getCosts(getEnvironmentsFn, getEnvironmentCostFn) {
+ const { envIdToCostInfo, envIdToEnvMetadata, duplicateEnvNames } = await getAccumulatedEnvCost(
+ getEnvironmentsFn,
+ getEnvironmentCostFn,
+ );
+
+ const indexNameToUserTotalCost = {};
+ Object.keys(envIdToCostInfo).forEach(envId => {
+ const indexName = envIdToEnvMetadata[envId].index;
+ if (indexNameToUserTotalCost[indexName] === undefined) {
+ indexNameToUserTotalCost[indexName] = {};
+ }
+ Object.keys(envIdToCostInfo[envId].pastMonthCostByUser).forEach(user => {
+ const currentUserCost = _.get(indexNameToUserTotalCost[indexName], user, 0);
+ indexNameToUserTotalCost[indexName][user] = currentUserCost + envIdToCostInfo[envId].pastMonthCostByUser[user];
+ });
+ });
+
+ const indexNameToTotalCost = {};
+ let totalCost = 0;
+ Object.keys(indexNameToUserTotalCost).forEach(indexName => {
+ let indexCost = 0;
+ Object.keys(indexNameToUserTotalCost[indexName]).forEach(user => {
+ indexCost += indexNameToUserTotalCost[indexName][user];
+ });
+ totalCost += indexCost;
+ indexNameToTotalCost[indexName] = indexCost;
+ });
+
+ return {
+ totalCost,
+ indexNameToTotalCost,
+ indexNameToUserTotalCost,
+ envIdToCostInfo,
+ envIdToEnvMetadata,
+ duplicateEnvNames,
+ };
+}
+
+function getLabels(envIdToCostInfo, envIdToEnvMetadata, duplicateEnvNames) {
+ const labels = Object.keys(envIdToCostInfo).map(envId => {
+ const envName = envIdToEnvMetadata[envId].name;
+ if (duplicateEnvNames.has(envName)) {
+ return `${envName}: ${envId}`;
+ }
+ return envName;
+ });
+ return labels;
+}
+
+async function getAccumulatedEnvCost(getEnvironmentsFn, getEnvironmentCostFn) {
+ const environments = await getEnvironmentsFn();
+ const duplicateEnvNames = new Set();
+ const envNameToEnvId = {};
+ const envIdToEnvMetadata = {};
+ environments.forEach(env => {
+ if (env.isExternal) return;
+ envIdToEnvMetadata[env.id] = {
+ index: env.indexId,
+ name: env.name,
+ };
+ if (envNameToEnvId[env.name] === undefined) {
+ envNameToEnvId[env.name] = env.id;
+ } else {
+ duplicateEnvNames.add(env.name);
+ }
+ });
+
+ const envIds = Object.keys(envIdToEnvMetadata);
+ const envCostPromises = envIds.map(envId => {
+ return getEnvironmentCostFn(envId, 30, false, true);
+ });
+
+ const envCostResults = await Promise.all(envCostPromises);
+ const pastMonthCostByUserArray = envCostResults.map(costResult => {
+ const createdByToCost = {};
+ _.forEach(costResult, costDate => {
+ const cost = costDate.cost;
+ Object.keys(cost).forEach(group => {
+ let createdBy = group.split('$')[1];
+ createdBy = createdBy || 'None';
+ const currentUserCost = _.get(createdByToCost, createdBy, 0);
+ createdByToCost[createdBy] = currentUserCost + cost[group].amount;
+ });
+ });
+ return createdByToCost;
+ });
+
+ const yesterdayCostArray = envCostResults.map(costResult => {
+ const yesterdayCost = costResult.length > 0 ? costResult[costResult.length - 1] : {};
+ let totalCost = 0;
+ if (yesterdayCost) {
+ const arrayOfCosts = _.flatMapDeep(yesterdayCost.cost);
+ arrayOfCosts.forEach(cost => {
+ totalCost += cost.amount;
+ });
+ }
+ return totalCost;
+ });
+
+ const envIdToCostInfo = {};
+ for (let i = 0; i < envIds.length; i++) {
+ envIdToCostInfo[envIds[i]] = {
+ pastMonthCostByUser: pastMonthCostByUserArray[i],
+ yesterdayCost: yesterdayCostArray[i],
+ };
+ }
+
+ return { envIdToCostInfo, envIdToEnvMetadata, duplicateEnvNames };
+}
+
// see https://medium.com/@mweststrate/mobx-4-better-simpler-faster-smaller-c1fbc08008da
decorate(Dashboard, {});
export default inject('userStore')(withRouter(observer(Dashboard)));
+export { getAccumulatedEnvCost, getCosts, getLabels };
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/dashboard/__tests__/Dashboard.test.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/dashboard/__tests__/Dashboard.test.js
new file mode 100644
index 0000000000..1408501376
--- /dev/null
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/dashboard/__tests__/Dashboard.test.js
@@ -0,0 +1,312 @@
+import { getCosts, getLabels } from '../Dashboard';
+
+describe('Dashboard tests', () => {
+ describe('Labels', () => {
+ it('Test labels with duplicates', async () => {
+ const duplicateEnvNames = new Set();
+ duplicateEnvNames.add('name2_2');
+ const envIdToCostInfo = {
+ id1: { pastMonthCostByUser: { 'user1@gmail.com': 5.64 }, yesterdayCost: 0 },
+ id2: { pastMonthCostByUser: { 'user2@gmail.com': 2 }, yesterdayCost: 0 },
+ id3: { pastMonthCostByUser: { 'user1@gmail.com': 3 }, yesterdayCost: 0 },
+ id4: { pastMonthCostByUser: { 'user1@gmail.com': 4 }, yesterdayCost: 4 },
+ id5: { pastMonthCostByUser: {}, yesterdayCost: 0 },
+ };
+ const envIdToEnvMetadata = {
+ id1: { index: 'index1', name: 'name1_1' },
+ id2: { index: 'index1', name: 'name2_1' },
+ id3: { index: 'index2', name: 'name1_2' },
+ id4: { index: 'index2', name: 'name2_2' },
+ id5: { index: 'index2', name: 'name2_2' },
+ };
+ const labels = getLabels(envIdToCostInfo, envIdToEnvMetadata, duplicateEnvNames);
+ expect(labels).toEqual(['name1_1', 'name2_1', 'name1_2', 'name2_2: id4', 'name2_2: id5']);
+ });
+
+ it('Test labels without duplicates', async () => {
+ const duplicateEnvNames = new Set();
+ const envIdToCostInfo = {
+ id1: { pastMonthCostByUser: { 'user1@gmail.com': 5.64 }, yesterdayCost: 0 },
+ id2: { pastMonthCostByUser: { 'user2@gmail.com': 2 }, yesterdayCost: 0 },
+ id3: { pastMonthCostByUser: { 'user1@gmail.com': 3 }, yesterdayCost: 0 },
+ id4: { pastMonthCostByUser: { 'user1@gmail.com': 4 }, yesterdayCost: 4 },
+ id5: { pastMonthCostByUser: {}, yesterdayCost: 0 },
+ };
+ const envIdToEnvMetadata = {
+ id1: { index: 'index1', name: 'name1_1' },
+ id2: { index: 'index1', name: 'name2_1' },
+ id3: { index: 'index2', name: 'name1_2' },
+ id4: { index: 'index2', name: 'name2_2' },
+ id5: { index: 'index2', name: 'name3_2' },
+ };
+ const labels = getLabels(envIdToCostInfo, envIdToEnvMetadata, duplicateEnvNames);
+ expect(labels).toEqual(['name1_1', 'name2_1', 'name1_2', 'name2_2', 'name3_2']);
+ });
+ });
+
+ describe('Cost aggregation', () => {
+ it('Test with no cost data', async () => {
+ function getEnvironments() {
+ return [
+ {
+ name: 'name1_1',
+ indexId: 'index1',
+ id: 'id1',
+ },
+ {
+ name: 'name2_1',
+ indexId: 'index1',
+ id: 'id2',
+ },
+ ];
+ }
+ const getEnvironmentCost = jest.fn();
+ getEnvironmentCost.mockImplementation((envId, days, groupByService, groupByUser) => {
+ expect(days).toEqual(30);
+ expect(groupByService).toEqual(false);
+ expect(groupByUser).toEqual(true);
+ switch (envId) {
+ case 'id1':
+ return [
+ {
+ startDate: '2021-03-31',
+ cost: {},
+ },
+ {
+ startDate: '2021-04-01',
+ cost: {},
+ },
+ {
+ startDate: '2021-04-02',
+ cost: {},
+ },
+ {
+ startDate: '2021-04-03',
+ cost: {},
+ },
+ ];
+
+ case 'id2':
+ return [
+ {
+ startDate: '2021-04-01',
+ cost: {},
+ },
+ {
+ startDate: '2021-04-02',
+ cost: {},
+ },
+ {
+ startDate: '2021-04-03',
+ cost: {},
+ },
+ ];
+
+ default:
+ throw Error('Invalid environmentId');
+ }
+ });
+ const aggregatedCosts = await getCosts(getEnvironments, getEnvironmentCost);
+ const duplicateEnvNames = new Set();
+ expect(aggregatedCosts.duplicateEnvNames).toEqual(duplicateEnvNames);
+ expect(aggregatedCosts.totalCost).toEqual(0);
+ expect(aggregatedCosts.indexNameToTotalCost).toEqual({
+ index1: 0,
+ });
+ expect(aggregatedCosts.envIdToEnvMetadata).toEqual({
+ id1: { index: 'index1', name: 'name1_1' },
+ id2: { index: 'index1', name: 'name2_1' },
+ });
+ expect(aggregatedCosts.indexNameToUserTotalCost).toEqual({
+ index1: {},
+ });
+ expect(aggregatedCosts.envIdToCostInfo).toEqual({
+ id1: { pastMonthCostByUser: {}, yesterdayCost: 0 },
+ id2: { pastMonthCostByUser: {}, yesterdayCost: 0 },
+ });
+ });
+
+ it('Test with multiple indexes, users and environments', async () => {
+ function getEnvironments() {
+ return [
+ {
+ name: 'name1_1',
+ indexId: 'index1',
+ id: 'id1',
+ },
+ {
+ name: 'name2_1',
+ indexId: 'index1',
+ id: 'id2',
+ },
+ {
+ name: 'name1_2',
+ indexId: 'index2',
+ id: 'id3',
+ },
+ {
+ name: 'name2_2',
+ indexId: 'index2',
+ id: 'id4',
+ },
+ {
+ name: 'name2_2',
+ indexId: 'index2',
+ id: 'id5',
+ },
+ ];
+ }
+ const getEnvironmentCost = jest.fn();
+ getEnvironmentCost.mockImplementation((envId, days, groupByService, groupByUser) => {
+ expect(days).toEqual(30);
+ expect(groupByService).toEqual(false);
+ expect(groupByUser).toEqual(true);
+ switch (envId) {
+ case 'id1':
+ return [
+ {
+ startDate: '2021-03-31',
+ cost: {
+ 'CreatedBy$user1@gmail.com': {
+ amount: 1.0,
+ unit: 'USD',
+ },
+ },
+ },
+ {
+ startDate: '2021-04-01',
+ cost: {
+ 'CreatedBy$user1@gmail.com': {
+ amount: 4.0,
+ unit: 'USD',
+ },
+ },
+ },
+ {
+ startDate: '2021-04-02',
+ cost: {
+ 'CreatedBy$user1@gmail.com': {
+ amount: 0.64,
+ unit: 'USD',
+ },
+ },
+ },
+ {
+ startDate: '2021-04-03',
+ cost: {},
+ },
+ ];
+
+ case 'id2':
+ return [
+ {
+ startDate: '2021-04-01',
+ cost: {
+ 'CreatedBy$user2@gmail.com': {
+ amount: 2.0,
+ unit: 'USD',
+ },
+ },
+ },
+ {
+ startDate: '2021-04-02',
+ cost: {},
+ },
+ {
+ startDate: '2021-04-03',
+ cost: {},
+ },
+ ];
+
+ case 'id3':
+ return [
+ {
+ startDate: '2021-04-01',
+ cost: {},
+ },
+ {
+ startDate: '2021-04-02',
+ cost: {
+ 'CreatedBy$user1@gmail.com': {
+ amount: 3.0,
+ unit: 'USD',
+ },
+ },
+ },
+ {
+ startDate: '2021-04-03',
+ cost: {},
+ },
+ ];
+
+ case 'id4':
+ return [
+ {
+ startDate: '2021-04-01',
+ cost: {},
+ },
+ {
+ startDate: '2021-04-02',
+ cost: {},
+ },
+ {
+ startDate: '2021-04-03',
+ cost: {
+ 'CreatedBy$user1@gmail.com': {
+ amount: 4.0,
+ unit: 'USD',
+ },
+ },
+ },
+ ];
+
+ case 'id5':
+ return [
+ {
+ startDate: '2021-04-01',
+ cost: {},
+ },
+ {
+ startDate: '2021-04-02',
+ cost: {},
+ },
+ {
+ startDate: '2021-04-03',
+ cost: {},
+ },
+ ];
+
+ default:
+ throw Error('Invalid environmentId');
+ }
+ });
+ const aggregatedCosts = await getCosts(getEnvironments, getEnvironmentCost);
+ const duplicateEnvNames = new Set();
+ duplicateEnvNames.add('name2_2');
+ expect(aggregatedCosts.duplicateEnvNames).toEqual(duplicateEnvNames);
+ expect(aggregatedCosts.totalCost).toEqual(14.64);
+ expect(aggregatedCosts.indexNameToTotalCost).toEqual({
+ index1: 7.64,
+ index2: 7,
+ });
+ expect(aggregatedCosts.envIdToEnvMetadata).toEqual({
+ id1: { index: 'index1', name: 'name1_1' },
+ id2: { index: 'index1', name: 'name2_1' },
+ id3: { index: 'index2', name: 'name1_2' },
+ id4: { index: 'index2', name: 'name2_2' },
+ id5: { index: 'index2', name: 'name2_2' },
+ });
+ expect(aggregatedCosts.indexNameToUserTotalCost).toEqual({
+ index1: { 'user1@gmail.com': 5.64, 'user2@gmail.com': 2 },
+ index2: { 'user1@gmail.com': 7 },
+ });
+ expect(aggregatedCosts.envIdToCostInfo).toEqual({
+ id1: { pastMonthCostByUser: { 'user1@gmail.com': 5.64 }, yesterdayCost: 0 },
+ id2: { pastMonthCostByUser: { 'user2@gmail.com': 2 }, yesterdayCost: 0 },
+ id3: { pastMonthCostByUser: { 'user1@gmail.com': 3 }, yesterdayCost: 0 },
+ id4: { pastMonthCostByUser: { 'user1@gmail.com': 4 }, yesterdayCost: 4 },
+ id5: { pastMonthCostByUser: {}, yesterdayCost: 0 },
+ });
+ });
+ });
+});