0
0
Fork 0
mirror of https://github.com/renovatebot/renovate.git synced 2025-05-14 16:32:26 +00:00
renovatebot_renovate/lib/modules/manager/custom/jsonata/utils.ts
RahulGautamSingh fc8b8f9b61
feat(managers/custom): generic manager for json files (#32784)
Co-authored-by: Rhys Arkins <rhys@arkins.net>
Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>
2025-01-30 11:52:39 +00:00

110 lines
3.4 KiB
TypeScript

import is from '@sindresorhus/is';
import jsonata from 'jsonata';
import { migrateDatasource } from '../../../../config/migrations/custom/datasource-migration';
import { logger } from '../../../../logger';
import * as template from '../../../../util/template';
import { parseUrl } from '../../../../util/url';
import type { PackageDependency } from '../../types';
import type { ValidMatchFields } from '../utils';
import { checkIsValidDependency, validMatchFields } from '../utils';
import { QueryResultZodSchema } from './schema';
import type { JSONataManagerTemplates, JsonataExtractConfig } from './types';
export async function handleMatching(
json: unknown,
packageFile: string,
config: JsonataExtractConfig,
): Promise<PackageDependency[]> {
let results: Record<string, string>[] = [];
const { matchStrings: jsonataQueries } = config;
for (const query of jsonataQueries) {
// won't fail as this is verified during config validation
const jsonataExpression = jsonata(query);
// this does not throw error, just returns undefined if no matches
const queryResult = await jsonataExpression.evaluate(json);
// allows empty dep object cause templates can be used to configure the required fields
// if some issues arise then the isValidDependency call will catch them later on
if (!queryResult || is.emptyArray(queryResult)) {
logger.debug(
{
jsonataQuery: query,
packageFile,
},
'The jsonata query returned no matches. Possible error, please check your query. Skipping',
);
return [];
}
const parsed = QueryResultZodSchema.safeParse(queryResult);
if (parsed.success) {
results = results.concat(parsed.data);
} else {
logger.warn(
{ err: parsed.error, jsonataQuery: query, packageFile, queryResult },
'Query results failed schema validation',
);
return [];
}
}
return results
.map((dep) => createDependency(dep, config))
.filter(is.truthy)
.filter((dep) =>
checkIsValidDependency(dep, packageFile, 'custom.jsonata'),
);
}
export function createDependency(
queryResult: Record<string, string>,
config: JsonataExtractConfig,
): PackageDependency | null {
const dependency: PackageDependency = {};
for (const field of validMatchFields) {
const fieldTemplate = `${field}Template` as keyof JSONataManagerTemplates;
const tmpl = config[fieldTemplate];
if (tmpl) {
try {
const compiled = template.compile(tmpl, queryResult, false);
updateDependency(field, compiled, dependency);
} catch {
logger.debug(
{ template: tmpl },
'Error compiling template for JSONata manager',
);
return null;
}
} else if (queryResult[field]) {
updateDependency(field, queryResult[field], dependency);
}
}
return dependency;
}
function updateDependency(
field: ValidMatchFields,
value: string,
dependency: PackageDependency,
): PackageDependency {
switch (field) {
case 'registryUrl': {
const url = parseUrl(value)?.toString();
if (!url) {
logger.debug({ url: value }, 'Invalid JSONata manager registryUrl');
break;
}
dependency.registryUrls = [url];
break;
}
case 'datasource':
dependency.datasource = migrateDatasource(value);
break;
default:
dependency[field] = value;
break;
}
return dependency;
}