@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
@@ -211,24 +211,44 @@ class AwsClient extends InfraWalletClient.InfraWalletClient {
211
211
  const [k, v] = tag.split(":");
212
212
  tagKeyValues[k.trim()] = v.trim();
213
213
  });
214
+ let processedRecords = 0;
215
+ let filteredOutZeroAmount = 0;
216
+ let filteredOutMissingFields = 0;
217
+ let filteredOutInvalidDate = 0;
218
+ const filteredOutTimeRange = 0;
219
+ const uniqueKeys = /* @__PURE__ */ new Set();
220
+ const totalRecords = costResponse?.length || 0;
214
221
  const transformedData = lodash.reduce(
215
222
  costResponse,
216
223
  (accumulator, row) => {
217
224
  const rowTime = row.TimePeriod?.Start;
218
225
  let period = "unknown";
219
- if (rowTime) {
220
- period = functions.getBillingPeriod(query.granularity, rowTime, "YYYY-MM-DD");
226
+ if (!rowTime) {
227
+ filteredOutInvalidDate++;
228
+ return accumulator;
221
229
  }
230
+ period = functions.getBillingPeriod(query.granularity, rowTime, "YYYY-MM-DD");
222
231
  if (row.Groups) {
223
232
  row.Groups.forEach((group) => {
224
233
  const accountId = group.Keys ? group.Keys[0] : "";
225
234
  const accountName = this.accounts.get(accountId) || accountId;
235
+ const serviceName = group.Keys ? group.Keys[1] : "";
236
+ const groupMetrics = group.Metrics;
237
+ if (!accountId || !serviceName || !groupMetrics?.UnblendedCost?.Amount) {
238
+ filteredOutMissingFields++;
239
+ return;
240
+ }
241
+ const amount = functions.parseCost(groupMetrics.UnblendedCost.Amount);
242
+ if (amount === 0) {
243
+ filteredOutZeroAmount++;
244
+ return;
245
+ }
226
246
  if (!this.evaluateIntegrationFilters(accountName, integrationConfig)) {
227
247
  return;
228
248
  }
229
- const serviceName = group.Keys ? group.Keys[1] : "";
230
249
  const keyName = `${accountId}_${serviceName}`;
231
250
  if (!accumulator[keyName]) {
251
+ uniqueKeys.add(keyName);
232
252
  accumulator[keyName] = {
233
253
  id: keyName,
234
254
  account: `${this.provider}/${accountName} (${accountId})`,
@@ -240,16 +260,23 @@ class AwsClient extends InfraWalletClient.InfraWalletClient {
240
260
  ...tagKeyValues
241
261
  };
242
262
  }
243
- const groupMetrics = group.Metrics;
244
- if (groupMetrics !== void 0) {
245
- accumulator[keyName].reports[period] = functions.parseCost(groupMetrics.UnblendedCost.Amount);
246
- }
263
+ accumulator[keyName].reports[period] = amount;
264
+ processedRecords++;
247
265
  });
248
266
  }
249
267
  return accumulator;
250
268
  },
251
269
  {}
252
270
  );
271
+ this.logTransformationSummary({
272
+ processed: processedRecords,
273
+ uniqueReports: uniqueKeys.size,
274
+ zeroAmount: filteredOutZeroAmount,
275
+ missingFields: filteredOutMissingFields,
276
+ invalidDate: filteredOutInvalidDate,
277
+ timeRange: filteredOutTimeRange,
278
+ totalRecords
279
+ });
253
280
  return Object.values(transformedData);
254
281
  }
255
282
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AwsClient.cjs.js","sources":["../../src/cost-clients/AwsClient.ts"],"sourcesContent":["import {\n CostExplorerClient,\n Dimension,\n Expression,\n GetCostAndUsageCommand,\n GetCostAndUsageCommandInput,\n GetTagsCommand,\n GetTagsCommandInput,\n Granularity,\n GroupDefinitionType,\n} from '@aws-sdk/client-cost-explorer';\nimport { AssumeRoleCommand, STSClient } from '@aws-sdk/client-sts';\nimport { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { CategoryMappingService } from '../service/CategoryMappingService';\nimport { CLOUD_PROVIDER, PROVIDER_TYPE } from '../service/consts';\nimport { getBillingPeriod, parseCost, parseTags } from '../service/functions';\nimport { CostQuery, Report, TagsQuery } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\nimport { AWSGetCostAndUsageResponseSchema, AWSGetTagsResponseSchema } from '../schemas/AWSBilling';\nimport { ZodError } from 'zod';\n\nexport class AwsClient extends InfraWalletClient {\n private readonly accounts: Map<string, string> = new Map();\n\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new AwsClient(CLOUD_PROVIDER.AWS, config, database, cache, logger);\n }\n\n protected convertServiceName(serviceName: string): string {\n let convertedName = serviceName;\n\n const prefixes = ['Amazon', 'AWS'];\n\n const aliases = new Map<string, string>([\n ['Elastic Compute Cloud - Compute', 'EC2 - Instances'],\n ['Virtual Private Cloud', 'VPC (Virtual Private Cloud)'],\n ['Relational Database Service', 'RDS (Relational Database Service)'],\n ['Simple Storage Service', 'S3 (Simple Storage Service)'],\n ['Managed Streaming for Apache Kafka', 'MSK (Managed Streaming for Apache Kafka)'],\n ['Elastic Container Service for Kubernetes', 'EKS (Elastic Container Service for Kubernetes)'],\n ['Elastic Container Service', 'ECS (Elastic Container Service)'],\n ['EC2 Container Registry (ECR)', 'ECR (Elastic Container Registry)'],\n ['Simple Queue Service', 'SQS (Simple Queue Service)'],\n ['Simple Notification Service', 'SNS (Simple Notification Service)'],\n ['Database Migration Service', 'DMS (Database Migration Service)'],\n ]);\n\n for (const prefix of prefixes) {\n if (serviceName.startsWith(prefix)) {\n convertedName = serviceName.slice(prefix.length).trim();\n }\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: Config): Promise<any> {\n const accountId = integrationConfig.getString('accountId');\n const assumedRoleName = integrationConfig.getOptionalString('assumedRoleName');\n const accessKeyId = integrationConfig.getOptionalString('accessKeyId');\n let secretAccessKey: string | undefined;\n const region = 'us-east-1';\n // Attempt to get the new, preferred key\n const newSecretAccessKey = integrationConfig.getOptionalString('secretAccessKey');\n // Attempt to get the old, deprecated key\n const oldAccessKeySecret = integrationConfig.getOptionalString('accessKeySecret');\n\n if (newSecretAccessKey) {\n // If the new key is present, use it\n secretAccessKey = newSecretAccessKey;\n } else if (oldAccessKeySecret) {\n // If the new key is NOT present, but the old one IS, use the old one and log a warning\n secretAccessKey = oldAccessKeySecret;\n this.logger.warn(`The 'accessKeySecret' configuration key is deprecated. Please rename it to 'secretAccessKey'.`);\n }\n\n if (!accessKeyId && !secretAccessKey && !assumedRoleName) {\n // No credentials provided in configuration, assuming credentials are available in the environment\n return new CostExplorerClient({ region: region });\n }\n\n let credentials = undefined;\n if (accessKeyId || secretAccessKey) {\n if (accessKeyId && secretAccessKey) {\n credentials = {\n accessKeyId: accessKeyId,\n secretAccessKey: secretAccessKey,\n };\n } else {\n throw new Error('Both accessKeyId and secretAccessKey must be provided');\n }\n }\n\n if (assumedRoleName === undefined) {\n return new CostExplorerClient({\n region: region,\n credentials: credentials,\n });\n }\n\n // Assume role\n const client = new STSClient({\n region: region,\n credentials: credentials,\n });\n const commandInput = {\n // AssumeRoleRequest\n RoleArn: `arn:aws:iam::${accountId}:role/${assumedRoleName}`,\n RoleSessionName: 'InfraWallet',\n };\n const assumeRoleCommand = new AssumeRoleCommand(commandInput);\n const assumeRoleResponse = await client.send(assumeRoleCommand);\n // init aws cost explorer client\n const awsCeClient = new CostExplorerClient({\n region: region,\n credentials: {\n accessKeyId: assumeRoleResponse.Credentials?.AccessKeyId as string,\n secretAccessKey: assumeRoleResponse.Credentials?.SecretAccessKey as string,\n sessionToken: assumeRoleResponse.Credentials?.SessionToken as string,\n },\n });\n\n return awsCeClient;\n }\n\n private async _fetchTags(client: any, query: TagsQuery, tagKey?: string): Promise<string[]> {\n const tags: string[] = [];\n let nextPageToken = undefined;\n\n do {\n const input: GetTagsCommandInput = {\n TimePeriod: {\n Start: moment(parseInt(query.startTime, 10)).format('YYYY-MM-DD'),\n End: moment(parseInt(query.endTime, 10)).format('YYYY-MM-DD'),\n },\n TagKey: tagKey,\n };\n const command = new GetTagsCommand(input);\n const response = await client.send(command);\n\n try {\n AWSGetTagsResponseSchema.parse(response);\n this.logger.debug(`AWS tags response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`AWS tags 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 for (const tag of response.Tags) {\n if (tag) {\n tags.push(tag);\n }\n }\n\n nextPageToken = response.NextPageToken;\n } while (nextPageToken);\n\n tags.sort((a, b) => a.localeCompare(b));\n return tags;\n }\n\n protected async fetchTagKeys(\n _integrationConfig: Config,\n client: any,\n query: TagsQuery,\n ): Promise<{ tagKeys: string[]; provider: CLOUD_PROVIDER }> {\n const tagKeys = await this._fetchTags(client, query);\n return { tagKeys: tagKeys, provider: this.provider };\n }\n\n protected async fetchTagValues(\n _integrationConfig: Config,\n client: any,\n query: TagsQuery,\n tagKey: string,\n ): Promise<{ tagValues: string[]; provider: CLOUD_PROVIDER }> {\n const tagValues = await this._fetchTags(client, query, tagKey);\n return { tagValues: tagValues, provider: this.provider };\n }\n\n protected async fetchCosts(_integrationConfig: Config, client: any, query: CostQuery): Promise<any> {\n // query this aws account's cost and usage using @aws-sdk/client-cost-explorer\n let costAndUsageResults: any[] = [];\n let nextPageToken = undefined;\n let filterExpression: Expression = { Dimensions: { Key: Dimension.RECORD_TYPE, Values: ['Usage'] } };\n const tags = parseTags(query.tags);\n if (tags.length) {\n let tagsExpression: Expression = {};\n\n if (tags.length === 1) {\n if (tags[0].value) {\n tagsExpression = { Tags: { Key: tags[0].key, Values: [tags[0].value] } };\n }\n } else {\n const tagList: Expression[] = [];\n for (const tag of tags) {\n if (tag.value) {\n tagList.push({ Tags: { Key: tag.key, Values: [tag.value] } });\n }\n }\n tagsExpression = { Or: tagList };\n }\n\n filterExpression = { And: [filterExpression, tagsExpression] };\n }\n\n do {\n const input: GetCostAndUsageCommandInput = {\n TimePeriod: {\n Start: moment(parseInt(query.startTime, 10)).format('YYYY-MM-DD'),\n End: moment(parseInt(query.endTime, 10)).format('YYYY-MM-DD'),\n },\n Granularity: query.granularity.toUpperCase() as Granularity,\n Filter: filterExpression,\n GroupBy: [\n { Type: GroupDefinitionType.DIMENSION, Key: Dimension.LINKED_ACCOUNT },\n { Type: GroupDefinitionType.DIMENSION, Key: Dimension.SERVICE },\n ],\n Metrics: ['UnblendedCost'],\n NextPageToken: nextPageToken,\n };\n\n const getCostCommand = new GetCostAndUsageCommand(input);\n const costAndUsageResponse = await client.send(getCostCommand);\n\n try {\n AWSGetCostAndUsageResponseSchema.parse(costAndUsageResponse);\n this.logger.debug(`AWS cost and usage response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`AWS cost and usage 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 // get AWS account names\n for (const accountAttributes of costAndUsageResponse.DimensionValueAttributes) {\n const accountId = accountAttributes.Value;\n const accountName = accountAttributes.Attributes.description;\n this.accounts.set(accountId, accountName);\n }\n\n costAndUsageResults = costAndUsageResults.concat(costAndUsageResponse.ResultsByTime);\n nextPageToken = costAndUsageResponse.NextPageToken;\n } while (nextPageToken);\n\n return costAndUsageResults;\n }\n\n protected async transformCostsData(\n integrationConfig: Config,\n query: CostQuery,\n costResponse: any,\n ): Promise<Report[]> {\n const categoryMappingService = CategoryMappingService.getInstance();\n const tags = integrationConfig.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 }, row) => {\n const rowTime = row.TimePeriod?.Start;\n let period = 'unknown';\n if (rowTime) {\n period = getBillingPeriod(query.granularity, rowTime, 'YYYY-MM-DD');\n }\n if (row.Groups) {\n row.Groups.forEach((group: any) => {\n const accountId = group.Keys ? group.Keys[0] : '';\n const accountName = this.accounts.get(accountId) || accountId;\n\n if (!this.evaluateIntegrationFilters(accountName, integrationConfig)) {\n return;\n }\n\n const serviceName = group.Keys ? group.Keys[1] : '';\n const keyName = `${accountId}_${serviceName}`;\n\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n account: `${this.provider}/${accountName} (${accountId})`,\n service: this.convertServiceName(serviceName),\n category: categoryMappingService.getCategoryByServiceName(this.provider, serviceName),\n provider: this.provider,\n providerType: PROVIDER_TYPE.INTEGRATION,\n reports: {},\n ...tagKeyValues,\n };\n }\n\n const groupMetrics = group.Metrics;\n\n if (groupMetrics !== undefined) {\n accumulator[keyName].reports[period] = parseCost(groupMetrics.UnblendedCost.Amount);\n }\n });\n }\n\n return accumulator;\n },\n {},\n );\n return Object.values(transformedData);\n }\n}\n"],"names":["InfraWalletClient","CLOUD_PROVIDER","CostExplorerClient","STSClient","AssumeRoleCommand","moment","GetTagsCommand","AWSGetTagsResponseSchema","ZodError","Dimension","parseTags","GroupDefinitionType","GetCostAndUsageCommand","AWSGetCostAndUsageResponseSchema","CategoryMappingService","reduce","getBillingPeriod","PROVIDER_TYPE","parseCost"],"mappings":"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"}
1
+ {"version":3,"file":"AwsClient.cjs.js","sources":["../../src/cost-clients/AwsClient.ts"],"sourcesContent":["import {\n CostExplorerClient,\n Dimension,\n Expression,\n GetCostAndUsageCommand,\n GetCostAndUsageCommandInput,\n GetTagsCommand,\n GetTagsCommandInput,\n Granularity,\n GroupDefinitionType,\n} from '@aws-sdk/client-cost-explorer';\nimport { AssumeRoleCommand, STSClient } from '@aws-sdk/client-sts';\nimport { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { CategoryMappingService } from '../service/CategoryMappingService';\nimport { CLOUD_PROVIDER, PROVIDER_TYPE } from '../service/consts';\nimport { getBillingPeriod, parseCost, parseTags } from '../service/functions';\nimport { CostQuery, Report, TagsQuery } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\nimport { AWSGetCostAndUsageResponseSchema, AWSGetTagsResponseSchema } from '../schemas/AWSBilling';\nimport { ZodError } from 'zod';\n\nexport class AwsClient extends InfraWalletClient {\n private readonly accounts: Map<string, string> = new Map();\n\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new AwsClient(CLOUD_PROVIDER.AWS, config, database, cache, logger);\n }\n\n protected convertServiceName(serviceName: string): string {\n let convertedName = serviceName;\n\n const prefixes = ['Amazon', 'AWS'];\n\n const aliases = new Map<string, string>([\n ['Elastic Compute Cloud - Compute', 'EC2 - Instances'],\n ['Virtual Private Cloud', 'VPC (Virtual Private Cloud)'],\n ['Relational Database Service', 'RDS (Relational Database Service)'],\n ['Simple Storage Service', 'S3 (Simple Storage Service)'],\n ['Managed Streaming for Apache Kafka', 'MSK (Managed Streaming for Apache Kafka)'],\n ['Elastic Container Service for Kubernetes', 'EKS (Elastic Container Service for Kubernetes)'],\n ['Elastic Container Service', 'ECS (Elastic Container Service)'],\n ['EC2 Container Registry (ECR)', 'ECR (Elastic Container Registry)'],\n ['Simple Queue Service', 'SQS (Simple Queue Service)'],\n ['Simple Notification Service', 'SNS (Simple Notification Service)'],\n ['Database Migration Service', 'DMS (Database Migration Service)'],\n ]);\n\n for (const prefix of prefixes) {\n if (serviceName.startsWith(prefix)) {\n convertedName = serviceName.slice(prefix.length).trim();\n }\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: Config): Promise<any> {\n const accountId = integrationConfig.getString('accountId');\n const assumedRoleName = integrationConfig.getOptionalString('assumedRoleName');\n const accessKeyId = integrationConfig.getOptionalString('accessKeyId');\n let secretAccessKey: string | undefined;\n const region = 'us-east-1';\n // Attempt to get the new, preferred key\n const newSecretAccessKey = integrationConfig.getOptionalString('secretAccessKey');\n // Attempt to get the old, deprecated key\n const oldAccessKeySecret = integrationConfig.getOptionalString('accessKeySecret');\n\n if (newSecretAccessKey) {\n // If the new key is present, use it\n secretAccessKey = newSecretAccessKey;\n } else if (oldAccessKeySecret) {\n // If the new key is NOT present, but the old one IS, use the old one and log a warning\n secretAccessKey = oldAccessKeySecret;\n this.logger.warn(`The 'accessKeySecret' configuration key is deprecated. Please rename it to 'secretAccessKey'.`);\n }\n\n if (!accessKeyId && !secretAccessKey && !assumedRoleName) {\n // No credentials provided in configuration, assuming credentials are available in the environment\n return new CostExplorerClient({ region: region });\n }\n\n let credentials = undefined;\n if (accessKeyId || secretAccessKey) {\n if (accessKeyId && secretAccessKey) {\n credentials = {\n accessKeyId: accessKeyId,\n secretAccessKey: secretAccessKey,\n };\n } else {\n throw new Error('Both accessKeyId and secretAccessKey must be provided');\n }\n }\n\n if (assumedRoleName === undefined) {\n return new CostExplorerClient({\n region: region,\n credentials: credentials,\n });\n }\n\n // Assume role\n const client = new STSClient({\n region: region,\n credentials: credentials,\n });\n const commandInput = {\n // AssumeRoleRequest\n RoleArn: `arn:aws:iam::${accountId}:role/${assumedRoleName}`,\n RoleSessionName: 'InfraWallet',\n };\n const assumeRoleCommand = new AssumeRoleCommand(commandInput);\n const assumeRoleResponse = await client.send(assumeRoleCommand);\n // init aws cost explorer client\n const awsCeClient = new CostExplorerClient({\n region: region,\n credentials: {\n accessKeyId: assumeRoleResponse.Credentials?.AccessKeyId as string,\n secretAccessKey: assumeRoleResponse.Credentials?.SecretAccessKey as string,\n sessionToken: assumeRoleResponse.Credentials?.SessionToken as string,\n },\n });\n\n return awsCeClient;\n }\n\n private async _fetchTags(client: any, query: TagsQuery, tagKey?: string): Promise<string[]> {\n const tags: string[] = [];\n let nextPageToken = undefined;\n\n do {\n const input: GetTagsCommandInput = {\n TimePeriod: {\n Start: moment(parseInt(query.startTime, 10)).format('YYYY-MM-DD'),\n End: moment(parseInt(query.endTime, 10)).format('YYYY-MM-DD'),\n },\n TagKey: tagKey,\n };\n const command = new GetTagsCommand(input);\n const response = await client.send(command);\n\n try {\n AWSGetTagsResponseSchema.parse(response);\n this.logger.debug(`AWS tags response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`AWS tags 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 for (const tag of response.Tags) {\n if (tag) {\n tags.push(tag);\n }\n }\n\n nextPageToken = response.NextPageToken;\n } while (nextPageToken);\n\n tags.sort((a, b) => a.localeCompare(b));\n return tags;\n }\n\n protected async fetchTagKeys(\n _integrationConfig: Config,\n client: any,\n query: TagsQuery,\n ): Promise<{ tagKeys: string[]; provider: CLOUD_PROVIDER }> {\n const tagKeys = await this._fetchTags(client, query);\n return { tagKeys: tagKeys, provider: this.provider };\n }\n\n protected async fetchTagValues(\n _integrationConfig: Config,\n client: any,\n query: TagsQuery,\n tagKey: string,\n ): Promise<{ tagValues: string[]; provider: CLOUD_PROVIDER }> {\n const tagValues = await this._fetchTags(client, query, tagKey);\n return { tagValues: tagValues, provider: this.provider };\n }\n\n protected async fetchCosts(_integrationConfig: Config, client: any, query: CostQuery): Promise<any> {\n // query this aws account's cost and usage using @aws-sdk/client-cost-explorer\n let costAndUsageResults: any[] = [];\n let nextPageToken = undefined;\n let filterExpression: Expression = { Dimensions: { Key: Dimension.RECORD_TYPE, Values: ['Usage'] } };\n const tags = parseTags(query.tags);\n if (tags.length) {\n let tagsExpression: Expression = {};\n\n if (tags.length === 1) {\n if (tags[0].value) {\n tagsExpression = { Tags: { Key: tags[0].key, Values: [tags[0].value] } };\n }\n } else {\n const tagList: Expression[] = [];\n for (const tag of tags) {\n if (tag.value) {\n tagList.push({ Tags: { Key: tag.key, Values: [tag.value] } });\n }\n }\n tagsExpression = { Or: tagList };\n }\n\n filterExpression = { And: [filterExpression, tagsExpression] };\n }\n\n do {\n const input: GetCostAndUsageCommandInput = {\n TimePeriod: {\n Start: moment(parseInt(query.startTime, 10)).format('YYYY-MM-DD'),\n End: moment(parseInt(query.endTime, 10)).format('YYYY-MM-DD'),\n },\n Granularity: query.granularity.toUpperCase() as Granularity,\n Filter: filterExpression,\n GroupBy: [\n { Type: GroupDefinitionType.DIMENSION, Key: Dimension.LINKED_ACCOUNT },\n { Type: GroupDefinitionType.DIMENSION, Key: Dimension.SERVICE },\n ],\n Metrics: ['UnblendedCost'],\n NextPageToken: nextPageToken,\n };\n\n const getCostCommand = new GetCostAndUsageCommand(input);\n const costAndUsageResponse = await client.send(getCostCommand);\n\n try {\n AWSGetCostAndUsageResponseSchema.parse(costAndUsageResponse);\n this.logger.debug(`AWS cost and usage response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`AWS cost and usage 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 // get AWS account names\n for (const accountAttributes of costAndUsageResponse.DimensionValueAttributes) {\n const accountId = accountAttributes.Value;\n const accountName = accountAttributes.Attributes.description;\n this.accounts.set(accountId, accountName);\n }\n\n costAndUsageResults = costAndUsageResults.concat(costAndUsageResponse.ResultsByTime);\n nextPageToken = costAndUsageResponse.NextPageToken;\n } while (nextPageToken);\n\n return costAndUsageResults;\n }\n\n protected async transformCostsData(\n integrationConfig: Config,\n query: CostQuery,\n costResponse: any,\n ): Promise<Report[]> {\n const categoryMappingService = CategoryMappingService.getInstance();\n const tags = integrationConfig.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 }, row) => {\n const rowTime = row.TimePeriod?.Start;\n let period = 'unknown';\n\n // Check for invalid date\n if (!rowTime) {\n filteredOutInvalidDate++;\n return accumulator;\n }\n\n period = getBillingPeriod(query.granularity, rowTime, 'YYYY-MM-DD');\n\n if (row.Groups) {\n row.Groups.forEach((group: any) => {\n const accountId = group.Keys ? group.Keys[0] : '';\n const accountName = this.accounts.get(accountId) || accountId;\n const serviceName = group.Keys ? group.Keys[1] : '';\n const groupMetrics = group.Metrics;\n\n // Check for missing fields\n if (!accountId || !serviceName || !groupMetrics?.UnblendedCost?.Amount) {\n filteredOutMissingFields++;\n return;\n }\n\n const amount = parseCost(groupMetrics.UnblendedCost.Amount);\n\n // Check for zero amount\n if (amount === 0) {\n filteredOutZeroAmount++;\n return;\n }\n\n if (!this.evaluateIntegrationFilters(accountName, integrationConfig)) {\n return;\n }\n\n const keyName = `${accountId}_${serviceName}`;\n\n if (!accumulator[keyName]) {\n uniqueKeys.add(keyName);\n accumulator[keyName] = {\n id: keyName,\n account: `${this.provider}/${accountName} (${accountId})`,\n service: this.convertServiceName(serviceName),\n category: categoryMappingService.getCategoryByServiceName(this.provider, serviceName),\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","CostExplorerClient","STSClient","AssumeRoleCommand","moment","GetTagsCommand","AWSGetTagsResponseSchema","ZodError","Dimension","parseTags","GroupDefinitionType","GetCostAndUsageCommand","AWSGetCostAndUsageResponseSchema","CategoryMappingService","reduce","getBillingPeriod","parseCost","PROVIDER_TYPE"],"mappings":"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"}
@@ -179,12 +179,28 @@ class AzureClient extends InfraWalletClient.InfraWalletClient {
179
179
  const [k, v] = tag.split(":");
180
180
  tagKeyValues[k.trim()] = v.trim();
181
181
  });
182
+ let processedRecords = 0;
183
+ let filteredOutZeroAmount = 0;
184
+ let filteredOutMissingFields = 0;
185
+ const filteredOutInvalidDate = 0;
186
+ let filteredOutTimeRange = 0;
187
+ const uniqueKeys = /* @__PURE__ */ new Set();
188
+ const totalRecords = costResponse?.length || 0;
182
189
  const transformedData = lodash.reduce(
183
190
  costResponse,
184
191
  (accumulator, row) => {
185
192
  const cost = row[0];
186
193
  let date = row[1];
187
194
  const serviceName = row[2];
195
+ if (cost === void 0 || cost === null || !date || !serviceName) {
196
+ filteredOutMissingFields++;
197
+ return accumulator;
198
+ }
199
+ const amount = functions.parseCost(cost);
200
+ if (amount === 0) {
201
+ filteredOutZeroAmount++;
202
+ return accumulator;
203
+ }
188
204
  if (query.granularity === consts.GRANULARITY.DAILY) {
189
205
  date = functions.getBillingPeriod(query.granularity, date.toString(), "YYYYMMDD");
190
206
  }
@@ -193,6 +209,7 @@ class AzureClient extends InfraWalletClient.InfraWalletClient {
193
209
  keyName += `->${row[i + 2]}`;
194
210
  }
195
211
  if (!accumulator[keyName]) {
212
+ uniqueKeys.add(keyName);
196
213
  accumulator[keyName] = {
197
214
  id: keyName,
198
215
  account: `${this.provider}/${accountName} (${subscriptionId})`,
@@ -207,15 +224,27 @@ class AzureClient extends InfraWalletClient.InfraWalletClient {
207
224
  if (!moment__default.default(date).isBefore(moment__default.default(parseInt(query.startTime, 10)))) {
208
225
  if (query.granularity === consts.GRANULARITY.MONTHLY) {
209
226
  const yearMonth = functions.getBillingPeriod(query.granularity, date, "YYYY-MM-DDTHH:mm:ss");
210
- accumulator[keyName].reports[yearMonth] = functions.parseCost(cost);
227
+ accumulator[keyName].reports[yearMonth] = amount;
211
228
  } else {
212
- accumulator[keyName].reports[date] = functions.parseCost(cost);
229
+ accumulator[keyName].reports[date] = amount;
213
230
  }
231
+ processedRecords++;
232
+ } else {
233
+ filteredOutTimeRange++;
214
234
  }
215
235
  return accumulator;
216
236
  },
217
237
  {}
218
238
  );
239
+ this.logTransformationSummary({
240
+ processed: processedRecords,
241
+ uniqueReports: uniqueKeys.size,
242
+ zeroAmount: filteredOutZeroAmount,
243
+ missingFields: filteredOutMissingFields,
244
+ invalidDate: filteredOutInvalidDate,
245
+ timeRange: filteredOutTimeRange,
246
+ totalRecords
247
+ });
219
248
  return Object.values(transformedData);
220
249
  }
221
250
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AzureClient.cjs.js","sources":["../../src/cost-clients/AzureClient.ts"],"sourcesContent":["import { CostManagementClient, QueryDefinition, QueryFilter } from '@azure/arm-costmanagement';\nimport { createHttpHeaders, createPipelineRequest } from '@azure/core-rest-pipeline';\nimport { ClientSecretCredential } from '@azure/identity';\nimport { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { CategoryMappingService } from '../service/CategoryMappingService';\nimport { CLOUD_PROVIDER, GRANULARITY, PROVIDER_TYPE } from '../service/consts';\nimport { getBillingPeriod, parseCost, parseTags } from '../service/functions';\nimport { CostQuery, Report, TagsQuery } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\nimport { AzureBillingResponseSchema } from '../schemas/AzureBilling';\nimport { ZodError } from 'zod';\n\nexport class AzureClient extends InfraWalletClient {\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new AzureClient(CLOUD_PROVIDER.AZURE, config, database, cache, logger);\n }\n\n protected convertServiceName(serviceName: string): string {\n let convertedName = serviceName;\n\n const prefixes = ['Azure'];\n\n for (const prefix of prefixes) {\n if (serviceName.startsWith(prefix)) {\n convertedName = serviceName.slice(prefix.length).trim();\n }\n }\n\n return `${this.provider}/${convertedName}`;\n }\n\n private async fetchDataWithRetry(client: CostManagementClient, url: string, body: any, maxRetries = 5): Promise<any> {\n let retries = 0;\n\n while (retries < maxRetries) {\n const request = createPipelineRequest({\n url: url,\n method: 'POST',\n body: JSON.stringify(body),\n headers: createHttpHeaders({\n 'Content-Type': 'application/json',\n ClientType: 'InfraWallet',\n }),\n });\n const response = await client.pipeline.sendRequest(client, request);\n if (response.status === 200) {\n const parsedResponse = JSON.parse(response.bodyAsText || '{}');\n\n try {\n AzureBillingResponseSchema.parse(parsedResponse);\n this.logger.debug(`Azure billing response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Azure billing 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 return parsedResponse;\n } else if (response.status === 429) {\n const retryAfter = parseInt(\n response.headers.get('x-ms-ratelimit-microsoft.costmanagement-entity-retry-after') || '60',\n 10,\n );\n this.logger.warn(`Hit Azure rate limit, retrying after ${retryAfter} seconds...`);\n await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));\n retries++;\n } else {\n throw new Error(response.bodyAsText as string);\n }\n }\n\n throw new Error('Max retries exceeded');\n }\n\n // If tagKey is specified, returns all tag values of that key.\n // Otherwise returns all available tag keys.\n private async _fetchTags(subAccountConfig: Config, client: any, query: TagsQuery, tagKey: string): Promise<string[]> {\n const subscriptionId = subAccountConfig.getString('subscriptionId');\n const url = `https://management.azure.com/subscriptions/${subscriptionId}/providers/Microsoft.CostManagement/query?api-version=2021-10-01`;\n\n const queryDefinition: QueryDefinition = {\n type: 'ActualCost',\n dataset: {\n granularity: 'None',\n grouping: [{ type: 'TagKey', name: tagKey }],\n },\n timeframe: 'Custom',\n timePeriod: {\n from: moment(parseInt(query.startTime, 10)).toDate(),\n to: moment(parseInt(query.endTime, 10)).toDate(),\n },\n };\n\n let result = await this.fetchDataWithRetry(client, url, queryDefinition);\n let allResults = result.properties.rows;\n\n while (result.properties.nextLink) {\n result = await this.fetchDataWithRetry(client, result.properties.nextLink, queryDefinition);\n allResults = allResults.concat(result.properties.rows);\n }\n\n const tags: string[] = [];\n for (const row of allResults) {\n if (tagKey === '') {\n if (row[0] && !row[0].startsWith('hidden-')) {\n tags.push(row[0]);\n }\n } else if (row[1]) {\n tags.push(row[1]);\n }\n }\n tags.sort((a, b) => a.localeCompare(b));\n\n return tags;\n }\n\n protected async initCloudClient(config: Config): Promise<any> {\n const tenantId = config.getString('tenantId');\n const clientId = config.getString('clientId');\n const clientSecret = config.getString('clientSecret');\n const credential = new ClientSecretCredential(tenantId as string, clientId as string, clientSecret as string);\n const client = new CostManagementClient(credential);\n\n return client;\n }\n\n protected async fetchTagKeys(\n subAccountConfig: Config,\n client: any,\n query: TagsQuery,\n ): Promise<{ tagKeys: string[]; provider: CLOUD_PROVIDER }> {\n const tagKeys = await this._fetchTags(subAccountConfig, client, query, '');\n return { tagKeys: tagKeys, provider: this.provider };\n }\n\n protected async fetchTagValues(\n subAccountConfig: Config,\n client: any,\n query: TagsQuery,\n tagKey: string,\n ): Promise<{ tagValues: string[]; provider: CLOUD_PROVIDER }> {\n const tagValues = await this._fetchTags(subAccountConfig, client, query, tagKey);\n return { tagValues: tagValues, provider: this.provider };\n }\n\n protected async fetchCosts(subAccountConfig: Config, client: any, query: CostQuery): Promise<any> {\n // Azure SDK doesn't support pagination, so sending HTTP request directly\n const subscriptionId = subAccountConfig.getString('subscriptionId');\n const url = `https://management.azure.com/subscriptions/${subscriptionId}/providers/Microsoft.CostManagement/query?api-version=2023-11-01`;\n\n const groupPairs = [{ type: 'Dimension', name: 'ServiceName' }];\n let filter: QueryFilter | undefined = undefined;\n const tags = parseTags(query.tags);\n if (tags.length) {\n if (tags.length === 1) {\n if (tags[0].value) {\n filter = {\n tags: { name: tags[0].key, operator: 'In', values: [tags[0].value] },\n };\n }\n } else {\n const tagList: QueryFilter[] = [];\n for (const tag of tags) {\n if (tag.value) {\n tagList.push({ tags: { name: tag.key, operator: 'In', values: [tag.value] } });\n }\n }\n filter = { or: tagList };\n }\n }\n\n const queryDefinition: QueryDefinition = {\n type: 'ActualCost',\n dataset: {\n granularity: query.granularity,\n aggregation: { totalCostUSD: { name: 'CostUSD', function: 'Sum' } },\n grouping: groupPairs,\n filter: filter,\n },\n timeframe: 'Custom',\n timePeriod: {\n from: moment(parseInt(query.startTime, 10)).toDate(),\n to: moment(parseInt(query.endTime, 10)).toDate(),\n },\n };\n\n let result = await this.fetchDataWithRetry(client, url, queryDefinition);\n let allResults = result.properties.rows;\n\n while (result.properties.nextLink) {\n result = await this.fetchDataWithRetry(client, result.properties.nextLink, queryDefinition);\n allResults = allResults.concat(result.properties.rows);\n }\n\n return allResults;\n }\n\n protected async transformCostsData(subAccountConfig: Config, query: CostQuery, costResponse: any): Promise<Report[]> {\n /*\n Monthly cost sample:\n [\n 123.456,\n \"2024-04-07T00:00:00\", // BillingMonth\n \"Azure App Service\",\n \"EUR\"\n ]\n\n Daily cost sample:\n [\n 12.3456,\n 20240407, // UsageDate\n \"Azure App Service\",\n \"EUR\"\n ]\n */\n const categoryMappingService = CategoryMappingService.getInstance();\n const accountName = subAccountConfig.getString('name');\n const subscriptionId = subAccountConfig.getString('subscriptionId');\n const groupPairs = [{ type: 'Dimension', name: 'ServiceName' }];\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 const transformedData = reduce(\n costResponse,\n (accumulator: { [key: string]: Report }, row) => {\n const cost = row[0];\n let date = row[1];\n const serviceName = row[2];\n\n if (query.granularity === GRANULARITY.DAILY) {\n // 20240407 -> \"2024-04-07\"\n date = getBillingPeriod(query.granularity, date.toString(), 'YYYYMMDD');\n }\n\n let keyName = accountName;\n for (let i = 0; i < groupPairs.length; i++) {\n keyName += `->${row[i + 2]}`;\n }\n\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n account: `${this.provider}/${accountName} (${subscriptionId})`,\n service: this.convertServiceName(serviceName),\n category: categoryMappingService.getCategoryByServiceName(this.provider, serviceName),\n provider: this.provider,\n providerType: PROVIDER_TYPE.INTEGRATION,\n reports: {},\n ...tagKeyValues,\n };\n }\n\n if (!moment(date).isBefore(moment(parseInt(query.startTime, 10)))) {\n if (query.granularity === GRANULARITY.MONTHLY) {\n const yearMonth = getBillingPeriod(query.granularity, date, 'YYYY-MM-DDTHH:mm:ss');\n accumulator[keyName].reports[yearMonth] = parseCost(cost);\n } else {\n accumulator[keyName].reports[date] = parseCost(cost);\n }\n }\n return accumulator;\n },\n {},\n );\n\n return Object.values(transformedData);\n }\n}\n"],"names":["InfraWalletClient","CLOUD_PROVIDER","createPipelineRequest","createHttpHeaders","AzureBillingResponseSchema","ZodError","moment","ClientSecretCredential","CostManagementClient","parseTags","CategoryMappingService","reduce","GRANULARITY","getBillingPeriod","PROVIDER_TYPE","parseCost"],"mappings":"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"}
1
+ {"version":3,"file":"AzureClient.cjs.js","sources":["../../src/cost-clients/AzureClient.ts"],"sourcesContent":["import { CostManagementClient, QueryDefinition, QueryFilter } from '@azure/arm-costmanagement';\nimport { createHttpHeaders, createPipelineRequest } from '@azure/core-rest-pipeline';\nimport { ClientSecretCredential } from '@azure/identity';\nimport { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { reduce } from 'lodash';\nimport moment from 'moment';\nimport { CategoryMappingService } from '../service/CategoryMappingService';\nimport { CLOUD_PROVIDER, GRANULARITY, PROVIDER_TYPE } from '../service/consts';\nimport { getBillingPeriod, parseCost, parseTags } from '../service/functions';\nimport { CostQuery, Report, TagsQuery } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\nimport { AzureBillingResponseSchema } from '../schemas/AzureBilling';\nimport { ZodError } from 'zod';\n\nexport class AzureClient extends InfraWalletClient {\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new AzureClient(CLOUD_PROVIDER.AZURE, config, database, cache, logger);\n }\n\n protected convertServiceName(serviceName: string): string {\n let convertedName = serviceName;\n\n const prefixes = ['Azure'];\n\n for (const prefix of prefixes) {\n if (serviceName.startsWith(prefix)) {\n convertedName = serviceName.slice(prefix.length).trim();\n }\n }\n\n return `${this.provider}/${convertedName}`;\n }\n\n private async fetchDataWithRetry(client: CostManagementClient, url: string, body: any, maxRetries = 5): Promise<any> {\n let retries = 0;\n\n while (retries < maxRetries) {\n const request = createPipelineRequest({\n url: url,\n method: 'POST',\n body: JSON.stringify(body),\n headers: createHttpHeaders({\n 'Content-Type': 'application/json',\n ClientType: 'InfraWallet',\n }),\n });\n const response = await client.pipeline.sendRequest(client, request);\n if (response.status === 200) {\n const parsedResponse = JSON.parse(response.bodyAsText || '{}');\n\n try {\n AzureBillingResponseSchema.parse(parsedResponse);\n this.logger.debug(`Azure billing response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Azure billing 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 return parsedResponse;\n } else if (response.status === 429) {\n const retryAfter = parseInt(\n response.headers.get('x-ms-ratelimit-microsoft.costmanagement-entity-retry-after') || '60',\n 10,\n );\n this.logger.warn(`Hit Azure rate limit, retrying after ${retryAfter} seconds...`);\n await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));\n retries++;\n } else {\n throw new Error(response.bodyAsText as string);\n }\n }\n\n throw new Error('Max retries exceeded');\n }\n\n // If tagKey is specified, returns all tag values of that key.\n // Otherwise returns all available tag keys.\n private async _fetchTags(subAccountConfig: Config, client: any, query: TagsQuery, tagKey: string): Promise<string[]> {\n const subscriptionId = subAccountConfig.getString('subscriptionId');\n const url = `https://management.azure.com/subscriptions/${subscriptionId}/providers/Microsoft.CostManagement/query?api-version=2021-10-01`;\n\n const queryDefinition: QueryDefinition = {\n type: 'ActualCost',\n dataset: {\n granularity: 'None',\n grouping: [{ type: 'TagKey', name: tagKey }],\n },\n timeframe: 'Custom',\n timePeriod: {\n from: moment(parseInt(query.startTime, 10)).toDate(),\n to: moment(parseInt(query.endTime, 10)).toDate(),\n },\n };\n\n let result = await this.fetchDataWithRetry(client, url, queryDefinition);\n let allResults = result.properties.rows;\n\n while (result.properties.nextLink) {\n result = await this.fetchDataWithRetry(client, result.properties.nextLink, queryDefinition);\n allResults = allResults.concat(result.properties.rows);\n }\n\n const tags: string[] = [];\n for (const row of allResults) {\n if (tagKey === '') {\n if (row[0] && !row[0].startsWith('hidden-')) {\n tags.push(row[0]);\n }\n } else if (row[1]) {\n tags.push(row[1]);\n }\n }\n tags.sort((a, b) => a.localeCompare(b));\n\n return tags;\n }\n\n protected async initCloudClient(config: Config): Promise<any> {\n const tenantId = config.getString('tenantId');\n const clientId = config.getString('clientId');\n const clientSecret = config.getString('clientSecret');\n const credential = new ClientSecretCredential(tenantId as string, clientId as string, clientSecret as string);\n const client = new CostManagementClient(credential);\n\n return client;\n }\n\n protected async fetchTagKeys(\n subAccountConfig: Config,\n client: any,\n query: TagsQuery,\n ): Promise<{ tagKeys: string[]; provider: CLOUD_PROVIDER }> {\n const tagKeys = await this._fetchTags(subAccountConfig, client, query, '');\n return { tagKeys: tagKeys, provider: this.provider };\n }\n\n protected async fetchTagValues(\n subAccountConfig: Config,\n client: any,\n query: TagsQuery,\n tagKey: string,\n ): Promise<{ tagValues: string[]; provider: CLOUD_PROVIDER }> {\n const tagValues = await this._fetchTags(subAccountConfig, client, query, tagKey);\n return { tagValues: tagValues, provider: this.provider };\n }\n\n protected async fetchCosts(subAccountConfig: Config, client: any, query: CostQuery): Promise<any> {\n // Azure SDK doesn't support pagination, so sending HTTP request directly\n const subscriptionId = subAccountConfig.getString('subscriptionId');\n const url = `https://management.azure.com/subscriptions/${subscriptionId}/providers/Microsoft.CostManagement/query?api-version=2023-11-01`;\n\n const groupPairs = [{ type: 'Dimension', name: 'ServiceName' }];\n let filter: QueryFilter | undefined = undefined;\n const tags = parseTags(query.tags);\n if (tags.length) {\n if (tags.length === 1) {\n if (tags[0].value) {\n filter = {\n tags: { name: tags[0].key, operator: 'In', values: [tags[0].value] },\n };\n }\n } else {\n const tagList: QueryFilter[] = [];\n for (const tag of tags) {\n if (tag.value) {\n tagList.push({ tags: { name: tag.key, operator: 'In', values: [tag.value] } });\n }\n }\n filter = { or: tagList };\n }\n }\n\n const queryDefinition: QueryDefinition = {\n type: 'ActualCost',\n dataset: {\n granularity: query.granularity,\n aggregation: { totalCostUSD: { name: 'CostUSD', function: 'Sum' } },\n grouping: groupPairs,\n filter: filter,\n },\n timeframe: 'Custom',\n timePeriod: {\n from: moment(parseInt(query.startTime, 10)).toDate(),\n to: moment(parseInt(query.endTime, 10)).toDate(),\n },\n };\n\n let result = await this.fetchDataWithRetry(client, url, queryDefinition);\n let allResults = result.properties.rows;\n\n while (result.properties.nextLink) {\n result = await this.fetchDataWithRetry(client, result.properties.nextLink, queryDefinition);\n allResults = allResults.concat(result.properties.rows);\n }\n\n return allResults;\n }\n\n protected async transformCostsData(subAccountConfig: Config, query: CostQuery, costResponse: any): Promise<Report[]> {\n /*\n Monthly cost sample:\n [\n 123.456,\n \"2024-04-07T00:00:00\", // BillingMonth\n \"Azure App Service\",\n \"EUR\"\n ]\n\n Daily cost sample:\n [\n 12.3456,\n 20240407, // UsageDate\n \"Azure App Service\",\n \"EUR\"\n ]\n */\n const categoryMappingService = CategoryMappingService.getInstance();\n const accountName = subAccountConfig.getString('name');\n const subscriptionId = subAccountConfig.getString('subscriptionId');\n const groupPairs = [{ type: 'Dimension', name: 'ServiceName' }];\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 // Initialize tracking variables\n let processedRecords = 0;\n let filteredOutZeroAmount = 0;\n let filteredOutMissingFields = 0;\n const filteredOutInvalidDate = 0;\n let 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 }, row) => {\n const cost = row[0];\n let date = row[1];\n const serviceName = row[2];\n\n // Check for missing fields\n if (cost === undefined || cost === null || !date || !serviceName) {\n filteredOutMissingFields++;\n return accumulator;\n }\n\n const amount = parseCost(cost);\n\n // Check for zero amount\n if (amount === 0) {\n filteredOutZeroAmount++;\n return accumulator;\n }\n\n if (query.granularity === GRANULARITY.DAILY) {\n // 20240407 -> \"2024-04-07\"\n date = getBillingPeriod(query.granularity, date.toString(), 'YYYYMMDD');\n }\n\n let keyName = accountName;\n for (let i = 0; i < groupPairs.length; i++) {\n keyName += `->${row[i + 2]}`;\n }\n\n if (!accumulator[keyName]) {\n uniqueKeys.add(keyName);\n accumulator[keyName] = {\n id: keyName,\n account: `${this.provider}/${accountName} (${subscriptionId})`,\n service: this.convertServiceName(serviceName),\n category: categoryMappingService.getCategoryByServiceName(this.provider, serviceName),\n provider: this.provider,\n providerType: PROVIDER_TYPE.INTEGRATION,\n reports: {},\n ...tagKeyValues,\n };\n }\n\n if (!moment(date).isBefore(moment(parseInt(query.startTime, 10)))) {\n if (query.granularity === GRANULARITY.MONTHLY) {\n const yearMonth = getBillingPeriod(query.granularity, date, 'YYYY-MM-DDTHH:mm:ss');\n accumulator[keyName].reports[yearMonth] = amount;\n } else {\n accumulator[keyName].reports[date] = amount;\n }\n processedRecords++;\n } else {\n filteredOutTimeRange++;\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","createPipelineRequest","createHttpHeaders","AzureBillingResponseSchema","ZodError","moment","ClientSecretCredential","CostManagementClient","parseTags","CategoryMappingService","reduce","parseCost","GRANULARITY","getBillingPeriod","PROVIDER_TYPE"],"mappings":"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"}
@@ -282,13 +282,36 @@ class ConfluentClient extends InfraWalletClient.InfraWalletClient {
282
282
  this.logger.warn("No valid cost data to transform");
283
283
  return [];
284
284
  }
285
+ this.logger.debug(`Starting transformation of ${costResponse.data.length} Confluent cost records`);
286
+ if (costResponse.data.length > 0) {
287
+ const sampleRecord = costResponse.data[0];
288
+ this.logger.debug(`Sample Confluent record structure: ${JSON.stringify(Object.keys(sampleRecord))}`);
289
+ this.logger.debug(
290
+ `Sample record - start_date: ${sampleRecord.start_date}, line_type: ${sampleRecord.line_type}, price: ${sampleRecord.price}, amount: ${sampleRecord.amount}, original_amount: ${sampleRecord.original_amount}, discount_amount: ${sampleRecord.discount_amount}, product: ${sampleRecord.product}, network_access_type: ${sampleRecord.network_access_type}`
291
+ );
292
+ }
293
+ let filteredOutZeroAmount = 0;
294
+ let filteredOutMissingFields = 0;
295
+ let filteredOutInvalidDate = 0;
296
+ let filteredOutTimeRange = 0;
297
+ let processedRecords = 0;
298
+ const uniqueKeys = /* @__PURE__ */ new Set();
299
+ const uniqueServices = /* @__PURE__ */ new Set();
300
+ const uniqueResources = /* @__PURE__ */ new Set();
285
301
  const transformedData = costResponse.data.reduce((accumulator, line) => {
286
- const amount = parseFloat(line.amount) || 0;
302
+ const amount = parseFloat(line.amount) || parseFloat(line.price) || 0;
287
303
  if (amount === 0) {
304
+ filteredOutZeroAmount++;
305
+ return accumulator;
306
+ }
307
+ if (!line.start_date || !line.line_type || !line.amount && !line.price) {
308
+ filteredOutMissingFields++;
288
309
  return accumulator;
289
310
  }
290
311
  const parsedStartDate = moment__default.default(line.start_date);
291
312
  if (!parsedStartDate.isValid()) {
313
+ filteredOutInvalidDate++;
314
+ this.logger.debug(`Invalid start_date: ${line.start_date}`);
292
315
  return accumulator;
293
316
  }
294
317
  let billingPeriod = void 0;
@@ -297,14 +320,20 @@ class ConfluentClient extends InfraWalletClient.InfraWalletClient {
297
320
  } else {
298
321
  billingPeriod = parsedStartDate.format("YYYY-MM-DD");
299
322
  }
300
- const serviceName = this.capitalizeWords(line.line_type);
323
+ const baseServiceName = this.capitalizeWords(line.line_type);
324
+ const productName = line.product ? this.capitalizeWords(line.product) : null;
325
+ const serviceName = productName && productName !== baseServiceName ? `${productName} ${baseServiceName}` : baseServiceName;
301
326
  const resourceName = line.resource?.display_name || "Unknown";
302
327
  const envDisplayName = line.envDisplayName || "Unknown";
328
+ const networkAccessType = line.network_access_type;
329
+ uniqueServices.add(serviceName);
330
+ uniqueResources.add(resourceName);
303
331
  const keyName = `${accountName}->${categoryMappingService.getCategoryByServiceName(
304
332
  this.provider,
305
333
  serviceName
306
334
  )}->${resourceName}`;
307
335
  if (!accumulator[keyName]) {
336
+ uniqueKeys.add(keyName);
308
337
  accumulator[keyName] = {
309
338
  id: keyName,
310
339
  account: `${this.provider}/${accountName}`,
@@ -315,14 +344,33 @@ class ConfluentClient extends InfraWalletClient.InfraWalletClient {
315
344
  reports: {},
316
345
  ...{ project: envDisplayName },
317
346
  ...{ cluster: resourceName },
347
+ ...networkAccessType && { networkAccessType },
318
348
  ...tagKeyValues
319
349
  };
320
350
  }
321
351
  if (!moment__default.default(billingPeriod).isBefore(moment__default.default(parseInt(query.startTime, 10)))) {
322
352
  accumulator[keyName].reports[billingPeriod] = (accumulator[keyName].reports[billingPeriod] || 0) + amount;
353
+ processedRecords++;
354
+ } else {
355
+ filteredOutTimeRange++;
323
356
  }
324
357
  return accumulator;
325
358
  }, {});
359
+ this.logTransformationSummary({
360
+ processed: processedRecords,
361
+ uniqueReports: uniqueKeys.size,
362
+ zeroAmount: filteredOutZeroAmount,
363
+ missingFields: filteredOutMissingFields,
364
+ invalidDate: filteredOutInvalidDate,
365
+ timeRange: filteredOutTimeRange,
366
+ totalRecords: costResponse.data.length
367
+ });
368
+ const keyArray = Array.from(uniqueKeys);
369
+ if (keyArray.length > 0) {
370
+ this.logger.info(`Unique services found: ${Array.from(uniqueServices).slice(0, 10).join(", ")}`);
371
+ this.logger.info(`Unique resources found: ${Array.from(uniqueResources).slice(0, 10).join(", ")}`);
372
+ this.logger.debug(`Sample unique keys: ${keyArray.slice(0, 5).join(", ")}`);
373
+ }
326
374
  return Object.values(transformedData);
327
375
  }
328
376
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ConfluentClient.cjs.js","sources":["../../src/cost-clients/ConfluentClient.ts"],"sourcesContent":["import { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { CostQuery, Report } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\nimport moment from 'moment';\nimport { CategoryMappingService } from '../service/CategoryMappingService';\nimport {\n CLOUD_PROVIDER,\n PROVIDER_TYPE,\n NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS,\n GRANULARITY,\n} from '../service/consts';\nimport { cryptoRandom } from '../service/crypto';\nimport { ConfluentEnvironmentSchema, ConfluentBillingResponseSchema } from '../schemas/ConfluentBilling';\nimport { ZodError } from 'zod';\n\nexport class ConfluentClient extends InfraWalletClient {\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new ConfluentClient(CLOUD_PROVIDER.CONFLUENT, config, database, cache, logger);\n }\n\n protected convertServiceName(serviceName: string): string {\n let convertedName = serviceName;\n\n const prefixes = ['Confluent'];\n\n for (const prefix of prefixes) {\n if (serviceName.startsWith(prefix)) {\n convertedName = serviceName.slice(prefix.length).trim();\n }\n }\n\n return `${this.provider}/${convertedName}`;\n }\n\n private capitalizeWords(str: string): string {\n return str\n .toLowerCase()\n .split('_')\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n }\n\n private async fetchEnvDisplayName(client: any, envId: string, retryCount = 0): Promise<string> {\n try {\n const url = `https://api.confluent.cloud/org/v2/environments/${envId}`;\n const response = await fetch(url, {\n method: 'GET',\n headers: client.headers,\n });\n\n if (response.status === 429 && retryCount < 3) {\n // Apply exponential backoff for rate limiting\n const retryAfter = parseInt(response.headers.get('retry-after') || '5', 10);\n const backoffTime = Math.min(30, retryAfter * Math.pow(2, retryCount));\n this.logger.info(\n `Rate limited when fetching environment name for ${envId}, backing off for ${backoffTime} seconds...`,\n );\n await new Promise(resolve => setTimeout(resolve, backoffTime * 1000));\n return this.fetchEnvDisplayName(client, envId, retryCount + 1);\n }\n\n if (!response.ok) {\n this.logger.warn(`Failed to fetch environment name for ${envId}: ${response.statusText}`);\n return envId;\n }\n\n const jsonResponse = await response.json();\n\n try {\n ConfluentEnvironmentSchema.parse(jsonResponse);\n this.logger.debug(`Confluent environment response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Confluent environment response validation failed: ${error.message}`);\n } else {\n this.logger.warn(`Unexpected validation error: ${error.message}`);\n }\n }\n\n return jsonResponse.display_name;\n } catch (error) {\n this.logger.warn(`Error fetching environment name for ${envId}: ${error.message}`);\n return envId;\n }\n }\n\n protected async initCloudClient(subAccountConfig: Config): Promise<any> {\n const apiKey = subAccountConfig.getString('apiKey');\n const apiSecret = subAccountConfig.getString('apiSecret');\n const auth = `${apiKey}:${apiSecret}`;\n\n const client = {\n headers: {\n Authorization: `Basic ${Buffer.from(auth).toString('base64')}`,\n 'Content-Type': 'application/json',\n },\n name: subAccountConfig.getString('name'),\n };\n\n return client;\n }\n\n private async fetchCostWithRetry(url: string, client: any, retryCount = 0, maxRetries = 5): Promise<any> {\n try {\n this.logger.debug(`Fetching Confluent costs from URL: ${url}, attempt ${retryCount + 1}`);\n\n const response = await fetch(url, {\n method: 'GET',\n headers: client.headers,\n });\n\n if (response.status === 403) {\n const errorText = await response.text();\n this.logger.error(`Failed to fetch Confluent costs: 403 Forbidden - ${errorText}`);\n throw new Error(`Authorization failed: ${errorText}`);\n }\n\n if (response.status === 429 && retryCount < maxRetries) {\n // Apply exponential backoff with jitter for rate limiting\n const retryAfter = parseInt(response.headers.get('retry-after') || '30', 10);\n const jitter = cryptoRandom() * 2;\n const backoffTime = Math.min(120, retryAfter * Math.pow(1.5, retryCount) * jitter);\n this.logger.warn(`Rate limited, backing off for ${Math.ceil(backoffTime)} seconds...`);\n await new Promise(resolve => setTimeout(resolve, backoffTime * 1000));\n return this.fetchCostWithRetry(url, client, retryCount + 1, maxRetries);\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`HTTP ${response.status}: ${errorText}`);\n }\n\n const jsonResponse = await response.json();\n\n try {\n ConfluentBillingResponseSchema.parse(jsonResponse);\n this.logger.debug(`Confluent billing response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Confluent billing 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 return jsonResponse;\n } catch (error) {\n if (retryCount < maxRetries) {\n // Apply exponential backoff for general errors\n const backoffTime = Math.min(60, Math.pow(2, retryCount) * 3);\n this.logger.warn(`Error fetching costs, retrying in ${backoffTime} seconds: ${error.message}`);\n await new Promise(resolve => setTimeout(resolve, backoffTime * 1000));\n return this.fetchCostWithRetry(url, client, retryCount + 1, maxRetries);\n }\n throw error;\n }\n }\n\n protected async fetchCosts(_subAccountConfig: Config, client: any, query: CostQuery): Promise<any> {\n // Confluent API limits:\n // 1. Can only fetch 1 month at a time\n // 2. Can only go back exactly the number of months defined in NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS\n const LOOKBACK_MONTHS = NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS[CLOUD_PROVIDER.CONFLUENT];\n\n // Calculate the earliest date we can fetch\n const now = moment();\n const earliestAllowed = now.clone().subtract(LOOKBACK_MONTHS, 'months').startOf('month');\n\n // Convert query dates to moment objects\n const requestStartDate = moment(parseInt(query.startTime, 10));\n const requestEndDate = moment(parseInt(query.endTime, 10));\n\n // Adjust start date to be within the allowed range\n let startDate = requestStartDate.clone();\n if (startDate.isBefore(earliestAllowed)) {\n this.logger.info(\n `Confluent API only allows lookback of ${LOOKBACK_MONTHS} months. Adjusting start date from ${startDate.format('YYYY-MM-DD')} to ${earliestAllowed.format('YYYY-MM-DD')}`,\n );\n startDate = earliestAllowed.clone();\n }\n\n // Ensure we don't go past the requested end date\n const endDate = moment.min(requestEndDate, now);\n\n // Build monthly time ranges\n const monthlyRanges = [];\n const currentMonth = startDate.clone().startOf('month');\n\n while (currentMonth.isBefore(endDate) || currentMonth.isSame(endDate, 'month')) {\n const monthStart = currentMonth.clone().startOf('month');\n const monthEnd = moment.min(currentMonth.clone().endOf('month'), endDate);\n\n monthlyRanges.push({\n start: monthStart,\n end: monthEnd,\n });\n\n currentMonth.add(1, 'month');\n }\n\n this.logger.info(\n `Fetching Confluent costs for ${monthlyRanges.length} months from ${startDate.format('YYYY-MM')} to ${endDate.format('YYYY-MM')}`,\n );\n\n // Maximum number of concurrent requests to avoid overwhelming the API\n const maxConcurrentRequests = 2;\n const maxRetries = 4; // Maximum number of retries for a single month\n\n let aggregatedData: any[] = [];\n const envIdToName: Record<string, string> = {};\n\n // Process test request to check API access and potentially get first month's data\n try {\n // Use the most recent month for the test request as it's more likely to succeed\n const latestRange = monthlyRanges[monthlyRanges.length - 1];\n const testUrl = `https://api.confluent.cloud/billing/v1/costs?start_date=${latestRange.start.format(\n 'YYYY-MM-DD',\n )}&end_date=${latestRange.end.clone().add(1, 'd').format('YYYY-MM-DD')}`;\n\n this.logger.debug(`Testing Confluent API access for ${latestRange.start.format('YYYY-MM')}`);\n\n const testResponse = await this.fetchCostWithRetry(testUrl, client, 0, 2);\n\n if (testResponse.data && testResponse.data.length > 0) {\n // Process environment names for this data\n const envIds = Array.from(\n new Set(\n testResponse.data.map((item: any) => item.resource?.environment?.id).filter((id: any) => id !== undefined),\n ),\n );\n\n if (envIds.length > 0) {\n // Fetch environment names\n for (const envId of envIds) {\n if (typeof envId === 'string') {\n try {\n const name = await this.fetchEnvDisplayName(client, envId);\n envIdToName[envId] = name;\n } catch (error) {\n this.logger.warn(`Error fetching name for environment ${envId}: ${error.message}`);\n envIdToName[envId] = envId; // Fallback to using the ID\n }\n }\n }\n\n const dataWithEnvNames = testResponse.data\n .filter((item: any) => item.resource?.environment?.id)\n .map((item: any) => {\n const envId = item.resource.environment.id;\n return {\n ...item,\n envDisplayName: envIdToName[envId] || 'Unknown',\n };\n });\n\n aggregatedData = aggregatedData.concat(dataWithEnvNames);\n this.logger.info(`Successfully fetched costs for ${latestRange.start.format('YYYY-MM')}`);\n }\n\n // Remove this month from the ranges to process since we already got it\n monthlyRanges.pop();\n }\n } catch (error) {\n this.logger.error(`Error testing Confluent API access: ${error.message}`);\n }\n\n // Process all remaining months in batches to respect rate limits\n // This uses a sliding window approach to process months in parallel but with a limit\n for (let i = 0; i < monthlyRanges.length; i += maxConcurrentRequests) {\n const batch = monthlyRanges.slice(i, i + maxConcurrentRequests);\n\n try {\n const batchResults = await Promise.all(\n batch.map(async range => {\n const url = `https://api.confluent.cloud/billing/v1/costs?start_date=${range.start.format(\n 'YYYY-MM-DD',\n )}&end_date=${range.end.clone().add(1, 'd').format('YYYY-MM-DD')}`;\n\n try {\n const response = await this.fetchCostWithRetry(url, client, 0, maxRetries);\n\n this.logger.info(`Successfully fetched costs for ${range.start.format('YYYY-MM')}`);\n\n return {\n month: range.start.format('YYYY-MM'),\n data: response.data || [],\n };\n } catch (error) {\n this.logger.error(\n `Failed to fetch costs for ${range.start.format('YYYY-MM')} after multiple retries: ${error.message}`,\n );\n return {\n month: range.start.format('YYYY-MM'),\n data: [],\n };\n }\n }),\n );\n\n // Process batch results\n for (const result of batchResults) {\n if (result.data.length > 0) {\n // Extract environment IDs from this batch\n const envIds = Array.from(\n new Set(\n result.data\n .map((item: any) => item.resource?.environment?.id)\n .filter((id: any): id is string => typeof id === 'string'),\n ),\n );\n\n // Fetch any new environment names\n for (const envId of envIds) {\n if (typeof envId === 'string' && !(envId in envIdToName)) {\n // Add slight delay between env name requests\n await new Promise(resolve => setTimeout(resolve, 100));\n try {\n const name = await this.fetchEnvDisplayName(client, envId);\n envIdToName[envId] = name;\n } catch (error) {\n this.logger.warn(`Error fetching name for environment ${envId}: ${error.message}`);\n envIdToName[envId] = envId; // Fallback to using the ID\n }\n }\n }\n\n // Add environment names to the data\n const dataWithEnvNames = result.data\n .filter((item: any) => item.resource?.environment?.id)\n .map((item: any) => {\n const envId = item.resource.environment.id;\n return {\n ...item,\n envDisplayName: envIdToName[envId] || 'Unknown',\n };\n });\n\n aggregatedData = aggregatedData.concat(dataWithEnvNames);\n }\n }\n\n // Add a small delay between batches to help avoid rate limiting\n if (i + maxConcurrentRequests < monthlyRanges.length) {\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n } catch (error) {\n this.logger.error(`Error processing batch of months: ${error.message}`);\n }\n }\n\n // Log a summary of what we got\n if (aggregatedData.length === 0) {\n this.logger.error(`No cost data could be fetched from Confluent API. Check API key permissions.`);\n } else {\n this.logger.info(`Successfully fetched ${aggregatedData.length} cost entries from Confluent API.`);\n }\n\n return {\n data: aggregatedData,\n };\n }\n\n protected async transformCostsData(subAccountConfig: Config, query: CostQuery, costResponse: any): Promise<Report[]> {\n const categoryMappingService = CategoryMappingService.getInstance();\n const accountName = subAccountConfig.getString('name');\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 // Handle empty or invalid data\n if (!costResponse || !costResponse.data || !Array.isArray(costResponse.data)) {\n this.logger.warn('No valid cost data to transform');\n return [];\n }\n\n const transformedData = costResponse.data.reduce((accumulator: { [key: string]: Report }, line: any) => {\n const amount = parseFloat(line.amount) || 0;\n\n if (amount === 0) {\n return accumulator;\n }\n\n const parsedStartDate = moment(line.start_date);\n\n if (!parsedStartDate.isValid()) {\n return accumulator;\n }\n\n let billingPeriod = undefined;\n if (query.granularity === GRANULARITY.MONTHLY) {\n billingPeriod = parsedStartDate.format('YYYY-MM');\n } else {\n billingPeriod = parsedStartDate.format('YYYY-MM-DD');\n }\n\n const serviceName = this.capitalizeWords(line.line_type);\n const resourceName = line.resource?.display_name || 'Unknown';\n const envDisplayName = line.envDisplayName || 'Unknown';\n\n const keyName = `${accountName}->${categoryMappingService.getCategoryByServiceName(\n this.provider,\n serviceName,\n )}->${resourceName}`;\n\n if (!accumulator[keyName]) {\n accumulator[keyName] = {\n id: keyName,\n account: `${this.provider}/${accountName}`,\n service: this.convertServiceName(serviceName),\n category: categoryMappingService.getCategoryByServiceName(this.provider, serviceName),\n provider: this.provider,\n providerType: PROVIDER_TYPE.INTEGRATION,\n reports: {},\n ...{ project: envDisplayName },\n ...{ cluster: resourceName },\n ...tagKeyValues,\n };\n }\n\n if (!moment(billingPeriod).isBefore(moment(parseInt(query.startTime, 10)))) {\n accumulator[keyName].reports[billingPeriod] = (accumulator[keyName].reports[billingPeriod] || 0) + amount;\n }\n\n return accumulator;\n }, {});\n\n return Object.values(transformedData);\n }\n}\n"],"names":["InfraWalletClient","CLOUD_PROVIDER","ConfluentEnvironmentSchema","ZodError","cryptoRandom","ConfluentBillingResponseSchema","NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS","moment","CategoryMappingService","GRANULARITY","PROVIDER_TYPE"],"mappings":"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"}
1
+ {"version":3,"file":"ConfluentClient.cjs.js","sources":["../../src/cost-clients/ConfluentClient.ts"],"sourcesContent":["import { CacheService, DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { CostQuery, Report } from '../service/types';\nimport { InfraWalletClient } from './InfraWalletClient';\nimport moment from 'moment';\nimport { CategoryMappingService } from '../service/CategoryMappingService';\nimport {\n CLOUD_PROVIDER,\n PROVIDER_TYPE,\n NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS,\n GRANULARITY,\n} from '../service/consts';\nimport { cryptoRandom } from '../service/crypto';\nimport { ConfluentEnvironmentSchema, ConfluentBillingResponseSchema } from '../schemas/ConfluentBilling';\nimport { ZodError } from 'zod';\n\nexport class ConfluentClient extends InfraWalletClient {\n static create(config: Config, database: DatabaseService, cache: CacheService, logger: LoggerService) {\n return new ConfluentClient(CLOUD_PROVIDER.CONFLUENT, config, database, cache, logger);\n }\n\n protected convertServiceName(serviceName: string): string {\n let convertedName = serviceName;\n\n const prefixes = ['Confluent'];\n\n for (const prefix of prefixes) {\n if (serviceName.startsWith(prefix)) {\n convertedName = serviceName.slice(prefix.length).trim();\n }\n }\n\n return `${this.provider}/${convertedName}`;\n }\n\n private capitalizeWords(str: string): string {\n return str\n .toLowerCase()\n .split('_')\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n }\n\n private async fetchEnvDisplayName(client: any, envId: string, retryCount = 0): Promise<string> {\n try {\n const url = `https://api.confluent.cloud/org/v2/environments/${envId}`;\n const response = await fetch(url, {\n method: 'GET',\n headers: client.headers,\n });\n\n if (response.status === 429 && retryCount < 3) {\n // Apply exponential backoff for rate limiting\n const retryAfter = parseInt(response.headers.get('retry-after') || '5', 10);\n const backoffTime = Math.min(30, retryAfter * Math.pow(2, retryCount));\n this.logger.info(\n `Rate limited when fetching environment name for ${envId}, backing off for ${backoffTime} seconds...`,\n );\n await new Promise(resolve => setTimeout(resolve, backoffTime * 1000));\n return this.fetchEnvDisplayName(client, envId, retryCount + 1);\n }\n\n if (!response.ok) {\n this.logger.warn(`Failed to fetch environment name for ${envId}: ${response.statusText}`);\n return envId;\n }\n\n const jsonResponse = await response.json();\n\n try {\n ConfluentEnvironmentSchema.parse(jsonResponse);\n this.logger.debug(`Confluent environment response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Confluent environment response validation failed: ${error.message}`);\n } else {\n this.logger.warn(`Unexpected validation error: ${error.message}`);\n }\n }\n\n return jsonResponse.display_name;\n } catch (error) {\n this.logger.warn(`Error fetching environment name for ${envId}: ${error.message}`);\n return envId;\n }\n }\n\n protected async initCloudClient(subAccountConfig: Config): Promise<any> {\n const apiKey = subAccountConfig.getString('apiKey');\n const apiSecret = subAccountConfig.getString('apiSecret');\n const auth = `${apiKey}:${apiSecret}`;\n\n const client = {\n headers: {\n Authorization: `Basic ${Buffer.from(auth).toString('base64')}`,\n 'Content-Type': 'application/json',\n },\n name: subAccountConfig.getString('name'),\n };\n\n return client;\n }\n\n private async fetchCostWithRetry(url: string, client: any, retryCount = 0, maxRetries = 5): Promise<any> {\n try {\n this.logger.debug(`Fetching Confluent costs from URL: ${url}, attempt ${retryCount + 1}`);\n\n const response = await fetch(url, {\n method: 'GET',\n headers: client.headers,\n });\n\n if (response.status === 403) {\n const errorText = await response.text();\n this.logger.error(`Failed to fetch Confluent costs: 403 Forbidden - ${errorText}`);\n throw new Error(`Authorization failed: ${errorText}`);\n }\n\n if (response.status === 429 && retryCount < maxRetries) {\n // Apply exponential backoff with jitter for rate limiting\n const retryAfter = parseInt(response.headers.get('retry-after') || '30', 10);\n const jitter = cryptoRandom() * 2;\n const backoffTime = Math.min(120, retryAfter * Math.pow(1.5, retryCount) * jitter);\n this.logger.warn(`Rate limited, backing off for ${Math.ceil(backoffTime)} seconds...`);\n await new Promise(resolve => setTimeout(resolve, backoffTime * 1000));\n return this.fetchCostWithRetry(url, client, retryCount + 1, maxRetries);\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`HTTP ${response.status}: ${errorText}`);\n }\n\n const jsonResponse = await response.json();\n\n try {\n ConfluentBillingResponseSchema.parse(jsonResponse);\n this.logger.debug(`Confluent billing response validation passed`);\n } catch (error) {\n if (error instanceof ZodError) {\n this.logger.warn(`Confluent billing 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 return jsonResponse;\n } catch (error) {\n if (retryCount < maxRetries) {\n // Apply exponential backoff for general errors\n const backoffTime = Math.min(60, Math.pow(2, retryCount) * 3);\n this.logger.warn(`Error fetching costs, retrying in ${backoffTime} seconds: ${error.message}`);\n await new Promise(resolve => setTimeout(resolve, backoffTime * 1000));\n return this.fetchCostWithRetry(url, client, retryCount + 1, maxRetries);\n }\n throw error;\n }\n }\n\n protected async fetchCosts(_subAccountConfig: Config, client: any, query: CostQuery): Promise<any> {\n // Confluent API limits:\n // 1. Can only fetch 1 month at a time\n // 2. Can only go back exactly the number of months defined in NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS\n const LOOKBACK_MONTHS = NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS[CLOUD_PROVIDER.CONFLUENT];\n\n // Calculate the earliest date we can fetch\n const now = moment();\n const earliestAllowed = now.clone().subtract(LOOKBACK_MONTHS, 'months').startOf('month');\n\n // Convert query dates to moment objects\n const requestStartDate = moment(parseInt(query.startTime, 10));\n const requestEndDate = moment(parseInt(query.endTime, 10));\n\n // Adjust start date to be within the allowed range\n let startDate = requestStartDate.clone();\n if (startDate.isBefore(earliestAllowed)) {\n this.logger.info(\n `Confluent API only allows lookback of ${LOOKBACK_MONTHS} months. Adjusting start date from ${startDate.format('YYYY-MM-DD')} to ${earliestAllowed.format('YYYY-MM-DD')}`,\n );\n startDate = earliestAllowed.clone();\n }\n\n // Ensure we don't go past the requested end date\n const endDate = moment.min(requestEndDate, now);\n\n // Build monthly time ranges\n const monthlyRanges = [];\n const currentMonth = startDate.clone().startOf('month');\n\n while (currentMonth.isBefore(endDate) || currentMonth.isSame(endDate, 'month')) {\n const monthStart = currentMonth.clone().startOf('month');\n const monthEnd = moment.min(currentMonth.clone().endOf('month'), endDate);\n\n monthlyRanges.push({\n start: monthStart,\n end: monthEnd,\n });\n\n currentMonth.add(1, 'month');\n }\n\n this.logger.info(\n `Fetching Confluent costs for ${monthlyRanges.length} months from ${startDate.format('YYYY-MM')} to ${endDate.format('YYYY-MM')}`,\n );\n\n // Maximum number of concurrent requests to avoid overwhelming the API\n const maxConcurrentRequests = 2;\n const maxRetries = 4; // Maximum number of retries for a single month\n\n let aggregatedData: any[] = [];\n const envIdToName: Record<string, string> = {};\n\n // Process test request to check API access and potentially get first month's data\n try {\n // Use the most recent month for the test request as it's more likely to succeed\n const latestRange = monthlyRanges[monthlyRanges.length - 1];\n const testUrl = `https://api.confluent.cloud/billing/v1/costs?start_date=${latestRange.start.format(\n 'YYYY-MM-DD',\n )}&end_date=${latestRange.end.clone().add(1, 'd').format('YYYY-MM-DD')}`;\n\n this.logger.debug(`Testing Confluent API access for ${latestRange.start.format('YYYY-MM')}`);\n\n const testResponse = await this.fetchCostWithRetry(testUrl, client, 0, 2);\n\n if (testResponse.data && testResponse.data.length > 0) {\n // Process environment names for this data\n const envIds = Array.from(\n new Set(\n testResponse.data.map((item: any) => item.resource?.environment?.id).filter((id: any) => id !== undefined),\n ),\n );\n\n if (envIds.length > 0) {\n // Fetch environment names\n for (const envId of envIds) {\n if (typeof envId === 'string') {\n try {\n const name = await this.fetchEnvDisplayName(client, envId);\n envIdToName[envId] = name;\n } catch (error) {\n this.logger.warn(`Error fetching name for environment ${envId}: ${error.message}`);\n envIdToName[envId] = envId; // Fallback to using the ID\n }\n }\n }\n\n const dataWithEnvNames = testResponse.data\n .filter((item: any) => item.resource?.environment?.id)\n .map((item: any) => {\n const envId = item.resource.environment.id;\n return {\n ...item,\n envDisplayName: envIdToName[envId] || 'Unknown',\n };\n });\n\n aggregatedData = aggregatedData.concat(dataWithEnvNames);\n this.logger.info(`Successfully fetched costs for ${latestRange.start.format('YYYY-MM')}`);\n }\n\n // Remove this month from the ranges to process since we already got it\n monthlyRanges.pop();\n }\n } catch (error) {\n this.logger.error(`Error testing Confluent API access: ${error.message}`);\n }\n\n // Process all remaining months in batches to respect rate limits\n // This uses a sliding window approach to process months in parallel but with a limit\n for (let i = 0; i < monthlyRanges.length; i += maxConcurrentRequests) {\n const batch = monthlyRanges.slice(i, i + maxConcurrentRequests);\n\n try {\n const batchResults = await Promise.all(\n batch.map(async range => {\n const url = `https://api.confluent.cloud/billing/v1/costs?start_date=${range.start.format(\n 'YYYY-MM-DD',\n )}&end_date=${range.end.clone().add(1, 'd').format('YYYY-MM-DD')}`;\n\n try {\n const response = await this.fetchCostWithRetry(url, client, 0, maxRetries);\n\n this.logger.info(`Successfully fetched costs for ${range.start.format('YYYY-MM')}`);\n\n return {\n month: range.start.format('YYYY-MM'),\n data: response.data || [],\n };\n } catch (error) {\n this.logger.error(\n `Failed to fetch costs for ${range.start.format('YYYY-MM')} after multiple retries: ${error.message}`,\n );\n return {\n month: range.start.format('YYYY-MM'),\n data: [],\n };\n }\n }),\n );\n\n // Process batch results\n for (const result of batchResults) {\n if (result.data.length > 0) {\n // Extract environment IDs from this batch\n const envIds = Array.from(\n new Set(\n result.data\n .map((item: any) => item.resource?.environment?.id)\n .filter((id: any): id is string => typeof id === 'string'),\n ),\n );\n\n // Fetch any new environment names\n for (const envId of envIds) {\n if (typeof envId === 'string' && !(envId in envIdToName)) {\n // Add slight delay between env name requests\n await new Promise(resolve => setTimeout(resolve, 100));\n try {\n const name = await this.fetchEnvDisplayName(client, envId);\n envIdToName[envId] = name;\n } catch (error) {\n this.logger.warn(`Error fetching name for environment ${envId}: ${error.message}`);\n envIdToName[envId] = envId; // Fallback to using the ID\n }\n }\n }\n\n // Add environment names to the data\n const dataWithEnvNames = result.data\n .filter((item: any) => item.resource?.environment?.id)\n .map((item: any) => {\n const envId = item.resource.environment.id;\n return {\n ...item,\n envDisplayName: envIdToName[envId] || 'Unknown',\n };\n });\n\n aggregatedData = aggregatedData.concat(dataWithEnvNames);\n }\n }\n\n // Add a small delay between batches to help avoid rate limiting\n if (i + maxConcurrentRequests < monthlyRanges.length) {\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n } catch (error) {\n this.logger.error(`Error processing batch of months: ${error.message}`);\n }\n }\n\n // Log a summary of what we got\n if (aggregatedData.length === 0) {\n this.logger.error(`No cost data could be fetched from Confluent API. Check API key permissions.`);\n } else {\n this.logger.info(`Successfully fetched ${aggregatedData.length} cost entries from Confluent API.`);\n }\n\n return {\n data: aggregatedData,\n };\n }\n\n protected async transformCostsData(subAccountConfig: Config, query: CostQuery, costResponse: any): Promise<Report[]> {\n const categoryMappingService = CategoryMappingService.getInstance();\n const accountName = subAccountConfig.getString('name');\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 // Handle empty or invalid data\n if (!costResponse || !costResponse.data || !Array.isArray(costResponse.data)) {\n this.logger.warn('No valid cost data to transform');\n return [];\n }\n\n this.logger.debug(`Starting transformation of ${costResponse.data.length} Confluent cost records`);\n\n // Log first few records structure for debugging\n if (costResponse.data.length > 0) {\n const sampleRecord = costResponse.data[0];\n this.logger.debug(`Sample Confluent record structure: ${JSON.stringify(Object.keys(sampleRecord))}`);\n this.logger.debug(\n `Sample record - start_date: ${sampleRecord.start_date}, line_type: ${sampleRecord.line_type}, price: ${sampleRecord.price}, amount: ${sampleRecord.amount}, original_amount: ${sampleRecord.original_amount}, discount_amount: ${sampleRecord.discount_amount}, product: ${sampleRecord.product}, network_access_type: ${sampleRecord.network_access_type}`,\n );\n }\n\n let filteredOutZeroAmount = 0;\n let filteredOutMissingFields = 0;\n let filteredOutInvalidDate = 0;\n let filteredOutTimeRange = 0;\n let processedRecords = 0;\n const uniqueKeys = new Set<string>();\n const uniqueServices = new Set<string>();\n const uniqueResources = new Set<string>();\n\n const transformedData = costResponse.data.reduce((accumulator: { [key: string]: Report }, line: any) => {\n // Use amount field first (final amount after discounts), then fall back to price field\n const amount = parseFloat(line.amount) || parseFloat(line.price) || 0;\n\n if (amount === 0) {\n filteredOutZeroAmount++;\n return accumulator;\n }\n\n // Skip records with missing critical fields\n if (!line.start_date || !line.line_type || (!line.amount && !line.price)) {\n filteredOutMissingFields++;\n return accumulator;\n }\n\n const parsedStartDate = moment(line.start_date);\n\n if (!parsedStartDate.isValid()) {\n filteredOutInvalidDate++;\n this.logger.debug(`Invalid start_date: ${line.start_date}`);\n return accumulator;\n }\n\n let billingPeriod = undefined;\n if (query.granularity === GRANULARITY.MONTHLY) {\n billingPeriod = parsedStartDate.format('YYYY-MM');\n } else {\n billingPeriod = parsedStartDate.format('YYYY-MM-DD');\n }\n\n // Create a more descriptive service name by combining product and line_type\n const baseServiceName = this.capitalizeWords(line.line_type);\n const productName = line.product ? this.capitalizeWords(line.product) : null;\n const serviceName =\n productName && productName !== baseServiceName ? `${productName} ${baseServiceName}` : baseServiceName;\n\n const resourceName = line.resource?.display_name || 'Unknown';\n const envDisplayName = line.envDisplayName || 'Unknown';\n const networkAccessType = line.network_access_type;\n\n uniqueServices.add(serviceName);\n uniqueResources.add(resourceName);\n\n const keyName = `${accountName}->${categoryMappingService.getCategoryByServiceName(\n this.provider,\n serviceName,\n )}->${resourceName}`;\n\n if (!accumulator[keyName]) {\n uniqueKeys.add(keyName);\n accumulator[keyName] = {\n id: keyName,\n account: `${this.provider}/${accountName}`,\n service: this.convertServiceName(serviceName),\n category: categoryMappingService.getCategoryByServiceName(this.provider, serviceName),\n provider: this.provider,\n providerType: PROVIDER_TYPE.INTEGRATION,\n reports: {},\n ...{ project: envDisplayName },\n ...{ cluster: resourceName },\n ...(networkAccessType && { networkAccessType }),\n ...tagKeyValues,\n };\n }\n\n if (!moment(billingPeriod).isBefore(moment(parseInt(query.startTime, 10)))) {\n accumulator[keyName].reports[billingPeriod] = (accumulator[keyName].reports[billingPeriod] || 0) + amount;\n processedRecords++;\n } else {\n filteredOutTimeRange++;\n }\n\n return accumulator;\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: costResponse.data.length,\n });\n\n // Log a few unique keys to understand the grouping\n const keyArray = Array.from(uniqueKeys);\n if (keyArray.length > 0) {\n this.logger.info(`Unique services found: ${Array.from(uniqueServices).slice(0, 10).join(', ')}`);\n this.logger.info(`Unique resources found: ${Array.from(uniqueResources).slice(0, 10).join(', ')}`);\n this.logger.debug(`Sample unique keys: ${keyArray.slice(0, 5).join(', ')}`);\n }\n\n return Object.values(transformedData);\n }\n}\n"],"names":["InfraWalletClient","CLOUD_PROVIDER","ConfluentEnvironmentSchema","ZodError","cryptoRandom","ConfluentBillingResponseSchema","NUMBER_OF_MONTHS_FETCHING_HISTORICAL_COSTS","moment","CategoryMappingService","GRANULARITY","PROVIDER_TYPE"],"mappings":";;;;;;;;;;;;;;AAgBO,MAAM,wBAAwBA,mCAAA,CAAkB;AAAA,EACrD,OAAO,MAAA,CAAO,MAAA,EAAgB,QAAA,EAA2B,OAAqB,MAAA,EAAuB;AACnG,IAAA,OAAO,IAAI,eAAA,CAAgBC,qBAAA,CAAe,WAAW,MAAA,EAAQ,QAAA,EAAU,OAAO,MAAM,CAAA;AAAA;AACtF,EAEU,mBAAmB,WAAA,EAA6B;AACxD,IAAA,IAAI,aAAA,GAAgB,WAAA;AAEpB,IAAA,MAAM,QAAA,GAAW,CAAC,WAAW,CAAA;AAE7B,IAAA,KAAA,MAAW,UAAU,QAAA,EAAU;AAC7B,MAAA,IAAI,WAAA,CAAY,UAAA,CAAW,MAAM,CAAA,EAAG;AAClC,QAAA,aAAA,GAAgB,WAAA,CAAY,KAAA,CAAM,MAAA,CAAO,MAAM,EAAE,IAAA,EAAK;AAAA;AACxD;AAGF,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,QAAQ,CAAA,CAAA,EAAI,aAAa,CAAA,CAAA;AAAA;AAC1C,EAEQ,gBAAgB,GAAA,EAAqB;AAC3C,IAAA,OAAO,GAAA,CACJ,aAAY,CACZ,KAAA,CAAM,GAAG,CAAA,CACT,GAAA,CAAI,UAAQ,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,KAAgB,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA,CACxD,KAAK,GAAG,CAAA;AAAA;AACb,EAEA,MAAc,mBAAA,CAAoB,MAAA,EAAa,KAAA,EAAe,aAAa,CAAA,EAAoB;AAC7F,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,mDAAmD,KAAK,CAAA,CAAA;AACpE,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,QAChC,MAAA,EAAQ,KAAA;AAAA,QACR,SAAS,MAAA,CAAO;AAAA,OACjB,CAAA;AAED,MAAA,IAAI,QAAA,CAAS,MAAA,KAAW,GAAA,IAAO,UAAA,GAAa,CAAA,EAAG;AAE7C,QAAA,MAAM,UAAA,GAAa,SAAS,QAAA,CAAS,OAAA,CAAQ,IAAI,aAAa,CAAA,IAAK,KAAK,EAAE,CAAA;AAC1E,QAAA,MAAM,WAAA,GAAc,KAAK,GAAA,CAAI,EAAA,EAAI,aAAa,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,UAAU,CAAC,CAAA;AACrE,QAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,UACV,CAAA,gDAAA,EAAmD,KAAK,CAAA,kBAAA,EAAqB,WAAW,CAAA,WAAA;AAAA,SAC1F;AACA,QAAA,MAAM,IAAI,OAAA,CAAQ,CAAA,OAAA,KAAW,WAAW,OAAA,EAAS,WAAA,GAAc,GAAI,CAAC,CAAA;AACpE,QAAA,OAAO,IAAA,CAAK,mBAAA,CAAoB,MAAA,EAAQ,KAAA,EAAO,aAAa,CAAC,CAAA;AAAA;AAG/D,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,qCAAA,EAAwC,KAAK,CAAA,EAAA,EAAK,QAAA,CAAS,UAAU,CAAA,CAAE,CAAA;AACxF,QAAA,OAAO,KAAA;AAAA;AAGT,MAAA,MAAM,YAAA,GAAe,MAAM,QAAA,CAAS,IAAA,EAAK;AAEzC,MAAA,IAAI;AACF,QAAAC,2CAAA,CAA2B,MAAM,YAAY,CAAA;AAC7C,QAAA,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,gDAAA,CAAkD,CAAA;AAAA,eAC7D,KAAA,EAAO;AACd,QAAA,IAAI,iBAAiBC,YAAA,EAAU;AAC7B,UAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,kDAAA,EAAqD,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,SACvF,MAAO;AACL,UAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,6BAAA,EAAgC,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA;AAClE;AAGF,MAAA,OAAO,YAAA,CAAa,YAAA;AAAA,aACb,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,oCAAA,EAAuC,KAAK,CAAA,EAAA,EAAK,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AACjF,MAAA,OAAO,KAAA;AAAA;AACT;AACF,EAEA,MAAgB,gBAAgB,gBAAA,EAAwC;AACtE,IAAA,MAAM,MAAA,GAAS,gBAAA,CAAiB,SAAA,CAAU,QAAQ,CAAA;AAClD,IAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,SAAA,CAAU,WAAW,CAAA;AACxD,IAAA,MAAM,IAAA,GAAO,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAEnC,IAAA,MAAM,MAAA,GAAS;AAAA,MACb,OAAA,EAAS;AAAA,QACP,aAAA,EAAe,SAAS,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,QAAA,CAAS,QAAQ,CAAC,CAAA,CAAA;AAAA,QAC5D,cAAA,EAAgB;AAAA,OAClB;AAAA,MACA,IAAA,EAAM,gBAAA,CAAiB,SAAA,CAAU,MAAM;AAAA,KACzC;AAEA,IAAA,OAAO,MAAA;AAAA;AACT,EAEA,MAAc,kBAAA,CAAmB,GAAA,EAAa,QAAa,UAAA,GAAa,CAAA,EAAG,aAAa,CAAA,EAAiB;AACvG,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,OAAO,KAAA,CAAM,CAAA,mCAAA,EAAsC,GAAG,CAAA,UAAA,EAAa,UAAA,GAAa,CAAC,CAAA,CAAE,CAAA;AAExF,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,QAChC,MAAA,EAAQ,KAAA;AAAA,QACR,SAAS,MAAA,CAAO;AAAA,OACjB,CAAA;AAED,MAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,QAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,IAAA,EAAK;AACtC,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,iDAAA,EAAoD,SAAS,CAAA,CAAE,CAAA;AACjF,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,SAAS,CAAA,CAAE,CAAA;AAAA;AAGtD,MAAA,IAAI,QAAA,CAAS,MAAA,KAAW,GAAA,IAAO,UAAA,GAAa,UAAA,EAAY;AAEtD,QAAA,MAAM,UAAA,GAAa,SAAS,QAAA,CAAS,OAAA,CAAQ,IAAI,aAAa,CAAA,IAAK,MAAM,EAAE,CAAA;AAC3E,QAAA,MAAM,MAAA,GAASC,qBAAa,GAAI,CAAA;AAChC,QAAA,MAAM,WAAA,GAAc,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,UAAA,GAAa,KAAK,GAAA,CAAI,GAAA,EAAK,UAAU,CAAA,GAAI,MAAM,CAAA;AACjF,QAAA,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,8BAAA,EAAiC,KAAK,IAAA,CAAK,WAAW,CAAC,CAAA,WAAA,CAAa,CAAA;AACrF,QAAA,MAAM,IAAI,OAAA,CAAQ,CAAA,OAAA,KAAW,WAAW,OAAA,EAAS,WAAA,GAAc,GAAI,CAAC,CAAA;AACpE,QAAA,OAAO,KAAK,kBAAA,CAAmB,GAAA,EAAK,MAAA,EAAQ,UAAA,GAAa,GAAG,UAAU,CAAA;AAAA;AAGxE,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,IAAA,EAAK;AACtC,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,KAAA,EAAQ,SAAS,MAAM,CAAA,EAAA,EAAK,SAAS,CAAA,CAAE,CAAA;AAAA;AAGzD,MAAA,MAAM,YAAA,GAAe,MAAM,QAAA,CAAS,IAAA,EAAK;AAEzC,MAAA,IAAI;AACF,QAAAC,+CAAA,CAA+B,MAAM,YAAY,CAAA;AACjD,QAAA,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,4CAAA,CAA8C,CAAA;AAAA,eACzD,KAAA,EAAO;AACd,QAAA,IAAI,iBAAiBF,YAAA,EAAU;AAC7B,UAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,8CAAA,EAAiD,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AACjF,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,OAAO,YAAA;AAAA,aACA,KAAA,EAAO;AACd,MAAA,IAAI,aAAa,UAAA,EAAY;AAE3B,QAAA,MAAM,WAAA,GAAc,KAAK,GAAA,CAAI,EAAA,EAAI,KAAK,GAAA,CAAI,CAAA,EAAG,UAAU,CAAA,GAAI,CAAC,CAAA;AAC5D,QAAA,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,kCAAA,EAAqC,WAAW,CAAA,UAAA,EAAa,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAC7F,QAAA,MAAM,IAAI,OAAA,CAAQ,CAAA,OAAA,KAAW,WAAW,OAAA,EAAS,WAAA,GAAc,GAAI,CAAC,CAAA;AACpE,QAAA,OAAO,KAAK,kBAAA,CAAmB,GAAA,EAAK,MAAA,EAAQ,UAAA,GAAa,GAAG,UAAU,CAAA;AAAA;AAExE,MAAA,MAAM,KAAA;AAAA;AACR;AACF,EAEA,MAAgB,UAAA,CAAW,iBAAA,EAA2B,MAAA,EAAa,KAAA,EAAgC;AAIjG,IAAA,MAAM,eAAA,GAAkBG,iDAAA,CAA2CL,qBAAA,CAAe,SAAS,CAAA;AAG3F,IAAA,MAAM,MAAMM,uBAAA,EAAO;AACnB,IAAA,MAAM,eAAA,GAAkB,IAAI,KAAA,EAAM,CAAE,SAAS,eAAA,EAAiB,QAAQ,CAAA,CAAE,OAAA,CAAQ,OAAO,CAAA;AAGvF,IAAA,MAAM,mBAAmBA,uBAAA,CAAO,QAAA,CAAS,KAAA,CAAM,SAAA,EAAW,EAAE,CAAC,CAAA;AAC7D,IAAA,MAAM,iBAAiBA,uBAAA,CAAO,QAAA,CAAS,KAAA,CAAM,OAAA,EAAS,EAAE,CAAC,CAAA;AAGzD,IAAA,IAAI,SAAA,GAAY,iBAAiB,KAAA,EAAM;AACvC,IAAA,IAAI,SAAA,CAAU,QAAA,CAAS,eAAe,CAAA,EAAG;AACvC,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,QACV,CAAA,sCAAA,EAAyC,eAAe,CAAA,mCAAA,EAAsC,SAAA,CAAU,MAAA,CAAO,YAAY,CAAC,CAAA,IAAA,EAAO,eAAA,CAAgB,MAAA,CAAO,YAAY,CAAC,CAAA;AAAA,OACzK;AACA,MAAA,SAAA,GAAY,gBAAgB,KAAA,EAAM;AAAA;AAIpC,IAAA,MAAM,OAAA,GAAUA,uBAAA,CAAO,GAAA,CAAI,cAAA,EAAgB,GAAG,CAAA;AAG9C,IAAA,MAAM,gBAAgB,EAAC;AACvB,IAAA,MAAM,YAAA,GAAe,SAAA,CAAU,KAAA,EAAM,CAAE,QAAQ,OAAO,CAAA;AAEtD,IAAA,OAAO,YAAA,CAAa,SAAS,OAAO,CAAA,IAAK,aAAa,MAAA,CAAO,OAAA,EAAS,OAAO,CAAA,EAAG;AAC9E,MAAA,MAAM,UAAA,GAAa,YAAA,CAAa,KAAA,EAAM,CAAE,QAAQ,OAAO,CAAA;AACvD,MAAA,MAAM,QAAA,GAAWA,wBAAO,GAAA,CAAI,YAAA,CAAa,OAAM,CAAE,KAAA,CAAM,OAAO,CAAA,EAAG,OAAO,CAAA;AAExE,MAAA,aAAA,CAAc,IAAA,CAAK;AAAA,QACjB,KAAA,EAAO,UAAA;AAAA,QACP,GAAA,EAAK;AAAA,OACN,CAAA;AAED,MAAA,YAAA,CAAa,GAAA,CAAI,GAAG,OAAO,CAAA;AAAA;AAG7B,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MACV,CAAA,6BAAA,EAAgC,aAAA,CAAc,MAAM,CAAA,aAAA,EAAgB,SAAA,CAAU,MAAA,CAAO,SAAS,CAAC,CAAA,IAAA,EAAO,OAAA,CAAQ,MAAA,CAAO,SAAS,CAAC,CAAA;AAAA,KACjI;AAGA,IAAA,MAAM,qBAAA,GAAwB,CAAA;AAC9B,IAAA,MAAM,UAAA,GAAa,CAAA;AAEnB,IAAA,IAAI,iBAAwB,EAAC;AAC7B,IAAA,MAAM,cAAsC,EAAC;AAG7C,IAAA,IAAI;AAEF,MAAA,MAAM,WAAA,GAAc,aAAA,CAAc,aAAA,CAAc,MAAA,GAAS,CAAC,CAAA;AAC1D,MAAA,MAAM,OAAA,GAAU,CAAA,wDAAA,EAA2D,WAAA,CAAY,KAAA,CAAM,MAAA;AAAA,QAC3F;AAAA,OACD,CAAA,UAAA,EAAa,WAAA,CAAY,GAAA,CAAI,KAAA,EAAM,CAAE,GAAA,CAAI,CAAA,EAAG,GAAG,CAAA,CAAE,MAAA,CAAO,YAAY,CAAC,CAAA,CAAA;AAEtE,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,iCAAA,EAAoC,WAAA,CAAY,MAAM,MAAA,CAAO,SAAS,CAAC,CAAA,CAAE,CAAA;AAE3F,MAAA,MAAM,eAAe,MAAM,IAAA,CAAK,mBAAmB,OAAA,EAAS,MAAA,EAAQ,GAAG,CAAC,CAAA;AAExE,MAAA,IAAI,YAAA,CAAa,IAAA,IAAQ,YAAA,CAAa,IAAA,CAAK,SAAS,CAAA,EAAG;AAErD,QAAA,MAAM,SAAS,KAAA,CAAM,IAAA;AAAA,UACnB,IAAI,GAAA;AAAA,YACF,YAAA,CAAa,IAAA,CAAK,GAAA,CAAI,CAAC,SAAc,IAAA,CAAK,QAAA,EAAU,WAAA,EAAa,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,EAAA,KAAY,OAAO,KAAA,CAAS;AAAA;AAC3G,SACF;AAEA,QAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AAErB,UAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,YAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,cAAA,IAAI;AACF,gBAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,mBAAA,CAAoB,QAAQ,KAAK,CAAA;AACzD,gBAAA,WAAA,CAAY,KAAK,CAAA,GAAI,IAAA;AAAA,uBACd,KAAA,EAAO;AACd,gBAAA,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,oCAAA,EAAuC,KAAK,CAAA,EAAA,EAAK,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AACjF,gBAAA,WAAA,CAAY,KAAK,CAAA,GAAI,KAAA;AAAA;AACvB;AACF;AAGF,UAAA,MAAM,gBAAA,GAAmB,YAAA,CAAa,IAAA,CACnC,MAAA,CAAO,CAAC,IAAA,KAAc,IAAA,CAAK,QAAA,EAAU,WAAA,EAAa,EAAE,CAAA,CACpD,GAAA,CAAI,CAAC,IAAA,KAAc;AAClB,YAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,QAAA,CAAS,WAAA,CAAY,EAAA;AACxC,YAAA,OAAO;AAAA,cACL,GAAG,IAAA;AAAA,cACH,cAAA,EAAgB,WAAA,CAAY,KAAK,CAAA,IAAK;AAAA,aACxC;AAAA,WACD,CAAA;AAEH,UAAA,cAAA,GAAiB,cAAA,CAAe,OAAO,gBAAgB,CAAA;AACvD,UAAA,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA,+BAAA,EAAkC,WAAA,CAAY,MAAM,MAAA,CAAO,SAAS,CAAC,CAAA,CAAE,CAAA;AAAA;AAI1F,QAAA,aAAA,CAAc,GAAA,EAAI;AAAA;AACpB,aACO,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,oCAAA,EAAuC,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA;AAK1E,IAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,aAAA,CAAc,MAAA,EAAQ,KAAK,qBAAA,EAAuB;AACpE,MAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,KAAA,CAAM,CAAA,EAAG,IAAI,qBAAqB,CAAA;AAE9D,MAAA,IAAI;AACF,QAAA,MAAM,YAAA,GAAe,MAAM,OAAA,CAAQ,GAAA;AAAA,UACjC,KAAA,CAAM,GAAA,CAAI,OAAM,KAAA,KAAS;AACvB,YAAA,MAAM,GAAA,GAAM,CAAA,wDAAA,EAA2D,KAAA,CAAM,KAAA,CAAM,MAAA;AAAA,cACjF;AAAA,aACD,CAAA,UAAA,EAAa,KAAA,CAAM,GAAA,CAAI,KAAA,EAAM,CAAE,GAAA,CAAI,CAAA,EAAG,GAAG,CAAA,CAAE,MAAA,CAAO,YAAY,CAAC,CAAA,CAAA;AAEhE,YAAA,IAAI;AACF,cAAA,MAAM,WAAW,MAAM,IAAA,CAAK,mBAAmB,GAAA,EAAK,MAAA,EAAQ,GAAG,UAAU,CAAA;AAEzE,cAAA,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA,+BAAA,EAAkC,KAAA,CAAM,MAAM,MAAA,CAAO,SAAS,CAAC,CAAA,CAAE,CAAA;AAElF,cAAA,OAAO;AAAA,gBACL,KAAA,EAAO,KAAA,CAAM,KAAA,CAAM,MAAA,CAAO,SAAS,CAAA;AAAA,gBACnC,IAAA,EAAM,QAAA,CAAS,IAAA,IAAQ;AAAC,eAC1B;AAAA,qBACO,KAAA,EAAO;AACd,cAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,gBACV,CAAA,0BAAA,EAA6B,MAAM,KAAA,CAAM,MAAA,CAAO,SAAS,CAAC,CAAA,yBAAA,EAA4B,MAAM,OAAO,CAAA;AAAA,eACrG;AACA,cAAA,OAAO;AAAA,gBACL,KAAA,EAAO,KAAA,CAAM,KAAA,CAAM,MAAA,CAAO,SAAS,CAAA;AAAA,gBACnC,MAAM;AAAC,eACT;AAAA;AACF,WACD;AAAA,SACH;AAGA,QAAA,KAAA,MAAW,UAAU,YAAA,EAAc;AACjC,UAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG;AAE1B,YAAA,MAAM,SAAS,KAAA,CAAM,IAAA;AAAA,cACnB,IAAI,GAAA;AAAA,gBACF,MAAA,CAAO,IAAA,CACJ,GAAA,CAAI,CAAC,SAAc,IAAA,CAAK,QAAA,EAAU,WAAA,EAAa,EAAE,EACjD,MAAA,CAAO,CAAC,EAAA,KAA0B,OAAO,OAAO,QAAQ;AAAA;AAC7D,aACF;AAGA,YAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,cAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,EAAE,SAAS,WAAA,CAAA,EAAc;AAExD,gBAAA,MAAM,IAAI,OAAA,CAAQ,CAAA,OAAA,KAAW,UAAA,CAAW,OAAA,EAAS,GAAG,CAAC,CAAA;AACrD,gBAAA,IAAI;AACF,kBAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,mBAAA,CAAoB,QAAQ,KAAK,CAAA;AACzD,kBAAA,WAAA,CAAY,KAAK,CAAA,GAAI,IAAA;AAAA,yBACd,KAAA,EAAO;AACd,kBAAA,IAAA,CAAK,OAAO,IAAA,CAAK,CAAA,oCAAA,EAAuC,KAAK,CAAA,EAAA,EAAK,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AACjF,kBAAA,WAAA,CAAY,KAAK,CAAA,GAAI,KAAA;AAAA;AACvB;AACF;AAIF,YAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,IAAA,CAC7B,MAAA,CAAO,CAAC,IAAA,KAAc,IAAA,CAAK,QAAA,EAAU,WAAA,EAAa,EAAE,CAAA,CACpD,GAAA,CAAI,CAAC,IAAA,KAAc;AAClB,cAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,QAAA,CAAS,WAAA,CAAY,EAAA;AACxC,cAAA,OAAO;AAAA,gBACL,GAAG,IAAA;AAAA,gBACH,cAAA,EAAgB,WAAA,CAAY,KAAK,CAAA,IAAK;AAAA,eACxC;AAAA,aACD,CAAA;AAEH,YAAA,cAAA,GAAiB,cAAA,CAAe,OAAO,gBAAgB,CAAA;AAAA;AACzD;AAIF,QAAA,IAAI,CAAA,GAAI,qBAAA,GAAwB,aAAA,CAAc,MAAA,EAAQ;AACpD,UAAA,MAAM,IAAI,OAAA,CAAQ,CAAA,OAAA,KAAW,UAAA,CAAW,OAAA,EAAS,GAAI,CAAC,CAAA;AAAA;AACxD,eACO,KAAA,EAAO;AACd,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,kCAAA,EAAqC,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA;AACxE;AAIF,IAAA,IAAI,cAAA,CAAe,WAAW,CAAA,EAAG;AAC/B,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,4EAAA,CAA8E,CAAA;AAAA,KAClG,MAAO;AACL,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,qBAAA,EAAwB,cAAA,CAAe,MAAM,CAAA,iCAAA,CAAmC,CAAA;AAAA;AAGnG,IAAA,OAAO;AAAA,MACL,IAAA,EAAM;AAAA,KACR;AAAA;AACF,EAEA,MAAgB,kBAAA,CAAmB,gBAAA,EAA0B,KAAA,EAAkB,YAAA,EAAsC;AACnH,IAAA,MAAM,sBAAA,GAAyBC,8CAAuB,WAAA,EAAY;AAClE,IAAA,MAAM,WAAA,GAAc,gBAAA,CAAiB,SAAA,CAAU,MAAM,CAAA;AACrD,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;AAGD,IAAA,IAAI,CAAC,YAAA,IAAgB,CAAC,YAAA,CAAa,IAAA,IAAQ,CAAC,KAAA,CAAM,OAAA,CAAQ,YAAA,CAAa,IAAI,CAAA,EAAG;AAC5E,MAAA,IAAA,CAAK,MAAA,CAAO,KAAK,iCAAiC,CAAA;AAClD,MAAA,OAAO,EAAC;AAAA;AAGV,IAAA,IAAA,CAAK,OAAO,KAAA,CAAM,CAAA,2BAAA,EAA8B,YAAA,CAAa,IAAA,CAAK,MAAM,CAAA,uBAAA,CAAyB,CAAA;AAGjG,IAAA,IAAI,YAAA,CAAa,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG;AAChC,MAAA,MAAM,YAAA,GAAe,YAAA,CAAa,IAAA,CAAK,CAAC,CAAA;AACxC,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,mCAAA,EAAsC,IAAA,CAAK,SAAA,CAAU,OAAO,IAAA,CAAK,YAAY,CAAC,CAAC,CAAA,CAAE,CAAA;AACnG,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,QACV,CAAA,4BAAA,EAA+B,aAAa,UAAU,CAAA,aAAA,EAAgB,aAAa,SAAS,CAAA,SAAA,EAAY,YAAA,CAAa,KAAK,CAAA,UAAA,EAAa,YAAA,CAAa,MAAM,CAAA,mBAAA,EAAsB,YAAA,CAAa,eAAe,CAAA,mBAAA,EAAsB,YAAA,CAAa,eAAe,cAAc,YAAA,CAAa,OAAO,CAAA,uBAAA,EAA0B,YAAA,CAAa,mBAAmB,CAAA;AAAA,OAC5V;AAAA;AAGF,IAAA,IAAI,qBAAA,GAAwB,CAAA;AAC5B,IAAA,IAAI,wBAAA,GAA2B,CAAA;AAC/B,IAAA,IAAI,sBAAA,GAAyB,CAAA;AAC7B,IAAA,IAAI,oBAAA,GAAuB,CAAA;AAC3B,IAAA,IAAI,gBAAA,GAAmB,CAAA;AACvB,IAAA,MAAM,UAAA,uBAAiB,GAAA,EAAY;AACnC,IAAA,MAAM,cAAA,uBAAqB,GAAA,EAAY;AACvC,IAAA,MAAM,eAAA,uBAAsB,GAAA,EAAY;AAExC,IAAA,MAAM,kBAAkB,YAAA,CAAa,IAAA,CAAK,MAAA,CAAO,CAAC,aAAwC,IAAA,KAAc;AAEtG,MAAA,MAAM,MAAA,GAAS,WAAW,IAAA,CAAK,MAAM,KAAK,UAAA,CAAW,IAAA,CAAK,KAAK,CAAA,IAAK,CAAA;AAEpE,MAAA,IAAI,WAAW,CAAA,EAAG;AAChB,QAAA,qBAAA,EAAA;AACA,QAAA,OAAO,WAAA;AAAA;AAIT,MAAA,IAAI,CAAC,IAAA,CAAK,UAAA,IAAc,CAAC,IAAA,CAAK,SAAA,IAAc,CAAC,IAAA,CAAK,MAAA,IAAU,CAAC,IAAA,CAAK,KAAA,EAAQ;AACxE,QAAA,wBAAA,EAAA;AACA,QAAA,OAAO,WAAA;AAAA;AAGT,MAAA,MAAM,eAAA,GAAkBD,uBAAA,CAAO,IAAA,CAAK,UAAU,CAAA;AAE9C,MAAA,IAAI,CAAC,eAAA,CAAgB,OAAA,EAAQ,EAAG;AAC9B,QAAA,sBAAA,EAAA;AACA,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,oBAAA,EAAuB,IAAA,CAAK,UAAU,CAAA,CAAE,CAAA;AAC1D,QAAA,OAAO,WAAA;AAAA;AAGT,MAAA,IAAI,aAAA,GAAgB,MAAA;AACpB,MAAA,IAAI,KAAA,CAAM,WAAA,KAAgBE,kBAAA,CAAY,OAAA,EAAS;AAC7C,QAAA,aAAA,GAAgB,eAAA,CAAgB,OAAO,SAAS,CAAA;AAAA,OAClD,MAAO;AACL,QAAA,aAAA,GAAgB,eAAA,CAAgB,OAAO,YAAY,CAAA;AAAA;AAIrD,MAAA,MAAM,eAAA,GAAkB,IAAA,CAAK,eAAA,CAAgB,IAAA,CAAK,SAAS,CAAA;AAC3D,MAAA,MAAM,cAAc,IAAA,CAAK,OAAA,GAAU,KAAK,eAAA,CAAgB,IAAA,CAAK,OAAO,CAAA,GAAI,IAAA;AACxE,MAAA,MAAM,WAAA,GACJ,eAAe,WAAA,KAAgB,eAAA,GAAkB,GAAG,WAAW,CAAA,CAAA,EAAI,eAAe,CAAA,CAAA,GAAK,eAAA;AAEzF,MAAA,MAAM,YAAA,GAAe,IAAA,CAAK,QAAA,EAAU,YAAA,IAAgB,SAAA;AACpD,MAAA,MAAM,cAAA,GAAiB,KAAK,cAAA,IAAkB,SAAA;AAC9C,MAAA,MAAM,oBAAoB,IAAA,CAAK,mBAAA;AAE/B,MAAA,cAAA,CAAe,IAAI,WAAW,CAAA;AAC9B,MAAA,eAAA,CAAgB,IAAI,YAAY,CAAA;AAEhC,MAAA,MAAM,OAAA,GAAU,CAAA,EAAG,WAAW,CAAA,EAAA,EAAK,sBAAA,CAAuB,wBAAA;AAAA,QACxD,IAAA,CAAK,QAAA;AAAA,QACL;AAAA,OACD,KAAK,YAAY,CAAA,CAAA;AAElB,MAAA,IAAI,CAAC,WAAA,CAAY,OAAO,CAAA,EAAG;AACzB,QAAA,UAAA,CAAW,IAAI,OAAO,CAAA;AACtB,QAAA,WAAA,CAAY,OAAO,CAAA,GAAI;AAAA,UACrB,EAAA,EAAI,OAAA;AAAA,UACJ,OAAA,EAAS,CAAA,EAAG,IAAA,CAAK,QAAQ,IAAI,WAAW,CAAA,CAAA;AAAA,UACxC,OAAA,EAAS,IAAA,CAAK,kBAAA,CAAmB,WAAW,CAAA;AAAA,UAC5C,QAAA,EAAU,sBAAA,CAAuB,wBAAA,CAAyB,IAAA,CAAK,UAAU,WAAW,CAAA;AAAA,UACpF,UAAU,IAAA,CAAK,QAAA;AAAA,UACf,cAAcC,oBAAA,CAAc,WAAA;AAAA,UAC5B,SAAS,EAAC;AAAA,UACV,GAAG,EAAE,OAAA,EAAS,cAAA,EAAe;AAAA,UAC7B,GAAG,EAAE,OAAA,EAAS,YAAA,EAAa;AAAA,UAC3B,GAAI,iBAAA,IAAqB,EAAE,iBAAA,EAAkB;AAAA,UAC7C,GAAG;AAAA,SACL;AAAA;AAGF,MAAA,IAAI,CAACH,uBAAA,CAAO,aAAa,CAAA,CAAE,QAAA,CAASA,uBAAA,CAAO,QAAA,CAAS,KAAA,CAAM,SAAA,EAAW,EAAE,CAAC,CAAC,CAAA,EAAG;AAC1E,QAAA,WAAA,CAAY,OAAO,CAAA,CAAE,OAAA,CAAQ,aAAa,CAAA,GAAA,CAAK,WAAA,CAAY,OAAO,CAAA,CAAE,OAAA,CAAQ,aAAa,CAAA,IAAK,CAAA,IAAK,MAAA;AACnG,QAAA,gBAAA,EAAA;AAAA,OACF,MAAO;AACL,QAAA,oBAAA,EAAA;AAAA;AAGF,MAAA,OAAO,WAAA;AAAA,KACT,EAAG,EAAE,CAAA;AAEL,IAAA,IAAA,CAAK,wBAAA,CAAyB;AAAA,MAC5B,SAAA,EAAW,gBAAA;AAAA,MACX,eAAe,UAAA,CAAW,IAAA;AAAA,MAC1B,UAAA,EAAY,qBAAA;AAAA,MACZ,aAAA,EAAe,wBAAA;AAAA,MACf,WAAA,EAAa,sBAAA;AAAA,MACb,SAAA,EAAW,oBAAA;AAAA,MACX,YAAA,EAAc,aAAa,IAAA,CAAK;AAAA,KACjC,CAAA;AAGD,IAAA,MAAM,QAAA,GAAW,KAAA,CAAM,IAAA,CAAK,UAAU,CAAA;AACtC,IAAA,IAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AACvB,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,uBAAA,EAA0B,KAAA,CAAM,KAAK,cAAc,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAC/F,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,wBAAA,EAA2B,KAAA,CAAM,KAAK,eAAe,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AACjG,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,oBAAA,EAAuB,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA;AAG5E,IAAA,OAAO,MAAA,CAAO,OAAO,eAAe,CAAA;AAAA;AAExC;;;;"}