{"version":3,"sources":["jsdelivr-header.js","/npm/@backstage/plugin-tech-insights-backend-module-jsonfc@0.1.50/src/constants.ts","/npm/@backstage/plugin-tech-insights-backend-module-jsonfc@0.1.50/src/service/CheckRegistry.ts","/npm/@backstage/plugin-tech-insights-backend-module-jsonfc@0.1.50/src/service/config.ts","/npm/@backstage/plugin-tech-insights-backend-module-jsonfc@0.1.50/src/service/JsonRulesEngineFactChecker.ts","/npm/@backstage/plugin-tech-insights-backend-module-jsonfc@0.1.50/src/module/techInsightsModuleJsonRulesEngineFactCheckerFactory.ts"],"names":[],"mappings":"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","file":"/npm/@backstage/plugin-tech-insights-backend-module-jsonfc@0.1.50/dist/index.cjs.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/@backstage/plugin-tech-insights-backend-module-jsonfc@0.1.50/dist/index.cjs.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @public\n */\nexport const JSON_RULE_ENGINE_CHECK_TYPE = 'json-rules-engine';\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport {\n  TechInsightCheck,\n  TechInsightCheckRegistry,\n} from '@backstage/plugin-tech-insights-node';\n\nexport class DefaultCheckRegistry<CheckType extends TechInsightCheck>\n  implements TechInsightCheckRegistry<CheckType>\n{\n  private readonly checks = new Map<string, CheckType>();\n\n  constructor(checks: CheckType[]) {\n    checks.forEach(check => {\n      this.register(check);\n    });\n  }\n\n  async register(check: CheckType): Promise<CheckType> {\n    if (this.checks.has(check.id)) {\n      throw new ConflictError(\n        `Tech insight check with id ${check.id} has already been registered`,\n      );\n    }\n    this.checks.set(check.id, check);\n    return check;\n  }\n\n  async get(checkId: string): Promise<CheckType> {\n    const check = this.checks.get(checkId);\n    if (!check) {\n      throw new NotFoundError(\n        `Tech insight check with id '${checkId}' is not registered.`,\n      );\n    }\n    return check;\n  }\n  async getAll(checks: string[]): Promise<CheckType[]> {\n    return Promise.all(checks.map(checkId => this.get(checkId)));\n  }\n\n  async list(): Promise<CheckType[]> {\n    return [...this.checks.values()];\n  }\n}\n","/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { TopLevelCondition } from 'json-rules-engine';\nimport { Rule, TechInsightJsonRuleCheck } from '../types';\n\n// copy of non-exported `ConditionProperties` from 'json-rules-engine'\ninterface ConditionProperties {\n  fact: string;\n  operator: string;\n  value: { fact: string } | any;\n  path?: string;\n  priority?: number;\n  params?: Record<string, any>;\n  name?: string;\n}\n\n// copy of non-exported `NestedCondition` from 'json-rules-engine'\ntype NestedCondition = ConditionProperties | TopLevelCondition;\n\nfunction readRuleConditionProperties(config: Config): ConditionProperties {\n  const fact = config.getString('fact');\n  const name = config.getOptionalString('name');\n  const operator = config.getString('operator');\n  const params = config.getOptionalConfig('params')?.get<Record<string, any>>();\n  const path = config.getOptionalString('path');\n  const priority = config.getOptionalNumber('priority');\n  const value: { fact: string } | any = config.get('value');\n\n  return {\n    fact,\n    name,\n    operator,\n    params,\n    path,\n    priority,\n    value,\n  };\n}\n\nfunction readRuleNestedCondition(config: Config): NestedCondition {\n  if (config.has('fact')) {\n    return readRuleConditionProperties(config);\n  }\n\n  return readRuleTopLevelCondition(config);\n}\n\nfunction readRuleTopLevelCondition(config: Config): TopLevelCondition {\n  const base = {\n    name: config.getOptionalString('name'),\n    priority: config.getOptionalNumber('priority'),\n  };\n\n  if (config.has('all')) {\n    const all = config\n      .getConfigArray('all')\n      .map(conditionConfig => readRuleNestedCondition(conditionConfig));\n    return {\n      ...base,\n      all,\n    };\n  }\n\n  if (config.has('any')) {\n    const any = config\n      .getConfigArray('any')\n      .map(conditionConfig => readRuleNestedCondition(conditionConfig));\n    return {\n      ...base,\n      any,\n    };\n  }\n\n  if (config.has('not')) {\n    const not = readRuleNestedCondition(config.getConfig('not'));\n    return {\n      ...base,\n      not,\n    };\n  }\n\n  const condition = config.getString('condition');\n\n  return {\n    ...base,\n    condition,\n  };\n}\n\nfunction readRuleFromRuleConfig(config: Config): Rule {\n  const conditions = readRuleTopLevelCondition(config.getConfig('conditions'));\n  const name = config.getOptionalString('name');\n  const priority = config.getOptionalNumber('priority');\n\n  return {\n    conditions,\n    name,\n    priority,\n  };\n}\n\nfunction readCheckFromCheckConfig(\n  id: string,\n  config: Config,\n): TechInsightJsonRuleCheck {\n  const type = config.getString('type');\n  const name = config.getString('name');\n  const description = config.getString('description');\n  const factIds = config.getStringArray('factIds');\n  const successMetadata = config\n    .getOptionalConfig('successMetadata')\n    ?.get<Record<string, any>>();\n  const failureMetadata = config\n    .getOptionalConfig('failureMetadata')\n    ?.get<Record<string, any>>();\n  const rule = readRuleFromRuleConfig(config.getConfig('rule'));\n\n  return {\n    description,\n    factIds,\n    failureMetadata,\n    id,\n    name,\n    rule,\n    successMetadata,\n    type,\n  };\n}\n\nexport function readChecksFromConfig(\n  config: Config,\n): TechInsightJsonRuleCheck[] {\n  const key = 'techInsights.factChecker.checks';\n  if (!config.has(key)) {\n    return [];\n  }\n\n  const checksConfig = config.getConfig(key);\n  const checks: TechInsightJsonRuleCheck[] = [];\n  checksConfig.keys().forEach(checkId => {\n    const checkConfig = checksConfig.getConfig(checkId);\n\n    checks.push(readCheckFromCheckConfig(checkId, checkConfig));\n  });\n\n  return checks;\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { isError } from '@backstage/errors';\nimport { FactResponse } from '@backstage/plugin-tech-insights-common';\nimport {\n  CheckValidationResponse,\n  FactChecker,\n  FlatTechInsightFact,\n  TechInsightCheckRegistry,\n  TechInsightsStore,\n} from '@backstage/plugin-tech-insights-node';\nimport Ajv, { SchemaObject } from 'ajv';\nimport {\n  Engine,\n  EngineResult,\n  Operator,\n  TopLevelCondition,\n} from 'json-rules-engine';\nimport { pick } from 'lodash';\nimport { JSON_RULE_ENGINE_CHECK_TYPE } from '../constants';\nimport { JsonRuleBooleanCheckResult, TechInsightJsonRuleCheck } from '../types';\nimport { DefaultCheckRegistry } from './CheckRegistry';\nimport { readChecksFromConfig } from './config';\nimport * as validationSchema from './validation-schema.json';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nconst noopEvent = {\n  type: 'noop',\n};\n\n/**\n * @public\n * Should actually be at-internal\n *\n * Constructor options for JsonRulesEngineFactChecker\n */\nexport type JsonRulesEngineFactCheckerOptions = {\n  checks: TechInsightJsonRuleCheck[];\n  repository: TechInsightsStore;\n  logger: LoggerService;\n  checkRegistry?: TechInsightCheckRegistry<any>;\n  operators?: Operator[];\n};\n\n/**\n * @public\n * Should actually be at-internal\n *\n * FactChecker implementation using json-rules-engine\n */\nexport class JsonRulesEngineFactChecker\n  implements FactChecker<TechInsightJsonRuleCheck, JsonRuleBooleanCheckResult>\n{\n  private readonly checkRegistry: TechInsightCheckRegistry<TechInsightJsonRuleCheck>;\n  private repository: TechInsightsStore;\n  private readonly logger: LoggerService;\n  private readonly validationSchema: SchemaObject;\n  private readonly operators: Operator[];\n\n  constructor(options: JsonRulesEngineFactCheckerOptions) {\n    const { checks, repository, logger, checkRegistry, operators } = options;\n\n    this.repository = repository;\n    this.logger = logger;\n    this.operators = operators || [];\n    this.validationSchema = JSON.parse(JSON.stringify(validationSchema));\n\n    this.operators.forEach(op => {\n      this.validationSchema.definitions.condition.properties.operator.anyOf.push(\n        { const: op.name },\n      );\n    });\n\n    checks.forEach(check => this.validate(check));\n    this.checkRegistry =\n      checkRegistry ??\n      new DefaultCheckRegistry<TechInsightJsonRuleCheck>(checks);\n  }\n\n  async runChecks(\n    entity: string,\n    checks?: string[],\n  ): Promise<JsonRuleBooleanCheckResult[]> {\n    const engine = new Engine();\n    this.operators.forEach(op => {\n      engine.addOperator(op);\n    });\n\n    const techInsightChecks = checks\n      ? await this.checkRegistry.getAll(checks)\n      : await this.checkRegistry.list();\n    const factIds = techInsightChecks.flatMap(it => it.factIds);\n    const facts = await this.repository.getLatestFactsByIds(factIds, entity);\n\n    techInsightChecks.forEach(techInsightCheck => {\n      const rule = techInsightCheck.rule;\n      rule.name = techInsightCheck.id;\n      // Only run checks that have all the facts available:\n      const hasFacts = techInsightCheck.factIds.some(factId => facts[factId]);\n      if (hasFacts) {\n        engine.addRule({ ...techInsightCheck.rule, event: noopEvent });\n      } else {\n        this.logger.debug(\n          `Skipping ${\n            rule.name\n          } due to missing facts: ${techInsightCheck.factIds\n            .filter(factId => !facts[factId])\n            .join(', ')}`,\n        );\n      }\n    });\n    const factValues = Object.values(facts).reduce(\n      (acc, it) => ({ ...acc, ...it.facts }),\n      {},\n    );\n\n    try {\n      const results = await engine.run(factValues);\n      return await this.ruleEngineResultsToCheckResponse(\n        results,\n        techInsightChecks,\n        Object.values(facts),\n      );\n    } catch (e) {\n      if (isError(e)) {\n        throw new Error(`Failed to run rules engine, ${e.message}`, {\n          cause: e,\n        });\n      }\n      throw e;\n    }\n  }\n\n  async validate(\n    check: TechInsightJsonRuleCheck,\n  ): Promise<CheckValidationResponse> {\n    const ajv = new Ajv({ verbose: true });\n    const validator = ajv.compile(this.validationSchema);\n    const isValidToSchema = validator(check.rule);\n    if (check.type !== JSON_RULE_ENGINE_CHECK_TYPE) {\n      const msg = `Only ${JSON_RULE_ENGINE_CHECK_TYPE} checks can be registered to this fact checker`;\n      this.logger.warn(msg);\n      return {\n        valid: false,\n        message: msg,\n      };\n    }\n    if (!isValidToSchema) {\n      const msg = 'Failed to to validate conditions against JSON schema';\n      this.logger.warn(\n        'Failed to to validate conditions against JSON schema',\n        new Error(JSON.stringify(validator.errors)),\n      );\n      return {\n        valid: false,\n        message: msg,\n        errors: validator.errors ? validator.errors : undefined,\n      };\n    }\n\n    const existingSchemas = await this.repository.getLatestSchemas(\n      check.factIds,\n    );\n    const references = this.retrieveIndividualFactReferences(\n      check.rule.conditions,\n    );\n    const results = references.map(ref => ({\n      ref,\n      result: existingSchemas.some(schema => schema.hasOwnProperty(ref)),\n    }));\n    const failedReferences = results.filter(it => !it.result);\n    failedReferences.forEach(it => {\n      this.logger.warn(\n        `Validation failed for check ${check.name}. Reference to value ${\n          it.ref\n        } does not exists in referred fact schemas: ${check.factIds.join(',')}`,\n      );\n    });\n    const valid = failedReferences.length === 0;\n    return {\n      valid,\n      ...(!valid\n        ? {\n            message: `Check is referencing missing values from fact schemas: ${failedReferences\n              .map(it => it.ref)\n              .join(',')}`,\n          }\n        : {}),\n    };\n  }\n\n  getChecks(): Promise<TechInsightJsonRuleCheck[]> {\n    return this.checkRegistry.list();\n  }\n\n  private retrieveIndividualFactReferences(\n    condition: TopLevelCondition | { fact: string },\n  ): string[] {\n    let results: string[] = [];\n    if ('all' in condition) {\n      results = results.concat(\n        condition.all.flatMap(con =>\n          this.retrieveIndividualFactReferences(con),\n        ),\n      );\n    } else if ('any' in condition) {\n      results = results.concat(\n        condition.any.flatMap(con =>\n          this.retrieveIndividualFactReferences(con),\n        ),\n      );\n    } else if ('not' in condition) {\n      results = results.concat(\n        this.retrieveIndividualFactReferences(condition.not),\n      );\n    } else if ('condition' in condition) {\n      // ignore the ConditionReference type\n    } else {\n      results.push(condition.fact);\n    }\n    return results;\n  }\n\n  private async ruleEngineResultsToCheckResponse(\n    results: EngineResult,\n    techInsightChecks: TechInsightJsonRuleCheck[],\n    facts: FlatTechInsightFact[],\n  ) {\n    return await Promise.all(\n      [\n        ...(results.results && results.results),\n        ...(results.failureResults && results.failureResults),\n      ].map(async result => {\n        const techInsightCheck = techInsightChecks.find(\n          check => check.id === result.name,\n        );\n        if (!techInsightCheck) {\n          // This should never happen, we just constructed these based on each other\n          throw new Error(\n            `Failed to determine tech insight check with id ${result.name}. Discrepancy between ran rule engine and configured checks.`,\n          );\n        }\n        const factResponse = await this.constructFactInformationResponse(\n          facts,\n          techInsightCheck,\n        );\n        return {\n          facts: factResponse,\n          result: result.result,\n          check: JsonRulesEngineFactChecker.constructCheckResponse(\n            techInsightCheck,\n            result,\n          ),\n        };\n      }),\n    );\n  }\n\n  private static constructCheckResponse(\n    techInsightCheck: TechInsightJsonRuleCheck,\n    result: any,\n  ) {\n    const returnable = {\n      id: techInsightCheck.id,\n      type: techInsightCheck.type,\n      name: techInsightCheck.name,\n      description: techInsightCheck.description,\n      factIds: techInsightCheck.factIds,\n      metadata: result.result\n        ? techInsightCheck.successMetadata\n        : techInsightCheck.failureMetadata,\n      rule: { conditions: {} },\n    };\n\n    if ('toJSON' in result) {\n      // Results from json-rules-engine serialize \"wrong\" since the objects are creating their own serialization implementations.\n      // 'toJSON' should always be present in the result object but it is missing from the types.\n      // Parsing the stringified representation into a plain object here to be able to serialize it later\n      // along with other items present in the returned response.\n      const rule = JSON.parse(result.toJSON());\n      return { ...returnable, rule: pick(rule, ['conditions']) };\n    }\n    return returnable;\n  }\n\n  private async constructFactInformationResponse(\n    facts: FlatTechInsightFact[],\n    techInsightCheck: TechInsightJsonRuleCheck,\n  ): Promise<FactResponse> {\n    const factSchemas = await this.repository.getLatestSchemas(\n      techInsightCheck.factIds,\n    );\n    const schemas = factSchemas.reduce(\n      (acc, schema) => ({ ...acc, ...schema }),\n      {},\n    );\n    const individualFacts = this.retrieveIndividualFactReferences(\n      techInsightCheck.rule.conditions,\n    );\n    const factValues = facts\n      .filter(factContainer =>\n        techInsightCheck.factIds.includes(factContainer.id),\n      )\n      .reduce(\n        (acc, factContainer) => ({\n          ...acc,\n          ...pick(factContainer.facts, individualFacts),\n        }),\n        {},\n      );\n    return Object.entries(factValues).reduce((acc, [key, value]) => {\n      return {\n        ...acc,\n        [key]: {\n          value,\n          ...schemas[key],\n        },\n      };\n    }, {});\n  }\n}\n\n/**\n * @public\n *\n * Constructor options for JsonRulesEngineFactCheckerFactory\n *\n * Implementation of checkRegistry is optional.\n * If there is a need to use persistent storage for checks, it is recommended to inject a storage implementation here.\n * Otherwise, an in-memory option is instantiated and used.\n */\nexport type JsonRulesEngineFactCheckerFactoryOptions = {\n  checks: TechInsightJsonRuleCheck[];\n  logger: LoggerService;\n  checkRegistry?: TechInsightCheckRegistry<TechInsightJsonRuleCheck>;\n  operators?: Operator[];\n};\n\n/**\n * @public\n *\n * Factory to construct JsonRulesEngineFactChecker\n * Can be constructed with optional implementation of CheckInsightCheckRegistry if needed.\n * Otherwise, defaults to using in-memory CheckRegistry.\n */\nexport class JsonRulesEngineFactCheckerFactory {\n  private readonly checks: TechInsightJsonRuleCheck[];\n  private readonly logger: LoggerService;\n  private readonly checkRegistry?: TechInsightCheckRegistry<TechInsightJsonRuleCheck>;\n  private readonly operators?: Operator[];\n\n  static fromConfig(\n    config: Config,\n    options: Omit<JsonRulesEngineFactCheckerFactoryOptions, 'checks'>,\n  ): JsonRulesEngineFactCheckerFactory {\n    const checks = readChecksFromConfig(config);\n\n    return new JsonRulesEngineFactCheckerFactory({\n      ...options,\n      checks,\n    });\n  }\n\n  constructor(options: JsonRulesEngineFactCheckerFactoryOptions) {\n    this.logger = options.logger;\n    this.checks = options.checks;\n    this.checkRegistry = options.checkRegistry;\n    this.operators = options.operators;\n  }\n\n  /**\n   * @param repository - Implementation of TechInsightsStore. Used by the returned JsonRulesEngineFactChecker\n   *                     to retrieve fact and fact schema data\n   * @returns JsonRulesEngineFactChecker implementation\n   */\n  construct(repository: TechInsightsStore) {\n    return new JsonRulesEngineFactChecker({\n      checks: this.checks,\n      logger: this.logger,\n      checkRegistry: this.checkRegistry,\n      repository,\n      operators: this.operators,\n    });\n  }\n}\n","/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n  coreServices,\n  createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { techInsightsFactCheckerFactoryExtensionPoint } from '@backstage/plugin-tech-insights-node';\nimport { JsonRulesEngineFactCheckerFactory } from '../service';\n\n/**\n * Sets a JsonRulesEngineFactCheckerFactory as FactCheckerFactory\n * loading checks from the config.\n *\n * @public\n */\nexport const techInsightsModuleJsonRulesEngineFactCheckerFactory =\n  createBackendModule({\n    pluginId: 'tech-insights',\n    moduleId: 'json-rules-engine-fact-checker-factory',\n    register(env) {\n      env.registerInit({\n        deps: {\n          config: coreServices.rootConfig,\n          logger: coreServices.logger,\n          techInsights: techInsightsFactCheckerFactoryExtensionPoint,\n        },\n        async init({ config, logger, techInsights }) {\n          const factory = JsonRulesEngineFactCheckerFactory.fromConfig(config, {\n            logger,\n          });\n          techInsights.setFactCheckerFactory(factory);\n        },\n      });\n    },\n  });\n"]}