mirror of
https://github.com/tale/headplane.git
synced 2026-07-27 08:08:56 +00:00
104 lines
2.4 KiB
TypeScript
104 lines
2.4 KiB
TypeScript
import canonicals from '~/openapi-canonical-families.json';
|
|
import hashes from '~/openapi-operation-hashes.json';
|
|
import log from '~/utils/log';
|
|
|
|
/**
|
|
* The known API versions based on operation hashes.
|
|
*/
|
|
export type Version = keyof typeof hashes;
|
|
const VERSIONS = Object.keys(hashes) as Version[];
|
|
|
|
/**
|
|
* Detects the closest matching API version using operation hashes.
|
|
* Falls back to the latest known version and emits a warning if unknown.
|
|
*
|
|
* @param observed - A mapping of operation identifiers to their hashes.
|
|
* @returns The detected API version.
|
|
*/
|
|
export function detectApiVersion(
|
|
observed: Record<string, string> | null,
|
|
): Version {
|
|
if (!observed) {
|
|
const latest = VERSIONS.at(-1)!;
|
|
log.warn(
|
|
'api',
|
|
'No operation hashes observed, defaulting to version %s',
|
|
latest,
|
|
);
|
|
return latest;
|
|
}
|
|
|
|
let bestVersion: Version | null = null;
|
|
let bestScore = -1;
|
|
|
|
for (const [version, known] of Object.entries(hashes) as [
|
|
Version,
|
|
Record<string, string>,
|
|
][]) {
|
|
let score = 0;
|
|
for (const [op, hash] of Object.entries(observed)) {
|
|
if (known[op] === hash) score++;
|
|
}
|
|
if (score > bestScore) {
|
|
bestVersion = version;
|
|
bestScore = score;
|
|
}
|
|
}
|
|
|
|
if (!bestVersion || bestScore === 0) {
|
|
const latest = VERSIONS.at(-1)!;
|
|
log.warn(
|
|
'api',
|
|
'Could not determine API version, defaulting to %s',
|
|
latest,
|
|
);
|
|
return latest;
|
|
}
|
|
|
|
if (bestScore < Object.keys(observed).length) {
|
|
log.warn(
|
|
'api',
|
|
'Partial version match: %d/%d endpoints for version %s',
|
|
bestScore,
|
|
Object.keys(observed).length,
|
|
bestVersion,
|
|
);
|
|
}
|
|
|
|
const canonical = Object.entries(canonicals).find(([_, family]) =>
|
|
family.includes(bestVersion),
|
|
)?.[0] as Version | undefined;
|
|
|
|
if (!canonical) {
|
|
log.warn(
|
|
'api',
|
|
'Could not canonicalize detected version %s, using as-is',
|
|
bestVersion,
|
|
);
|
|
|
|
return bestVersion;
|
|
}
|
|
|
|
if (canonical !== bestVersion) {
|
|
log.info(
|
|
'api',
|
|
'Canonicalizing detected version %s → %s (same schema)',
|
|
bestVersion,
|
|
canonical,
|
|
);
|
|
}
|
|
|
|
return canonical;
|
|
}
|
|
|
|
/**
|
|
* Checks if the current version is at least the baseline version.
|
|
*
|
|
* @param current - The current API version.
|
|
* @param baseline - The baseline API version to compare against.
|
|
* @returns True if current is at least baseline, false otherwise.
|
|
*/
|
|
export function isAtLeast(current: Version, baseline: Version) {
|
|
return VERSIONS.indexOf(current) >= VERSIONS.indexOf(baseline);
|
|
}
|