@electrolux-oss/plugin-infrawallet-backend 1.1.0-20260112100533-68a81f8 → 1.1.0-20260115072024-d6f33e2
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.
|
@@ -176,6 +176,66 @@ class InfraWalletClient {
|
|
|
176
176
|
errors
|
|
177
177
|
};
|
|
178
178
|
}
|
|
179
|
+
// Helper method to check if database autoload should be used
|
|
180
|
+
shouldUseAutoloadFromDatabase(query, autoloadCostData) {
|
|
181
|
+
return query.tags === "()" && query.groups === "" && autoloadCostData && this.provider !== consts.CLOUD_PROVIDER.MOCK;
|
|
182
|
+
}
|
|
183
|
+
// Helper method to check if current month is included in the query
|
|
184
|
+
isCurrentMonthIncluded(query) {
|
|
185
|
+
const now = /* @__PURE__ */ new Date();
|
|
186
|
+
return query.granularity === consts.GRANULARITY.MONTHLY && parseInt(query.endTime, 10) >= new Date(now.getFullYear(), now.getMonth(), 1).getTime();
|
|
187
|
+
}
|
|
188
|
+
// Helper method to handle cached data retrieval
|
|
189
|
+
async handleCachedData(integrationName, query, results, forecasts) {
|
|
190
|
+
const cachedCosts = await functions.getReportsFromCache(this.cache, this.provider, integrationName, query);
|
|
191
|
+
if (!cachedCosts) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
this.logger.debug(`${this.provider}/${integrationName} costs from cache`);
|
|
195
|
+
cachedCosts.forEach((cost) => results.push(cost));
|
|
196
|
+
if (this.isCurrentMonthIncluded(query)) {
|
|
197
|
+
const cachedForecast = await functions.getForecastFromCache(this.cache, this.provider, integrationName, query);
|
|
198
|
+
if (cachedForecast !== void 0) {
|
|
199
|
+
this.logger.debug(`${this.provider}/${integrationName} forecast from cache: ${cachedForecast}`);
|
|
200
|
+
forecasts[this.provider] = cachedForecast;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
// Helper method to process fresh data from API
|
|
206
|
+
async processFreshData(integrationConfig, integrationName, query, results, forecasts) {
|
|
207
|
+
const client = await this.initCloudClient(integrationConfig);
|
|
208
|
+
const costResponse = await this.fetchCosts(integrationConfig, client, query);
|
|
209
|
+
const transformedReports = await this.transformCostsData(integrationConfig, query, costResponse);
|
|
210
|
+
await functions.setReportsToCache(
|
|
211
|
+
this.cache,
|
|
212
|
+
transformedReports,
|
|
213
|
+
this.provider,
|
|
214
|
+
integrationName,
|
|
215
|
+
query,
|
|
216
|
+
functions.getDefaultCacheTTL(consts.CACHE_CATEGORY.COSTS, this.provider)
|
|
217
|
+
);
|
|
218
|
+
transformedReports.forEach((value) => results.push(value));
|
|
219
|
+
if (this.isCurrentMonthIncluded(query)) {
|
|
220
|
+
await this.handleForecastData(integrationConfig, integrationName, query, forecasts);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Helper method to handle forecast data processing
|
|
224
|
+
async handleForecastData(integrationConfig, integrationName, query, forecasts) {
|
|
225
|
+
const integrationForecast = await this.fetchForecast(integrationConfig);
|
|
226
|
+
if (integrationForecast !== null && integrationForecast > 0) {
|
|
227
|
+
forecasts[this.provider] = integrationForecast;
|
|
228
|
+
await functions.setForecastToCache(
|
|
229
|
+
this.cache,
|
|
230
|
+
integrationForecast,
|
|
231
|
+
this.provider,
|
|
232
|
+
integrationName,
|
|
233
|
+
query,
|
|
234
|
+
functions.getDefaultCacheTTL(consts.CACHE_CATEGORY.COSTS, this.provider)
|
|
235
|
+
);
|
|
236
|
+
this.logger.debug(`${this.provider}/${integrationName} forecast cached: ${integrationForecast}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
179
239
|
async getCostReports(query) {
|
|
180
240
|
const autoloadCostData = this.config.getOptionalBoolean("backend.infraWallet.autoload.enabled") ?? false;
|
|
181
241
|
const integrationConfigs = this.config.getOptionalConfigArray(
|
|
@@ -187,47 +247,20 @@ class InfraWalletClient {
|
|
|
187
247
|
const results = [];
|
|
188
248
|
const forecasts = {};
|
|
189
249
|
const errors = [];
|
|
190
|
-
|
|
191
|
-
const isCurrentMonthIncluded = query.granularity === consts.GRANULARITY.MONTHLY && parseInt(query.endTime, 10) >= new Date(now.getFullYear(), now.getMonth(), 1).getTime();
|
|
192
|
-
if (query.tags === "()" && query.groups === "" && autoloadCostData && this.provider !== consts.CLOUD_PROVIDER.MOCK) {
|
|
250
|
+
if (this.shouldUseAutoloadFromDatabase(query, autoloadCostData)) {
|
|
193
251
|
const reportsFromDatabase = await this.getCostReportsFromDatabase(query);
|
|
194
|
-
reportsFromDatabase.forEach((report) =>
|
|
195
|
-
results.push(report);
|
|
196
|
-
});
|
|
252
|
+
reportsFromDatabase.forEach((report) => results.push(report));
|
|
197
253
|
} else {
|
|
198
254
|
const promises = [];
|
|
199
255
|
for (const integrationConfig of integrationConfigs) {
|
|
200
256
|
const integrationName = integrationConfig.getString("name");
|
|
201
|
-
const
|
|
202
|
-
if (
|
|
203
|
-
this.logger.debug(`${this.provider}/${integrationName} costs from cache`);
|
|
204
|
-
cachedCosts.forEach((cost) => {
|
|
205
|
-
results.push(cost);
|
|
206
|
-
});
|
|
257
|
+
const foundCachedData = await this.handleCachedData(integrationName, query, results, forecasts);
|
|
258
|
+
if (foundCachedData) {
|
|
207
259
|
continue;
|
|
208
260
|
}
|
|
209
261
|
const promise = (async () => {
|
|
210
262
|
try {
|
|
211
|
-
|
|
212
|
-
const costResponse = await this.fetchCosts(integrationConfig, client, query);
|
|
213
|
-
const transformedReports = await this.transformCostsData(integrationConfig, query, costResponse);
|
|
214
|
-
await functions.setReportsToCache(
|
|
215
|
-
this.cache,
|
|
216
|
-
transformedReports,
|
|
217
|
-
this.provider,
|
|
218
|
-
integrationName,
|
|
219
|
-
query,
|
|
220
|
-
functions.getDefaultCacheTTL(consts.CACHE_CATEGORY.COSTS, this.provider)
|
|
221
|
-
);
|
|
222
|
-
transformedReports.forEach((value) => {
|
|
223
|
-
results.push(value);
|
|
224
|
-
});
|
|
225
|
-
if (isCurrentMonthIncluded) {
|
|
226
|
-
const integrationForecast = await this.fetchForecast(integrationConfig);
|
|
227
|
-
if (integrationForecast !== null) {
|
|
228
|
-
forecasts[this.provider] = integrationForecast;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
263
|
+
await this.processFreshData(integrationConfig, integrationName, query, results, forecasts);
|
|
231
264
|
} catch (e) {
|
|
232
265
|
this.logger.error(e);
|
|
233
266
|
errors.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InfraWalletClient.cjs.js","sources":["../../src/cost-clients/InfraWalletClient.ts"],"sourcesContent":["import { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { addMonths, endOfMonth, format, startOfMonth } from 'date-fns';\nimport { reduce } from 'lodash';\nimport { getWallet } from '../controllers/MetricSettingController';\nimport { CostItem, bulkInsertCostItems, countCostItems, getCostItems } from '../models/CostItem';\nimport {\n CACHE_CATEGORY,\n CLOUD_PROVIDER,\n GRANULARITY,\n NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS,\n PROVIDER_TYPE,\n} from '../service/consts';\nimport {\n getDefaultCacheTTL,\n getReportsFromCache,\n getTagKeysFromCache,\n getTagValuesFromCache,\n logTransformationSummary,\n setReportsToCache,\n setTagKeysToCache,\n setTagValuesToCache,\n tagExists,\n usageDateToPeriodString,\n} from '../service/functions';\nimport {\n ClientResponse,\n CloudProviderError,\n CostQuery,\n Filter,\n Report,\n Tag,\n TagsQuery,\n TagsResponse,\n TransformationSummary,\n Wallet,\n} from '../service/types';\n\nexport abstract class InfraWalletClient {\n constructor(\n protected readonly provider: CLOUD_PROVIDER,\n protected readonly config: Config,\n protected readonly database: DatabaseService,\n protected readonly cache: CacheService,\n protected readonly logger: LoggerService,\n ) {}\n\n protected convertServiceName(serviceName: string): string {\n return `${this.provider}/${serviceName}`;\n }\n\n protected evaluateIntegrationFilters(account: string, integrationConfig: Config): boolean {\n const filters: Filter[] = [];\n for (const filter of integrationConfig.getOptionalConfigArray('filters') || []) {\n filters.push({\n type: filter.getString('type'),\n attribute: filter.getString('attribute'),\n pattern: filter.getString('pattern'),\n });\n }\n return this.evaluateFilters(account, filters);\n }\n\n private evaluateFilters(account: string, filters: Filter[]): boolean {\n if (!filters || filters.length === 0) {\n // include if no filter\n return true;\n }\n\n let included = false;\n let hasIncludeFilter = false;\n\n for (const filter of filters) {\n const regex = new RegExp(filter.pattern);\n\n if (filter.type === 'exclude' && regex.test(account)) {\n // exclude immediately if an exclude filter matches\n return false;\n }\n\n if (filter.type === 'include') {\n hasIncludeFilter = true;\n\n if (regex.test(account)) {\n included = true;\n }\n }\n }\n\n if (hasIncludeFilter) {\n return included;\n }\n\n return true;\n }\n\n protected abstract initCloudClient(integrationConfig: Config): Promise<any>;\n\n // Get all cost allocation tag keys from one account\n protected async fetchTagKeys(\n _integrationConfig: Config,\n _client: any,\n _query: TagsQuery,\n ): Promise<{ tagKeys: string[]; provider: CLOUD_PROVIDER }> {\n // To be implemented by each provider client\n return { tagKeys: [], provider: this.provider };\n }\n\n // Get all tag values of the specified tag key from one account\n protected async fetchTagValues(\n _integrationConfig: Config,\n _client: any,\n _query: TagsQuery,\n _tagKey: string,\n ): Promise<{ tagValues: string[]; provider: CLOUD_PROVIDER }> {\n // To be implemented by each provider client\n return { tagValues: [], provider: this.provider };\n }\n\n protected abstract fetchCosts(integrationConfig: Config, client: any, query: CostQuery): Promise<any>;\n protected async fetchForecast(_integrationConfig: Config): Promise<number | null> {\n return null;\n }\n\n protected abstract transformCostsData(\n integrationConfig: Config,\n query: CostQuery,\n costResponse: any,\n ): Promise<Report[]>;\n\n protected logTransformationSummary(summary: TransformationSummary): void {\n logTransformationSummary(this.logger, this.provider, summary);\n }\n\n // Get aggregated unique tag keys across all accounts of this cloud provider\n async getTagKeys(query: TagsQuery): Promise<TagsResponse> {\n const integrationConfigs = this.config.getOptionalConfigArray(\n `backend.infraWallet.integrations.${this.provider.toLowerCase()}`,\n );\n if (!integrationConfigs) {\n return { tags: [], errors: [] };\n }\n\n const promises = [];\n const aggregatedTags: Tag[] = [];\n const errors: CloudProviderError[] = [];\n\n for (const integrationConfig of integrationConfigs) {\n const integrationName = integrationConfig.getString('name');\n\n const cachedTagKeys = await getTagKeysFromCache(this.cache, this.provider, integrationName, query);\n if (cachedTagKeys) {\n this.logger.info(`Reuse ${this.provider}/${integrationName} tag keys from cache`);\n\n for (const tag of cachedTagKeys) {\n if (!tagExists(aggregatedTags, tag)) {\n aggregatedTags.push(tag);\n }\n }\n\n continue;\n }\n\n const promise = (async () => {\n try {\n const client = await this.initCloudClient(integrationConfig);\n const response = await this.fetchTagKeys(integrationConfig, client, query);\n const tagKeysCache: Tag[] = [];\n\n for (const tagKey of response.tagKeys) {\n const tag = { key: tagKey, provider: response.provider };\n tagKeysCache.push(tag);\n\n if (!tagExists(aggregatedTags, tag)) {\n aggregatedTags.push(tag);\n }\n }\n await setTagKeysToCache(this.cache, tagKeysCache, this.provider, integrationName, query);\n } catch (e) {\n this.logger.error(e);\n errors.push({\n provider: this.provider,\n name: `${this.provider}/${integrationName}`,\n error: e.message,\n });\n }\n })();\n promises.push(promise);\n }\n await Promise.all(promises);\n\n aggregatedTags.sort((a, b) => `${a.provider}/${a.key}`.localeCompare(`${b.provider}/${b.key}`));\n\n return {\n tags: aggregatedTags,\n errors: errors,\n };\n }\n\n // Get aggregated tag values of the specified tag key across all accounts of this cloud provider\n async getTagValues(query: TagsQuery, tagKey: string): Promise<TagsResponse> {\n const integrationConfigs = this.config.getOptionalConfigArray(\n `backend.infraWallet.integrations.${this.provider.toLowerCase()}`,\n );\n if (!integrationConfigs) {\n return { tags: [], errors: [] };\n }\n\n const promises = [];\n const aggregatedTags: Tag[] = [];\n const errors: CloudProviderError[] = [];\n\n for (const integrationConfig of integrationConfigs) {\n const integrationName = integrationConfig.getString('name');\n\n const cachedTagValues = await getTagValuesFromCache(this.cache, this.provider, integrationName, tagKey, query);\n if (cachedTagValues) {\n this.logger.info(`Reuse ${this.provider}/${integrationName}/${tagKey} tag values from cache`);\n\n for (const tag of cachedTagValues) {\n if (!tagExists(aggregatedTags, tag)) {\n aggregatedTags.push(tag);\n }\n }\n\n continue;\n }\n\n const promise = (async () => {\n try {\n const client = await this.initCloudClient(integrationConfig);\n const response = await this.fetchTagValues(integrationConfig, client, query, tagKey);\n const tagValuesCache: Tag[] = [];\n\n for (const tagValue of response.tagValues) {\n const tag = { key: tagKey, value: tagValue, provider: response.provider };\n tagValuesCache.push(tag);\n\n if (!tagExists(aggregatedTags, tag)) {\n aggregatedTags.push(tag);\n }\n }\n await setTagValuesToCache(this.cache, tagValuesCache, this.provider, integrationName, tagKey, query);\n } catch (e) {\n this.logger.error(e);\n errors.push({\n provider: this.provider,\n name: `${this.provider}/${integrationName}`,\n error: e.message,\n });\n }\n })();\n promises.push(promise);\n }\n await Promise.all(promises);\n\n aggregatedTags.sort((a, b) =>\n `${a.provider}/${a.key}=${a.value}`.localeCompare(`${b.provider}/${b.key}=${b.value}`),\n );\n\n return {\n tags: aggregatedTags,\n errors: errors,\n };\n }\n\n async getCostReports(query: CostQuery): Promise<ClientResponse> {\n const autoloadCostData = this.config.getOptionalBoolean('backend.infraWallet.autoload.enabled') ?? false;\n const integrationConfigs = this.config.getOptionalConfigArray(\n `backend.infraWallet.integrations.${this.provider.toLowerCase()}`,\n );\n if (!integrationConfigs) {\n return { reports: [], errors: [] };\n }\n\n const results: Report[] = [];\n const forecasts: Record<string, number> = {};\n const errors: CloudProviderError[] = [];\n\n const now = new Date();\n const isCurrentMonthIncluded =\n query.granularity === GRANULARITY.MONTHLY &&\n parseInt(query.endTime, 10) >= new Date(now.getFullYear(), now.getMonth(), 1).getTime();\n\n // if autoloadCostData enabled, for a query without any tags or groups, we get the results from the plugin database\n // skip Mock provider for autoloading data\n if (query.tags === '()' && query.groups === '' && autoloadCostData && this.provider !== CLOUD_PROVIDER.MOCK) {\n const reportsFromDatabase = await this.getCostReportsFromDatabase(query);\n reportsFromDatabase.forEach(report => {\n results.push(report);\n });\n } else {\n const promises = [];\n for (const integrationConfig of integrationConfigs) {\n const integrationName = integrationConfig.getString('name');\n\n // first check if there is any cached\n const cachedCosts = await getReportsFromCache(this.cache, this.provider, integrationName, query);\n if (cachedCosts) {\n this.logger.debug(`${this.provider}/${integrationName} costs from cache`);\n cachedCosts.forEach(cost => {\n results.push(cost);\n });\n continue;\n }\n\n const promise = (async () => {\n try {\n const client = await this.initCloudClient(integrationConfig);\n const costResponse = await this.fetchCosts(integrationConfig, client, query);\n\n const transformedReports = await this.transformCostsData(integrationConfig, query, costResponse);\n\n // cache the results\n await setReportsToCache(\n this.cache,\n transformedReports,\n this.provider,\n integrationName,\n query,\n getDefaultCacheTTL(CACHE_CATEGORY.COSTS, this.provider),\n );\n\n transformedReports.forEach((value: any) => {\n results.push(value);\n });\n if (isCurrentMonthIncluded) {\n const integrationForecast = await this.fetchForecast(integrationConfig);\n if (integrationForecast !== null) {\n forecasts[this.provider] = integrationForecast;\n }\n }\n } catch (e) {\n this.logger.error(e);\n errors.push({\n provider: this.provider,\n name: `${this.provider}/${integrationName}`,\n error: e.message,\n });\n }\n })();\n promises.push(promise);\n }\n await Promise.all(promises);\n }\n\n return {\n reports: results,\n forecasts: forecasts,\n errors: errors,\n };\n }\n\n async saveCostReportsToDatabase(wallet: Wallet, granularity: GRANULARITY): Promise<void> {\n const count = await countCostItems(this.database, wallet.id, this.provider, granularity);\n\n const endTime = endOfMonth(new Date());\n let startTime = startOfMonth(addMonths(new Date(), -1));\n if (count === 0) {\n // if there is no record, the first call is going to fetch the last 364 days' cost data\n // it cannot be 365 day or 1 year because Azure API will responds with the following error\n // Invalid query definition: The time period for pulling the data cannot exceed 1 year(s)\n startTime = startOfMonth(\n addMonths(new Date(), -1 * NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS[this.provider] + 1),\n );\n }\n\n this.logger.debug(`Fetching ${granularity} costs from ${startTime} to ${endTime} for ${this.provider}`);\n\n const results: Report[] = [];\n const usageDateFormat = granularity === GRANULARITY.DAILY ? 'yyyyMMdd' : 'yyyyMM';\n try {\n const clientResponse = await this.getCostReports({\n filters: '',\n tags: '',\n groups: '',\n granularity: granularity,\n startTime: startTime.getTime().toString(),\n endTime: endTime.getTime().toString(),\n });\n clientResponse.reports.forEach((cost: Report) => {\n results.push(cost);\n });\n } catch (e) {\n this.logger.error(e);\n }\n\n await bulkInsertCostItems(\n this.database,\n wallet.id,\n this.provider,\n granularity,\n parseInt(format(startTime, usageDateFormat), 10),\n parseInt(format(endTime, usageDateFormat), 10),\n results,\n );\n }\n\n async getCostReportsFromDatabase(query: CostQuery): Promise<Report[]> {\n // TODO: support searching for different wallets in the future, for now it is always the default wallet\n const defaultWallet = await getWallet(this.database, 'default');\n if (defaultWallet !== undefined) {\n // query the database\n const usageDateFormat = query.granularity === 'daily' ? 'yyyyMMdd' : 'yyyyMM';\n const startUsageDate = parseInt(format(parseInt(query.startTime, 10), usageDateFormat), 10);\n const endUsageDate = parseInt(format(parseInt(query.endTime, 10), usageDateFormat), 10);\n const costItems = await getCostItems(\n this.database,\n defaultWallet.id,\n this.provider,\n query.granularity,\n startUsageDate,\n endUsageDate,\n );\n\n // transform the cost items into cost reports\n const transformedData = reduce(\n costItems,\n (accumulator: { [key: string]: Report }, row: CostItem) => {\n const key = row.key;\n const otherColumns =\n typeof row.other_columns === 'string' ? JSON.parse(row.other_columns) : row.other_columns;\n\n if (!accumulator[key]) {\n accumulator[key] = {\n id: key,\n account: row.account,\n service: row.service,\n category: row.category,\n provider: row.provider,\n providerType: PROVIDER_TYPE.INTEGRATION,\n reports: {},\n ...otherColumns,\n };\n }\n accumulator[key].reports[usageDateToPeriodString(row.usage_date)] = parseFloat(row.cost as string);\n\n return accumulator;\n },\n {},\n );\n\n return Object.values(transformedData);\n }\n\n return [];\n }\n}\n"],"names":["logTransformationSummary","getTagKeysFromCache","tagExists","setTagKeysToCache","getTagValuesFromCache","setTagValuesToCache","GRANULARITY","CLOUD_PROVIDER","getReportsFromCache","setReportsToCache","getDefaultCacheTTL","CACHE_CATEGORY","countCostItems","endOfMonth","startOfMonth","addMonths","NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS","bulkInsertCostItems","format","getWallet","getCostItems","reduce","PROVIDER_TYPE","usageDateToPeriodString"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InfraWalletClient.cjs.js","sources":["../../src/cost-clients/InfraWalletClient.ts"],"sourcesContent":["import { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { addMonths, endOfMonth, format, startOfMonth } from 'date-fns';\nimport { reduce } from 'lodash';\nimport { getWallet } from '../controllers/MetricSettingController';\nimport { CostItem, bulkInsertCostItems, countCostItems, getCostItems } from '../models/CostItem';\nimport {\n CACHE_CATEGORY,\n CLOUD_PROVIDER,\n GRANULARITY,\n NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS,\n PROVIDER_TYPE,\n} from '../service/consts';\nimport {\n getDefaultCacheTTL,\n getForecastFromCache,\n getReportsFromCache,\n getTagKeysFromCache,\n getTagValuesFromCache,\n logTransformationSummary,\n setForecastToCache,\n setReportsToCache,\n setTagKeysToCache,\n setTagValuesToCache,\n tagExists,\n usageDateToPeriodString,\n} from '../service/functions';\nimport {\n ClientResponse,\n CloudProviderError,\n CostQuery,\n Filter,\n Report,\n Tag,\n TagsQuery,\n TagsResponse,\n TransformationSummary,\n Wallet,\n} from '../service/types';\n\nexport abstract class InfraWalletClient {\n constructor(\n protected readonly provider: CLOUD_PROVIDER,\n protected readonly config: Config,\n protected readonly database: DatabaseService,\n protected readonly cache: CacheService,\n protected readonly logger: LoggerService,\n ) {}\n\n protected convertServiceName(serviceName: string): string {\n return `${this.provider}/${serviceName}`;\n }\n\n protected evaluateIntegrationFilters(account: string, integrationConfig: Config): boolean {\n const filters: Filter[] = [];\n for (const filter of integrationConfig.getOptionalConfigArray('filters') || []) {\n filters.push({\n type: filter.getString('type'),\n attribute: filter.getString('attribute'),\n pattern: filter.getString('pattern'),\n });\n }\n return this.evaluateFilters(account, filters);\n }\n\n private evaluateFilters(account: string, filters: Filter[]): boolean {\n if (!filters || filters.length === 0) {\n // include if no filter\n return true;\n }\n\n let included = false;\n let hasIncludeFilter = false;\n\n for (const filter of filters) {\n const regex = new RegExp(filter.pattern);\n\n if (filter.type === 'exclude' && regex.test(account)) {\n // exclude immediately if an exclude filter matches\n return false;\n }\n\n if (filter.type === 'include') {\n hasIncludeFilter = true;\n\n if (regex.test(account)) {\n included = true;\n }\n }\n }\n\n if (hasIncludeFilter) {\n return included;\n }\n\n return true;\n }\n\n protected abstract initCloudClient(integrationConfig: Config): Promise<any>;\n\n // Get all cost allocation tag keys from one account\n protected async fetchTagKeys(\n _integrationConfig: Config,\n _client: any,\n _query: TagsQuery,\n ): Promise<{ tagKeys: string[]; provider: CLOUD_PROVIDER }> {\n // To be implemented by each provider client\n return { tagKeys: [], provider: this.provider };\n }\n\n // Get all tag values of the specified tag key from one account\n protected async fetchTagValues(\n _integrationConfig: Config,\n _client: any,\n _query: TagsQuery,\n _tagKey: string,\n ): Promise<{ tagValues: string[]; provider: CLOUD_PROVIDER }> {\n // To be implemented by each provider client\n return { tagValues: [], provider: this.provider };\n }\n\n protected abstract fetchCosts(integrationConfig: Config, client: any, query: CostQuery): Promise<any>;\n protected async fetchForecast(_integrationConfig: Config): Promise<number | null> {\n return null;\n }\n\n protected abstract transformCostsData(\n integrationConfig: Config,\n query: CostQuery,\n costResponse: any,\n ): Promise<Report[]>;\n\n protected logTransformationSummary(summary: TransformationSummary): void {\n logTransformationSummary(this.logger, this.provider, summary);\n }\n\n // Get aggregated unique tag keys across all accounts of this cloud provider\n async getTagKeys(query: TagsQuery): Promise<TagsResponse> {\n const integrationConfigs = this.config.getOptionalConfigArray(\n `backend.infraWallet.integrations.${this.provider.toLowerCase()}`,\n );\n if (!integrationConfigs) {\n return { tags: [], errors: [] };\n }\n\n const promises = [];\n const aggregatedTags: Tag[] = [];\n const errors: CloudProviderError[] = [];\n\n for (const integrationConfig of integrationConfigs) {\n const integrationName = integrationConfig.getString('name');\n\n const cachedTagKeys = await getTagKeysFromCache(this.cache, this.provider, integrationName, query);\n if (cachedTagKeys) {\n this.logger.info(`Reuse ${this.provider}/${integrationName} tag keys from cache`);\n\n for (const tag of cachedTagKeys) {\n if (!tagExists(aggregatedTags, tag)) {\n aggregatedTags.push(tag);\n }\n }\n\n continue;\n }\n\n const promise = (async () => {\n try {\n const client = await this.initCloudClient(integrationConfig);\n const response = await this.fetchTagKeys(integrationConfig, client, query);\n const tagKeysCache: Tag[] = [];\n\n for (const tagKey of response.tagKeys) {\n const tag = { key: tagKey, provider: response.provider };\n tagKeysCache.push(tag);\n\n if (!tagExists(aggregatedTags, tag)) {\n aggregatedTags.push(tag);\n }\n }\n await setTagKeysToCache(this.cache, tagKeysCache, this.provider, integrationName, query);\n } catch (e) {\n this.logger.error(e);\n errors.push({\n provider: this.provider,\n name: `${this.provider}/${integrationName}`,\n error: e.message,\n });\n }\n })();\n promises.push(promise);\n }\n await Promise.all(promises);\n\n aggregatedTags.sort((a, b) => `${a.provider}/${a.key}`.localeCompare(`${b.provider}/${b.key}`));\n\n return {\n tags: aggregatedTags,\n errors: errors,\n };\n }\n\n // Get aggregated tag values of the specified tag key across all accounts of this cloud provider\n async getTagValues(query: TagsQuery, tagKey: string): Promise<TagsResponse> {\n const integrationConfigs = this.config.getOptionalConfigArray(\n `backend.infraWallet.integrations.${this.provider.toLowerCase()}`,\n );\n if (!integrationConfigs) {\n return { tags: [], errors: [] };\n }\n\n const promises = [];\n const aggregatedTags: Tag[] = [];\n const errors: CloudProviderError[] = [];\n\n for (const integrationConfig of integrationConfigs) {\n const integrationName = integrationConfig.getString('name');\n\n const cachedTagValues = await getTagValuesFromCache(this.cache, this.provider, integrationName, tagKey, query);\n if (cachedTagValues) {\n this.logger.info(`Reuse ${this.provider}/${integrationName}/${tagKey} tag values from cache`);\n\n for (const tag of cachedTagValues) {\n if (!tagExists(aggregatedTags, tag)) {\n aggregatedTags.push(tag);\n }\n }\n\n continue;\n }\n\n const promise = (async () => {\n try {\n const client = await this.initCloudClient(integrationConfig);\n const response = await this.fetchTagValues(integrationConfig, client, query, tagKey);\n const tagValuesCache: Tag[] = [];\n\n for (const tagValue of response.tagValues) {\n const tag = { key: tagKey, value: tagValue, provider: response.provider };\n tagValuesCache.push(tag);\n\n if (!tagExists(aggregatedTags, tag)) {\n aggregatedTags.push(tag);\n }\n }\n await setTagValuesToCache(this.cache, tagValuesCache, this.provider, integrationName, tagKey, query);\n } catch (e) {\n this.logger.error(e);\n errors.push({\n provider: this.provider,\n name: `${this.provider}/${integrationName}`,\n error: e.message,\n });\n }\n })();\n promises.push(promise);\n }\n await Promise.all(promises);\n\n aggregatedTags.sort((a, b) =>\n `${a.provider}/${a.key}=${a.value}`.localeCompare(`${b.provider}/${b.key}=${b.value}`),\n );\n\n return {\n tags: aggregatedTags,\n errors: errors,\n };\n }\n\n // Helper method to check if database autoload should be used\n private shouldUseAutoloadFromDatabase(query: CostQuery, autoloadCostData: boolean): boolean {\n return query.tags === '()' && query.groups === '' && autoloadCostData && this.provider !== CLOUD_PROVIDER.MOCK;\n }\n\n // Helper method to check if current month is included in the query\n private isCurrentMonthIncluded(query: CostQuery): boolean {\n const now = new Date();\n return (\n query.granularity === GRANULARITY.MONTHLY &&\n parseInt(query.endTime, 10) >= new Date(now.getFullYear(), now.getMonth(), 1).getTime()\n );\n }\n\n // Helper method to handle cached data retrieval\n private async handleCachedData(\n integrationName: string,\n query: CostQuery,\n results: Report[],\n forecasts: Record<string, number>,\n ): Promise<boolean> {\n const cachedCosts = await getReportsFromCache(this.cache, this.provider, integrationName, query);\n if (!cachedCosts) {\n return false;\n }\n\n this.logger.debug(`${this.provider}/${integrationName} costs from cache`);\n cachedCosts.forEach(cost => results.push(cost));\n\n if (this.isCurrentMonthIncluded(query)) {\n const cachedForecast = await getForecastFromCache(this.cache, this.provider, integrationName, query);\n if (cachedForecast !== undefined) {\n this.logger.debug(`${this.provider}/${integrationName} forecast from cache: ${cachedForecast}`);\n forecasts[this.provider] = cachedForecast;\n }\n }\n return true;\n }\n\n // Helper method to process fresh data from API\n private async processFreshData(\n integrationConfig: Config,\n integrationName: string,\n query: CostQuery,\n results: Report[],\n forecasts: Record<string, number>,\n ): Promise<void> {\n const client = await this.initCloudClient(integrationConfig);\n const costResponse = await this.fetchCosts(integrationConfig, client, query);\n const transformedReports = await this.transformCostsData(integrationConfig, query, costResponse);\n\n // Cache the results\n await setReportsToCache(\n this.cache,\n transformedReports,\n this.provider,\n integrationName,\n query,\n getDefaultCacheTTL(CACHE_CATEGORY.COSTS, this.provider),\n );\n\n transformedReports.forEach((value: any) => results.push(value));\n\n if (this.isCurrentMonthIncluded(query)) {\n await this.handleForecastData(integrationConfig, integrationName, query, forecasts);\n }\n }\n\n // Helper method to handle forecast data processing\n private async handleForecastData(\n integrationConfig: Config,\n integrationName: string,\n query: CostQuery,\n forecasts: Record<string, number>,\n ): Promise<void> {\n const integrationForecast = await this.fetchForecast(integrationConfig);\n if (integrationForecast !== null && integrationForecast > 0) {\n forecasts[this.provider] = integrationForecast;\n\n await setForecastToCache(\n this.cache,\n integrationForecast,\n this.provider,\n integrationName,\n query,\n getDefaultCacheTTL(CACHE_CATEGORY.COSTS, this.provider),\n );\n this.logger.debug(`${this.provider}/${integrationName} forecast cached: ${integrationForecast}`);\n }\n }\n\n async getCostReports(query: CostQuery): Promise<ClientResponse> {\n const autoloadCostData = this.config.getOptionalBoolean('backend.infraWallet.autoload.enabled') ?? false;\n const integrationConfigs = this.config.getOptionalConfigArray(\n `backend.infraWallet.integrations.${this.provider.toLowerCase()}`,\n );\n\n if (!integrationConfigs) {\n return { reports: [], errors: [] };\n }\n\n const results: Report[] = [];\n const forecasts: Record<string, number> = {};\n const errors: CloudProviderError[] = [];\n\n // Use autoload from database if conditions are met\n if (this.shouldUseAutoloadFromDatabase(query, autoloadCostData)) {\n const reportsFromDatabase = await this.getCostReportsFromDatabase(query);\n reportsFromDatabase.forEach(report => results.push(report));\n } else {\n const promises = [];\n\n for (const integrationConfig of integrationConfigs) {\n const integrationName = integrationConfig.getString('name');\n\n // Check for cached data first\n const foundCachedData = await this.handleCachedData(integrationName, query, results, forecasts);\n\n if (foundCachedData) {\n continue;\n }\n\n // Process fresh data from API\n const promise = (async () => {\n try {\n await this.processFreshData(integrationConfig, integrationName, query, results, forecasts);\n } catch (e) {\n this.logger.error(e);\n errors.push({\n provider: this.provider,\n name: `${this.provider}/${integrationName}`,\n error: e.message,\n });\n }\n })();\n promises.push(promise);\n }\n await Promise.all(promises);\n }\n\n return {\n reports: results,\n forecasts: forecasts,\n errors: errors,\n };\n }\n\n async saveCostReportsToDatabase(wallet: Wallet, granularity: GRANULARITY): Promise<void> {\n const count = await countCostItems(this.database, wallet.id, this.provider, granularity);\n\n const endTime = endOfMonth(new Date());\n let startTime = startOfMonth(addMonths(new Date(), -1));\n if (count === 0) {\n // if there is no record, the first call is going to fetch the last 364 days' cost data\n // it cannot be 365 day or 1 year because Azure API will responds with the following error\n // Invalid query definition: The time period for pulling the data cannot exceed 1 year(s)\n startTime = startOfMonth(\n addMonths(new Date(), -1 * NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS[this.provider] + 1),\n );\n }\n\n this.logger.debug(`Fetching ${granularity} costs from ${startTime} to ${endTime} for ${this.provider}`);\n\n const results: Report[] = [];\n const usageDateFormat = granularity === GRANULARITY.DAILY ? 'yyyyMMdd' : 'yyyyMM';\n try {\n const clientResponse = await this.getCostReports({\n filters: '',\n tags: '',\n groups: '',\n granularity: granularity,\n startTime: startTime.getTime().toString(),\n endTime: endTime.getTime().toString(),\n });\n clientResponse.reports.forEach((cost: Report) => {\n results.push(cost);\n });\n } catch (e) {\n this.logger.error(e);\n }\n\n await bulkInsertCostItems(\n this.database,\n wallet.id,\n this.provider,\n granularity,\n parseInt(format(startTime, usageDateFormat), 10),\n parseInt(format(endTime, usageDateFormat), 10),\n results,\n );\n }\n\n async getCostReportsFromDatabase(query: CostQuery): Promise<Report[]> {\n // TODO: support searching for different wallets in the future, for now it is always the default wallet\n const defaultWallet = await getWallet(this.database, 'default');\n if (defaultWallet !== undefined) {\n // query the database\n const usageDateFormat = query.granularity === 'daily' ? 'yyyyMMdd' : 'yyyyMM';\n const startUsageDate = parseInt(format(parseInt(query.startTime, 10), usageDateFormat), 10);\n const endUsageDate = parseInt(format(parseInt(query.endTime, 10), usageDateFormat), 10);\n const costItems = await getCostItems(\n this.database,\n defaultWallet.id,\n this.provider,\n query.granularity,\n startUsageDate,\n endUsageDate,\n );\n\n // transform the cost items into cost reports\n const transformedData = reduce(\n costItems,\n (accumulator: { [key: string]: Report }, row: CostItem) => {\n const key = row.key;\n const otherColumns =\n typeof row.other_columns === 'string' ? JSON.parse(row.other_columns) : row.other_columns;\n\n if (!accumulator[key]) {\n accumulator[key] = {\n id: key,\n account: row.account,\n service: row.service,\n category: row.category,\n provider: row.provider,\n providerType: PROVIDER_TYPE.INTEGRATION,\n reports: {},\n ...otherColumns,\n };\n }\n accumulator[key].reports[usageDateToPeriodString(row.usage_date)] = parseFloat(row.cost as string);\n\n return accumulator;\n },\n {},\n );\n\n return Object.values(transformedData);\n }\n\n return [];\n }\n}\n"],"names":["logTransformationSummary","getTagKeysFromCache","tagExists","setTagKeysToCache","getTagValuesFromCache","setTagValuesToCache","CLOUD_PROVIDER","GRANULARITY","getReportsFromCache","getForecastFromCache","setReportsToCache","getDefaultCacheTTL","CACHE_CATEGORY","setForecastToCache","countCostItems","endOfMonth","startOfMonth","addMonths","NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS","bulkInsertCostItems","format","getWallet","getCostItems","reduce","PROVIDER_TYPE","usageDateToPeriodString"],"mappings":"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"}
|
|
@@ -76,6 +76,21 @@ async function getReportsFromCache(cache, provider, configKey, query) {
|
|
|
76
76
|
const cachedCosts = await cache.get(cacheKey);
|
|
77
77
|
return cachedCosts;
|
|
78
78
|
}
|
|
79
|
+
async function getForecastFromCache(cache, provider, configKey, query) {
|
|
80
|
+
const cacheKey = [
|
|
81
|
+
"forecast",
|
|
82
|
+
provider,
|
|
83
|
+
configKey,
|
|
84
|
+
query.filters,
|
|
85
|
+
query.tags,
|
|
86
|
+
query.groups,
|
|
87
|
+
query.granularity,
|
|
88
|
+
query.startTime,
|
|
89
|
+
query.endTime
|
|
90
|
+
].join("_");
|
|
91
|
+
const cachedForecast = await cache.get(cacheKey);
|
|
92
|
+
return cachedForecast;
|
|
93
|
+
}
|
|
79
94
|
async function getMetricsFromCache(cache, provider, configKey, query) {
|
|
80
95
|
const cacheKey = [
|
|
81
96
|
provider,
|
|
@@ -125,6 +140,23 @@ async function setReportsToCache(cache, reports, provider, configKey, query, ttl
|
|
|
125
140
|
ttl: ttl ?? 60 * 60 * 2 * 1e3
|
|
126
141
|
});
|
|
127
142
|
}
|
|
143
|
+
async function setForecastToCache(cache, forecast, provider, configKey, query, ttl) {
|
|
144
|
+
const cacheKey = [
|
|
145
|
+
"forecast",
|
|
146
|
+
provider,
|
|
147
|
+
configKey,
|
|
148
|
+
query.filters,
|
|
149
|
+
query.tags,
|
|
150
|
+
query.groups,
|
|
151
|
+
query.granularity,
|
|
152
|
+
query.startTime,
|
|
153
|
+
query.endTime
|
|
154
|
+
].join("_");
|
|
155
|
+
await cache.set(cacheKey, forecast, {
|
|
156
|
+
ttl: ttl ?? 60 * 60 * 2 * 1e3
|
|
157
|
+
// cache for 2 hours by default, same as reports
|
|
158
|
+
});
|
|
159
|
+
}
|
|
128
160
|
async function setMetricsToCache(cache, metrics, provider, configKey, query, ttl) {
|
|
129
161
|
const cacheKey = [
|
|
130
162
|
provider,
|
|
@@ -207,6 +239,7 @@ exports.getBillingPeriod = getBillingPeriod;
|
|
|
207
239
|
exports.getBillingPeriodFormat = getBillingPeriodFormat;
|
|
208
240
|
exports.getDailyPeriodStringsForOneMonth = getDailyPeriodStringsForOneMonth;
|
|
209
241
|
exports.getDefaultCacheTTL = getDefaultCacheTTL;
|
|
242
|
+
exports.getForecastFromCache = getForecastFromCache;
|
|
210
243
|
exports.getMetricsFromCache = getMetricsFromCache;
|
|
211
244
|
exports.getReportsFromCache = getReportsFromCache;
|
|
212
245
|
exports.getTagKeysFromCache = getTagKeysFromCache;
|
|
@@ -215,6 +248,7 @@ exports.logTransformationSummary = logTransformationSummary;
|
|
|
215
248
|
exports.parseCost = parseCost;
|
|
216
249
|
exports.parseFilters = parseFilters;
|
|
217
250
|
exports.parseTags = parseTags;
|
|
251
|
+
exports.setForecastToCache = setForecastToCache;
|
|
218
252
|
exports.setMetricsToCache = setMetricsToCache;
|
|
219
253
|
exports.setReportsToCache = setReportsToCache;
|
|
220
254
|
exports.setTagKeysToCache = setTagKeysToCache;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"functions.cjs.js","sources":["../../src/service/functions.ts"],"sourcesContent":["import { CacheService, LoggerService } from '@backstage/backend-plugin-api';\nimport { CACHE_CATEGORY, CLOUD_PROVIDER, DEFAULT_COSTS_CACHE_TTL, DEFAULT_TAGS_CACHE_TTL, GRANULARITY } from './consts';\nimport { CostQuery, Metric, MetricQuery, Report, Tag, TagsQuery, TransformationSummary } from './types';\nimport moment from 'moment';\n\n// In URL, tags are defined in this format:\n// tags=(provider1:key1=value1 OR provider2:key2=value2)\nexport function parseTags(tags: string): Tag[] {\n if (!tags.startsWith('(') || !tags.endsWith(')')) {\n return [];\n }\n\n const tagString = tags.slice(1, -1);\n if (!tagString) {\n return [];\n }\n\n const keyValuePairs = tagString.split(' OR ');\n return keyValuePairs.map(pair => {\n const [providerAndKey, value] = pair.split('=');\n const firstColonIndex = providerAndKey.indexOf(':');\n const provider = providerAndKey.slice(0, firstColonIndex);\n const key = providerAndKey.slice(firstColonIndex + 1);\n return { key: key, value: value, provider: provider };\n });\n}\n\n// convert Tag array to (provider1:key1=value1 OR provider2:key2=value2) format\nexport function tagsToString(tags: Tag[]): string {\n if (!tags || 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\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\nexport function getDefaultCacheTTL(cacheCategory: CACHE_CATEGORY, provider: CLOUD_PROVIDER): number {\n if (cacheCategory === CACHE_CATEGORY.TAGS) {\n return DEFAULT_TAGS_CACHE_TTL[provider];\n } else if (cacheCategory === CACHE_CATEGORY.COSTS) {\n return DEFAULT_COSTS_CACHE_TTL[provider];\n }\n\n return 0;\n}\n\nexport async function getTagKeysFromCache(\n cache: CacheService,\n provider: CLOUD_PROVIDER,\n configKey: string,\n query: TagsQuery,\n): Promise<Tag[] | undefined> {\n const cacheKey = [CACHE_CATEGORY.TAGS, 'tag-keys', provider, configKey, query.startTime, query.endTime].join('_');\n const data = (await cache.get(cacheKey)) as Tag[] | undefined;\n return data;\n}\n\nexport async function getTagValuesFromCache(\n cache: CacheService,\n provider: CLOUD_PROVIDER,\n configKey: string,\n tagKey: string,\n query: TagsQuery,\n): Promise<Tag[] | undefined> {\n const cacheKey = [\n CACHE_CATEGORY.TAGS,\n 'tag-values',\n provider,\n configKey,\n tagKey,\n query.startTime,\n query.endTime,\n ].join('_');\n const data = (await cache.get(cacheKey)) as Tag[] | undefined;\n return data;\n}\n\nexport async function getReportsFromCache(\n cache: CacheService,\n provider: string,\n configKey: string,\n query: CostQuery,\n): Promise<Report[] | undefined> {\n const cacheKey = [\n provider,\n configKey,\n query.filters,\n query.tags,\n query.groups,\n query.granularity,\n query.startTime,\n query.endTime,\n ].join('_');\n const cachedCosts = (await cache.get(cacheKey)) as Report[] | undefined;\n return cachedCosts;\n}\n\nexport async function getMetricsFromCache(\n cache: CacheService,\n provider: string,\n configKey: string,\n query: MetricQuery,\n): Promise<Metric[] | undefined> {\n const cacheKey = [\n provider,\n configKey,\n query.name,\n query.query,\n query.granularity,\n query.startTime,\n query.endTime,\n ].join('_');\n const crypto = require('crypto');\n const cachedMetrics = (await cache.get(crypto.createHash('md5').update(cacheKey).digest('hex'))) as\n | Metric[]\n | undefined;\n return cachedMetrics;\n}\n\nexport async function setTagKeysToCache(\n cache: CacheService,\n tags: Tag[],\n provider: CLOUD_PROVIDER,\n configKey: string,\n query: TagsQuery,\n ttl?: number,\n) {\n const cacheKey = [CACHE_CATEGORY.TAGS, 'tag-keys', provider, configKey, query.startTime, query.endTime].join('_');\n await cache.set(cacheKey, tags, {\n ttl: ttl ?? getDefaultCacheTTL(CACHE_CATEGORY.TAGS, provider),\n });\n}\n\nexport async function setTagValuesToCache(\n cache: CacheService,\n tags: Tag[],\n provider: CLOUD_PROVIDER,\n configKey: string,\n tagKey: string,\n query: TagsQuery,\n ttl?: number,\n) {\n const cacheKey = [\n CACHE_CATEGORY.TAGS,\n 'tag-values',\n provider,\n configKey,\n tagKey,\n query.startTime,\n query.endTime,\n ].join('_');\n await cache.set(cacheKey, tags, {\n ttl: ttl ?? getDefaultCacheTTL(CACHE_CATEGORY.TAGS, provider),\n });\n}\n\nexport async function setReportsToCache(\n cache: CacheService,\n reports: Report[],\n provider: string,\n configKey: string,\n query: CostQuery,\n ttl?: number,\n) {\n const cacheKey = [\n provider,\n configKey,\n query.filters,\n query.tags,\n query.groups,\n query.granularity,\n query.startTime,\n query.endTime,\n ].join('_');\n await cache.set(cacheKey, reports, {\n ttl: ttl ?? 60 * 60 * 2 * 1000,\n }); // cache for 2 hours by default\n}\n\nexport async function setMetricsToCache(\n cache: CacheService,\n metrics: Metric[],\n provider: string,\n configKey: string,\n query: MetricQuery,\n ttl?: number,\n) {\n const cacheKey = [\n provider,\n configKey,\n query.name,\n query.query,\n query.granularity,\n query.startTime,\n query.endTime,\n ].join('_');\n const crypto = require('crypto');\n await cache.set(crypto.createHash('md5').update(cacheKey).digest('hex'), metrics, {\n ttl: ttl ?? 60 * 60 * 2 * 1000,\n }); // cache for 2 hours by default\n}\n\nexport function parseFilters(filters: string): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n\n if (!filters.startsWith('(') || !filters.endsWith(')')) {\n return result;\n }\n\n const filterString = filters.slice(1, -1);\n if (!filterString) {\n return result;\n }\n\n const keyValuePairs = filterString.split(',').map(pair => pair.trim());\n\n keyValuePairs.forEach(pair => {\n const [key, value] = pair.split(':').map(s => s.trim());\n if (key && value) {\n if (value.startsWith('(') && value.endsWith(')')) {\n // multiple values\n const values = value\n .slice(1, -1)\n .split('|')\n .map(v => v.trim());\n result[key] = values;\n } else {\n // single value\n result[key] = [value];\n }\n }\n });\n\n return result;\n}\n\n/**\n * Returns the date format string for a billing period based on granularity.\n */\nexport function getBillingPeriodFormat(granularity: GRANULARITY): string {\n if (granularity === GRANULARITY.MONTHLY) {\n return 'YYYY-MM';\n } else if (granularity === GRANULARITY.DAILY) {\n return 'YYYY-MM-DD';\n }\n throw new Error('Invalid granularity');\n}\n\n/**\n * Converts a source date string to a billing period string based on granularity and source format.\n */\nexport function getBillingPeriod(granularity: GRANULARITY, sourceDate: string, sourceFormat: string): string {\n return moment(sourceDate, sourceFormat, true).format(getBillingPeriodFormat(granularity));\n}\n\n/**\n * Parses a cost value and rounds to 2 decimal places.\n * Returns 0 if the value is not a valid number.\n */\nexport function parseCost(value: any): number {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? Math.round(parsed * 100) / 100 : 0;\n}\n\nexport function getDailyPeriodStringsForOneMonth(yyyymm: number): string[] {\n const dateOjb = moment(yyyymm.toString(), 'YYYYMM');\n const startOfMonth = moment(dateOjb).startOf('month');\n const endOfMonth = moment(dateOjb).endOf('month');\n const periods: string[] = [];\n\n for (let date = startOfMonth; date.isBefore(endOfMonth) || date.isSame(endOfMonth); date.add(1, 'day')) {\n periods.push(date.format('YYYY-MM-DD'));\n }\n\n return periods;\n}\n\nexport function usageDateToPeriodString(usageDate: number): string {\n // usageDate format: either yyyymm (monthly) or yyyymmdd (daily)\n // output format: yyyy-mm (monthly) or yyyy-mm-dd (daily)\n const usageDateStr = usageDate.toString();\n if (usageDateStr.length === 6) {\n // Monthly format\n return `${usageDateStr.slice(0, 4)}-${usageDateStr.slice(4, 6)}`;\n } else if (usageDateStr.length === 8) {\n // Daily format\n return `${usageDateStr.slice(0, 4)}-${usageDateStr.slice(4, 6)}-${usageDateStr.slice(6, 8)}`;\n }\n throw new Error('Invalid usageDate format');\n}\n\n/**\n * Logs a standardized transformation summary for cost client data processing.\n * This provides consistent logging across all cost providers.\n */\nexport function logTransformationSummary(\n logger: LoggerService,\n provider: string,\n summary: TransformationSummary,\n): void {\n logger.info(\n `${provider} transformation summary: processed=${summary.processed}, uniqueReports=${summary.uniqueReports}, zeroAmount=${summary.zeroAmount}, missingFields=${summary.missingFields}, invalidDate=${summary.invalidDate}, timeRange=${summary.timeRange}, totalRecords=${summary.totalRecords}`,\n );\n}\n"],"names":["CACHE_CATEGORY","DEFAULT_TAGS_CACHE_TTL","DEFAULT_COSTS_CACHE_TTL","GRANULARITY","moment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"functions.cjs.js","sources":["../../src/service/functions.ts"],"sourcesContent":["import { CacheService, LoggerService } from '@backstage/backend-plugin-api';\nimport { CACHE_CATEGORY, CLOUD_PROVIDER, DEFAULT_COSTS_CACHE_TTL, DEFAULT_TAGS_CACHE_TTL, GRANULARITY } from './consts';\nimport { CostQuery, Metric, MetricQuery, Report, Tag, TagsQuery, TransformationSummary } from './types';\nimport moment from 'moment';\n\n// In URL, tags are defined in this format:\n// tags=(provider1:key1=value1 OR provider2:key2=value2)\nexport function parseTags(tags: string): Tag[] {\n if (!tags.startsWith('(') || !tags.endsWith(')')) {\n return [];\n }\n\n const tagString = tags.slice(1, -1);\n if (!tagString) {\n return [];\n }\n\n const keyValuePairs = tagString.split(' OR ');\n return keyValuePairs.map(pair => {\n const [providerAndKey, value] = pair.split('=');\n const firstColonIndex = providerAndKey.indexOf(':');\n const provider = providerAndKey.slice(0, firstColonIndex);\n const key = providerAndKey.slice(firstColonIndex + 1);\n return { key: key, value: value, provider: provider };\n });\n}\n\n// convert Tag array to (provider1:key1=value1 OR provider2:key2=value2) format\nexport function tagsToString(tags: Tag[]): string {\n if (!tags || 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\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\nexport function getDefaultCacheTTL(cacheCategory: CACHE_CATEGORY, provider: CLOUD_PROVIDER): number {\n if (cacheCategory === CACHE_CATEGORY.TAGS) {\n return DEFAULT_TAGS_CACHE_TTL[provider];\n } else if (cacheCategory === CACHE_CATEGORY.COSTS) {\n return DEFAULT_COSTS_CACHE_TTL[provider];\n }\n\n return 0;\n}\n\nexport async function getTagKeysFromCache(\n cache: CacheService,\n provider: CLOUD_PROVIDER,\n configKey: string,\n query: TagsQuery,\n): Promise<Tag[] | undefined> {\n const cacheKey = [CACHE_CATEGORY.TAGS, 'tag-keys', provider, configKey, query.startTime, query.endTime].join('_');\n const data = (await cache.get(cacheKey)) as Tag[] | undefined;\n return data;\n}\n\nexport async function getTagValuesFromCache(\n cache: CacheService,\n provider: CLOUD_PROVIDER,\n configKey: string,\n tagKey: string,\n query: TagsQuery,\n): Promise<Tag[] | undefined> {\n const cacheKey = [\n CACHE_CATEGORY.TAGS,\n 'tag-values',\n provider,\n configKey,\n tagKey,\n query.startTime,\n query.endTime,\n ].join('_');\n const data = (await cache.get(cacheKey)) as Tag[] | undefined;\n return data;\n}\n\nexport async function getReportsFromCache(\n cache: CacheService,\n provider: string,\n configKey: string,\n query: CostQuery,\n): Promise<Report[] | undefined> {\n const cacheKey = [\n provider,\n configKey,\n query.filters,\n query.tags,\n query.groups,\n query.granularity,\n query.startTime,\n query.endTime,\n ].join('_');\n const cachedCosts = (await cache.get(cacheKey)) as Report[] | undefined;\n return cachedCosts;\n}\n\nexport async function getForecastFromCache(\n cache: CacheService,\n provider: string,\n configKey: string,\n query: CostQuery,\n): Promise<number | undefined> {\n const cacheKey = [\n 'forecast',\n provider,\n configKey,\n query.filters,\n query.tags,\n query.groups,\n query.granularity,\n query.startTime,\n query.endTime,\n ].join('_');\n const cachedForecast = (await cache.get(cacheKey)) as number | undefined;\n return cachedForecast;\n}\n\nexport async function getMetricsFromCache(\n cache: CacheService,\n provider: string,\n configKey: string,\n query: MetricQuery,\n): Promise<Metric[] | undefined> {\n const cacheKey = [\n provider,\n configKey,\n query.name,\n query.query,\n query.granularity,\n query.startTime,\n query.endTime,\n ].join('_');\n const crypto = require('crypto');\n const cachedMetrics = (await cache.get(crypto.createHash('md5').update(cacheKey).digest('hex'))) as\n | Metric[]\n | undefined;\n return cachedMetrics;\n}\n\nexport async function setTagKeysToCache(\n cache: CacheService,\n tags: Tag[],\n provider: CLOUD_PROVIDER,\n configKey: string,\n query: TagsQuery,\n ttl?: number,\n) {\n const cacheKey = [CACHE_CATEGORY.TAGS, 'tag-keys', provider, configKey, query.startTime, query.endTime].join('_');\n await cache.set(cacheKey, tags, {\n ttl: ttl ?? getDefaultCacheTTL(CACHE_CATEGORY.TAGS, provider),\n });\n}\n\nexport async function setTagValuesToCache(\n cache: CacheService,\n tags: Tag[],\n provider: CLOUD_PROVIDER,\n configKey: string,\n tagKey: string,\n query: TagsQuery,\n ttl?: number,\n) {\n const cacheKey = [\n CACHE_CATEGORY.TAGS,\n 'tag-values',\n provider,\n configKey,\n tagKey,\n query.startTime,\n query.endTime,\n ].join('_');\n await cache.set(cacheKey, tags, {\n ttl: ttl ?? getDefaultCacheTTL(CACHE_CATEGORY.TAGS, provider),\n });\n}\n\nexport async function setReportsToCache(\n cache: CacheService,\n reports: Report[],\n provider: string,\n configKey: string,\n query: CostQuery,\n ttl?: number,\n) {\n const cacheKey = [\n provider,\n configKey,\n query.filters,\n query.tags,\n query.groups,\n query.granularity,\n query.startTime,\n query.endTime,\n ].join('_');\n await cache.set(cacheKey, reports, {\n ttl: ttl ?? 60 * 60 * 2 * 1000,\n }); // cache for 2 hours by default\n}\n\nexport async function setForecastToCache(\n cache: CacheService,\n forecast: number,\n provider: string,\n configKey: string,\n query: CostQuery,\n ttl?: number,\n) {\n const cacheKey = [\n 'forecast',\n provider,\n configKey,\n query.filters,\n query.tags,\n query.groups,\n query.granularity,\n query.startTime,\n query.endTime,\n ].join('_');\n await cache.set(cacheKey, forecast, {\n ttl: ttl ?? 60 * 60 * 2 * 1000, // cache for 2 hours by default, same as reports\n });\n}\n\nexport async function setMetricsToCache(\n cache: CacheService,\n metrics: Metric[],\n provider: string,\n configKey: string,\n query: MetricQuery,\n ttl?: number,\n) {\n const cacheKey = [\n provider,\n configKey,\n query.name,\n query.query,\n query.granularity,\n query.startTime,\n query.endTime,\n ].join('_');\n const crypto = require('crypto');\n await cache.set(crypto.createHash('md5').update(cacheKey).digest('hex'), metrics, {\n ttl: ttl ?? 60 * 60 * 2 * 1000,\n }); // cache for 2 hours by default\n}\n\nexport function parseFilters(filters: string): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n\n if (!filters.startsWith('(') || !filters.endsWith(')')) {\n return result;\n }\n\n const filterString = filters.slice(1, -1);\n if (!filterString) {\n return result;\n }\n\n const keyValuePairs = filterString.split(',').map(pair => pair.trim());\n\n keyValuePairs.forEach(pair => {\n const [key, value] = pair.split(':').map(s => s.trim());\n if (key && value) {\n if (value.startsWith('(') && value.endsWith(')')) {\n // multiple values\n const values = value\n .slice(1, -1)\n .split('|')\n .map(v => v.trim());\n result[key] = values;\n } else {\n // single value\n result[key] = [value];\n }\n }\n });\n\n return result;\n}\n\n/**\n * Returns the date format string for a billing period based on granularity.\n */\nexport function getBillingPeriodFormat(granularity: GRANULARITY): string {\n if (granularity === GRANULARITY.MONTHLY) {\n return 'YYYY-MM';\n } else if (granularity === GRANULARITY.DAILY) {\n return 'YYYY-MM-DD';\n }\n throw new Error('Invalid granularity');\n}\n\n/**\n * Converts a source date string to a billing period string based on granularity and source format.\n */\nexport function getBillingPeriod(granularity: GRANULARITY, sourceDate: string, sourceFormat: string): string {\n return moment(sourceDate, sourceFormat, true).format(getBillingPeriodFormat(granularity));\n}\n\n/**\n * Parses a cost value and rounds to 2 decimal places.\n * Returns 0 if the value is not a valid number.\n */\nexport function parseCost(value: any): number {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? Math.round(parsed * 100) / 100 : 0;\n}\n\nexport function getDailyPeriodStringsForOneMonth(yyyymm: number): string[] {\n const dateOjb = moment(yyyymm.toString(), 'YYYYMM');\n const startOfMonth = moment(dateOjb).startOf('month');\n const endOfMonth = moment(dateOjb).endOf('month');\n const periods: string[] = [];\n\n for (let date = startOfMonth; date.isBefore(endOfMonth) || date.isSame(endOfMonth); date.add(1, 'day')) {\n periods.push(date.format('YYYY-MM-DD'));\n }\n\n return periods;\n}\n\nexport function usageDateToPeriodString(usageDate: number): string {\n // usageDate format: either yyyymm (monthly) or yyyymmdd (daily)\n // output format: yyyy-mm (monthly) or yyyy-mm-dd (daily)\n const usageDateStr = usageDate.toString();\n if (usageDateStr.length === 6) {\n // Monthly format\n return `${usageDateStr.slice(0, 4)}-${usageDateStr.slice(4, 6)}`;\n } else if (usageDateStr.length === 8) {\n // Daily format\n return `${usageDateStr.slice(0, 4)}-${usageDateStr.slice(4, 6)}-${usageDateStr.slice(6, 8)}`;\n }\n throw new Error('Invalid usageDate format');\n}\n\n/**\n * Logs a standardized transformation summary for cost client data processing.\n * This provides consistent logging across all cost providers.\n */\nexport function logTransformationSummary(\n logger: LoggerService,\n provider: string,\n summary: TransformationSummary,\n): void {\n logger.info(\n `${provider} transformation summary: processed=${summary.processed}, uniqueReports=${summary.uniqueReports}, zeroAmount=${summary.zeroAmount}, missingFields=${summary.missingFields}, invalidDate=${summary.invalidDate}, timeRange=${summary.timeRange}, totalRecords=${summary.totalRecords}`,\n );\n}\n"],"names":["CACHE_CATEGORY","DEFAULT_TAGS_CACHE_TTL","DEFAULT_COSTS_CACHE_TTL","GRANULARITY","moment"],"mappings":"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"}
|