@electrolux-oss/plugin-infrawallet 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/README.md +79 -25
  2. package/dist/api/InfraWalletApi.esm.js.map +1 -1
  3. package/dist/api/InfraWalletApiClient.esm.js +12 -2
  4. package/dist/api/InfraWalletApiClient.esm.js.map +1 -1
  5. package/dist/api/functions.esm.js +33 -4
  6. package/dist/api/functions.esm.js.map +1 -1
  7. package/dist/components/ColumnsChartComponent/ColumnsChartComponent.esm.js +45 -28
  8. package/dist/components/ColumnsChartComponent/ColumnsChartComponent.esm.js.map +1 -1
  9. package/dist/components/CostReportsTableComponent/CostReportsTableComponent.esm.js +42 -37
  10. package/dist/components/CostReportsTableComponent/CostReportsTableComponent.esm.js.map +1 -1
  11. package/dist/components/ErrorsAlertComponent/ErrorsAlertComponent.esm.js +3 -2
  12. package/dist/components/ErrorsAlertComponent/ErrorsAlertComponent.esm.js.map +1 -1
  13. package/dist/components/FiltersComponent/FiltersComponent.esm.js +216 -10
  14. package/dist/components/FiltersComponent/FiltersComponent.esm.js.map +1 -1
  15. package/dist/components/MetricConfigurationComponent/MetricConfigurationComponent.esm.js +7 -0
  16. package/dist/components/MetricConfigurationComponent/MetricConfigurationComponent.esm.js.map +1 -1
  17. package/dist/components/ProviderIcons/ProviderIcons.esm.js +322 -0
  18. package/dist/components/ProviderIcons/ProviderIcons.esm.js.map +1 -0
  19. package/dist/components/ReportsComponent/ReportsComponent.esm.js +30 -15
  20. package/dist/components/ReportsComponent/ReportsComponent.esm.js.map +1 -1
  21. package/dist/components/TopbarComponent/TopbarComponent.esm.js +1 -1
  22. package/dist/components/TopbarComponent/TopbarComponent.esm.js.map +1 -1
  23. package/package.json +4 -6
  24. package/dist/components/CostReportsTableComponent/TrendBarComponent.esm.js +0 -43
  25. package/dist/components/CostReportsTableComponent/TrendBarComponent.esm.js.map +0 -1
package/README.md CHANGED
@@ -24,36 +24,49 @@ query period. Add the following configurations to your `app-config.yaml` file if
24
24
  # note that infraWallet exists at the root level, it is not the same one for backend configurations
25
25
  infraWallet:
26
26
  settings:
27
- defaultGroupBy: none # none by default, or provider, category, service, tag:<tag_key>
27
+ defaultGroupBy: none # none by default, or account, provider, category, service, tag:<tag_key>
28
28
  defaultShowLastXMonths: 3 # 3 by default, or other numbers, we recommend it less than 12
29
29
  ```
30
30
 
31
- ### Default Settings for Frontend
31
+ #### Customizing the InfraWalletPage Title and Subtitle
32
32
 
33
- Site admins can configure the default view for InfraWallet, including the default group by dimension, and the default
34
- query period. Add the following configurations to your `app-config.yaml` file if the default view needs to be changed.
33
+ By default, the `InfraWalletPage` component is configured in the `packages/app/src/App.tsx` file as follows:
35
34
 
36
- ```yaml
37
- # note that infraWallet exists at the root level, it is not the same one for backend configurations
38
- infraWallet:
39
- settings:
40
- defaultGroupBy: none # none by default, or provider, category, service, tag:<tag_key>
41
- defaultShowLastXMonths: 3 # 3 by default, or other numbers, we recommend it less than 12
35
+ ```ts
36
+ <Route path="/infrawallet" element={<InfraWalletPage />} />
37
+ ```
38
+
39
+ To customize the title and subtitle of the InfraWalletPage, you can modify the route in the same file as shown below:
40
+
41
+ ```ts
42
+ <Route path="/infrawallet" element={<InfraWalletPage title="Custom title" subTitle="Custom subTitle" />} />
42
43
  ```
43
44
 
44
- ### Define Cloud Accounts in app-config.yaml
45
+ ### Defining Provider Integrations
46
+
47
+ InfraWallet's configuration schema is specified in in [plugins/infrawallet-backend/config.d.ts](../infrawallet-backend/config.d.ts). To set up provider integrations, users must configure them in the `app-config.yaml` file located in the root directory.
48
+
49
+ #### AWS Integration
50
+
51
+ InfraWallet uses an IAM role to retrieve cost and usage data via the AWS Cost Explorer APIs. Before configuring InfraWallet, you must set up the necessary AWS IAM role and policy.
52
+
53
+ ##### For Management Accounts
45
54
 
