@electrolux-oss/plugin-infrawallet-backend 1.1.0-20250901061058-90165b1 → 1.1.0-20250929154750-e4ed5e3

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 (38) hide show
  1. package/dist/cost-clients/AwsClient.cjs.js +34 -7
  2. package/dist/cost-clients/AwsClient.cjs.js.map +1 -1
  3. package/dist/cost-clients/AzureClient.cjs.js +31 -2
  4. package/dist/cost-clients/AzureClient.cjs.js.map +1 -1
  5. package/dist/cost-clients/ConfluentClient.cjs.js +50 -2
  6. package/dist/cost-clients/ConfluentClient.cjs.js.map +1 -1
  7. package/dist/cost-clients/CustomProviderClient.cjs.js +30 -1
  8. package/dist/cost-clients/CustomProviderClient.cjs.js.map +1 -1
  9. package/dist/cost-clients/DatadogClient.cjs.js +38 -2
  10. package/dist/cost-clients/DatadogClient.cjs.js.map +1 -1
  11. package/dist/cost-clients/ElasticCloudClient.cjs.js +39 -7
  12. package/dist/cost-clients/ElasticCloudClient.cjs.js.map +1 -1
  13. package/dist/cost-clients/GCPClient.cjs.js +29 -2
  14. package/dist/cost-clients/GCPClient.cjs.js.map +1 -1
  15. package/dist/cost-clients/GitHubClient.cjs.js +28 -2
  16. package/dist/cost-clients/GitHubClient.cjs.js.map +1 -1
  17. package/dist/cost-clients/InfraWalletClient.cjs.js +3 -0
  18. package/dist/cost-clients/InfraWalletClient.cjs.js.map +1 -1
  19. package/dist/cost-clients/MockClient.cjs.js +18 -0
  20. package/dist/cost-clients/MockClient.cjs.js.map +1 -1
  21. package/dist/cost-clients/MongoAtlasClient.cjs.js +56 -24
  22. package/dist/cost-clients/MongoAtlasClient.cjs.js.map +1 -1
  23. package/dist/schemas/AzureBilling.cjs.js +15 -7
  24. package/dist/schemas/AzureBilling.cjs.js.map +1 -1
  25. package/dist/schemas/ConfluentBilling.cjs.js +20 -8
  26. package/dist/schemas/ConfluentBilling.cjs.js.map +1 -1
  27. package/dist/schemas/DatadogBilling.cjs.js +14 -5
  28. package/dist/schemas/DatadogBilling.cjs.js.map +1 -1
  29. package/dist/schemas/ElasticBilling.cjs.js +5 -5
  30. package/dist/schemas/ElasticBilling.cjs.js.map +1 -1
  31. package/dist/schemas/GCPBilling.cjs.js +48 -18
  32. package/dist/schemas/GCPBilling.cjs.js.map +1 -1
  33. package/dist/service/functions.cjs.js +6 -0
  34. package/dist/service/functions.cjs.js.map +1 -1
  35. package/dist/tasks/fetchAndSaveCosts.cjs.js +1 -1
  36. package/dist/tasks/fetchAndSaveCosts.cjs.js.map +1 -1
  37. package/mock/mock_response.json +252 -0
  38. package/package.json +1 -1
@@ -42,9 +42,25 @@ class CustomProviderClient extends InfraWalletClient.InfraWalletClient {
42
42
  }
43
43
  }
44
44
  }
45
+ let processedRecords = 0;
46
+ let filteredOutZeroAmount = 0;
47
+ let filteredOutMissingFields = 0;
48
+ const filteredOutInvalidDate = 0;
49
+ const filteredOutTimeRange = 0;
50
+ const uniqueKeys = /* @__PURE__ */ new Set();
51
+ const totalRecords = Array.isArray(costResponse) ? costResponse.length : 0;
45
52
  const transformedData = lodash.reduce(
46
53
  costResponse,
47
54
  (accumulator, record) => {
55
+ if (!record.provider || !record.account || !record.service || !record.category || record.cost === void 0 || record.cost === null || !record.usage_month) {
56
+ filteredOutMissingFields++;
57
+ return accumulator;
58
+ }
59
+ const cost = parseFloat(record.cost);
60
+ if (cost === 0) {
61
+ filteredOutZeroAmount++;
62
+ return accumulator;
63
+ }
48
64
  let periodFormat = "YYYY-MM";
49
65
  if (query.granularity === consts.GRANULARITY.DAILY) {
50
66
  periodFormat = "YYYY-MM-DD";
@@ -59,6 +75,7 @@ class CustomProviderClient extends InfraWalletClient.InfraWalletClient {
59
75
  }
60
76
  }
61
77
  if (!accumulator[keyName]) {
78
+ uniqueKeys.add(keyName);
62
79
  accumulator[keyName] = {
63
80
  id: keyName,
64
81
  account: record.account,
@@ -70,10 +87,10 @@ class CustomProviderClient extends InfraWalletClient.InfraWalletClient {
70
87
  };
71
88
  }
72
89
  accumulator[keyName] = { ...accumulator[keyName], ...record.tags };
73
- const cost = parseFloat(record.cost);
74
90
  if (query.granularity === consts.GRANULARITY.MONTHLY) {
75
91
  const period = moment__default.default(record.usage_month.toString(), "YYYYMM").format(periodFormat);
76
92
  accumulator[keyName].reports[period] = cost;
93
+ processedRecords++;
77
94
  } else {
78
95
  if (record.amortization_mode === "average") {
79
96
  const periods = functions.getDailyPeriodStringsForOneMonth(record.usage_month);
@@ -81,18 +98,30 @@ class CustomProviderClient extends InfraWalletClient.InfraWalletClient {
81
98
  periods.forEach((period) => {
82
99
  accumulator[keyName].reports[period] = averageCost;
83
100
  });
101
+ processedRecords += periods.length;
84
102
  } else if (record.amortization_mode === "start_day") {
85
103
  const period = moment__default.default(record.usage_month.toString(), "YYYYMM").startOf("month").format(periodFormat);
86
104
  accumulator[keyName].reports[period] = cost;
105
+ processedRecords++;
87
106
  } else {
88
107
  const period = moment__default.default(record.usage_month.toString(), "YYYYMM").endOf("month").format(periodFormat);
89
108
  accumulator[keyName].reports[period] = cost;
109
+ processedRecords++;
90
110
  }
91
111
  }
92
112
  return accumulator;
93
113
  },
94
114
  {}
95
115
  );
116
+ this.logTransformationSummary({
117
+ processed: processedRecords,
118
+ uniqueReports: uniqueKeys.size,
119
+ zeroAmount: filteredOutZeroAmount,
120
+ missingFields: filteredOutMissingFields,
121
+ invalidDate: filteredOutInvalidDate,
122
+ timeRange: filteredOutTimeRange,
123
+ totalRecords
124
+ });
96
125
  return Object.values(transformedData);
97
126
  }
98
127
  // override this method so that we do not read from the config file
@@ -1 +1 @@
1
- {"version":3,"file":"CustomProviderClient.cjs.js","sources":["../../src/cost-clients/CustomProviderClient.ts"],"sourcesContent":["import { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { getCustomCostsByDateRange } from '../models/CustomCost';\nimport { CACHE_CATEGORY, CLOUD_PROVIDER, PROVIDER_TYPE, GRANULARITY } from '../service/consts';\nimport {\n getDailyPeriodStringsForOneMonth,\n getDefaultCacheTTL,\n getReportsFromCache,\n setReportsToCache,\n} from '../service/functions';\nimport { ClientResponse, CloudProviderError, CostQuery, Report } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\nimport { CustomCostRecordSchema } from '../schemas/CustomProviderBilling';\nimport { ZodError } from 'zod';\n\nexport class CustomProviderClient extends InfraWalletClient {\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new CustomProviderClient(CLOUD_PROVIDER.CUSTOM, config, database, cache, logger);\n }\n\n protected async initCloudClient(_config: Config): Promise<any> {\n return null;\n }\n\n protected async fetchCosts(_integrationConfig: Config | null, _client: any, query: CostQuery): Promise<any> {\n const records = getCustomCostsByDateRange(\n this.database,\n moment(parseInt(query.startTime, 10)).toDate(),\n moment(parseInt(query.endTime, 10)).toDate(),\n );\n return records;\n }\n\n protected async transformCostsData(\n _subAccountConfig: Config | null,\n query: CostQuery,\n costResponse: any,\n ): Promise<Report[]> {\n if (Array.isArray(costResponse)) {\n try {\n costResponse.forEach((record: any) => CustomCostRecordSchema.parse(record));\n this.logger.debug(`Custom cost records validation passed for ${costResponse.length} records`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Custom cost records validation failed: ${error.message}`);\n this.logger.debug(`Sample validation errors: ${JSON.stringify(error.errors.slice(0, 3))}`);\n } else {\n this.logger.warn(`Unexpected validation error: ${error.message}`);\n }\n }\n }\n\n const transformedData = reduce(\n costResponse,\n (accumulator: { [key: string]: Report }, record) => {\n let periodFormat = 'YYYY-MM';\n if (query.granularity === GRANULARITY.DAILY) {\n periodFormat = 'YYYY-MM-DD';\n }\n\n const keyName = `${record.provider}-${record.account}-${record.service}`;\n\n // make it compatible with SQLite database\n if (typeof record.tags === 'string') {\n try {\n record.tags = JSON.parse(record.tags);\n } catch (error) {\n this.logger.error(`Failed to parse tags for custom cost ${keyName}, tags: ${record.tags}`);\n record.tags = {};\n }\n }\n\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n account: record.account,\n service: record.service,\n category: record.category,\n provider: record.provider,\n providerType: PROVIDER_TYPE.CUSTOM,\n reports: {},\n };\n }\n\n // if custom costs with same provider+account+service values, but contain different tags\n // we merge these tags, but the tag with same key will be overriden\n accumulator[keyName] = { ...accumulator[keyName], ...record.tags };\n\n const cost = parseFloat(record.cost);\n if (query.granularity === GRANULARITY.MONTHLY) {\n const period = moment(record.usage_month.toString(), 'YYYYMM').format(periodFormat);\n accumulator[keyName].reports[period] = cost;\n } else {\n if (record.amortization_mode === 'average') {\n // calculate the average daily cost\n const periods = getDailyPeriodStringsForOneMonth(record.usage_month);\n const averageCost = parseFloat(record.cost) / periods.length;\n periods.forEach(period => {\n accumulator[keyName].reports[period] = averageCost;\n });\n } else if (record.amortization_mode === 'start_day') {\n const period = moment(record.usage_month.toString(), 'YYYYMM').startOf('month').format(periodFormat);\n accumulator[keyName].reports[period] = cost;\n } else {\n const period = moment(record.usage_month.toString(), 'YYYYMM').endOf('month').format(periodFormat);\n accumulator[keyName].reports[period] = cost;\n }\n }\n\n return accumulator;\n },\n {},\n );\n\n return Object.values(transformedData);\n }\n\n // override this method so that we do not read from the config file\n async getCostReports(query: CostQuery): Promise<ClientResponse> {\n const results: Report[] = [];\n const errors: CloudProviderError[] = [];\n\n // first check if there is any cached\n const cachedCosts = await getReportsFromCache(this.cache, this.provider, 'custom', query);\n if (cachedCosts) {\n this.logger.debug(`${this.provider} costs from cache`);\n cachedCosts.forEach(cost => {\n results.push(cost);\n });\n }\n\n try {\n const costResponse = await this.fetchCosts(null, null, query);\n\n const transformedReports = await this.transformCostsData(null, query, costResponse);\n\n // cache the results\n await setReportsToCache(\n this.cache,\n transformedReports,\n this.provider,\n 'custom',\n query,\n getDefaultCacheTTL(CACHE_CATEGORY.COSTS, this.provider),\n );\n\n transformedReports.forEach((value: any) => {\n results.push(value);\n });\n } catch (e) {\n this.logger.error(e);\n errors.push({\n provider: this.provider,\n name: this.provider,\n error: e.message,\n });\n }\n return {\n reports: results,\n errors: errors,\n };\n }\n}\n"],"names":["InfraWalletClient","CLOUD_PROVIDER","getCustomCostsByDateRange","moment","CustomCostRecordSchema","ZodError","reduce","GRANULARITY","PROVIDER_TYPE","getDailyPeriodStringsForOneMonth","getReportsFromCache","setReportsToCache","getDefaultCacheTTL","CACHE_CATEGORY"],"mappings":"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"}
1
+ {"version":3,"file":"CustomProviderClient.cjs.js","sources":["../../src/cost-clients/CustomProviderClient.ts"],"sourcesContent":["import { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { getCustomCostsByDateRange } from '../models/CustomCost';\nimport { CACHE_CATEGORY, CLOUD_PROVIDER, PROVIDER_TYPE, GRANULARITY } from '../service/consts';\nimport {\n getDailyPeriodStringsForOneMonth,\n getDefaultCacheTTL,\n getReportsFromCache,\n setReportsToCache,\n} from '../service/functions';\nimport { ClientResponse, CloudProviderError, CostQuery, Report } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\nimport { CustomCostRecordSchema } from '../schemas/CustomProviderBilling';\nimport { ZodError } from 'zod';\n\nexport class CustomProviderClient extends InfraWalletClient {\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new CustomProviderClient(CLOUD_PROVIDER.CUSTOM, config, database, cache, logger);\n }\n\n protected async initCloudClient(_config: Config): Promise<any> {\n return null;\n }\n\n protected async fetchCosts(_integrationConfig: Config | null, _client: any, query: CostQuery): Promise<any> {\n const records = getCustomCostsByDateRange(\n this.database,\n moment(parseInt(query.startTime, 10)).toDate(),\n moment(parseInt(query.endTime, 10)).toDate(),\n );\n return records;\n }\n\n protected async transformCostsData(\n _subAccountConfig: Config | null,\n query: CostQuery,\n costResponse: any,\n ): Promise<Report[]> {\n if (Array.isArray(costResponse)) {\n try {\n costResponse.forEach((record: any) => CustomCostRecordSchema.parse(record));\n this.logger.debug(`Custom cost records validation passed for ${costResponse.length} records`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Custom cost records validation failed: ${error.message}`);\n this.logger.debug(`Sample validation errors: ${JSON.stringify(error.errors.slice(0, 3))}`);\n } else {\n this.logger.warn(`Unexpected validation error: ${error.message}`);\n }\n }\n }\n\n // Initialize tracking variables\n let processedRecords = 0;\n let filteredOutZeroAmount = 0;\n let filteredOutMissingFields = 0;\n const filteredOutInvalidDate = 0;\n const filteredOutTimeRange = 0;\n const uniqueKeys = new Set<string>();\n const totalRecords = Array.isArray(costResponse) ? costResponse.length : 0;\n\n const transformedData = reduce(\n costResponse,\n (accumulator: { [key: string]: Report }, record) => {\n // Check for missing fields\n if (\n !record.provider ||\n !record.account ||\n !record.service ||\n !record.category ||\n record.cost === undefined ||\n record.cost === null ||\n !record.usage_month\n ) {\n filteredOutMissingFields++;\n return accumulator;\n }\n\n const cost = parseFloat(record.cost);\n\n // Check for zero amount\n if (cost === 0) {\n filteredOutZeroAmount++;\n return accumulator;\n }\n\n let periodFormat = 'YYYY-MM';\n if (query.granularity === GRANULARITY.DAILY) {\n periodFormat = 'YYYY-MM-DD';\n }\n\n const keyName = `${record.provider}-${record.account}-${record.service}`;\n\n // make it compatible with SQLite database\n if (typeof record.tags === 'string') {\n try {\n record.tags = JSON.parse(record.tags);\n } catch (error) {\n this.logger.error(`Failed to parse tags for custom cost ${keyName}, tags: ${record.tags}`);\n record.tags = {};\n }\n }\n\n if (!accumulator[keyName]) {\n uniqueKeys.add(keyName);\n accumulator[keyName] = {\n id: keyName,\n account: record.account,\n service: record.service,\n category: record.category,\n provider: record.provider,\n providerType: PROVIDER_TYPE.CUSTOM,\n reports: {},\n };\n }\n\n // if custom costs with same provider+account+service values, but contain different tags\n // we merge these tags, but the tag with same key will be overriden\n accumulator[keyName] = { ...accumulator[keyName], ...record.tags };\n\n if (query.granularity === GRANULARITY.MONTHLY) {\n const period = moment(record.usage_month.toString(), 'YYYYMM').format(periodFormat);\n accumulator[keyName].reports[period] = cost;\n processedRecords++;\n } else {\n if (record.amortization_mode === 'average') {\n // calculate the average daily cost\n const periods = getDailyPeriodStringsForOneMonth(record.usage_month);\n const averageCost = parseFloat(record.cost) / periods.length;\n periods.forEach(period => {\n accumulator[keyName].reports[period] = averageCost;\n });\n processedRecords += periods.length;\n } else if (record.amortization_mode === 'start_day') {\n const period = moment(record.usage_month.toString(), 'YYYYMM').startOf('month').format(periodFormat);\n accumulator[keyName].reports[period] = cost;\n processedRecords++;\n } else {\n const period = moment(record.usage_month.toString(), 'YYYYMM').endOf('month').format(periodFormat);\n accumulator[keyName].reports[period] = cost;\n processedRecords++;\n }\n }\n\n return accumulator;\n },\n {},\n );\n\n this.logTransformationSummary({\n processed: processedRecords,\n uniqueReports: uniqueKeys.size,\n zeroAmount: filteredOutZeroAmount,\n missingFields: filteredOutMissingFields,\n invalidDate: filteredOutInvalidDate,\n timeRange: filteredOutTimeRange,\n totalRecords,\n });\n\n return Object.values(transformedData);\n }\n\n // override this method so that we do not read from the config file\n async getCostReports(query: CostQuery): Promise<ClientResponse> {\n const results: Report[] = [];\n const errors: CloudProviderError[] = [];\n\n // first check if there is any cached\n const cachedCosts = await getReportsFromCache(this.cache, this.provider, 'custom', query);\n if (cachedCosts) {\n this.logger.debug(`${this.provider} costs from cache`);\n cachedCosts.forEach(cost => {\n results.push(cost);\n });\n }\n\n try {\n const costResponse = await this.fetchCosts(null, null, query);\n\n const transformedReports = await this.transformCostsData(null, query, costResponse);\n\n // cache the results\n await setReportsToCache(\n this.cache,\n transformedReports,\n this.provider,\n 'custom',\n query,\n getDefaultCacheTTL(CACHE_CATEGORY.COSTS, this.provider),\n );\n\n transformedReports.forEach((value: any) => {\n results.push(value);\n });\n } catch (e) {\n this.logger.error(e);\n errors.push({\n provider: this.provider,\n name: this.provider,\n error: e.message,\n });\n }\n return {\n reports: results,\n errors: errors,\n };\n }\n}\n"],"names":["InfraWalletClient","CLOUD_PROVIDER","getCustomCostsByDateRange","moment","CustomCostRecordSchema","ZodError","reduce","GRANULARITY","PROVIDER_TYPE","getDailyPeriodStringsForOneMonth","getReportsFromCache","setReportsToCache","getDefaultCacheTTL","CACHE_CATEGORY"],"mappings":"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"}
@@ -189,22 +189,48 @@ class DatadogClient extends InfraWalletClient.InfraWalletClient {
189
189
  const [k, v] = tag.split(":");
190
190
  tagKeyValues[k.trim()] = v.trim();
191
191
  });
