UNPKG

@secustor/backstage-plugin-renovate-backend

Version:
256 lines (250 loc) 9.11 kB
'use strict'; var backendPluginApi = require('@backstage/backend-plugin-api'); var types = require('./types.cjs.js'); var is = require('@sindresorhus/is'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } var is__default = /*#__PURE__*/_interopDefaultCompat(is); const migrationsDir = backendPluginApi.resolvePackagePath( "@secustor/backstage-plugin-renovate-backend", "migrations" ); class DatabaseHandler { constructor(client, logger) { this.client = client; this.logger = logger; } static async create(options) { const { database, logger } = options; const client = await database.getClient(); if (!database.migrations?.skip) { await client.migrate.latest({ directory: migrationsDir }); } return new DatabaseHandler(client, logger); } async addReport(options) { const { runID, taskID, report, target } = options; const logger = options.logger ?? this.logger; const timestamp = /* @__PURE__ */ new Date(); const inserts = []; for (const [repository, value] of Object.entries(report.repositories)) { inserts.push({ run_id: runID, task_id: taskID, timestamp, host: target.host, repository, report: value }); } await this.client("reports").insert(inserts).catch((reason) => logger.error("Failed insert data", reason)); await this.updateDependencies(timestamp, options); } async getReports(query) { const builder = this.client.select(); if (query) { builder.where(query); } const rows = await builder.from("reports"); return rows.map((row) => { return { runID: row.run_id, taskID: row.task_id, timestamp: row.timestamp, host: row.host, repository: row.repository, // if the JSON field has not been auto-parsed do it manually report: is__default.default.string(row.report) ? JSON.parse(row.report) : row.report }; }); } async getTargets(table = "reports") { return this.client.select().distinct("host", "repository").from(table); } async deleteReportsByTarget({ host, repository }, options) { const offset = getOffset(options); const toBeDeletedIDs = this.client("reports").select("run_id").where("host", host).andWhere("repository", repository).orderBy("timestamp", "DESC").offset(offset); return this.client("reports").delete().whereIn("run_id", [toBeDeletedIDs]); } async deleteReports(options) { const targets = await this.getTargets(); const modified = await Promise.all( targets.map((target) => this.deleteReportsByTarget(target, options)) ); return modified.reduce((a, b) => a + b, 0); } async updateDependencies(timestamp, options) { const { runID, report, target } = options; const dependencies = []; for (const [repository, repositoryContent] of Object.entries( report.repositories )) { for (const [manager, packageFiles] of Object.entries( repositoryContent.packageFiles )) { for (const packageFile of packageFiles) { const packageFilePath = packageFile.packageFile; for (const dependency of packageFile.deps) { const { packageName, depName, depType, datasource, currentValue, currentVersion, skipReason, registryUrl, sourceUrl, currentVersionTimestamp } = dependency; const massagedDepName = depName ?? packageName; if (!massagedDepName) { continue; } dependencies.push({ run_id: runID, host: target.host, extractionTimestamp: timestamp, repository, manager, datasource: datasource ?? packageFile.datasource ?? "no-datasource", depName: massagedDepName, packageName, packageFile: packageFilePath, depType, currentValue, currentVersion, currentVersionTimestamp, skipReason, registryUrl, sourceUrl }); } } } } await this.client("dependencies").insert(dependencies); } async getDependencies(filters, pagination) { const page = pagination?.page ?? 0; const pageSize = pagination?.pageSize ?? 500; const builder = this.client("dependencies").select(); this.applyDependencyFilters(builder, filters); const total = await this.getDependenciesCount(filters); const offset = page * pageSize; return { result: await builder.offset(offset).limit(pageSize), total, page, pageSize, pageCount: Math.ceil(total / pageSize) }; } async getDependenciesCount(filters) { const builder = this.client("dependencies").count({ count: "*" }); this.applyDependencyFilters(builder, filters); const count = await builder.first().then((result) => result?.count); if (is__default.default.string(count)) { return Number.parseInt(count, 10); } return count ?? 0; } applyDependencyFilters(builder, filters) { if (filters.host) { builder.whereIn("host", filters.host); } if (filters.repository) { builder.whereIn("repository", filters.repository); } if (filters.manager) { builder.whereIn("manager", filters.manager); } if (filters.datasource) { builder.whereIn("datasource", filters.datasource); } if (filters.depName) { builder.whereIn("depName", filters.depName); } if (filters.depType) { builder.whereIn("depType", filters.depType); } if (filters.latestOnly) { const runIDs = this.client("dependencies").select("d.run_id").from("dependencies as d").join( this.client("dependencies").select("host", "repository").max("extractionTimestamp as max_timestamp").groupBy("host", "repository").as("max_d"), // eslint-disable-next-line func-names function() { this.on("d.host", "=", "max_d.host").andOn("d.repository", "=", "max_d.repository").andOn("d.extractionTimestamp", "=", "max_d.max_timestamp"); } ).distinct(); builder.whereIn("run_id", runIDs); } } /** * Gets the available values for the dependencies stored in the database. * If filters are supplied, OTHER values are filtered accordingly, if a filter is supplied, all values are returned * @param filters */ async getDependenciesValues(filters) { const baseBuilder = this.client( "dependencies" ); const limitedValuesBuilder = baseBuilder.clone(); const allValuesKeys = []; const limitedValuesKeys = []; for (const filterKey of types.DependencyValueFiltersKeys) { const suppliedFilter = filters?.[filterKey]; if (suppliedFilter) { limitedValuesBuilder.whereIn(filterKey, suppliedFilter); limitedValuesKeys.push(filterKey); continue; } allValuesKeys.push(filterKey); } const result = { datasource: [], manager: [], depType: [], depName: [], host: [], packageFile: [], repository: [] }; const allValues = allValuesKeys.map( async (filterKey) => { const values = await limitedValuesBuilder.clone().select(filterKey).distinct().pluck(filterKey); result[filterKey] = values.filter(is__default.default.string); } ); const limitedValues = limitedValuesKeys.map(async (filterKey) => { const values = await baseBuilder.clone().select(filterKey).distinct().pluck(filterKey); result[filterKey] = values.filter(is__default.default.string); }); await Promise.all([...allValues, ...limitedValues]); return result; } async deleteDependencies(options) { const targets = await this.getTargets("dependencies"); const modified = await Promise.all( targets.map((target) => this.deleteDependenciesByTarget(target, options)) ); return modified.reduce((a, b) => a + b, 0); } async deleteDependenciesByTarget({ host, repository }, options) { const offset = getOffset(options); const dependencies = this.client("dependencies").select("run_id", "extractionTimestamp").distinct("host", "repository").where("host", host).andWhere("repository", repository).as("runs"); const toBeDeletedIDs = this.client(dependencies).select("run_id").orderBy("extractionTimestamp", "DESC").offset(offset); return this.client("dependencies").delete().whereIn("run_id", [toBeDeletedIDs]); } } function getOffset(options) { let offset = 0; if (is__default.default.nullOrUndefined(options?.keepLatest) || is__default.default.boolean(options?.keepLatest)) { offset = options?.keepLatest ? 1 : 0; } else { offset = options.keepLatest; } return offset; } exports.DatabaseHandler = DatabaseHandler; //# sourceMappingURL=databaseHandler.cjs.js.map