Files
zitadel/console/src/app/utils/enum.utils.ts
T
Max PeintnerandGitHub b6a2b7c70e fix(console): Improve SAML SP form typing (#10557)
This PR fixes a problem for the SAML provider in console where the
binding selection was not correctly applied when editing existing
providers

# Which Problems Are Solved

- SAML provider binding selection was not correctly applied when editing
existing providers
- Form used untyped reactive forms leading to potential runtime errors
- Hardcoded enum handling made the code fragile to API changes

# How the Problems Are Solved

- Created reusable utility functions (enum.utils.ts) that properly
convert between numeric enum values (from backend) and string keys (for
form controls)
- Improved type safety: Migrated from
UntypedFormGroup/UntypedFormControl to strongly typed
FormGroup<SAMLProviderForm> with FormControl<T>
2025-08-27 09:29:14 +02:00

85 lines
2.8 KiB
TypeScript

/**
* Utility functions for working with TypeScript enums in Angular forms
*
* Example usage:
* ```typescript
* // Get dropdown options
* const bindingOptions = getEnumKeys(SAMLBinding);
*
* // Convert backend enum values to form-friendly string keys
* const formConfig = convertEnumValuesToKeys(backendConfig, {
* binding: SAMLBinding,
* nameIdFormat: SAMLNameIDFormat
* });
*
* // Find specific enum key
* const defaultBinding = getEnumKeyFromValue(SAMLBinding, SAMLBinding.SAML_BINDING_POST);
* ```
*/
// Type constraint for TypeScript numeric enums
type NumericEnum = Record<string, string | number> & Record<number, string>;
/**
* Get string keys from a TypeScript enum, excluding numeric reverse mappings
* @param enumObject The enum object
* @returns Array of string keys
* @example
* ```typescript
* const bindingOptions = getEnumKeys(SAMLBinding);
* // Returns: ['SAML_BINDING_UNSPECIFIED', 'SAML_BINDING_POST', ...]
* ```
*/
export function getEnumKeys<T extends NumericEnum>(enumObject: T): string[] {
return Object.keys(enumObject).filter((key) => isNaN(Number(key)));
}
/**
* Find the string key for a given numeric enum value
* @param enumObject The enum object
* @param value The numeric enum value
* @returns The corresponding string key or undefined if not found
* @example
* ```typescript
* const key = getEnumKeyFromValue(SAMLBinding, 1);
* // Returns: 'SAML_BINDING_POST'
* ```
*/
export function getEnumKeyFromValue<T extends NumericEnum>(enumObject: T, value: number): string | undefined {
return Object.keys(enumObject).find((key) => enumObject[key] === value && isNaN(Number(key)));
}
/**
* Convert enum values to string keys for form controls
* @param config Object containing enum values to convert
* @param enumMappings Object mapping config property names to enum objects
* @returns Modified config object with string keys
* @example
* ```typescript
* const formConfig = convertEnumValuesToKeys(samlConfig, {
* binding: SAMLBinding,
* nameIdFormat: SAMLNameIDFormat
* });
* // Converts: { binding: 1, nameIdFormat: 2 }
* // To: { binding: 'SAML_BINDING_POST', nameIdFormat: 'SAML_NAME_ID_FORMAT_PERSISTENT' }
* ```
*/
export function convertEnumValuesToKeys<T extends Record<string, unknown>>(
config: T,
enumMappings: { [K in keyof Partial<T>]: NumericEnum },
): T {
const converted = { ...config };
for (const [propertyName, enumObject] of Object.entries(enumMappings)) {
const typedPropertyName = propertyName as keyof T;
if (converted[typedPropertyName] !== undefined && typeof converted[typedPropertyName] === 'number') {
const key = getEnumKeyFromValue(enumObject, converted[typedPropertyName] as number);
if (key) {
(converted as Record<string, unknown>)[propertyName] = key;
}
}
}
return converted;
}