192
+ let processedRecords = 0;
193
+ let filteredOutZeroAmount = 0;
194
+ let filteredOutMissingFields = 0;
195
+ let filteredOutInvalidDate = 0;
196
+ const filteredOutTimeRange = 0;
197
+ const uniqueKeys = /* @__PURE__ */ new Set();
198
+ const totalRecords = costResponse?.length || 0;
192
199
  const transformedData = lodash.reduce(
193
200
  costResponse,
194
201
  (accumulator, costByOrg) => {
195
202
  const account = costByOrg.orgName;
196
203
  const charges = costByOrg.charges;
204
+ if (!account || !costByOrg.date) {
205
+ filteredOutMissingFields++;
206
+ return accumulator;
207
+ }
197
208
  let periodFormat = "YYYY-MM";
198
209
  if (query.granularity === consts.GRANULARITY.DAILY) {
199
210
  periodFormat = "YYYY-MM-DD";
200
211
  }
201
- const period = moment__default.default(costByOrg.date).format(periodFormat);
212
+ const dateObj = moment__default.default(costByOrg.date);
213
+ if (!dateObj.isValid()) {
214
+ filteredOutInvalidDate++;
215
+ return accumulator;
216
+ }
217
+ const period = dateObj.format(periodFormat);
202
218
  if (charges) {
203
219
  charges.forEach((charge) => {
204
220
  const productName = charge.productName;
205
221
  const cost = charge.cost;
222
+ if (!productName || cost === void 0 || cost === null) {
223
+ filteredOutMissingFields++;
224
+ return;
225
+ }
226
+ const amount = functions.parseCost(cost);
227
+ if (amount === 0) {
228
+ filteredOutZeroAmount++;
229
+ return;
230
+ }
206
231
  const keyName = `${account}->${productName} (${charge.chargeType})`;
207
232
  if (!accumulator[keyName]) {
233
+ uniqueKeys.add(keyName);
208
234
  accumulator[keyName] = {
209
235
  id: keyName,
210
236
  account: `${this.provider}/${account}`,
@@ -216,13 +242,23 @@ class DatadogClient extends InfraWalletClient.InfraWalletClient {
216
242
  ...tagKeyValues
217
243
  };
218
244
  }
219
- accumulator[keyName].reports[period] = functions.parseCost(cost);
245
+ accumulator[keyName].reports[period] = amount;
246
+ processedRecords++;
220
247
  });
221
248
  }
222
249
  return accumulator;
223
250
  },
224
251
  {}
225
252
  );
253
+ this.logTransformationSummary({
254
+ processed: processedRecords,
255
+ uniqueReports: uniqueKeys.size,
256
+ zeroAmount: filteredOutZeroAmount,
257
+ missingFields: filteredOutMissingFields,
258
+ invalidDate: filteredOutInvalidDate,
259
+ timeRange: filteredOutTimeRange,
260
+ totalRecords
261
+ });
226
262
  return Object.values(transformedData);
227
263
  }
