vault/ui/app/utils/version-utils.ts
Vault Automation af07b60f99
[VAULT-33083] support mount external engine (#11659) (#12284)
* [VAULT-33083] support mount external engine

* add "Plugin type" and "Plugin version" fields to the enable mount page

* add changelog

* address copilot review comments

* address PR comments, code cleanup

* fix test failures

* Add support for external plugins registered without a plugin version

* external plugin should be enabled for enterprise only, plugin version should be mandatory for external plugins

* fix tests

* address copilot feedback

* fix failing tests, add unit test coverage

* address PR comments

* address PR comments

* remove dead code

* move no external versions alert

* Only show un-versioned plugin message if there are un-versioned plugins in the catalog.

* address PR comments

* use ApiService instead of custom PluginPinsService; fix failing tests

* revert changes to forms/mount.ts and forms/auth/method.ts

Co-authored-by: Shannon Roberts (Beagin) <beagins@users.noreply.github.com>
2026-02-10 14:18:14 -08:00

130 lines
4 KiB
TypeScript

/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import { isKnownExternalPlugin } from 'vault/utils/external-plugin-helpers';
/**
* Utility functions for semantic version handling
*/
/**
* Clean a version string by removing prefixes and suffixes
* @param version - The version string to clean (e.g., "v1.2.3+ent")
* @returns The cleaned version string (e.g., "1.2.3")
*/
export function cleanVersion(version: string): string {
return version.replace(/^v/, '').split(/[+-]/)[0] || '';
}
/**
* Parse a version string into numeric parts
* @param version - The version string to parse
* @returns Array of numeric version parts
*/
export function parseVersion(version: string): number[] {
const cleanVer = cleanVersion(version);
return cleanVer.split('.').map((n) => parseInt(n) || 0);
}
/**
* Compare two version strings using semantic version rules
* @param a - First version to compare
* @param b - Second version to compare
* @returns Negative if a < b, positive if a > b, 0 if equal
*/
export function compareVersions(a: string, b: string): number {
const aParts = parseVersion(a);
const bParts = parseVersion(b);
const maxLength = Math.max(aParts.length, bParts.length);
for (let i = 0; i < maxLength; i++) {
const aPart = aParts[i] || 0;
const bPart = bParts[i] || 0;
if (aPart !== bPart) {
return aPart - bPart;
}
}
return 0;
}
/**
* Sort an array of version strings in semantic version order
* @param versions - Array of version strings to sort
* @param descending - If true, sort highest version first (default: false)
* @returns New sorted array (does not mutate original)
*/
export function sortVersions(versions: string[], descending = false): string[] {
const sorted = versions.slice().sort((a, b) => compareVersions(a, b));
return descending ? sorted.reverse() : sorted;
}
/**
* Find the highest version from an array of version strings
* @param versions - Array of version strings
* @returns The highest version string, or null if array is empty
*/
export function getHighestVersion(versions: string[]): string | null {
if (versions.length === 0) return null;
const sorted = sortVersions(versions, true);
return sorted[0] || null;
}
/**
* Check if version A is greater than version B
* @param a - First version
* @param b - Second version
* @returns True if a > b
*/
export function isVersionGreater(a: string, b: string): boolean {
return compareVersions(a, b) > 0;
}
/**
* Check if two versions are equal
* @param a - First version
* @param b - Second version
* @returns True if versions are equal
*/
export function areVersionsEqual(a: string, b: string): boolean {
return compareVersions(a, b) === 0;
}
/**
* Check if a version string is valid and non-empty
* @param version - The version string to validate
* @returns True if the version is valid
*/
export function isValidVersion(version: string): boolean {
if (!version || typeof version !== 'string') return false;
const trimmed = version.trim();
if (trimmed === '' || trimmed === 'null') return false;
// Basic semantic version pattern check (allows prefixes like 'v' and suffixes like '+ent')
const semverPattern = /^v?\d+(\.\d+)*([+-].+)?$/;
const cleanVer = cleanVersion(trimmed);
return semverPattern.test(`v${cleanVer}`);
}
/**
* Check if a plugin version is required for external plugins
* @param pluginType - The plugin type (e.g., 'keymgmt' or 'vault-plugin-secrets-keymgmt')
* @param pluginVersion - The plugin version string
* @returns True if the plugin version requirement is satisfied
*/
export function isPluginVersionValidForType(pluginType: string, pluginVersion?: string): boolean {
if (!pluginType) return false;
if (isKnownExternalPlugin(pluginType)) {
// External plugins require a valid version
return isValidVersion(pluginVersion || '');
} else {
// Builtin plugins should not have a version specified
return !pluginVersion || pluginVersion.trim() === '' || pluginVersion.trim() === 'null';
}
}