46
- The configuration schema of InfraWallet is defined in the [plugins/infrawallet-backend/config.d.ts](plugins/infrawallet-backend/config.d.ts) file. Users need to configure their cloud accounts in the `app-config.yaml` in the root folder.
55
+ If you have a [management account](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_getting-started_concepts.html#management-account), this setup only needs to be done once within the management account. InfraWallet will then be able to retrieve cost data across all [member accounts](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_getting-started_concepts.html#member-account).
47
56
 
48
- #### AWS
57
+ ##### For Non-Management Accounts
49
58
 
50
- For AWS, InfraWallet relies on an IAM role to fetch cost and usage data using AWS Cost Explorer APIs. Thus before adding the configurations, AWS IAM user, role, and policy need to be set up. If you have multiple AWS accounts, you can reuse the IAM user in one account and grant the necessary permissions to the role in each account. The role to be assumed in an AWS account needs the following permission:
59
+ If you're not using a [management account](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_getting-started_concepts.html#management-account), you'll need to create a role in each AWS account and configure trust relationships individually.
60
+
61
+ ##### Required IAM Role Permissions
62
+
63
+ The IAM role must have the following permissions to access cost and usage data:
51
64
 
52
65
  ```json
53
66
  {
54
67
  "Statement": [
55
68
  {
56
- "Action": "ce:GetCostAndUsage",
69
+ "Action": ["ce:GetCostAndUsage", "ce:GetTags"],
57
70
  "Effect": "Allow",
58
71
  "Resource": "*",
59
72
  "Sid": ""
@@ -63,37 +76,49 @@ For AWS, InfraWallet relies on an IAM role to fetch cost and usage data using AW
63
76
  }
64
77
  ```
65
78
 
66
- After getting the IAM-related resources ready, put the following configuration into `app-config.yaml`:
79
+ ##### Configuration
80
+
81
+ Once the IAM roles and policies are in place, add the following configuration to your `app-config.yaml` file:
67
82
 
68
83
  ```yaml
69
84
  backend:
70
85
  infraWallet:
71
86
  integrations:
72
87
  aws:
73
- - name: <unique_name_of_this_account>
88
+ - name: <unique_name_of_this_integration>
74
89
  accountId: '<12-digit_account_ID>' # quoted as a string
75
90
  assumedRoleName: <name_of_the_AWS_IAM_role_to_be_assumed>
76
- accessKeyId: <access_key_ID_of_AWS_IAM_user_that_assumes_the_role>
77
- accessKeySecret: <access_key_secret_of_AWS_IAM_user_that_assumes_the_role>
91
+ accessKeyId: <access_key_ID_of_AWS_IAM_user_that_assumes_the_role> # optional, only needed when an IAM user is used to assume the role
92
+ accessKeySecret: <access_key_secret_of_AWS_IAM_user_that_assumes_the_role> # optional, only needed when an IAM user is used to assume the role
78
93
  ```
79
94
 
80
- #### Azure
95
+ InfraWallet's AWS client is built using the AWS SDK for JavaScript. If both `accessKeyId` and `accessKeySecret` are provided in the configuration, the client will use the specified IAM user to assume the role. Otherwise, it follows the [default credential provider chain](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html#credchain).
96
+
97
+ #### Azure Integration
81
98
 
82
- In order to manage Azure costs, an application needs to be registered on Azure. InfraWallet is only tested with subscription-level cost data. After creating the application, users need to go to the `Subscriptions` page, choose the target subscription and then visit the `Access control (IAM)` page. Assign the `Cost Management Reader` role to the created application. Create a new client secret for the application, and add the following configurations in `app-config.yaml`:
99
+ To manage Azure costs with InfraWallet, you need to register an application in Azure. Note that InfraWallet has been tested with subscription-level cost data only.
100
+
101
+ ##### Steps:
102
+
103
+ 1. After registering the application, navigate to the `Subscriptions` page and select the target subscription.
104
+ 2. Go to the `Access control (IAM)` section and assign the `Cost Management Reader` role to the newly created application.
105
+ 3. Generate a client secret for the application.
106
+
107
+ Add the following configurations to your `app-config.yaml` file:
83
108
 
84
109
  ```yaml
85
110
  backend:
86
111
  infraWallet:
87
112
  integrations:
88
113
  azure:
89
- - name: <unique_name_of_this_account>
114
+ - name: <unique_name_of_this_integration>
90
115
  subscriptionId: <Azure_subscription_ID>
91
116
  tenantId: <Azure_tenant_ID>
92
117
  clientId: <Client_ID_of_the_created_application>
93
118
  clientSecret: <Client_secret_of_the_created_application>
94
119
  ```
95
120
 
96
- #### GCP
121
+ #### GCP Integration
97
122
 
98
123
  InfraWallet relies on GCP Big Query to fetch cost data. This means that the billing data needs to be exported to a big query dataset, and a service account needs to be created for InfraWallet. The steps of exporting billing data to Big Query can be found [here](https://cloud.google.com/billing/docs/how-to/export-data-bigquery). Then, visit Google Cloud Console and navigate to the `IAM & Admin` section in the billing account. Click `Service Accounts`, and create a new service account. The service account needs to have `BigQuery Data Viewer` and `BigQuery Job User` roles. On the `Service Accounts` page, click the three dots (menu) in the `Actions` column for the newly created service account and select `Manage keys`. There click `Add key` -> `Create new key`, and use `JSON` as the format. Download the JSON key file and keep it safe.
99
124
 
@@ -104,16 +129,45 @@ backend:
104
129
  infraWallet:
105
130
  integrations:
106
131
  gcp:
107
- - name: <unique_name_of_this_account>
132
+ - name: <unique_name_of_this_integration>
108
133
  keyFilePath: <path_to_your_json_key_file> # if you run it in a k8s pod, you may need to create a secret and mount it to the pod
109
134
  projectId: <GCP_project_that_your_big_query_dataset_belongs_to>
110
135
  datasetId: <big_query_dataset_id>
111
136
  tableId: <big_query_table_id>
112
137
  ```
113
138
 
139
+ #### Confluent Cloud Integration
140
+
141
+ To manage Confluent Cloud costs, you need to create an API key (Service account) for your Organization with the 'Cloud resource management' resource scope, you can find the documentation [here](https://docs.confluent.io/cloud/current/security/authenticate/workload-identities/service-accounts/api-keys/manage-api-keys.html#add-an-api-key). Once you have your API key details, add the following settings to `app-config.yaml`:
142
+
143
+ ```yaml
144
+ backend:
145
+ infraWallet:
146
+ integrations:
147
+ confluent:
148
+ - name: <unique_name_of_this_integration>
149
+ apiKey: <your_api_key>
150
+ apiSecret: <your_api_key_secret>
151
+ ```
152
+
153
+ #### MongoDB Atlas Integration
154
+
155
+ To manage Mongo Atlas costs, you need to create an API key for your Organization with `Organization Billing Viewer` permission, you can find the documentation [here](https://www.mongodb.com/docs/atlas/configure-api-access/#std-label-about-org-api-keys). Once you have your API key details, add the following settings to `app-config.yaml`:
156
+
157
+ ```yaml
158
+ backend:
159
+ infraWallet:
160
+ integrations:
161
+ mongoatlas:
162
+ - name: <unique_name_of_this_integration>
163
+ orgId: <id_organization_mongo_atlas>
164
+ publicKey: <public_key_of_your_api_key>
165
+ privateKey: <private_key_of_your_api_key>
166
+ ```
167
+
114
168
  ### Adjust Category Mappings if Needed
115
169
 
116
- The category mappings are stored in the plugin's database. If there is no mapping found in the DB when initializing the plugin, the default mappings will be used. The default mappings can be found in the [plugins/infrawallet-backend/seeds/init.js](plugins/infrawallet-backend/seeds/init.js) file. You can adjust this seed file to fit your needs, or update the database directly later on.
170
+ The category mappings are stored in the plugin's database. If there is no mapping found in the DB when initializing the plugin, the default mappings will be used. The default mappings can be found in the [plugins/infrawallet-backend/seeds/init.js](../infrawallet-backend/seeds/init.js) file. You can adjust this seed file to fit your needs, or update the database directly later on.
117
171
 
118
172
  ### Install the Plugin
119
173
 
@@ -1 +1 @@
1
- {"version":3,"file":"InfraWalletApi.esm.js","sources":["../../src/api/InfraWalletApi.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport {\n CostReportsResponse,\n GetWalletResponse,\n MetricConfigsResponse,\n MetricSetting,\n MetricsResponse,\n MetricsSettingResponse,\n} from './types';\n\n/** @public */\nexport const infraWalletApiRef = createApiRef<InfraWalletApi>({\n id: 'plugin.infrawallet',\n});\n\n/** @public */\nexport interface InfraWalletApi {\n getCostReports(\n filters: string,\n groups: string,\n granularity: string,\n startTime: Date,\n endTime: Date,\n ): Promise<CostReportsResponse>;\n getMetrics(walletName: string, granularity: string, startTime: Date, endTime: Date): Promise<MetricsResponse>;\n getMetricConfigs(): Promise<MetricConfigsResponse>;\n getWalletMetricsSetting(walletName: string): Promise<MetricsSettingResponse>;\n updateWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ updated: boolean; status: number }>;\n deleteWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ deleted: boolean; status: number }>;\n getWalletByName(walletName: string): Promise<GetWalletResponse>;\n}\n"],"names":[],"mappings":";;AAWO,MAAM,oBAAoB,YAA6B,CAAA;AAAA,EAC5D,EAAI,EAAA,oBAAA;AACN,CAAC;;;;"}
1
+ {"version":3,"file":"InfraWalletApi.esm.js","sources":["../../src/api/InfraWalletApi.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport {\n CostReportsResponse,\n GetWalletResponse,\n MetricConfigsResponse,\n MetricSetting,\n MetricsResponse,\n MetricsSettingResponse,\n Tag,\n TagResponse,\n} from './types';\n\n/** @public */\nexport const infraWalletApiRef = createApiRef<InfraWalletApi>({\n id: 'plugin.infrawallet',\n});\n\n/** @public */\nexport interface InfraWalletApi {\n getCostReports(\n filters: string,\n tags: Tag[],\n groups: string,\n granularity: string,\n startTime: Date,\n endTime: Date,\n ): Promise<CostReportsResponse>;\n getTagKeys(provider: string, startTime: Date, endTime: Date): Promise<TagResponse>;\n getTagValues(tag: Tag, startTime: Date, endTime: Date): Promise<TagResponse>;\n getMetrics(walletName: string, granularity: string, startTime: Date, endTime: Date): Promise<MetricsResponse>;\n getMetricConfigs(): Promise<MetricConfigsResponse>;\n getWalletMetricsSetting(walletName: string): Promise<MetricsSettingResponse>;\n updateWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ updated: boolean; status: number }>;\n deleteWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ deleted: boolean; status: number }>;\n getWalletByName(walletName: string): Promise<GetWalletResponse>;\n}\n"],"names":[],"mappings":";;AAaO,MAAM,oBAAoB,YAA6B,CAAA;AAAA,EAC5D,EAAI,EAAA,oBAAA;AACN,CAAC;;;;"}
@@ -1,4 +1,5 @@
1
1
  import fetch from 'node-fetch';
2
+ import { tagsToString } from './functions.esm.js';
2
3
 
3
4
  var __defProp = Object.defineProperty;
4
5
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -35,8 +36,17 @@ class InfraWalletApiClient {
35
36
  }
36
37
  return await response.json();
37
38
  }
38
- async getCostReports(filters, groups, granularity, startTime, endTime) {
39
- const url = `api/infrawallet/reports?&filters=${filters}&groups=${groups}&granularity=${granularity}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;
39
+ async getCostReports(filters, tags, groups, granularity, startTime, endTime) {
40
+ const tagsString = tagsToString(tags);
41
+ const url = `api/infrawallet/reports?&filters=${filters}&tags=${tagsString}&groups=${groups}&granularity=${granularity}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;
42
+ return await this.request(url);
43
+ }
44
+ async getTagKeys(provider, startTime, endTime) {
45
+ const url = `api/infrawallet/tag-keys?provider=${provider}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;
46
+ return await this.request(url);
47
+ }
48
+ async getTagValues(tag, startTime, endTime) {
49
+ const url = `api/infrawallet/tag-values?provider=${tag.provider}&tag=${tag.key}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;
40
50
  return await this.request(url);
41
51
  }
42
52
  async getWalletByName(walletName) {
@@ -1 +1 @@
1
- {"version":3,"file":"InfraWalletApiClient.esm.js","sources":["../../src/api/InfraWalletApiClient.ts"],"sourcesContent":["import { ConfigApi, IdentityApi } from '@backstage/core-plugin-api';\nimport fetch from 'node-fetch';\nimport { InfraWalletApi } from './InfraWalletApi';\nimport {\n CostReportsResponse,\n GetWalletResponse,\n MetricConfigsResponse,\n MetricSetting,\n MetricsResponse,\n MetricsSettingResponse,\n} from './types';\n\n/** @public */\nexport class InfraWalletApiClient implements InfraWalletApi {\n private readonly identityApi: IdentityApi;\n private readonly backendUrl: string;\n\n constructor(options: { identityApi: IdentityApi; configApi: ConfigApi }) {\n this.identityApi = options.identityApi;\n this.backendUrl = options.configApi.getString('backend.baseUrl');\n }\n\n async request(path: string, method?: string, payload?: Record<string, string | undefined>) {\n const url = `${this.backendUrl}/${path}`;\n const { token: idToken } = await this.identityApi.getCredentials();\n const headers: Record<string, string> = idToken ? { Authorization: `Bearer ${idToken}` } : {};\n\n if (method !== undefined && method !== 'GET') {\n headers['Content-Type'] = 'application/json';\n }\n\n const request: any = {\n headers: headers,\n method: method ?? 'GET',\n };\n\n if (payload) {\n request.body = JSON.stringify(payload);\n }\n\n const response = await fetch(url, request);\n\n if (!response.ok) {\n const res = await response.text();\n const message = `Request failed with ${response.status} ${response.statusText}, ${res}`;\n throw new Error(message);\n }\n\n return await response.json();\n }\n\n async getCostReports(\n filters: string,\n groups: string,\n granularity: string,\n startTime: Date,\n endTime: Date,\n ): Promise<CostReportsResponse> {\n const url = `api/infrawallet/reports?&filters=${filters}&groups=${groups}&granularity=${granularity}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getWalletByName(walletName: string): Promise<GetWalletResponse> {\n const url = `api/infrawallet/${walletName}`;\n return await this.request(url);\n }\n\n async getMetrics(walletName: string, granularity: string, startTime: Date, endTime: Date): Promise<MetricsResponse> {\n const url = `api/infrawallet/${walletName}/metrics?&granularity=${granularity}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getMetricConfigs(): Promise<MetricConfigsResponse> {\n const url = 'api/infrawallet/metric/metric_configs';\n return await this.request(url);\n }\n\n async getWalletMetricsSetting(walletName: string): Promise<MetricsSettingResponse> {\n const url = `api/infrawallet/${walletName}/metrics_setting`;\n return await this.request(url);\n }\n async updateWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ updated: boolean; status: number }> {\n const url = `api/infrawallet/${walletName}/metrics_setting`;\n return await this.request(url, 'PUT', metricSetting);\n }\n\n async deleteWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ deleted: boolean; status: number }> {\n const url = `api/infrawallet/${walletName}/metrics_setting`;\n return await this.request(url, 'DELETE', metricSetting);\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"InfraWalletApiClient.esm.js","sources":["../../src/api/InfraWalletApiClient.ts"],"sourcesContent":["import { ConfigApi, IdentityApi } from '@backstage/core-plugin-api';\nimport fetch from 'node-fetch';\nimport { InfraWalletApi } from './InfraWalletApi';\nimport {\n CostReportsResponse,\n GetWalletResponse,\n MetricConfigsResponse,\n MetricSetting,\n MetricsResponse,\n MetricsSettingResponse,\n Tag,\n TagResponse,\n} from './types';\nimport { tagsToString } from './functions';\n\n/** @public */\nexport class InfraWalletApiClient implements InfraWalletApi {\n private readonly identityApi: IdentityApi;\n private readonly backendUrl: string;\n\n constructor(options: { identityApi: IdentityApi; configApi: ConfigApi }) {\n this.identityApi = options.identityApi;\n this.backendUrl = options.configApi.getString('backend.baseUrl');\n }\n\n async request(path: string, method?: string, payload?: Record<string, string | undefined>) {\n const url = `${this.backendUrl}/${path}`;\n const { token: idToken } = await this.identityApi.getCredentials();\n const headers: Record<string, string> = idToken ? { Authorization: `Bearer ${idToken}` } : {};\n\n if (method !== undefined && method !== 'GET') {\n headers['Content-Type'] = 'application/json';\n }\n\n const request: any = {\n headers: headers,\n method: method ?? 'GET',\n };\n\n if (payload) {\n request.body = JSON.stringify(payload);\n }\n\n const response = await fetch(url, request);\n\n if (!response.ok) {\n const res = await response.text();\n const message = `Request failed with ${response.status} ${response.statusText}, ${res}`;\n throw new Error(message);\n }\n\n return await response.json();\n }\n\n async getCostReports(\n filters: string,\n tags: Tag[],\n groups: string,\n granularity: string,\n startTime: Date,\n endTime: Date,\n ): Promise<CostReportsResponse> {\n const tagsString = tagsToString(tags);\n const url = `api/infrawallet/reports?&filters=${filters}&tags=${tagsString}&groups=${groups}&granularity=${granularity}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getTagKeys(provider: string, startTime: Date, endTime: Date): Promise<TagResponse> {\n const url = `api/infrawallet/tag-keys?provider=${provider}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getTagValues(tag: Tag, startTime: Date, endTime: Date): Promise<TagResponse> {\n const url = `api/infrawallet/tag-values?provider=${tag.provider}&tag=${\n tag.key\n }&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getWalletByName(walletName: string): Promise<GetWalletResponse> {\n const url = `api/infrawallet/${walletName}`;\n return await this.request(url);\n }\n\n async getMetrics(walletName: string, granularity: string, startTime: Date, endTime: Date): Promise<MetricsResponse> {\n const url = `api/infrawallet/${walletName}/metrics?&granularity=${granularity}&startTime=${startTime.getTime()}&endTime=${endTime.getTime()}`;\n return await this.request(url);\n }\n\n async getMetricConfigs(): Promise<MetricConfigsResponse> {\n const url = 'api/infrawallet/metric/metric_configs';\n return await this.request(url);\n }\n\n async getWalletMetricsSetting(walletName: string): Promise<MetricsSettingResponse> {\n const url = `api/infrawallet/${walletName}/metrics_setting`;\n return await this.request(url);\n }\n async updateWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ updated: boolean; status: number }> {\n const url = `api/infrawallet/${walletName}/metrics_setting`;\n return await this.request(url, 'PUT', metricSetting);\n }\n\n async deleteWalletMetricSetting(\n walletName: string,\n metricSetting: MetricSetting,\n ): Promise<{ deleted: boolean; status: number }> {\n const url = `api/infrawallet/${walletName}/metrics_setting`;\n return await this.request(url, 'DELETE', metricSetting);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -64,6 +64,7 @@ const aggregateCostReports = (reports, aggregatedBy) => {
64
64
  if (!accumulator[keyName]) {
65
65
  accumulator[keyName] = {
66
66
  id: keyName,
67
+ provider: report.provider,
67
68
  reports: {}
68
69
  };
69
70
  if (aggregatedBy !== void 0) {
@@ -91,9 +92,8 @@ const getReportKeyAndValues = (reports) => {
91
92
  if (!excludedKeys.includes(key)) {
92
93
  if (keyValueSets[key] === void 0) {
93
94
  keyValueSets[key] = /* @__PURE__ */ new Set();
94
- } else {
95
- keyValueSets[key].add(report[key]);
96
95
  }
96
+ keyValueSets[key].add(report[key]);
97
97
  }
98
98
  });
99
99
  });
@@ -103,9 +103,38 @@ const getReportKeyAndValues = (reports) => {
103
103
  });
104
104
  return keyValues;
105
105
  };
106
+ const extractProvider = (input) => {
107
+ let provider = void 0;
108
+ if (input && input.indexOf("/") !== -1) {
109
+ provider = input.split("/")[0];
110
+ }
111
+ return provider;
112
+ };
113
+ const extractAccountInfo = (input) => {
114
+ const regex = /^(.*?)\s*\(([^)]+)\)$/;
115
+ const match = input.match(regex);
116
+ if (match) {
117
+ const accountName = match[1];
118
+ const accountId = match[2];
119
+ return { accountName, accountId };
120
+ }
121
+ return { accountName: input };
122
+ };
123
+ function tagExists(tags, targetTag) {
124
+ return tags.some(
125
+ (tag) => tag.provider === targetTag.provider && tag.key === targetTag.key && tag.value === targetTag.value
126
+ );
127
+ }
128
+ const tagsToString = (tags) => {
129
+ if (tags.length === 0) {
130
+ return "()";
131
+ }
132
+ const keyValuePairs = tags.map((tag) => `${tag.provider}:${tag.key}=${tag.value}`);
133
+ return `(${keyValuePairs.join(" OR ")})`;
134
+ };
106
135
  const getAllReportTags = (reports) => {
107
136
  const tags = /* @__PURE__ */ new Set();
108
- const reservedKeys = ["id", "name", "service", "category", "provider", "reports"];
137
+ const reservedKeys = ["id", "account", "service", "category", "provider", "reports"];
109
138
  reports.forEach((report) => {
110
139
  Object.keys(report).forEach((key) => {
111
140
  if (reservedKeys.indexOf(key) === -1) {
@@ -135,5 +164,5 @@ const getPeriodStrings = (granularity, startTime, endTime) => {
135
164
  return result;
136
165
  };
137
166
 
138
- export { aggregateCostReports, filterCostReports, getAllReportTags, getPeriodStrings, getPreviousMonth, getReportKeyAndValues, mergeCostReports };
167
+ export { aggregateCostReports, extractAccountInfo, extractProvider, filterCostReports, getAllReportTags, getPeriodStrings, getPreviousMonth, getReportKeyAndValues, mergeCostReports, tagExists, tagsToString };
139
168
  //# sourceMappingURL=functions.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"functions.esm.js","sources":["../../src/api/functions.ts"],"sourcesContent":["import { format, parse, subMonths } from 'date-fns';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { Report, Filters } from './types';\n\nexport const mergeCostReports = (reports: Report[], threshold: number): Report[] => {\n const totalCosts: { id: string; total: number }[] = [];\n reports.forEach(report => {\n let total = 0;\n Object.values(report.reports).forEach(v => {\n total += v as number;\n });\n totalCosts.push({ id: report.id, total: total });\n });\n const sortedTotalCosts = totalCosts.sort((a, b) => b.total - a.total);\n const idsToBeKept = sortedTotalCosts.slice(0, threshold).map(v => v.id);\n\n const mergedReports = reduce(\n reports,\n (accumulator: { [key: string]: Report }, report) => {\n let keyName = 'others';\n if (idsToBeKept.includes(report.id)) {\n keyName = report.id;\n }\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n reports: {},\n };\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {},\n );\n\n return Object.values(mergedReports);\n};\n\nexport const filterCostReports = (reports: Report[], filters: Filters): Report[] => {\n const filteredReports = reports.filter(report => {\n let match = true;\n Object.keys(filters).forEach(key => {\n if (filters[key].length > 0 && !filters[key].includes(report[key] as string)) {\n match = false;\n }\n });\n return match;\n });\n\n return filteredReports;\n};\n\nexport const aggregateCostReports = (reports: Report[], aggregatedBy?: string): Report[] => {\n const aggregatedReports: { [key: string]: Report } = reduce(\n reports,\n (accumulator, report) => {\n let keyName: string = 'no value';\n if (aggregatedBy && aggregatedBy in report) {\n keyName = report[aggregatedBy] as string;\n } else if (aggregatedBy === 'none') {\n keyName = 'Total';\n }\n\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n reports: {},\n } as {\n id: string;\n reports: { [key: string]: number };\n [key: string]: any;\n };\n\n if (aggregatedBy !== undefined) {\n accumulator[keyName][aggregatedBy] = keyName;\n }\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {} as { [key: string]: Report },\n );\n return Object.values(aggregatedReports);\n};\n\nexport const getReportKeyAndValues = (reports: Report[]): { [key: string]: string[] } => {\n const excludedKeys = ['id', 'reports'];\n const keyValueSets: { [key: string]: Set<string> } = {};\n reports.forEach(report => {\n Object.keys(report).forEach(key => {\n if (!excludedKeys.includes(key)) {\n if (keyValueSets[key] === undefined) {\n keyValueSets[key] = new Set<string>();\n } else {\n keyValueSets[key].add(report[key] as string);\n }\n }\n });\n });\n\n const keyValues: { [key: string]: string[] } = {};\n Object.keys(keyValueSets).forEach((key: string) => {\n keyValues[key] = Array.from(keyValueSets[key]);\n });\n return keyValues;\n};\n\nexport const getAllReportTags = (reports: Report[]): string[] => {\n const tags = new Set<string>();\n const reservedKeys = ['id', 'name', 'service', 'category', 'provider', 'reports'];\n reports.forEach(report => {\n Object.keys(report).forEach(key => {\n if (reservedKeys.indexOf(key) === -1) {\n tags.add(key);\n }\n });\n });\n return Array.from(tags);\n};\n\nexport const getPreviousMonth = (month: string): string => {\n const date = parse(month, 'yyyy-MM', new Date());\n const previousMonth = subMonths(date, 1);\n return format(previousMonth, 'yyyy-MM');\n};\n\nexport const getPeriodStrings = (granularity: string, startTime: Date, endTime: Date): string[] => {\n const result: string[] = [];\n const current = moment(startTime);\n\n while (current.isSameOrBefore(endTime) && current.isSameOrBefore(moment())) {\n if (granularity === 'monthly') {\n result.push(current.format('YYYY-MM'));\n current.add(1, 'months');\n } else {\n result.push(current.format('YYYY-MM-DD'));\n current.add(1, 'days');\n }\n }\n\n return result;\n};\n"],"names":[],"mappings":";;;;AAKa,MAAA,gBAAA,GAAmB,CAAC,OAAA,EAAmB,SAAgC,KAAA;AAClF,EAAA,MAAM,aAA8C,EAAC,CAAA;AACrD,EAAA,OAAA,CAAQ,QAAQ,CAAU,MAAA,KAAA;AACxB,IAAA,IAAI,KAAQ,GAAA,CAAA,CAAA;AACZ,IAAA,MAAA,CAAO,MAAO,CAAA,MAAA,CAAO,OAAO,CAAA,CAAE,QAAQ,CAAK,CAAA,KAAA;AACzC,MAAS,KAAA,IAAA,CAAA,CAAA;AAAA,KACV,CAAA,CAAA;AACD,IAAA,UAAA,CAAW,KAAK,EAAE,EAAA,EAAI,MAAO,CAAA,EAAA,EAAI,OAAc,CAAA,CAAA;AAAA,GAChD,CAAA,CAAA;AACD,EAAM,MAAA,gBAAA,GAAmB,WAAW,IAAK,CAAA,CAAC,GAAG,CAAM,KAAA,CAAA,CAAE,KAAQ,GAAA,CAAA,CAAE,KAAK,CAAA,CAAA;AACpE,EAAM,MAAA,WAAA,GAAc,iBAAiB,KAAM,CAAA,CAAA,EAAG,SAAS,CAAE,CAAA,GAAA,CAAI,CAAK,CAAA,KAAA,CAAA,CAAE,EAAE,CAAA,CAAA;AAEtE,EAAA,MAAM,aAAgB,GAAA,MAAA;AAAA,IACpB,OAAA;AAAA,IACA,CAAC,aAAwC,MAAW,KAAA;AAClD,MAAA,IAAI,OAAU,GAAA,QAAA,CAAA;AACd,MAAA,IAAI,WAAY,CAAA,QAAA,CAAS,MAAO,CAAA,EAAE,CAAG,EAAA;AACnC,QAAA,OAAA,GAAU,MAAO,CAAA,EAAA,CAAA;AAAA,OACnB;AACA,MAAI,IAAA,CAAC,WAAY,CAAA,OAAO,CAAG,EAAA;AACzB,QAAA,WAAA,CAAY,OAAO,CAAI,GAAA;AAAA,UACrB,EAAI,EAAA,OAAA;AAAA,UACJ,SAAS,EAAC;AAAA,SACZ,CAAA;AAAA,OACF;AAEA,MAAA,MAAA,CAAO,IAAK,CAAA,MAAA,CAAO,OAAO,CAAA,CAAE,QAAQ,CAAO,GAAA,KAAA;AACzC,QAAA,IAAI,WAAY,CAAA,OAAO,CAAE,CAAA,OAAA,CAAQ,GAAG,CAAG,EAAA;AACrC,UAAA,WAAA,CAAY,OAAO,CAAE,CAAA,OAAA,CAAQ,GAAG,CAAK,IAAA,MAAA,CAAO,QAAQ,GAAG,CAAA,CAAA;AAAA,SAClD,MAAA;AACL,UAAA,WAAA,CAAY,OAAO,CAAE,CAAA,OAAA,CAAQ,GAAG,CAAI,GAAA,MAAA,CAAO,QAAQ,GAAG,CAAA,CAAA;AAAA,SACxD;AAAA,OACD,CAAA,CAAA;AACD,MAAO,OAAA,WAAA,CAAA;AAAA,KACT;AAAA,IACA,EAAC;AAAA,GACH,CAAA;AAEA,EAAO,OAAA,MAAA,CAAO,OAAO,aAAa,CAAA,CAAA;AACpC,EAAA;AAEa,MAAA,iBAAA,GAAoB,CAAC,OAAA,EAAmB,OAA+B,KAAA;AAClF,EAAM,MAAA,eAAA,GAAkB,OAAQ,CAAA,MAAA,CAAO,CAAU,MAAA,KAAA;AAC/C,IAAA,IAAI,KAAQ,GAAA,IAAA,CAAA;AACZ,IAAA,MAAA,CAAO,IAAK,CAAA,OAAO,CAAE,CAAA,OAAA,CAAQ,CAAO,GAAA,KAAA;AAClC,MAAA,IAAI,OAAQ,CAAA,GAAG,CAAE,CAAA,MAAA,GAAS,CAAK,IAAA,CAAC,OAAQ,CAAA,GAAG,CAAE,CAAA,QAAA,CAAS,MAAO,CAAA,GAAG,CAAW,CAAG,EAAA;AAC5E,QAAQ,KAAA,GAAA,KAAA,CAAA;AAAA,OACV;AAAA,KACD,CAAA,CAAA;AACD,IAAO,OAAA,KAAA,CAAA;AAAA,GACR,CAAA,CAAA;AAED,EAAO,OAAA,eAAA,CAAA;AACT,EAAA;AAEa,MAAA,oBAAA,GAAuB,CAAC,OAAA,EAAmB,YAAoC,KAAA;AAC1F,EAAA,MAAM,iBAA+C,GAAA,MAAA;AAAA,IACnD,OAAA;AAAA,IACA,CAAC,aAAa,MAAW,KAAA;AACvB,MAAA,IAAI,OAAkB,GAAA,UAAA,CAAA;AACtB,MAAI,IAAA,YAAA,IAAgB,gBAAgB,MAAQ,EAAA;AAC1C,QAAA,OAAA,GAAU,OAAO,YAAY,CAAA,CAAA;AAAA,OAC/B,MAAA,IAAW,iBAAiB,MAAQ,EAAA;AAClC,QAAU,OAAA,GAAA,OAAA,CAAA;AAAA,OACZ;AAEA,MAAI,IAAA,CAAC,WAAY,CAAA,OAAO,CAAG,EAAA;AACzB,QAAA,WAAA,CAAY,OAAO,CAAI,GAAA;AAAA,UACrB,EAAI,EAAA,OAAA;AAAA,UACJ,SAAS,EAAC;AAAA,SACZ,CAAA;AAMA,QAAA,IAAI,iBAAiB,KAAW,CAAA,EAAA;AAC9B,UAAY,WAAA,CAAA,OAAO,CAAE,CAAA,YAAY,CAAI,GAAA,OAAA,CAAA;AAAA,SACvC;AAAA,OACF;AAEA,MAAA,MAAA,CAAO,IAAK,CAAA,MAAA,CAAO,OAAO,CAAA,CAAE,QAAQ,CAAO,GAAA,KAAA;AACzC,QAAA,IAAI,WAAY,CAAA,OAAO,CAAE,CAAA,OAAA,CAAQ,GAAG,CAAG,EAAA;AACrC,UAAA,WAAA,CAAY,OAAO,CAAE,CAAA,OAAA,CAAQ,GAAG,CAAK,IAAA,MAAA,CAAO,QAAQ,GAAG,CAAA,CAAA;AAAA,SAClD,MAAA;AACL,UAAA,WAAA,CAAY,OAAO,CAAE,CAAA,OAAA,CAAQ,GAAG,CAAI,GAAA,MAAA,CAAO,QAAQ,GAAG,CAAA,CAAA;AAAA,SACxD;AAAA,OACD,CAAA,CAAA;AACD,MAAO,OAAA,WAAA,CAAA;AAAA,KACT;AAAA,IACA,EAAC;AAAA,GACH,CAAA;AACA,EAAO,OAAA,MAAA,CAAO,OAAO,iBAAiB,CAAA,CAAA;AACxC,EAAA;AAEa,MAAA,qBAAA,GAAwB,CAAC,OAAmD,KAAA;AACvF,EAAM,MAAA,YAAA,GAAe,CAAC,IAAA,EAAM,SAAS,CAAA,CAAA;AACrC,EAAA,MAAM,eAA+C,EAAC,CAAA;AACtD,EAAA,OAAA,CAAQ,QAAQ,CAAU,MAAA,KAAA;AACxB,IAAA,MAAA,CAAO,IAAK,CAAA,MAAM,CAAE,CAAA,OAAA,CAAQ,CAAO,GAAA,KAAA;AACjC,MAAA,IAAI,CAAC,YAAA,CAAa,QAAS,CAAA,GAAG,CAAG,EAAA;AAC/B,QAAI,IAAA,YAAA,CAAa,GAAG,CAAA,KAAM,KAAW,CAAA,EAAA;AACnC,UAAa,YAAA,CAAA,GAAG,CAAI,mBAAA,IAAI,GAAY,EAAA,CAAA;AAAA,SAC/B,MAAA;AACL,UAAA,YAAA,CAAa,GAAG,CAAA,CAAE,GAAI,CAAA,MAAA,CAAO,GAAG,CAAW,CAAA,CAAA;AAAA,SAC7C;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACF,CAAA,CAAA;AAED,EAAA,MAAM,YAAyC,EAAC,CAAA;AAChD,EAAA,MAAA,CAAO,IAAK,CAAA,YAAY,CAAE,CAAA,OAAA,CAAQ,CAAC,GAAgB,KAAA;AACjD,IAAA,SAAA,CAAU,GAAG,CAAI,GAAA,KAAA,CAAM,IAAK,CAAA,YAAA,CAAa,GAAG,CAAC,CAAA,CAAA;AAAA,GAC9C,CAAA,CAAA;AACD,EAAO,OAAA,SAAA,CAAA;AACT,EAAA;AAEa,MAAA,gBAAA,GAAmB,CAAC,OAAgC,KAAA;AAC/D,EAAM,MAAA,IAAA,uBAAW,GAAY,EAAA,CAAA;AAC7B,EAAA,MAAM,eAAe,CAAC,IAAA,EAAM,QAAQ,SAAW,EAAA,UAAA,EAAY,YAAY,SAAS,CAAA,CAAA;AAChF,EAAA,OAAA,CAAQ,QAAQ,CAAU,MAAA,KAAA;AACxB,IAAA,MAAA,CAAO,IAAK,CAAA,MAAM,CAAE,CAAA,OAAA,CAAQ,CAAO,GAAA,KAAA;AACjC,MAAA,IAAI,YAAa,CAAA,OAAA,CAAQ,GAAG,CAAA,KAAM,CAAI,CAAA,EAAA;AACpC,QAAA,IAAA,CAAK,IAAI,GAAG,CAAA,CAAA;AAAA,OACd;AAAA,KACD,CAAA,CAAA;AAAA,GACF,CAAA,CAAA;AACD,EAAO,OAAA,KAAA,CAAM,KAAK,IAAI,CAAA,CAAA;AACxB,EAAA;AAEa,MAAA,gBAAA,GAAmB,CAAC,KAA0B,KAAA;AACzD,EAAA,MAAM,OAAO,KAAM,CAAA,KAAA,EAAO,SAAW,kBAAA,IAAI,MAAM,CAAA,CAAA;AAC/C,EAAM,MAAA,aAAA,GAAgB,SAAU,CAAA,IAAA,EAAM,CAAC,CAAA,CAAA;AACvC,EAAO,OAAA,MAAA,CAAO,eAAe,SAAS,CAAA,CAAA;AACxC,EAAA;AAEO,MAAM,gBAAmB,GAAA,CAAC,WAAqB,EAAA,SAAA,EAAiB,OAA4B,KAAA;AACjG,EAAA,MAAM,SAAmB,EAAC,CAAA;AAC1B,EAAM,MAAA,OAAA,GAAU,OAAO,SAAS,CAAA,CAAA;AAEhC,EAAO,OAAA,OAAA,CAAQ,eAAe,OAAO,CAAA,IAAK,QAAQ,cAAe,CAAA,MAAA,EAAQ,CAAG,EAAA;AAC1E,IAAA,IAAI,gBAAgB,SAAW,EAAA;AAC7B,MAAA,MAAA,CAAO,IAAK,CAAA,OAAA,CAAQ,MAAO,CAAA,SAAS,CAAC,CAAA,CAAA;AACrC,MAAQ,OAAA,CAAA,GAAA,CAAI,GAAG,QAAQ,CAAA,CAAA;AAAA,KAClB,MAAA;AACL,MAAA,MAAA,CAAO,IAAK,CAAA,OAAA,CAAQ,MAAO,CAAA,YAAY,CAAC,CAAA,CAAA;AACxC,MAAQ,OAAA,CAAA,GAAA,CAAI,GAAG,MAAM,CAAA,CAAA;AAAA,KACvB;AAAA,GACF;AAEA,EAAO,OAAA,MAAA,CAAA;AACT;;;;"}
1
+ {"version":3,"file":"functions.esm.js","sources":["../../src/api/functions.ts"],"sourcesContent":["import { format, parse, subMonths } from 'date-fns';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { Report, Filters, Tag } from './types';\n\nexport const mergeCostReports = (reports: Report[], threshold: number): Report[] => {\n const totalCosts: { id: string; total: number }[] = [];\n reports.forEach(report => {\n let total = 0;\n Object.values(report.reports).forEach(v => {\n total += v as number;\n });\n totalCosts.push({ id: report.id, total: total });\n });\n const sortedTotalCosts = totalCosts.sort((a, b) => b.total - a.total);\n const idsToBeKept = sortedTotalCosts.slice(0, threshold).map(v => v.id);\n\n const mergedReports = reduce(\n reports,\n (accumulator: { [key: string]: Report }, report) => {\n let keyName = 'others';\n if (idsToBeKept.includes(report.id)) {\n keyName = report.id;\n }\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n reports: {},\n };\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {},\n );\n\n return Object.values(mergedReports);\n};\n\nexport const filterCostReports = (reports: Report[], filters: Filters): Report[] => {\n const filteredReports = reports.filter(report => {\n let match = true;\n Object.keys(filters).forEach(key => {\n if (filters[key].length > 0 && !filters[key].includes(report[key] as string)) {\n match = false;\n }\n });\n return match;\n });\n\n return filteredReports;\n};\n\nexport const aggregateCostReports = (reports: Report[], aggregatedBy?: string): Report[] => {\n const aggregatedReports: { [key: string]: Report } = reduce(\n reports,\n (accumulator, report) => {\n let keyName: string = 'no value';\n if (aggregatedBy && aggregatedBy in report) {\n keyName = report[aggregatedBy] as string;\n } else if (aggregatedBy === 'none') {\n keyName = 'Total';\n }\n\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n provider: report.provider,\n reports: {},\n } as {\n id: string;\n reports: { [key: string]: number };\n [key: string]: any;\n };\n\n if (aggregatedBy !== undefined) {\n accumulator[keyName][aggregatedBy] = keyName;\n }\n }\n\n Object.keys(report.reports).forEach(key => {\n if (accumulator[keyName].reports[key]) {\n accumulator[keyName].reports[key] += report.reports[key];\n } else {\n accumulator[keyName].reports[key] = report.reports[key];\n }\n });\n return accumulator;\n },\n {} as { [key: string]: Report },\n );\n return Object.values(aggregatedReports);\n};\n\nexport const getReportKeyAndValues = (reports: Report[]): { [key: string]: string[] } => {\n const excludedKeys = ['id', 'reports'];\n const keyValueSets: { [key: string]: Set<string> } = {};\n reports.forEach(report => {\n Object.keys(report).forEach(key => {\n if (!excludedKeys.includes(key)) {\n if (keyValueSets[key] === undefined) {\n keyValueSets[key] = new Set<string>();\n }\n\n keyValueSets[key].add(report[key] as string);\n }\n });\n });\n\n const keyValues: { [key: string]: string[] } = {};\n Object.keys(keyValueSets).forEach((key: string) => {\n keyValues[key] = Array.from(keyValueSets[key]);\n });\n return keyValues;\n};\n\nexport const extractProvider = (input: string): string | undefined => {\n let provider = undefined;\n if (input && input.indexOf('/') !== -1) {\n provider = input.split('/')[0];\n }\n\n return provider;\n};\n\nexport const extractAccountInfo = (input: string): { accountName: string; accountId?: string } => {\n // try to match format: accountName (accountId), e.g. aws-dev (123456789012)\n const regex = /^(.*?)\\s*\\(([^)]+)\\)$/;\n const match = input.match(regex);\n\n if (match) {\n const accountName = match[1];\n const accountId = match[2];\n return { accountName: accountName, accountId: accountId };\n }\n\n return { accountName: input };\n};\n\n// check if targetTag exists in tags\nexport function tagExists(tags: Tag[], targetTag: Tag): boolean {\n return tags.some(\n tag => tag.provider === targetTag.provider && tag.key === targetTag.key && tag.value === targetTag.value,\n );\n}\n\n// convert Tag array to (provider1:key1=value1 OR provider2:key2=value2) format\nexport const tagsToString = (tags: Tag[]): string => {\n if (tags.length === 0) {\n return '()';\n }\n\n const keyValuePairs = tags.map(tag => `${tag.provider}:${tag.key}=${tag.value}`);\n return `(${keyValuePairs.join(' OR ')})`;\n};\n\nexport const getAllReportTags = (reports: Report[]): string[] => {\n const tags = new Set<string>();\n const reservedKeys = ['id', 'account', 'service', 'category', 'provider', 'reports'];\n reports.forEach(report => {\n Object.keys(report).forEach(key => {\n if (reservedKeys.indexOf(key) === -1) {\n tags.add(key);\n }\n });\n });\n return Array.from(tags);\n};\n\nexport const getPreviousMonth = (month: string): string => {\n const date = parse(month, 'yyyy-MM', new Date());\n const previousMonth = subMonths(date, 1);\n return format(previousMonth, 'yyyy-MM');\n};\n\nexport const getPeriodStrings = (granularity: string, startTime: Date, endTime: Date): string[] => {\n const result: string[] = [];\n const current = moment(startTime);\n\n while (current.isSameOrBefore(endTime) && current.isSameOrBefore(moment())) {\n if (granularity === 'monthly') {\n result.push(current.format('YYYY-MM'));\n current.add(1, 'months');\n } else {\n result.push(current.format('YYYY-MM-DD'));\n current.add(1, 'days');\n }\n }\n\n return result;\n};\n"],"names":[],"mappings":"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"}
@@ -30,7 +30,7 @@ const ColumnsChartComponent = ({
30
30
  }
31
31
  });
32
32
  const classes = useStyles();
33
- const [showMetrics, setShowMetrics] = useState(true);
33
+ const [showMetrics, setShowMetrics] = useState(false);
34
34
  const [seriesArray, setSeriesArray] = useState([]);
35
35
  const [yaxisArray, setYaxisArray] = useState([]);
36
36
  const [strokeWidthArray, setStrokeWidthArray] = useState([]);
@@ -117,9 +117,18 @@ const ColumnsChartComponent = ({
117
117
  series: seriesArray
118
118
  };
119
119
  const initChartCallback = useCallback(async () => {
120
+ const labelFormatter = (value) => {
121
+ const scale = humanFormat.Scale.create(["", "K", "M", "B"], 1e3);
122
+ if (typeof value !== "number" || isNaN(value)) {
123
+ return "";
124
+ }
125
+ return `$${humanFormat(value, {
126
+ scale,
127
+ separator: ""
128
+ })}`;
129
+ };
120
130
  const strokeWidth = Array(series.length).fill(0);
121
131
  const seriesResult = series.map((s) => s);
122
- const scale = humanFormat.Scale.create(["", "K", "M", "B"], 1e3);
123
132
  const yaxisResult = [
124
133
  {
125
134
  seriesName: series.map((s) => s.name),
@@ -128,41 +137,49 @@ const ColumnsChartComponent = ({
128
137
  text: "Costs in USD"
129
138
  },
130
139
  labels: {
131
- formatter: (value) => {
132
- if (typeof value !== "number" || isNaN(value)) {
133
- return "";
134
- }
135
- return `$${humanFormat(value, {
136
- scale,
137
- separator: ""
138
- })}`;
139
- }
140
+ formatter: labelFormatter
140
141
  }
141
142
  }
142
143
  ];
143
144
  if (metrics && showMetrics) {
145
+ const metricGroups = {};
144
146
  metrics.forEach((metric) => {
145
147
  strokeWidth.push(3);
146
148
  seriesResult.push(metric);
147
- yaxisResult.push({
148
- seriesName: [metric.name],
149
- decimalsInFloat: 2,
150
- opposite: true,
151
- title: {
152
- text: metric.name
153
- },
154
- labels: {
155
- formatter: (value) => {
156
- if (typeof value !== "number" || isNaN(value)) {
157
- return "";
149
+ if (metric.group) {
150
+ if (!metricGroups[metric.group]) {
151
+ metricGroups[metric.group] = {
152
+ seriesName: [metric.name],
153
+ decimalsInFloat: 2,
154
+ opposite: true,
155
+ forceNiceScale: true,
156
+ title: {
157
+ text: metric.group
158
+ },
159
+ labels: {
160
+ formatter: labelFormatter
158
161
  }
159
- return humanFormat(value, {
160
- scale,
161
- separator: ""
162
- });
163
- }
162
+ };
163
+ } else {
164
+ metricGroups[metric.group].seriesName.push(metric.name);
164
165
  }
165
- });
166
+ } else {
167
+ yaxisResult.push({
168
+ seriesName: [metric.name],
169
+ decimalsInFloat: 2,
170
+ opposite: true,
171
+ forceNiceScale: true,
172
+ title: {
173
+ text: metric.name
174
+ },
175
+ labels: {
176
+ formatter: labelFormatter
177
+ }
178
+ });
179
+ }
180
+ });
181
+ Object.values(metricGroups).forEach((group) => {
182
+ yaxisResult.push(group);
166
183
  });
167
184
  }
168
185
  setSeriesArray(seriesResult);
@@ -1 +1 @@
1
- {"version":3,"file":"ColumnsChartComponent.esm.js","sources":["../../../src/components/ColumnsChartComponent/ColumnsChartComponent.tsx"],"sourcesContent":["import { Grid, Paper, Switch } from '@material-ui/core';\nimport { makeStyles, useTheme } from '@material-ui/core/styles';\nimport humanFormat from 'human-format';\nimport React, { FC, useCallback, useEffect, useState } from 'react';\nimport Chart from 'react-apexcharts';\nimport { colorList } from '../constants';\nimport { ColumnsChartComponentProps } from '../types';\n\ntype CurveType =\n | 'smooth'\n | 'straight'\n | 'stepline'\n | 'linestep'\n | 'monotoneCubic'\n | ('smooth' | 'straight' | 'stepline' | 'linestep' | 'monotoneCubic')[]\n | undefined;\n\nexport const ColumnsChartComponent: FC<ColumnsChartComponentProps> = ({\n granularitySetter,\n categories,\n series,\n metrics,\n height,\n thumbnail,\n dataPointSelectionHandler,\n}) => {\n const defaultTheme = useTheme();\n const useStyles = makeStyles({\n fixedHeightPaper: {\n padding: '16px',\n display: 'flex',\n flexDirection: 'column',\n height: height ? height : 300,\n },\n thumbnailPaper: {\n display: 'flex',\n overflow: 'hidden',\n flexDirection: 'column',\n height: height ? height - 70 : 80,\n },\n });\n const classes = useStyles();\n const [showMetrics, setShowMetrics] = useState<boolean>(true);\n const [seriesArray, setSeriesArray] = useState<any[]>([]);\n const [yaxisArray, setYaxisArray] = useState<any[]>([]);\n const [strokeWidthArray, setStrokeWidthArray] = useState<number[]>([]);\n const [strokeDashArray, setStrokeDashArray] = useState<number[]>([]);\n const customScale = humanFormat.Scale.create(['', 'K', 'M', 'B'], 1000);\n\n const state = thumbnail\n ? {\n options: {\n chart: {\n animations: {\n enabled: false,\n },\n zoom: {\n enabled: false,\n },\n stacked: true,\n toolbar: {\n show: false,\n },\n sparkline: {\n enabled: true,\n },\n },\n xaxis: {\n categories: categories,\n },\n theme: {\n mode: defaultTheme.palette.type,\n },\n },\n series: series,\n }\n : {\n options: {\n chart: {\n animations: {\n enabled: false,\n },\n stacked: true,\n toolbar: {\n show: false,\n },\n events: {\n dataPointSelection: dataPointSelectionHandler,\n },\n },\n xaxis: {\n categories: categories,\n },\n stroke: {\n width: strokeWidthArray,\n dashArray: strokeDashArray,\n curve: 'smooth' as CurveType,\n },\n yaxis: yaxisArray,\n dataLabels: {\n enabled: false,\n },\n tooltip: {\n y: {\n formatter: (value: number, { seriesIndex }: { seriesIndex: number }) => {\n if (!value) {\n return '';\n }\n const prefix = seriesIndex <= series.length - 1 ? '$' : '';\n return `${prefix}${humanFormat(value, {\n scale: customScale,\n separator: '',\n })}`;\n },\n },\n fixed: {\n enabled: true,\n position: 'topRight',\n },\n },\n legend: {\n showForSingleSeries: true,\n },\n theme: {\n mode: defaultTheme.palette.type,\n },\n // there are only 5 colors by default, here we extend it to 50 different colors\n colors: colorList,\n },\n series: seriesArray,\n };\n\n const initChartCallback = useCallback(async () => {\n const strokeWidth = Array<number>(series.length).fill(0);\n const seriesResult = series.map(s => s);\n // init a scale here as well, it seems that adding the predefined customScale as a dependency is buggy\n const scale = humanFormat.Scale.create(['', 'K', 'M', 'B'], 1000);\n const yaxisResult: any[] = [\n {\n seriesName: series.map(s => s.name),\n decimalsInFloat: 2,\n title: {\n text: 'Costs in USD',\n },\n labels: {\n formatter: (value: number) => {\n if (typeof value !== 'number' || isNaN(value)) {\n return '';\n }\n return `$${humanFormat(value, {\n scale: scale,\n separator: '',\n })}`;\n },\n },\n },\n ];\n\n if (metrics && showMetrics) {\n metrics.forEach(metric => {\n strokeWidth.push(3);\n seriesResult.push(metric);\n yaxisResult.push({\n seriesName: [metric.name],\n decimalsInFloat: 2,\n opposite: true,\n title: {\n text: metric.name,\n },\n labels: {\n formatter: (value: number) => {\n if (typeof value !== 'number' || isNaN(value)) {\n return '';\n }\n return humanFormat(value, {\n scale: scale,\n separator: '',\n });\n },\n },\n });\n });\n }\n\n setSeriesArray(seriesResult);\n setYaxisArray(yaxisResult);\n setStrokeWidthArray(strokeWidth);\n setStrokeDashArray(Array<number>(seriesResult.length).fill(0));\n }, [metrics, series, showMetrics]);\n\n useEffect(() => {\n initChartCallback();\n }, [initChartCallback]);\n\n return (\n <Paper className={thumbnail ? classes.thumbnailPaper : classes.fixedHeightPaper}>\n <Grid container justifyContent=\"flex-end\" spacing={1}>\n <Grid item>Monthly</Grid>\n <Grid item>\n <Switch size=\"small\" onChange={event => granularitySetter(event.target.checked ? 'daily' : 'monthly')} />\n </Grid>\n <Grid item>Daily</Grid>\n <Grid item> | </Grid>\n <Grid item>\n <Switch size=\"small\" checked={showMetrics} onChange={_ => setShowMetrics((ori: boolean) => !ori)} />\n </Grid>\n <Grid item>Show Metrics</Grid>\n </Grid>\n {seriesArray && (\n <Chart options={state.options} series={state.series} type=\"line\" height={height ? height - 70 : 230} />\n )}\n </Paper>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ColumnsChartComponent.esm.js","sources":["../../../src/components/ColumnsChartComponent/ColumnsChartComponent.tsx"],"sourcesContent":["import { Grid, Paper, Switch } from '@material-ui/core';\nimport { makeStyles, useTheme } from '@material-ui/core/styles';\nimport humanFormat from 'human-format';\nimport React, { FC, useCallback, useEffect, useState } from 'react';\nimport Chart from 'react-apexcharts';\nimport { colorList } from '../constants';\nimport { ColumnsChartComponentProps } from '../types';\n\ntype CurveType =\n | 'smooth'\n | 'straight'\n | 'stepline'\n | 'linestep'\n | 'monotoneCubic'\n | ('smooth' | 'straight' | 'stepline' | 'linestep' | 'monotoneCubic')[]\n | undefined;\n\nexport const ColumnsChartComponent: FC<ColumnsChartComponentProps> = ({\n granularitySetter,\n categories,\n series,\n metrics,\n height,\n thumbnail,\n dataPointSelectionHandler,\n}) => {\n const defaultTheme = useTheme();\n const useStyles = makeStyles({\n fixedHeightPaper: {\n padding: '16px',\n display: 'flex',\n flexDirection: 'column',\n height: height ? height : 300,\n },\n thumbnailPaper: {\n display: 'flex',\n overflow: 'hidden',\n flexDirection: 'column',\n height: height ? height - 70 : 80,\n },\n });\n const classes = useStyles();\n const [showMetrics, setShowMetrics] = useState<boolean>(false);\n const [seriesArray, setSeriesArray] = useState<any[]>([]);\n const [yaxisArray, setYaxisArray] = useState<any[]>([]);\n const [strokeWidthArray, setStrokeWidthArray] = useState<number[]>([]);\n const [strokeDashArray, setStrokeDashArray] = useState<number[]>([]);\n const customScale = humanFormat.Scale.create(['', 'K', 'M', 'B'], 1000);\n\n const state = thumbnail\n ? {\n options: {\n chart: {\n animations: {\n enabled: false,\n },\n zoom: {\n enabled: false,\n },\n stacked: true,\n toolbar: {\n show: false,\n },\n sparkline: {\n enabled: true,\n },\n },\n xaxis: {\n categories: categories,\n },\n theme: {\n mode: defaultTheme.palette.type,\n },\n },\n series: series,\n }\n : {\n options: {\n chart: {\n animations: {\n enabled: false,\n },\n stacked: true,\n toolbar: {\n show: false,\n },\n events: {\n dataPointSelection: dataPointSelectionHandler,\n },\n },\n xaxis: {\n categories: categories,\n },\n stroke: {\n width: strokeWidthArray,\n dashArray: strokeDashArray,\n curve: 'smooth' as CurveType,\n },\n yaxis: yaxisArray,\n dataLabels: {\n enabled: false,\n },\n tooltip: {\n y: {\n formatter: (value: number, { seriesIndex }: { seriesIndex: number }) => {\n if (!value) {\n return '';\n }\n const prefix = seriesIndex <= series.length - 1 ? '$' : '';\n return `${prefix}${humanFormat(value, {\n scale: customScale,\n separator: '',\n })}`;\n },\n },\n fixed: {\n enabled: true,\n position: 'topRight',\n },\n },\n legend: {\n showForSingleSeries: true,\n },\n theme: {\n mode: defaultTheme.palette.type,\n },\n // there are only 5 colors by default, here we extend it to 50 different colors\n colors: colorList,\n },\n series: seriesArray,\n };\n\n const initChartCallback = useCallback(async () => {\n const labelFormatter = (value: number): string => {\n const scale = humanFormat.Scale.create(['', 'K', 'M', 'B'], 1000);\n if (typeof value !== 'number' || isNaN(value)) {\n return '';\n }\n return `$${humanFormat(value, {\n scale: scale,\n separator: '',\n })}`;\n };\n\n const strokeWidth = Array<number>(series.length).fill(0);\n const seriesResult = series.map(s => s);\n const yaxisResult: any[] = [\n {\n seriesName: series.map(s => s.name),\n decimalsInFloat: 2,\n title: {\n text: 'Costs in USD',\n },\n labels: {\n formatter: labelFormatter,\n },\n },\n ];\n\n if (metrics && showMetrics) {\n const metricGroups: any = {};\n metrics.forEach(metric => {\n strokeWidth.push(3);\n seriesResult.push(metric);\n\n if (metric.group) {\n if (!metricGroups[metric.group]) {\n metricGroups[metric.group] = {\n seriesName: [metric.name],\n decimalsInFloat: 2,\n opposite: true,\n forceNiceScale: true,\n title: {\n text: metric.group,\n },\n labels: {\n formatter: labelFormatter,\n },\n };\n } else {\n // yaxis already exists, add the series to the existing one\n metricGroups[metric.group].seriesName.push(metric.name);\n }\n } else {\n // the metric does not have a group, create a separate yaxis for it\n yaxisResult.push({\n seriesName: [metric.name],\n decimalsInFloat: 2,\n opposite: true,\n forceNiceScale: true,\n title: {\n text: metric.name,\n },\n labels: {\n formatter: labelFormatter,\n },\n });\n }\n });\n Object.values(metricGroups).forEach((group: any) => {\n yaxisResult.push(group);\n });\n }\n\n setSeriesArray(seriesResult);\n setYaxisArray(yaxisResult);\n setStrokeWidthArray(strokeWidth);\n setStrokeDashArray(Array<number>(seriesResult.length).fill(0));\n }, [metrics, series, showMetrics]);\n\n useEffect(() => {\n initChartCallback();\n }, [initChartCallback]);\n\n return (\n <Paper className={thumbnail ? classes.thumbnailPaper : classes.fixedHeightPaper}>\n <Grid container justifyContent=\"flex-end\" spacing={1}>\n <Grid item>Monthly</Grid>\n <Grid item>\n <Switch size=\"small\" onChange={event => granularitySetter(event.target.checked ? 'daily' : 'monthly')} />\n </Grid>\n <Grid item>Daily</Grid>\n <Grid item> | </Grid>\n <Grid item>\n <Switch size=\"small\" checked={showMetrics} onChange={_ => setShowMetrics((ori: boolean) => !ori)} />\n </Grid>\n <Grid item>Show Metrics</Grid>\n </Grid>\n {seriesArray && (\n <Chart options={state.options} series={state.series} type=\"line\" height={height ? height - 70 : 230} />\n )}\n </Paper>\n );\n};\n"],"names":[],"mappings":"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"}