228
264
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DatadogClient.cjs.js","sources":["../../src/cost-clients/DatadogClient.ts"],"sourcesContent":["import { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { v2 as datadogApiV2, client as datadogClient } from '@datadog/datadog-api-client';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { CLOUD_PROVIDER, GRANULARITY, PROVIDER_TYPE } from '../service/consts';\nimport { parseCost } from '../service/functions';\nimport { CostQuery, Report } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\nimport { DatadogCostByOrgResponseSchema } from '../schemas/DatadogBilling';\nimport { ZodError } from 'zod';\n\nexport class DatadogClient extends InfraWalletClient {\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new DatadogClient(CLOUD_PROVIDER.DATADOG, config, database, cache, logger);\n }\n\n protected convertServiceName(serviceName: string): string {\n let convertedName = serviceName;\n\n // Datadog doesn't have this documented offically, raise a PR if any service is missed\n const aliases = new Map<string, string>([\n ['apm_host', 'APM Hosts'],\n ['apm_host_enterprise', 'APM Enterprise Hosts'],\n ['application_vulnerability_management_oss_host', 'Application Security - SCA Host'],\n ['application_security_host', 'ASM - Threat Management Hosts'],\n ['audit_trail', 'Audit Trail'],\n ['ci_pipeline', 'CI Visibility Committers'],\n ['ci_pipeline_indexed_spans', 'CI Visibility Spans'],\n ['cloud_cost_management', 'Cloud Cost Hosts'],\n ['cspm_container', 'Cloud Security Management Containers Pro'],\n ['cspm_host', 'Cloud Security Management Hosts Pro'],\n ['csm_host_pro', 'Cloud Security Management Hosts Pro'],\n ['cws_host', 'Cloud Workload Security Hosts'],\n ['infra_container', 'Containers'],\n ['infra_container_excl_agent', 'Containers'],\n ['timeseries', 'Custom Metrics'],\n ['error_tracking', 'Error Tracking'],\n ['incident_management', 'Incident Management'],\n ['logs_indexed_15day', 'Indexed Logs (15-day Retention)'],\n ['logs_indexed_180day', 'Indexed Logs (180-day Retention)'],\n ['logs_indexed_1day', 'Indexed Logs (1-day Retention)'],\n ['logs_indexed_30day', 'Indexed Logs (30-day Retention)'],\n ['logs_indexed_360day', 'Indexed Logs (360-day Retention)'],\n ['logs_indexed_3day', 'Indexed Logs (3-day Retention)'],\n ['logs_indexed_45day', 'Indexed Logs (45-day Retention)'],\n ['logs_indexed_60day', 'Indexed Logs (60-day Retention)'],\n ['logs_indexed_7day', 'Indexed Logs (7-day Retention)'],\n ['logs_indexed_90day', 'Indexed Logs (90-day Retention)'],\n ['apm_trace_search', 'Indexed Spans'],\n ['infra_host', 'Infra Hosts'],\n ['logs_ingested', 'Ingested Logs'],\n ['ingested_spans', 'Ingested Spans'],\n ['iot', 'IoT Devices'],\n ['npm_host', 'Network Hosts'],\n ['prof_container', 'Profiled Containers'],\n ['prof_host', 'Profiled Hosts'],\n ['rum_lite', 'RUM Sessions'],\n ['rum_replay', 'RUM with Session Replay Sessions'],\n ['siem_indexed', 'Security Analyzed and Indexed Logs'],\n ['sensitive_data_scanner', 'Sensitive Data Scanner'],\n ['serverless_apps', 'Serverless App Instances'],\n ['serverless_apm', 'Serverless Traced Invocations'],\n ['serverless_infra', 'Serverless Workload Functions'],\n ['siem', 'SIEM - Analyzed Logs'],\n ['synthetics_api_tests', 'Synthetics API Test Runs'],\n ['synthetics_browser_checks', 'Synthetics Browser Test Runs'],\n ['ci_testing', 'Test Visibility Committers'],\n ['ci_test_indexed_spans', 'Test Visibility Spans'],\n ]);\n\n if (aliases.has(convertedName)) {\n convertedName = aliases.get(convertedName) || convertedName;\n }\n\n return `${this.provider}/${convertedName}`;\n }\n\n protected async initCloudClient(integrationConfig: any): Promise<any> {\n const apiKey = integrationConfig.getString('apiKey');\n const applicationKey = integrationConfig.getString('applicationKey');\n const ddSite = integrationConfig.getString('ddSite');\n const configuration = datadogClient.createConfiguration({\n baseServer: new datadogClient.BaseServerConfiguration(ddSite, {}),\n authMethods: {\n apiKeyAuth: apiKey,\n appKeyAuth: applicationKey,\n },\n });\n const client = new datadogApiV2.UsageMeteringApi(configuration);\n return client;\n }\n\n protected async fetchCosts(integrationConfig: Config, client: any, query: CostQuery): Promise<any> {\n const costData: datadogApiV2.CostByOrg[] = [];\n const startTime = moment(parseInt(query.startTime, 10));\n const endTime = moment(parseInt(query.endTime, 10));\n const firstDayOfLastMonth = moment().subtract(1, 'M').startOf('M');\n\n // check if costs prior to 2 months ago are in query, if yes, use historical_cost API\n // https://docs.datadoghq.com/api/latest/usage-metering/#get-historical-cost-across-your-account\n if (startTime.isBefore(firstDayOfLastMonth)) {\n const historicalCost: datadogApiV2.CostByOrgResponse = await client.getHistoricalCostByOrg({\n startMonth: startTime,\n endMonth: firstDayOfLastMonth.subtract(1, 'd'),\n view: 'sub-org',\n });\n\n try {\n DatadogCostByOrgResponseSchema.parse(historicalCost);\n this.logger.debug(`Datadog historical cost response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Datadog historical cost response validation failed: ${error.message}`);\n this.logger.debug(`Sample validation errors: ${JSON.stringify(error.errors.slice(0, 3))}`);\n } else {\n this.logger.warn(`Unexpected validation error: ${error.message}`);\n }\n }\n\n if (historicalCost.data) {\n costData.push(...historicalCost.data);\n }\n }\n\n // check if current/last month costs are in query, if yes, use estimated_cost API\n // https://docs.datadoghq.com/api/latest/usage-metering/#get-estimated-cost-across-your-account\n if (endTime.isSameOrAfter(firstDayOfLastMonth)) {\n let estimatedCostStartTime = startTime;\n if (startTime.isBefore(firstDayOfLastMonth)) {\n estimatedCostStartTime = firstDayOfLastMonth;\n }\n\n const estimatedCost: datadogApiV2.CostByOrgResponse = await client.getEstimatedCostByOrg({\n startMonth: estimatedCostStartTime,\n endMonth: endTime,\n view: 'sub-org',\n });\n\n try {\n DatadogCostByOrgResponseSchema.parse(estimatedCost);\n this.logger.debug(`Datadog estimated cost response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Datadog estimated cost response validation failed: ${error.message}`);\n this.logger.debug(`Sample validation errors: ${JSON.stringify(error.errors.slice(0, 3))}`);\n } else {\n this.logger.warn(`Unexpected validation error: ${error.message}`);\n }\n }\n\n if (estimatedCost.data) {\n costData.push(...estimatedCost.data);\n }\n }\n\n const costs: any[] = [];\n\n if (query.granularity === GRANULARITY.MONTHLY) {\n costData.forEach(costByOrg => {\n const orgName = costByOrg.attributes?.orgName as string;\n if (!this.evaluateIntegrationFilters(orgName, integrationConfig)) {\n return;\n }\n\n costs.push({\n orgName: orgName,\n date: costByOrg.attributes?.date,\n // only keep cost breakdown\n charges: costByOrg.attributes?.charges?.filter(charge => charge.chargeType !== 'total'),\n });\n });\n } else {\n // Datadog doesn't provide daily costs based on usage, so we allocate monthly costs evenly by day\n costData.forEach(costByOrg => {\n const orgName = costByOrg.attributes?.orgName as string;\n if (!this.evaluateIntegrationFilters(orgName, integrationConfig)) {\n return;\n }\n\n const daysInMonth = moment(costByOrg.attributes?.date).daysInMonth();\n costByOrg.attributes?.charges?.forEach(charge => {\n if (charge.chargeType === 'total') {\n // only keep cost breakdown\n return;\n }\n\n for (let i = 0; i < daysInMonth; i++) {\n const dailyCost = {\n orgName: orgName,\n date: moment(costByOrg.attributes?.date).add(i, 'd'),\n charges: [\n {\n productName: charge.productName,\n cost: (charge.cost || 0) / daysInMonth,\n chargeType: charge.chargeType,\n },\n ],\n };\n costs.push(dailyCost);\n }\n });\n });\n }\n\n return costs;\n }\n\n protected async transformCostsData(subAccountConfig: Config, query: CostQuery, costResponse: any): Promise<Report[]> {\n const tags = subAccountConfig.getOptionalStringArray('tags');\n const tagKeyValues: { [key: string]: string } = {};\n tags?.forEach(tag => {\n const [k, v] = tag.split(':');\n tagKeyValues[k.trim()] = v.trim();\n });\n\n const transformedData = reduce(\n costResponse,\n (accumulator: { [key: string]: Report }, costByOrg) => {\n const account = costByOrg.orgName;\n const charges = costByOrg.charges;\n\n let periodFormat = 'YYYY-MM';\n if (query.granularity === GRANULARITY.DAILY) {\n periodFormat = 'YYYY-MM-DD';\n }\n const period = moment(costByOrg.date).format(periodFormat);\n\n if (charges) {\n charges.forEach((charge: datadogApiV2.ChargebackBreakdown) => {\n const productName = charge.productName;\n const cost = charge.cost;\n const keyName = `${account}->${productName} (${charge.chargeType})`;\n\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n account: `${this.provider}/${account}`,\n service: `${this.convertServiceName(productName as string)} (${charge.chargeType})`,\n category: 'Observability',\n provider: this.provider,\n providerType: PROVIDER_TYPE.INTEGRATION,\n reports: {},\n ...tagKeyValues,\n };\n }\n\n accumulator[keyName].reports[period] = parseCost(cost);\n });\n }\n\n return accumulator;\n },\n {},\n );\n\n return Object.values(transformedData);\n }\n}\n"],"names":["InfraWalletClient","CLOUD_PROVIDER","datadogClient","datadogApiV2","moment","DatadogCostByOrgResponseSchema","ZodError","GRANULARITY","reduce","PROVIDER_TYPE","parseCost"],"mappings":";;;;;;;;;;;;;;;AAYO,MAAM,sBAAsBA,mCAAA,CAAkB;AAAA,EACnD,OAAO,MAAA,CAAO,MAAA,EAAgB,QAAA,EAA2B,OAAqB,MAAA,EAAuB;AACnG,IAAA,OAAO,IAAI,aAAA,CAAcC,qBAAA,CAAe,SAAS,MAAA,EAAQ,QAAA,EAAU,OAAO,MAAM,CAAA;AAAA;AAClF,EAEU,mBAAmB,WAAA,EAA6B;AACxD,IAAA,IAAI,aAAA,GAAgB,WAAA;AAGpB,IAAA,MAAM,OAAA,uBAAc,GAAA,CAAoB;AAAA,MACtC,CAAC,YAAY,WAAW,CAAA;AAAA,MACxB,CAAC,uBAAuB,sBAAsB,CAAA;AAAA,MAC9C,CAAC,iDAAiD,iCAAiC,CAAA;AAAA,MACnF,CAAC,6BAA6B,+BAA+B,CAAA;AAAA,MAC7D,CAAC,eAAe,aAAa,CAAA;AAAA,MAC7B,CAAC,eAAe,0BAA0B,CAAA;AAAA,MAC1C,CAAC,6BAA6B,qBAAqB,CAAA;AAAA,MACnD,CAAC,yBAAyB,kBAAkB,CAAA;AAAA,MAC5C,CAAC,kBAAkB,0CAA0C,CAAA;AAAA,MAC7D,CAAC,aAAa,qCAAqC,CAAA;AAAA,MACnD,CAAC,gBAAgB,qCAAqC,CAAA;AAAA,MACtD,CAAC,YAAY,+BAA+B,CAAA;AAAA,MAC5C,CAAC,mBAAmB,YAAY,CAAA;AAAA,MAChC,CAAC,8BAA8B,YAAY,CAAA;AAAA,MAC3C,CAAC,cAAc,gBAAgB,CAAA;AAAA,MAC/B,CAAC,kBAAkB,gBAAgB,CAAA;AAAA,MACnC,CAAC,uBAAuB,qBAAqB,CAAA;AAAA,MAC7C,CAAC,sBAAsB,iCAAiC,CAAA;AAAA,MACxD,CAAC,uBAAuB,kCAAkC,CAAA;AAAA,MAC1D,CAAC,qBAAqB,gCAAgC,CAAA;AAAA,MACtD,CAAC,sBAAsB,iCAAiC,CAAA;AAAA,MACxD,CAAC,uBAAuB,kCAAkC,CAAA;AAAA,MAC1D,CAAC,qBAAqB,gCAAgC,CAAA;AAAA,MACtD,CAAC,sBAAsB,iCAAiC,CAAA;AAAA,MACxD,CAAC,sBAAsB,iCAAiC,CAAA;AAAA,MACxD,CAAC,qBAAqB,gCAAgC,CAAA;AAAA,MACtD,CAAC,sBAAsB,iCAAiC,CAAA;AAAA,MACxD,CAAC,oBAAoB,eAAe,CAAA;AAAA,MACpC,CAAC,cAAc,aAAa,CAAA;AAAA,MAC5B,CAAC,iBAAiB,eAAe,CAAA;AAAA,MACjC,CAAC,kBAAkB,gBAAgB,CAAA;AAAA,MACnC,CAAC,OAAO,aAAa,CAAA;AAAA,MACrB,CAAC,YAAY,eAAe,CAAA;AAAA,MAC5B,CAAC,kBAAkB,qBAAqB,CAAA;AAAA,MACxC,CAAC,aAAa,gBAAgB,CAAA;AAAA,MAC9B,CAAC,YAAY,cAAc,CAAA;AAAA,MAC3B,CAAC,cAAc,kCAAkC,CAAA;AAAA,MACjD,CAAC,gBAAgB,oCAAoC,CAAA;AAAA,MACrD,CAAC,0BAA0B,wBAAwB,CAAA;AAAA,MACnD,CAAC,mBAAmB,0BAA0B,CAAA;AAAA,MAC9C,CAAC,kBAAkB,+BAA+B,CAAA;AAAA,MAClD,CAAC,oBAAoB,+BAA+B,CAAA;AAAA,MACpD,CAAC,QAAQ,sBAAsB,CAAA;AAAA,MAC/B,CAAC,wBAAwB,0BAA0B,CAAA;AAAA,MACnD,CAAC,6BAA6B,8BAA8B,CAAA;AAAA,MAC5D,CAAC,cAAc,4BAA4B,CAAA;AAAA,MAC3C,CAAC,yBAAyB,uBAAuB;AAAA,KAClD,CAAA;AAED,IAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAA,EAAG;AAC9B,MAAA,aAAA,GAAgB,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAA,IAAK,aAAA;AAAA;AAGhD,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,QAAQ,CAAA,CAAA,EAAI,aAAa,CAAA,CAAA;AAAA;AAC1C,EAEA,MAAgB,gBAAgB,iBAAA,EAAsC;AACpE,IAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,SAAA,CAAU,QAAQ,CAAA;AACnD,IAAA,MAAM,cAAA,GAAiB,iBAAA,CAAkB,SAAA,CAAU,gBAAgB,CAAA;AACnE,IAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,SAAA,CAAU,QAAQ,CAAA;AACnD,IAAA,MAAM,aAAA,GAAgBC,wBAAc,mBAAA,CAAoB;AAAA,MACtD,YAAY,IAAIA,uBAAA,CAAc,uBAAA,CAAwB,MAAA,EAAQ,EAAE,CAAA;AAAA,MAChE,WAAA,EAAa;AAAA,QACX,UAAA,EAAY,MAAA;AAAA,QACZ,UAAA,EAAY;AAAA;AACd,KACD,CAAA;AACD,IAAA,MAAM,MAAA,GAAS,IAAIC,mBAAA,CAAa,gBAAA,CAAiB,aAAa,CAAA;AAC9D,IAAA,OAAO,MAAA;AAAA;AACT,EAEA,MAAgB,UAAA,CAAW,iBAAA,EAA2B,MAAA,EAAa,KAAA,EAAgC;AACjG,IAAA,MAAM,WAAqC,EAAC;AAC5C,IAAA,MAAM,YAAYC,uBAAA,CAAO,QAAA,CAAS,KAAA,CAAM,SAAA,EAAW,EAAE,CAAC,CAAA;AACtD,IAAA,MAAM,UAAUA,uBAAA,CAAO,QAAA,CAAS,KAAA,CAAM,OAAA,EAAS,EAAE,CAAC,CAAA;AAClD,IAAA,MAAM,mBAAA,GAAsBA,yBAAO,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA,CAAE,QAAQ,GAAG,CAAA;AAIjE,IAAA,IAAI,SAAA,CAAU,QAAA,CAAS,mBAAmB,CAAA,EAAG;AAC3C,MAAA,MAAM,cAAA,GAAiD,MAAM,MAAA,CAAO,sBAAA,CAAuB;AAAA,QACzF,UAAA,EAAY,SAAA;AAAA,QACZ,QAAA,EAAU,mBAAA,CAAoB,QAAA,CAAS,CAAA,EAAG,GAAG,CAAA;AAAA,QAC7C,IAAA,EAAM;AAAA,OACP,CAAA;AAED,MAAA,IAAI;AACF,QAAAC,6CAAA,CAA+B,MAAM,cAAc,CAAA;AACnD,QAAA,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,kDAAA,CAAoD,CAAA;AAAA,eAC/D,KAAA,EAAO;AACd,QAAA,IAAI,iBAAiBC,YAAA,EAAU;AAC7B,UAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,oDAAA,EAAuD,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AACvF,UAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,0BAAA,EAA6B,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,SAC3F,MAAO;AACL,UAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,6BAAA,EAAgC,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA;AAClE;AAGF,MAAA,IAAI,eAAe,IAAA,EAAM;AACvB,QAAA,QAAA,CAAS,IAAA,CAAK,GAAG,cAAA,CAAe,IAAI,CAAA;AAAA;AACtC;AAKF,IAAA,IAAI,OAAA,CAAQ,aAAA,CAAc,mBAAmB,CAAA,EAAG;AAC9C,MAAA,IAAI,sBAAA,GAAyB,SAAA;AAC7B,MAAA,IAAI,SAAA,CAAU,QAAA,CAAS,mBAAmB,CAAA,EAAG;AAC3C,QAAA,sBAAA,GAAyB,mBAAA;AAAA;AAG3B,MAAA,MAAM,aAAA,GAAgD,MAAM,MAAA,CAAO,qBAAA,CAAsB;AAAA,QACvF,UAAA,EAAY,sBAAA;AAAA,QACZ,QAAA,EAAU,OAAA;AAAA,QACV,IAAA,EAAM;AAAA,OACP,CAAA;AAED,MAAA,IAAI;AACF,QAAAD,6CAAA,CAA+B,MAAM,aAAa,CAAA;AAClD,QAAA,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,iDAAA,CAAmD,CAAA;AAAA,eAC9D,KAAA,EAAO;AACd,QAAA,IAAI,iBAAiBC,YAAA,EAAU;AAC7B,UAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,mDAAA,EAAsD,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AACtF,UAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,0BAAA,EAA6B,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,SAC3F,MAAO;AACL,UAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,6BAAA,EAAgC,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA;AAClE;AAGF,MAAA,IAAI,cAAc,IAAA,EAAM;AACtB,QAAA,QAAA,CAAS,IAAA,CAAK,GAAG,aAAA,CAAc,IAAI,CAAA;AAAA;AACrC;AAGF,IAAA,MAAM,QAAe,EAAC;AAEtB,IAAA,IAAI,KAAA,CAAM,WAAA,KAAgBC,kBAAA,CAAY,OAAA,EAAS;AAC7C,MAAA,QAAA,CAAS,QAAQ,CAAA,SAAA,KAAa;AAC5B,QAAA,MAAM,OAAA,GAAU,UAAU,UAAA,EAAY,OAAA;AACtC,QAAA,IAAI,CAAC,IAAA,CAAK,0BAAA,CAA2B,OAAA,EAAS,iBAAiB,CAAA,EAAG;AAChE,UAAA;AAAA;AAGF,QAAA,KAAA,CAAM,IAAA,CAAK;AAAA,UACT,OAAA;AAAA,UACA,IAAA,EAAM,UAAU,UAAA,EAAY,IAAA;AAAA;AAAA,UAE5B,OAAA,EAAS,UAAU,UAAA,EAAY,OAAA,EAAS,OAAO,CAAA,MAAA,KAAU,MAAA,CAAO,eAAe,OAAO;AAAA,SACvF,CAAA;AAAA,OACF,CAAA;AAAA,KACH,MAAO;AAEL,MAAA,QAAA,CAAS,QAAQ,CAAA,SAAA,KAAa;AAC5B,QAAA,MAAM,OAAA,GAAU,UAAU,UAAA,EAAY,OAAA;AACtC,QAAA,IAAI,CAAC,IAAA,CAAK,0BAAA,CAA2B,OAAA,EAAS,iBAAiB,CAAA,EAAG;AAChE,UAAA;AAAA;AAGF,QAAA,MAAM,cAAcH,uBAAA,CAAO,SAAA,CAAU,UAAA,EAAY,IAAI,EAAE,WAAA,EAAY;AACnE,QAAA,SAAA,CAAU,UAAA,EAAY,OAAA,EAAS,OAAA,CAAQ,CAAA,MAAA,KAAU;AAC/C,UAAA,IAAI,MAAA,CAAO,eAAe,OAAA,EAAS;AAEjC,YAAA;AAAA;AAGF,UAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,WAAA,EAAa,CAAA,EAAA,EAAK;AACpC,YAAA,MAAM,SAAA,GAAY;AAAA,cAChB,OAAA;AAAA,cACA,IAAA,EAAMA,wBAAO,SAAA,CAAU,UAAA,EAAY,IAAI,CAAA,CAAE,GAAA,CAAI,GAAG,GAAG,CAAA;AAAA,cACnD,OAAA,EAAS;AAAA,gBACP;AAAA,kBACE,aAAa,MAAA,CAAO,WAAA;AAAA,kBACpB,IAAA,EAAA,CAAO,MAAA,CAAO,IAAA,IAAQ,CAAA,IAAK,WAAA;AAAA,kBAC3B,YAAY,MAAA,CAAO;AAAA;AACrB;AACF,aACF;AACA,YAAA,KAAA,CAAM,KAAK,SAAS,CAAA;AAAA;AACtB,SACD,CAAA;AAAA,OACF,CAAA;AAAA;AAGH,IAAA,OAAO,KAAA;AAAA;AACT,EAEA,MAAgB,kBAAA,CAAmB,gBAAA,EAA0B,KAAA,EAAkB,YAAA,EAAsC;AACnH,IAAA,MAAM,IAAA,GAAO,gBAAA,CAAiB,sBAAA,CAAuB,MAAM,CAAA;AAC3D,IAAA,MAAM,eAA0C,EAAC;AACjD,IAAA,IAAA,EAAM,QAAQ,CAAA,GAAA,KAAO;AACnB,MAAA,MAAM,CAAC,CAAA,EAAG,CAAC,CAAA,GAAI,GAAA,CAAI,MAAM,GAAG,CAAA;AAC5B,MAAA,YAAA,CAAa,CAAA,CAAE,IAAA,EAAM,CAAA,GAAI,EAAE,IAAA,EAAK;AAAA,KACjC,CAAA;AAED,IAAA,MAAM,eAAA,GAAkBI,aAAA;AAAA,MACtB,YAAA;AAAA,MACA,CAAC,aAAwC,SAAA,KAAc;AACrD,QAAA,MAAM,UAAU,SAAA,CAAU,OAAA;AAC1B,QAAA,MAAM,UAAU,SAAA,CAAU,OAAA;AAE1B,QAAA,IAAI,YAAA,GAAe,SAAA;AACnB,QAAA,IAAI,KAAA,CAAM,WAAA,KAAgBD,kBAAA,CAAY,KAAA,EAAO;AAC3C,UAAA,YAAA,GAAe,YAAA;AAAA;AAEjB,QAAA,MAAM,SAASH,uBAAA,CAAO,SAAA,CAAU,IAAI,CAAA,CAAE,OAAO,YAAY,CAAA;AAEzD,QAAA,IAAI,OAAA,EAAS;AACX,UAAA,OAAA,CAAQ,OAAA,CAAQ,CAAC,MAAA,KAA6C;AAC5D,YAAA,MAAM,cAAc,MAAA,CAAO,WAAA;AAC3B,YAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AACpB,YAAA,MAAM,UAAU,CAAA,EAAG,OAAO,KAAK,WAAW,CAAA,EAAA,EAAK,OAAO,UAAU,CAAA,CAAA,CAAA;AAEhE,YAAA,IAAI,CAAC,WAAA,CAAY,OAAO,CAAA,EAAG;AACzB,cAAA,WAAA,CAAY,OAAO,CAAA,GAAI;AAAA,gBACrB,EAAA,EAAI,OAAA;AAAA,gBACJ,OAAA,EAAS,CAAA,EAAG,IAAA,CAAK,QAAQ,IAAI,OAAO,CAAA,CAAA;AAAA,gBACpC,OAAA,EAAS,GAAG,IAAA,CAAK,kBAAA,CAAmB,WAAqB,CAAC,CAAA,EAAA,EAAK,OAAO,UAAU,CAAA,CAAA,CAAA;AAAA,gBAChF,QAAA,EAAU,eAAA;AAAA,gBACV,UAAU,IAAA,CAAK,QAAA;AAAA,gBACf,cAAcK,oBAAA,CAAc,WAAA;AAAA,gBAC5B,SAAS,EAAC;AAAA,gBACV,GAAG;AAAA,eACL;AAAA;AAGF,YAAA,WAAA,CAAY,OAAO,CAAA,CAAE,OAAA,CAAQ,MAAM,CAAA,GAAIC,oBAAU,IAAI,CAAA;AAAA,WACtD,CAAA;AAAA;AAGH,QAAA,OAAO,WAAA;AAAA,OACT;AAAA,MACA;AAAC,KACH;AAEA,IAAA,OAAO,MAAA,CAAO,OAAO,eAAe,CAAA;AAAA;AAExC;;;;"}
1
+ {"version":3,"file":"DatadogClient.cjs.js","sources":["../../src/cost-clients/DatadogClient.ts"],"sourcesContent":["import { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { v2 as datadogApiV2, client as datadogClient } from '@datadog/datadog-api-client';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { CLOUD_PROVIDER, GRANULARITY, PROVIDER_TYPE } from '../service/consts';\nimport { parseCost } from '../service/functions';\nimport { CostQuery, Report } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\nimport { DatadogCostByOrgResponseSchema } from '../schemas/DatadogBilling';\nimport { ZodError } from 'zod';\n\nexport class DatadogClient extends InfraWalletClient {\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new DatadogClient(CLOUD_PROVIDER.DATADOG, config, database, cache, logger);\n }\n\n protected convertServiceName(serviceName: string): string {\n let convertedName = serviceName;\n\n // Datadog doesn't have this documented offically, raise a PR if any service is missed\n const aliases = new Map<string, string>([\n ['apm_host', 'APM Hosts'],\n ['apm_host_enterprise', 'APM Enterprise Hosts'],\n ['application_vulnerability_management_oss_host', 'Application Security - SCA Host'],\n ['application_security_host', 'ASM - Threat Management Hosts'],\n ['audit_trail', 'Audit Trail'],\n ['ci_pipeline', 'CI Visibility Committers'],\n ['ci_pipeline_indexed_spans', 'CI Visibility Spans'],\n ['cloud_cost_management', 'Cloud Cost Hosts'],\n ['cspm_container', 'Cloud Security Management Containers Pro'],\n ['cspm_host', 'Cloud Security Management Hosts Pro'],\n ['csm_host_pro', 'Cloud Security Management Hosts Pro'],\n ['cws_host', 'Cloud Workload Security Hosts'],\n ['infra_container', 'Containers'],\n ['infra_container_excl_agent', 'Containers'],\n ['timeseries', 'Custom Metrics'],\n ['error_tracking', 'Error Tracking'],\n ['incident_management', 'Incident Management'],\n ['logs_indexed_15day', 'Indexed Logs (15-day Retention)'],\n ['logs_indexed_180day', 'Indexed Logs (180-day Retention)'],\n ['logs_indexed_1day', 'Indexed Logs (1-day Retention)'],\n ['logs_indexed_30day', 'Indexed Logs (30-day Retention)'],\n ['logs_indexed_360day', 'Indexed Logs (360-day Retention)'],\n ['logs_indexed_3day', 'Indexed Logs (3-day Retention)'],\n ['logs_indexed_45day', 'Indexed Logs (45-day Retention)'],\n ['logs_indexed_60day', 'Indexed Logs (60-day Retention)'],\n ['logs_indexed_7day', 'Indexed Logs (7-day Retention)'],\n ['logs_indexed_90day', 'Indexed Logs (90-day Retention)'],\n ['apm_trace_search', 'Indexed Spans'],\n ['infra_host', 'Infra Hosts'],\n ['logs_ingested', 'Ingested Logs'],\n ['ingested_spans', 'Ingested Spans'],\n ['iot', 'IoT Devices'],\n ['npm_host', 'Network Hosts'],\n ['prof_container', 'Profiled Containers'],\n ['prof_host', 'Profiled Hosts'],\n ['rum_lite', 'RUM Sessions'],\n ['rum_replay', 'RUM with Session Replay Sessions'],\n ['siem_indexed', 'Security Analyzed and Indexed Logs'],\n ['sensitive_data_scanner', 'Sensitive Data Scanner'],\n ['serverless_apps', 'Serverless App Instances'],\n ['serverless_apm', 'Serverless Traced Invocations'],\n ['serverless_infra', 'Serverless Workload Functions'],\n ['siem', 'SIEM - Analyzed Logs'],\n ['synthetics_api_tests', 'Synthetics API Test Runs'],\n ['synthetics_browser_checks', 'Synthetics Browser Test Runs'],\n ['ci_testing', 'Test Visibility Committers'],\n ['ci_test_indexed_spans', 'Test Visibility Spans'],\n ]);\n\n if (aliases.has(convertedName)) {\n convertedName = aliases.get(convertedName) || convertedName;\n }\n\n return `${this.provider}/${convertedName}`;\n }\n\n protected async initCloudClient(integrationConfig: any): Promise<any> {\n const apiKey = integrationConfig.getString('apiKey');\n const applicationKey = integrationConfig.getString('applicationKey');\n const ddSite = integrationConfig.getString('ddSite');\n const configuration = datadogClient.createConfiguration({\n baseServer: new datadogClient.BaseServerConfiguration(ddSite, {}),\n authMethods: {\n apiKeyAuth: apiKey,\n appKeyAuth: applicationKey,\n },\n });\n const client = new datadogApiV2.UsageMeteringApi(configuration);\n return client;\n }\n\n protected async fetchCosts(integrationConfig: Config, client: any, query: CostQuery): Promise<any> {\n const costData: datadogApiV2.CostByOrg[] = [];\n const startTime = moment(parseInt(query.startTime, 10));\n const endTime = moment(parseInt(query.endTime, 10));\n const firstDayOfLastMonth = moment().subtract(1, 'M').startOf('M');\n\n // check if costs prior to 2 months ago are in query, if yes, use historical_cost API\n // https://docs.datadoghq.com/api/latest/usage-metering/#get-historical-cost-across-your-account\n if (startTime.isBefore(firstDayOfLastMonth)) {\n const historicalCost: datadogApiV2.CostByOrgResponse = await client.getHistoricalCostByOrg({\n startMonth: startTime,\n endMonth: firstDayOfLastMonth.subtract(1, 'd'),\n view: 'sub-org',\n });\n\n try {\n DatadogCostByOrgResponseSchema.parse(historicalCost);\n this.logger.debug(`Datadog historical cost response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Datadog historical cost response validation failed: ${error.message}`);\n this.logger.debug(`Sample validation errors: ${JSON.stringify(error.errors.slice(0, 3))}`);\n } else {\n this.logger.warn(`Unexpected validation error: ${error.message}`);\n }\n }\n\n if (historicalCost.data) {\n costData.push(...historicalCost.data);\n }\n }\n\n // check if current/last month costs are in query, if yes, use estimated_cost API\n // https://docs.datadoghq.com/api/latest/usage-metering/#get-estimated-cost-across-your-account\n if (endTime.isSameOrAfter(firstDayOfLastMonth)) {\n let estimatedCostStartTime = startTime;\n if (startTime.isBefore(firstDayOfLastMonth)) {\n estimatedCostStartTime = firstDayOfLastMonth;\n }\n\n const estimatedCost: datadogApiV2.CostByOrgResponse = await client.getEstimatedCostByOrg({\n startMonth: estimatedCostStartTime,\n endMonth: endTime,\n view: 'sub-org',\n });\n\n try {\n DatadogCostByOrgResponseSchema.parse(estimatedCost);\n this.logger.debug(`Datadog estimated cost response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Datadog estimated cost response validation failed: ${error.message}`);\n this.logger.debug(`Sample validation errors: ${JSON.stringify(error.errors.slice(0, 3))}`);\n } else {\n this.logger.warn(`Unexpected validation error: ${error.message}`);\n }\n }\n\n if (estimatedCost.data) {\n costData.push(...estimatedCost.data);\n }\n }\n\n const costs: any[] = [];\n\n if (query.granularity === GRANULARITY.MONTHLY) {\n costData.forEach(costByOrg => {\n const orgName = costByOrg.attributes?.orgName as string;\n if (!this.evaluateIntegrationFilters(orgName, integrationConfig)) {\n return;\n }\n\n costs.push({\n orgName: orgName,\n date: costByOrg.attributes?.date,\n // only keep cost breakdown\n charges: costByOrg.attributes?.charges?.filter(charge => charge.chargeType !== 'total'),\n });\n });\n } else {\n // Datadog doesn't provide daily costs based on usage, so we allocate monthly costs evenly by day\n costData.forEach(costByOrg => {\n const orgName = costByOrg.attributes?.orgName as string;\n if (!this.evaluateIntegrationFilters(orgName, integrationConfig)) {\n return;\n }\n\n const daysInMonth = moment(costByOrg.attributes?.date).daysInMonth();\n costByOrg.attributes?.charges?.forEach(charge => {\n if (charge.chargeType === 'total') {\n // only keep cost breakdown\n return;\n }\n\n for (let i = 0; i < daysInMonth; i++) {\n const dailyCost = {\n orgName: orgName,\n date: moment(costByOrg.attributes?.date).add(i, 'd'),\n charges: [\n {\n productName: charge.productName,\n cost: (charge.cost || 0) / daysInMonth,\n chargeType: charge.chargeType,\n },\n ],\n };\n costs.push(dailyCost);\n }\n });\n });\n }\n\n return costs;\n }\n\n protected async transformCostsData(subAccountConfig: Config, query: CostQuery, costResponse: any): Promise<Report[]> {\n const tags = subAccountConfig.getOptionalStringArray('tags');\n const tagKeyValues: { [key: string]: string } = {};\n tags?.forEach(tag => {\n const [k, v] = tag.split(':');\n tagKeyValues[k.trim()] = v.trim();\n });\n\n // Initialize tracking variables\n let processedRecords = 0;\n let filteredOutZeroAmount = 0;\n let filteredOutMissingFields = 0;\n let filteredOutInvalidDate = 0;\n const filteredOutTimeRange = 0;\n const uniqueKeys = new Set<string>();\n const totalRecords = costResponse?.length || 0;\n\n const transformedData = reduce(\n costResponse,\n (accumulator: { [key: string]: Report }, costByOrg) => {\n const account = costByOrg.orgName;\n const charges = costByOrg.charges;\n\n // Check for missing fields\n if (!account || !costByOrg.date) {\n filteredOutMissingFields++;\n return accumulator;\n }\n\n let periodFormat = 'YYYY-MM';\n if (query.granularity === GRANULARITY.DAILY) {\n periodFormat = 'YYYY-MM-DD';\n }\n\n const dateObj = moment(costByOrg.date);\n if (!dateObj.isValid()) {\n filteredOutInvalidDate++;\n return accumulator;\n }\n\n const period = dateObj.format(periodFormat);\n\n if (charges) {\n charges.forEach((charge: datadogApiV2.ChargebackBreakdown) => {\n const productName = charge.productName;\n const cost = charge.cost;\n\n // Check for missing fields\n if (!productName || cost === undefined || cost === null) {\n filteredOutMissingFields++;\n return;\n }\n\n const amount = parseCost(cost);\n\n // Check for zero amount\n if (amount === 0) {\n filteredOutZeroAmount++;\n return;\n }\n\n const keyName = `${account}->${productName} (${charge.chargeType})`;\n\n if (!accumulator[keyName]) {\n uniqueKeys.add(keyName);\n accumulator[keyName] = {\n id: keyName,\n account: `${this.provider}/${account}`,\n service: `${this.convertServiceName(productName as string)} (${charge.chargeType})`,\n category: 'Observability',\n provider: this.provider,\n providerType: PROVIDER_TYPE.INTEGRATION,\n reports: {},\n ...tagKeyValues,\n };\n }\n\n accumulator[keyName].reports[period] = amount;\n processedRecords++;\n });\n }\n\n return accumulator;\n },\n {},\n );\n\n this.logTransformationSummary({\n processed: processedRecords,\n uniqueReports: uniqueKeys.size,\n zeroAmount: filteredOutZeroAmount,\n missingFields: filteredOutMissingFields,\n invalidDate: filteredOutInvalidDate,\n timeRange: filteredOutTimeRange,\n totalRecords,\n });\n\n return Object.values(transformedData);\n }\n}\n"],"names":["InfraWalletClient","CLOUD_PROVIDER","datadogClient","datadogApiV2","moment","DatadogCostByOrgResponseSchema","ZodError","GRANULARITY","reduce","parseCost","PROVIDER_TYPE"],"mappings":"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"}
@@ -141,16 +141,42 @@ class ElasticCloudClient extends InfraWalletClient.InfraWalletClient {
141
141
  async transformCostsData(integrationConfig, query, costResponse) {
142
142
  const accountName = integrationConfig.getString("name");
143
143
  const tagKeyValues = this.extractConfigTags(integrationConfig);
144
+ let totalRecords = 0;
145
+ if (costResponse?.instanceCosts?.instances?.length) {
146
+ totalRecords += costResponse.instanceCosts.instances.length;
147
+ }
148
+ if (costResponse?.itemCosts?.products?.length) {
149
+ totalRecords += costResponse.itemCosts.products.length;
150
+ }
151
+ if (costResponse?.charts?.data?.length) {
152
+ totalRecords += costResponse.charts.data.length;
153
+ }
144
154
  try {
145
155
  const reports = /* @__PURE__ */ new Map();
146
156
  const periodFormat = this.getPeriodFormat(query);
157
+ const uniqueKeys = /* @__PURE__ */ new Set();
158
+ const metrics = {
159
+ processed: 0,
160
+ zeroAmount: 0,
161
+ missingFields: 0,
162
+ invalidDate: 0,
163
+ timeRange: 0
164
+ };
147
165
  this.processInstanceCosts(costResponse, reports, accountName, integrationConfig, tagKeyValues);
148
166
  this.processChartData(costResponse, reports, periodFormat);
149
167
  this.distributeRemainingCosts(costResponse, reports, query);
150
168
  const filteredReports = this.prepareReportsForOutput(reports, periodFormat);
151
- this.logger.info(
152
- `Returning ${filteredReports.length} reports with ${Object.keys(filteredReports.reduce((acc, r) => ({ ...acc, ...r.reports }), {})).length} periods`
153
- );
169
+ metrics.processed = filteredReports.reduce((sum, report) => sum + Object.keys(report.reports).length, 0);
170
+ filteredReports.forEach((report) => uniqueKeys.add(report.id));
171
+ this.logTransformationSummary({
172
+ processed: metrics.processed,
173
+ uniqueReports: uniqueKeys.size,
174
+ zeroAmount: metrics.zeroAmount,
175
+ missingFields: metrics.missingFields,
176
+ invalidDate: metrics.invalidDate,
177
+ timeRange: metrics.timeRange,
178
+ totalRecords
179
+ });
154
180
  return filteredReports;
155
181
  } catch (error) {
156
182
  this.logger.error(`Error transforming Elastic Cloud cost data: ${error.message}`);
@@ -220,16 +246,20 @@ class ElasticCloudClient extends InfraWalletClient.InfraWalletClient {
220
246
  if (!value?.id) continue;
221
247
  const instanceKey = `instance-${value.id}`;
222
248
  if (reports.has(instanceKey)) {
223
- reports.get(instanceKey).reports[period] = value.value ?? 0;
249
+ const rawValue = value.value ?? 0;
250
+ this.logger.debug(`Chart value for ${instanceKey} period ${period}: raw=${rawValue}`);
251
+ reports.get(instanceKey).reports[period] = rawValue;
224
252
  }
225
253
  this.matchValueWithItemReports(value, period, reports);
226
254
  }
227
255
  }
228
256
  matchValueWithItemReports(value, period, reports) {
229
- if (!value.name) return;
257
+ if (!value?.name) return;
230
258
  for (const [key, report] of reports.entries()) {
231
259
  if (key.startsWith("item-") && (report.service && value.name.includes(report.service) || report.productType && value.name.includes(report.productType))) {
232
- report.reports[period] = value.value ?? 0;
260
+ const rawValue = value.value ?? 0;
261
+ this.logger.debug(`Item value for ${key} period ${period}: raw=${rawValue}`);
262
+ report.reports[period] = rawValue;
233
263
  }
234
264
  }
235
265
  }
@@ -252,7 +282,9 @@ class ElasticCloudClient extends InfraWalletClient.InfraWalletClient {
252
282
  this.logger.debug(`Distributed costs across ${distributedItems} periods for items without time series data`);
253
283
  }
254
284
  distributeItemCost(report, lineItem, query, periodFormat) {
255
- const totalCost = (lineItem.total_ecu ?? 0) / 100;
285
+ const rawTotalEcu = lineItem.total_ecu ?? 0;
286
+ const totalCost = rawTotalEcu / 100;
287
+ this.logger.debug(`Distribute item cost for ${report.id}: raw_ecu=${rawTotalEcu}, converted_dollars=${totalCost}`);
256
288
  if (totalCost <= 0) return 0;
257
289
  const startMonth = moment__default.default(parseInt(query.startTime, 10));
258
290
  const endMonth = moment__default.default(parseInt(query.endTime, 10));
@@ -1 +1 @@
1
- {"version":3,"file":"ElasticCloudClient.cjs.js","sources":["../../src/cost-clients/ElasticCloudClient.ts"],"sourcesContent":["import { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport moment from 'moment';\nimport {\n GetChartsRequestSchema,\n ChartsResponseSchema,\n InstancesResponseSchema,\n ItemsResponseSchema,\n} from '../schemas/ElasticBilling';\nimport { ZodError } from 'zod';\nimport { CLOUD_PROVIDER, PROVIDER_TYPE } from '../service/consts';\nimport { CostQuery, Report } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\n\n/**\n * Client for fetching and processing cost data from Elastic Cloud\n */\nexport class ElasticCloudClient extends InfraWalletClient {\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new ElasticCloudClient(CLOUD_PROVIDER.ELASTIC_CLOUD, config, database, cache, logger);\n }\n\n protected convertServiceName(serviceName: string): string {\n return serviceName.startsWith('Elastic')\n ? `${this.provider}/${serviceName.slice('Elastic'.length).trim()}`\n : `${this.provider}/${serviceName}`;\n }\n\n protected async initCloudClient(integrationConfig: Config): Promise<any> {\n const apiKey = integrationConfig.getString('apiKey');\n\n return {\n baseUrl: 'https://billing.elastic-cloud.com',\n headers: {\n Authorization: `ApiKey ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n };\n }\n\n private async fetchWithRetry(url: string, headers: any, maxRetries = 3): Promise<any> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(url, { method: 'GET', headers });\n\n if (response.status === 429 && attempt < maxRetries) {\n const retryAfter = parseInt(response.headers.get('retry-after') ?? '5', 10);\n this.logger.warn(`Rate limited by Elastic Cloud API, retrying after ${retryAfter} seconds...`);\n await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));\n continue;\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Elastic Cloud API error (${response.status}): ${errorText}`);\n }\n\n return await response.json();\n } catch (error) {\n if (attempt < maxRetries) {\n const backoffTime = Math.pow(2, attempt) * 1000;\n this.logger.warn(`Error fetching from Elastic Cloud, retrying in ${backoffTime}ms: ${error.message}`);\n await new Promise(resolve => setTimeout(resolve, backoffTime));\n } else {\n throw error;\n }\n }\n }\n throw new Error(`Failed to fetch from Elastic Cloud API after ${maxRetries} attempts`);\n }\n\n protected async fetchCosts(integrationConfig: Config, client: any, query: CostQuery): Promise<any> {\n const { baseUrl, headers } = client;\n const organizationId = integrationConfig.getString('organizationId');\n // Convert to ISO 8601 format that's compliant with Zod's datetime validation\n // Use the Z suffix instead of +00:00 which may not be recognized by the schema\n const startDate = moment(parseInt(query.startTime, 10)).toISOString();\n const endDate = moment(parseInt(query.endTime, 10)).toISOString();\n const bucketingStrategy = query.granularity.toLowerCase() === 'daily' ? 'daily' : 'monthly';\n\n this.logger.info(\n `Fetching Elastic Cloud cost data from ${startDate} to ${endDate} with ${bucketingStrategy} granularity`,\n );\n\n try {\n const params = {\n from: startDate,\n to: endDate,\n bucketing_strategy: bucketingStrategy,\n };\n\n // The Elastic Cloud API requires ISO format dates, but the zod schemas expect a specific format\n // Instead of modifying the schemas, we'll use the params directly, logging validation issues\n try {\n GetChartsRequestSchema.parse(params);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(\n `Request parameters didn't match schema for GetChartsRequest: ${JSON.stringify(error.errors)}`,\n );\n } else {\n this.logger.warn(`Unexpected validation error: ${error.message}`);\n }\n this.logger.debug(`Using params directly: ${JSON.stringify(params)}`);\n }\n\n const chartParams = params;\n const instanceParams = { ...params, include_names: true };\n const itemsParams = { from: params.from, to: params.to };\n\n const createQueryString = (queryParams: Record<string, any>) => {\n return Object.entries(queryParams)\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)\n .join('&');\n };\n\n const [instanceCostsResponse, itemCostsResponse, chartsResponse] = await Promise.all([\n this.fetchWithRetry(\n `${baseUrl}/api/v2/billing/organizations/${organizationId}/costs/instances?${createQueryString(instanceParams)}`,\n headers,\n ).then(data => {\n const validationResult = InstancesResponseSchema.safeParse(data);\n if (!validationResult.success) {\n this.logger.warn(`Response validation failed for instance costs: ${validationResult.error.message}`);\n } else {\n this.logger.debug(`Response validation passed for instance costs`);\n }\n this.logger.debug(`Received instance costs data with ${data?.instances?.length ?? 0} instances`);\n return data;\n }),\n\n this.fetchWithRetry(\n `${baseUrl}/api/v2/billing/organizations/${organizationId}/costs/items?${createQueryString(itemsParams)}`,\n headers,\n ).then(data => {\n const validationResult = ItemsResponseSchema.safeParse(data);\n if (!validationResult.success) {\n this.logger.warn(`Response validation failed for item costs: ${validationResult.error.message}`);\n } else {\n this.logger.debug(`Response validation passed for item costs`);\n }\n this.logger.debug(`Received item costs data with ${data?.products?.length ?? 0} products`);\n return data;\n }),\n\n this.fetchWithRetry(\n `${baseUrl}/api/v2/billing/organizations/${organizationId}/charts?${createQueryString(chartParams)}`,\n headers,\n ).then(data => {\n const validationResult = ChartsResponseSchema.safeParse(data);\n if (!validationResult.success) {\n this.logger.warn(`Response validation failed for charts data: ${validationResult.error.message}`);\n } else {\n this.logger.debug(`Response validation passed for charts data`);\n }\n this.logger.debug(`Received charts data with ${data?.data?.length ?? 0} data points`);\n return data;\n }),\n ]);\n\n return {\n instanceCosts: instanceCostsResponse,\n itemCosts: itemCostsResponse,\n charts: chartsResponse,\n };\n } catch (error) {\n this.logger.error(`Error fetching Elastic Cloud costs: ${error.message}`);\n throw error;\n }\n }\n\n protected async transformCostsData(\n integrationConfig: Config,\n query: CostQuery,\n costResponse: any,\n ): Promise<Report[]> {\n const accountName = integrationConfig.getString('name');\n const tagKeyValues = this.extractConfigTags(integrationConfig);\n\n try {\n const reports = new Map();\n const periodFormat = this.getPeriodFormat(query);\n\n this.processInstanceCosts(costResponse, reports, accountName, integrationConfig, tagKeyValues);\n this.processChartData(costResponse, reports, periodFormat);\n this.distributeRemainingCosts(costResponse, reports, query);\n\n const filteredReports = this.prepareReportsForOutput(reports, periodFormat);\n\n this.logger.info(\n `Returning ${filteredReports.length} reports with ${\n Object.keys(filteredReports.reduce((acc, r) => ({ ...acc, ...r.reports }), {})).length\n } periods`,\n );\n\n return filteredReports;\n } catch (error) {\n this.logger.error(`Error transforming Elastic Cloud cost data: ${error.message}`);\n throw error;\n }\n }\n\n private extractConfigTags(integrationConfig: Config): Record<string, string> {\n const tags = integrationConfig.getOptionalStringArray('tags') ?? [];\n const tagKeyValues: Record<string, string> = {};\n\n tags.forEach(tag => {\n const [k, v] = tag.split(':').map(part => part.trim());\n tagKeyValues[k] = v;\n });\n\n return tagKeyValues;\n }\n\n // Determine period format based on granularity\n private getPeriodFormat(query: CostQuery): string {\n return query.granularity.toLowerCase() === 'daily' ? 'YYYY-MM-DD' : 'YYYY-MM';\n }\n\n private processInstanceCosts(\n costResponse: any,\n reports: Map<string, Report>,\n accountName: string,\n integrationConfig: Config,\n tagKeyValues: Record<string, string>,\n ): void {\n if (!costResponse?.instanceCosts?.instances?.length) {\n return;\n }\n\n const validInstances = costResponse.instanceCosts.instances.filter(\n (instance: { id: any; name: any }) => instance?.id && instance?.name,\n );\n\n this.logger.debug(\n `Processing ${validInstances.length} valid instances out of ${costResponse.instanceCosts.instances.length}`,\n );\n\n for (const instance of validInstances) {\n // Skip filtered instances\n if (!this.evaluateIntegrationFilters(instance.name, integrationConfig)) {\n continue;\n }\n\n const keyName = `instance-${instance.id}`;\n reports.set(keyName, {\n id: keyName,\n account: `${this.provider}/${accountName}`,\n service: this.convertServiceName(instance.name),\n category: 'Database', // TODO: find a better way in the EC API to determine the category\n provider: this.provider,\n providerType: PROVIDER_TYPE.INTEGRATION,\n reports: {},\n instanceId: instance.id,\n instanceName: instance.name,\n ...tagKeyValues,\n });\n }\n }\n\n private processChartData(costResponse: any, reports: Map<string, Report>, periodFormat: string): void {\n if (!costResponse?.charts?.data?.length) {\n return;\n }\n\n this.logger.info(`Processing ${costResponse.charts.data.length} time points from chart data`);\n\n for (const timePoint of costResponse.charts.data) {\n if (!timePoint?.timestamp) continue;\n\n const period = this.formatTimestamp(timePoint.timestamp, periodFormat);\n if (!period) continue;\n\n this.processTimePointValues(timePoint, period, reports);\n }\n }\n\n private processTimePointValues(timePoint: any, period: string, reports: Map<string, Report>): void {\n if (!Array.isArray(timePoint.values)) {\n return;\n }\n\n for (const value of timePoint.values) {\n if (!value?.id) continue;\n\n const instanceKey = `instance-${value.id}`;\n if (reports.has(instanceKey)) {\n reports.get(instanceKey)!.reports[period] = value.value ?? 0;\n }\n\n this.matchValueWithItemReports(value, period, reports);\n }\n }\n\n private matchValueWithItemReports(value: any, period: string, reports: Map<string, Report>): void {\n if (!value.name) return;\n\n for (const [key, report] of reports.entries()) {\n if (\n key.startsWith('item-') &&\n ((report.service && value.name.includes(report.service)) ||\n (report.productType && value.name.includes(report.productType)))\n ) {\n report.reports[period] = value.value ?? 0;\n }\n }\n }\n\n private distributeRemainingCosts(costResponse: any, reports: Map<string, Report>, query: CostQuery): void {\n const itemsData = costResponse?.itemCosts?.products;\n if (!Array.isArray(itemsData)) return;\n\n let distributedItems = 0;\n const periodFormat = this.getPeriodFormat(query);\n\n for (const product of itemsData) {\n if (!product?.type || !Array.isArray(product?.product_line_items)) continue;\n\n for (const lineItem of product.product_line_items) {\n if (!lineItem?.name) continue;\n\n const keyName = `item-${product.type}-${lineItem.name}`;\n const report = reports.get(keyName);\n\n if (report && Object.keys(report.reports).length === 0) {\n distributedItems += this.distributeItemCost(report, lineItem, query, periodFormat);\n }\n }\n }\n\n this.logger.debug(`Distributed costs across ${distributedItems} periods for items without time series data`);\n }\n\n private distributeItemCost(report: Report, lineItem: any, query: CostQuery, periodFormat: string): number {\n const totalCost = (lineItem.total_ecu ?? 0) / 100; // Convert ECU to dollars\n if (totalCost <= 0) return 0;\n\n const startMonth = moment(parseInt(query.startTime, 10));\n const endMonth = moment(parseInt(query.endTime, 10));\n const monthCount = Math.max(1, endMonth.diff(startMonth, 'months'));\n let periodsCreated = 0;\n\n for (\n let currentMonth = startMonth.clone().startOf('month');\n currentMonth.isSameOrBefore(endMonth, 'month');\n currentMonth.add(1, 'month')\n ) {\n const period = currentMonth.format(periodFormat);\n report.reports[period] = totalCost / monthCount;\n periodsCreated++;\n }\n\n return periodsCreated;\n }\n\n private prepareReportsForOutput(reports: Map<string, Report>, periodFormat: string): Report[] {\n const allReports = [...reports.values()];\n\n const filteredReports = allReports.filter(report => Object.keys(report.reports).length > 0);\n\n for (const report of filteredReports) {\n this.standardizePeriods(report, periodFormat);\n }\n\n return filteredReports;\n }\n\n private formatTimestamp(timestamp: string | number, periodFormat: string): string | null {\n try {\n if (typeof timestamp === 'number') {\n const timestampMs = timestamp < 10000000000 ? timestamp * 1000 : timestamp;\n return moment(timestampMs).format(periodFormat);\n }\n return moment(timestamp).format(periodFormat);\n } catch (error) {\n this.logger.warn(\n `Error formatting timestamp ${timestamp}: ${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n }\n\n private standardizePeriods(report: Report, periodFormat: string): void {\n const formattedReports: Record<string, number> = {};\n\n for (const [period, cost] of Object.entries(report.reports)) {\n const momentDate = moment(period);\n if (!momentDate.isValid()) {\n this.logger.warn(`Invalid period format: ${period} in report ${report.id}`);\n continue;\n }\n\n const formattedPeriod = momentDate.format(periodFormat);\n formattedReports[formattedPeriod] = cost as number;\n }\n\n report.reports = formattedReports;\n }\n\n async getCostReportsFromDatabase(query: CostQuery): Promise<Report[]> {\n const reports = await super.getCostReportsFromDatabase(query);\n this.logger.debug(`Retrieved ${reports.length} ElasticCloud reports from database`);\n\n if (reports.length === 0) {\n this.logger.warn(`No ElasticCloud reports found in database for query: ${JSON.stringify(query)}`);\n }\n\n return reports;\n }\n}\n"],"names":["InfraWalletClient","CLOUD_PROVIDER","moment","GetChartsRequestSchema","ZodError","InstancesResponseSchema","ItemsResponseSchema","ChartsResponseSchema","PROVIDER_TYPE"],"mappings":"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"}
1
+ {"version":3,"file":"ElasticCloudClient.cjs.js","sources":["../../src/cost-clients/ElasticCloudClient.ts"],"sourcesContent":["import { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport moment from 'moment';\nimport {\n GetChartsRequestSchema,\n ChartsResponseSchema,\n InstancesResponseSchema,\n ItemsResponseSchema,\n} from '../schemas/ElasticBilling';\nimport { ZodError } from 'zod';\nimport { CLOUD_PROVIDER, PROVIDER_TYPE } from '../service/consts';\nimport { CostQuery, Report } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\n\n/**\n * Client for fetching and processing cost data from Elastic Cloud\n */\nexport class ElasticCloudClient extends InfraWalletClient {\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new ElasticCloudClient(CLOUD_PROVIDER.ELASTIC_CLOUD, config, database, cache, logger);\n }\n\n protected convertServiceName(serviceName: string): string {\n return serviceName.startsWith('Elastic')\n ? `${this.provider}/${serviceName.slice('Elastic'.length).trim()}`\n : `${this.provider}/${serviceName}`;\n }\n\n protected async initCloudClient(integrationConfig: Config): Promise<any> {\n const apiKey = integrationConfig.getString('apiKey');\n\n return {\n baseUrl: 'https://billing.elastic-cloud.com',\n headers: {\n Authorization: `ApiKey ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n };\n }\n\n private async fetchWithRetry(url: string, headers: any, maxRetries = 3): Promise<any> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(url, { method: 'GET', headers });\n\n if (response.status === 429 && attempt < maxRetries) {\n const retryAfter = parseInt(response.headers.get('retry-after') ?? '5', 10);\n this.logger.warn(`Rate limited by Elastic Cloud API, retrying after ${retryAfter} seconds...`);\n await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));\n continue;\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Elastic Cloud API error (${response.status}): ${errorText}`);\n }\n\n return await response.json();\n } catch (error) {\n if (attempt < maxRetries) {\n const backoffTime = Math.pow(2, attempt) * 1000;\n this.logger.warn(`Error fetching from Elastic Cloud, retrying in ${backoffTime}ms: ${error.message}`);\n await new Promise(resolve => setTimeout(resolve, backoffTime));\n } else {\n throw error;\n }\n }\n }\n throw new Error(`Failed to fetch from Elastic Cloud API after ${maxRetries} attempts`);\n }\n\n protected async fetchCosts(integrationConfig: Config, client: any, query: CostQuery): Promise<any> {\n const { baseUrl, headers } = client;\n const organizationId = integrationConfig.getString('organizationId');\n // Convert to ISO 8601 format that's compliant with Zod's datetime validation\n // Use the Z suffix instead of +00:00 which may not be recognized by the schema\n const startDate = moment(parseInt(query.startTime, 10)).toISOString();\n const endDate = moment(parseInt(query.endTime, 10)).toISOString();\n const bucketingStrategy = query.granularity.toLowerCase() === 'daily' ? 'daily' : 'monthly';\n\n this.logger.info(\n `Fetching Elastic Cloud cost data from ${startDate} to ${endDate} with ${bucketingStrategy} granularity`,\n );\n\n try {\n const params = {\n from: startDate,\n to: endDate,\n bucketing_strategy: bucketingStrategy,\n };\n\n // The Elastic Cloud API requires ISO format dates, but the zod schemas expect a specific format\n // Instead of modifying the schemas, we'll use the params directly, logging validation issues\n try {\n GetChartsRequestSchema.parse(params);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(\n `Request parameters didn't match schema for GetChartsRequest: ${JSON.stringify(error.errors)}`,\n );\n } else {\n this.logger.warn(`Unexpected validation error: ${error.message}`);\n }\n this.logger.debug(`Using params directly: ${JSON.stringify(params)}`);\n }\n\n const chartParams = params;\n const instanceParams = { ...params, include_names: true };\n const itemsParams = { from: params.from, to: params.to };\n\n const createQueryString = (queryParams: Record<string, any>) => {\n return Object.entries(queryParams)\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)\n .join('&');\n };\n\n const [instanceCostsResponse, itemCostsResponse, chartsResponse] = await Promise.all([\n this.fetchWithRetry(\n `${baseUrl}/api/v2/billing/organizations/${organizationId}/costs/instances?${createQueryString(instanceParams)}`,\n headers,\n ).then(data => {\n const validationResult = InstancesResponseSchema.safeParse(data);\n if (!validationResult.success) {\n this.logger.warn(`Response validation failed for instance costs: ${validationResult.error.message}`);\n } else {\n this.logger.debug(`Response validation passed for instance costs`);\n }\n this.logger.debug(`Received instance costs data with ${data?.instances?.length ?? 0} instances`);\n return data;\n }),\n\n this.fetchWithRetry(\n `${baseUrl}/api/v2/billing/organizations/${organizationId}/costs/items?${createQueryString(itemsParams)}`,\n headers,\n ).then(data => {\n const validationResult = ItemsResponseSchema.safeParse(data);\n if (!validationResult.success) {\n this.logger.warn(`Response validation failed for item costs: ${validationResult.error.message}`);\n } else {\n this.logger.debug(`Response validation passed for item costs`);\n }\n this.logger.debug(`Received item costs data with ${data?.products?.length ?? 0} products`);\n return data;\n }),\n\n this.fetchWithRetry(\n `${baseUrl}/api/v2/billing/organizations/${organizationId}/charts?${createQueryString(chartParams)}`,\n headers,\n ).then(data => {\n const validationResult = ChartsResponseSchema.safeParse(data);\n if (!validationResult.success) {\n this.logger.warn(`Response validation failed for charts data: ${validationResult.error.message}`);\n } else {\n this.logger.debug(`Response validation passed for charts data`);\n }\n this.logger.debug(`Received charts data with ${data?.data?.length ?? 0} data points`);\n return data;\n }),\n ]);\n\n return {\n instanceCosts: instanceCostsResponse,\n itemCosts: itemCostsResponse,\n charts: chartsResponse,\n };\n } catch (error) {\n this.logger.error(`Error fetching Elastic Cloud costs: ${error.message}`);\n throw error;\n }\n }\n\n protected async transformCostsData(\n integrationConfig: Config,\n query: CostQuery,\n costResponse: any,\n ): Promise<Report[]> {\n const accountName = integrationConfig.getString('name');\n const tagKeyValues = this.extractConfigTags(integrationConfig);\n\n // Initialize tracking variables - calculate totalRecords from different parts of costResponse\n let totalRecords = 0;\n\n if (costResponse?.instanceCosts?.instances?.length) {\n totalRecords += costResponse.instanceCosts.instances.length;\n }\n if (costResponse?.itemCosts?.products?.length) {\n totalRecords += costResponse.itemCosts.products.length;\n }\n if (costResponse?.charts?.data?.length) {\n totalRecords += costResponse.charts.data.length;\n }\n\n try {\n const reports = new Map();\n const periodFormat = this.getPeriodFormat(query);\n const uniqueKeys = new Set<string>();\n\n // Track metrics during processing\n const metrics = {\n processed: 0,\n zeroAmount: 0,\n missingFields: 0,\n invalidDate: 0,\n timeRange: 0,\n };\n\n this.processInstanceCosts(costResponse, reports, accountName, integrationConfig, tagKeyValues);\n this.processChartData(costResponse, reports, periodFormat);\n this.distributeRemainingCosts(costResponse, reports, query);\n\n const filteredReports = this.prepareReportsForOutput(reports, periodFormat);\n\n // Estimate processed records based on successful reports\n metrics.processed = filteredReports.reduce((sum, report) => sum + Object.keys(report.reports).length, 0);\n\n // Count unique keys from final reports\n filteredReports.forEach(report => uniqueKeys.add(report.id));\n\n this.logTransformationSummary({\n processed: metrics.processed,\n uniqueReports: uniqueKeys.size,\n zeroAmount: metrics.zeroAmount,\n missingFields: metrics.missingFields,\n invalidDate: metrics.invalidDate,\n timeRange: metrics.timeRange,\n totalRecords,\n });\n\n return filteredReports;\n } catch (error) {\n this.logger.error(`Error transforming Elastic Cloud cost data: ${error.message}`);\n throw error;\n }\n }\n\n private extractConfigTags(integrationConfig: Config): Record<string, string> {\n const tags = integrationConfig.getOptionalStringArray('tags') ?? [];\n const tagKeyValues: Record<string, string> = {};\n\n tags.forEach(tag => {\n const [k, v] = tag.split(':').map(part => part.trim());\n tagKeyValues[k] = v;\n });\n\n return tagKeyValues;\n }\n\n // Determine period format based on granularity\n private getPeriodFormat(query: CostQuery): string {\n return query.granularity.toLowerCase() === 'daily' ? 'YYYY-MM-DD' : 'YYYY-MM';\n }\n\n private processInstanceCosts(\n costResponse: any,\n reports: Map<string, Report>,\n accountName: string,\n integrationConfig: Config,\n tagKeyValues: Record<string, string>,\n ): void {\n if (!costResponse?.instanceCosts?.instances?.length) {\n return;\n }\n\n const validInstances = costResponse.instanceCosts.instances.filter(\n (instance: { id: any; name: any }) => instance?.id && instance?.name,\n );\n\n this.logger.debug(\n `Processing ${validInstances.length} valid instances out of ${costResponse.instanceCosts.instances.length}`,\n );\n\n for (const instance of validInstances) {\n // Skip filtered instances\n if (!this.evaluateIntegrationFilters(instance.name, integrationConfig)) {\n continue;\n }\n\n const keyName = `instance-${instance.id}`;\n reports.set(keyName, {\n id: keyName,\n account: `${this.provider}/${accountName}`,\n service: this.convertServiceName(instance.name),\n category: 'Database', // TODO: find a better way in the EC API to determine the category\n provider: this.provider,\n providerType: PROVIDER_TYPE.INTEGRATION,\n reports: {},\n instanceId: instance.id,\n instanceName: instance.name,\n ...tagKeyValues,\n });\n }\n }\n\n private processChartData(costResponse: any, reports: Map<string, Report>, periodFormat: string): void {\n if (!costResponse?.charts?.data?.length) {\n return;\n }\n\n this.logger.info(`Processing ${costResponse.charts.data.length} time points from chart data`);\n\n for (const timePoint of costResponse.charts.data) {\n if (!timePoint?.timestamp) continue;\n\n const period = this.formatTimestamp(timePoint.timestamp, periodFormat);\n if (!period) continue;\n\n this.processTimePointValues(timePoint, period, reports);\n }\n }\n\n private processTimePointValues(timePoint: any, period: string, reports: Map<string, Report>): void {\n if (!Array.isArray(timePoint.values)) {\n return;\n }\n\n for (const value of timePoint.values) {\n // Skip if value is null/undefined or missing required fields\n if (!value?.id) continue;\n\n const instanceKey = `instance-${value.id}`;\n if (reports.has(instanceKey)) {\n const rawValue = value.value ?? 0;\n this.logger.debug(`Chart value for ${instanceKey} period ${period}: raw=${rawValue}`);\n reports.get(instanceKey)!.reports[period] = rawValue; // Using raw value for now\n }\n\n this.matchValueWithItemReports(value, period, reports);\n }\n }\n\n private matchValueWithItemReports(value: any, period: string, reports: Map<string, Report>): void {\n if (!value?.name) return;\n\n for (const [key, report] of reports.entries()) {\n if (\n key.startsWith('item-') &&\n ((report.service && value.name.includes(report.service)) ||\n (report.productType && value.name.includes(report.productType)))\n ) {\n const rawValue = value.value ?? 0;\n this.logger.debug(`Item value for ${key} period ${period}: raw=${rawValue}`);\n report.reports[period] = rawValue; // Using raw value for now\n }\n }\n }\n\n private distributeRemainingCosts(costResponse: any, reports: Map<string, Report>, query: CostQuery): void {\n const itemsData = costResponse?.itemCosts?.products;\n if (!Array.isArray(itemsData)) return;\n\n let distributedItems = 0;\n const periodFormat = this.getPeriodFormat(query);\n\n for (const product of itemsData) {\n if (!product?.type || !Array.isArray(product?.product_line_items)) continue;\n\n for (const lineItem of product.product_line_items) {\n if (!lineItem?.name) continue;\n\n const keyName = `item-${product.type}-${lineItem.name}`;\n const report = reports.get(keyName);\n\n if (report && Object.keys(report.reports).length === 0) {\n distributedItems += this.distributeItemCost(report, lineItem, query, periodFormat);\n }\n }\n }\n\n this.logger.debug(`Distributed costs across ${distributedItems} periods for items without time series data`);\n }\n\n private distributeItemCost(report: Report, lineItem: any, query: CostQuery, periodFormat: string): number {\n const rawTotalEcu = lineItem.total_ecu ?? 0;\n const totalCost = rawTotalEcu / 100; // Keep original conversion for line items\n this.logger.debug(`Distribute item cost for ${report.id}: raw_ecu=${rawTotalEcu}, converted_dollars=${totalCost}`);\n if (totalCost <= 0) return 0;\n\n const startMonth = moment(parseInt(query.startTime, 10));\n const endMonth = moment(parseInt(query.endTime, 10));\n const monthCount = Math.max(1, endMonth.diff(startMonth, 'months'));\n let periodsCreated = 0;\n\n for (\n let currentMonth = startMonth.clone().startOf('month');\n currentMonth.isSameOrBefore(endMonth, 'month');\n currentMonth.add(1, 'month')\n ) {\n const period = currentMonth.format(periodFormat);\n report.reports[period] = totalCost / monthCount;\n periodsCreated++;\n }\n\n return periodsCreated;\n }\n\n private prepareReportsForOutput(reports: Map<string, Report>, periodFormat: string): Report[] {\n const allReports = [...reports.values()];\n\n const filteredReports = allReports.filter(report => Object.keys(report.reports).length > 0);\n\n for (const report of filteredReports) {\n this.standardizePeriods(report, periodFormat);\n }\n\n return filteredReports;\n }\n\n private formatTimestamp(timestamp: string | number, periodFormat: string): string | null {\n try {\n if (typeof timestamp === 'number') {\n const timestampMs = timestamp < 10000000000 ? timestamp * 1000 : timestamp;\n return moment(timestampMs).format(periodFormat);\n }\n return moment(timestamp).format(periodFormat);\n } catch (error) {\n this.logger.warn(\n `Error formatting timestamp ${timestamp}: ${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n }\n\n private standardizePeriods(report: Report, periodFormat: string): void {\n const formattedReports: Record<string, number> = {};\n\n for (const [period, cost] of Object.entries(report.reports)) {\n const momentDate = moment(period);\n if (!momentDate.isValid()) {\n this.logger.warn(`Invalid period format: ${period} in report ${report.id}`);\n continue;\n }\n\n const formattedPeriod = momentDate.format(periodFormat);\n formattedReports[formattedPeriod] = cost as number;\n }\n\n report.reports = formattedReports;\n }\n\n async getCostReportsFromDatabase(query: CostQuery): Promise<Report[]> {\n const reports = await super.getCostReportsFromDatabase(query);\n this.logger.debug(`Retrieved ${reports.length} ElasticCloud reports from database`);\n\n if (reports.length === 0) {\n this.logger.warn(`No ElasticCloud reports found in database for query: ${JSON.stringify(query)}`);\n }\n\n return reports;\n }\n}\n"],"names":["InfraWalletClient","CLOUD_PROVIDER","moment","GetChartsRequestSchema","ZodError","InstancesResponseSchema","ItemsResponseSchema","ChartsResponseSchema","PROVIDER_TYPE"],"mappings":"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"}
@@ -102,7 +102,7 @@ class GCPClient extends InfraWalletClient.InfraWalletClient {
102
102
  const [job] = await client.createQueryJob(queryOptions);
103
103
  const [rows] = await job.getQueryResults();
104
104
  try {
105
- GCPBilling.GCPBillingQueryResultSchema.parse(rows);
105
+ GCPBilling.GCPCustomQueryResultSchema.parse(rows);
106
106
  this.logger.debug(`GCP billing data validation passed for ${rows.length} records`);
107
107
  } catch (error) {
108
108
  if (error instanceof zod.ZodError) {
@@ -181,12 +181,29 @@ class GCPClient extends InfraWalletClient.InfraWalletClient {
181
181
  const [k, v] = tag.split(":");
182
182
  tagKeyValues[k.trim()] = v.trim();
183
183
  });
184
+ let processedRecords = 0;
185
+ let filteredOutZeroAmount = 0;
186
+ let filteredOutMissingFields = 0;
187
+ const filteredOutInvalidDate = 0;
188
+ const filteredOutTimeRange = 0;
189
+ const uniqueKeys = /* @__PURE__ */ new Set();
190
+ const totalRecords = costResponse?.length || 0;
184
191
  const transformedData = lodash.reduce(
185
192
  costResponse,
186
193
  (acc, row) => {
194
+ if (!row.period || !row.project || !row.service || row.total_cost === void 0 || row.total_cost === null) {
195
+ filteredOutMissingFields++;
196
+ return acc;
197
+ }
198
+ const amount = functions.parseCost(row.total_cost);
199
+ if (amount === 0) {
200
+ filteredOutZeroAmount++;
201
+ return acc;
202
+ }
187
203
  const period = row.period;
188
204
  const keyName = `${accountName}_${row.project}_${row.service}`;
189
205
  if (!acc[keyName]) {
206
+ uniqueKeys.add(keyName);
190
207
  acc[keyName] = {
191
208
  id: keyName,
192
209
  account: `${this.provider}/${accountName}`,
@@ -201,11 +218,21 @@ class GCPClient extends InfraWalletClient.InfraWalletClient {
201
218
  // note that if there is a tag `project:foo` in config, it overrides the project field set above
202
219
  };
203
220
  }
204
- acc[keyName].reports[period] = functions.parseCost(row.total_cost);
221
+ acc[keyName].reports[period] = amount;
222
+ processedRecords++;
205
223
  return acc;
206
224
  },
207
225
  {}
208
226
  );
227
+ this.logTransformationSummary({
228
+ processed: processedRecords,
229
+ uniqueReports: uniqueKeys.size,
230
+ zeroAmount: filteredOutZeroAmount,
231
+ missingFields: filteredOutMissingFields,
232
+ invalidDate: filteredOutInvalidDate,
233
+ timeRange: filteredOutTimeRange,
234
+ totalRecords
235
+ });
209
236
  return Object.values(transformedData);
210
237
  }
211
238
  }