mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-13 08:36:45 +02:00
96 lines
4.0 KiB
TypeScript
96 lines
4.0 KiB
TypeScript
/**
|
|
* Copyright IBM Corp. 2026, 2025
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
import { validate } from 'vault/utils/forms/validate';
|
|
import { set } from '@ember/object';
|
|
import { TrackedObject } from 'tracked-built-ins';
|
|
|
|
import type { Validations } from 'vault/app-types';
|
|
import type FormField from 'vault/utils/forms/field';
|
|
import type FormFieldGroup from 'vault/utils/forms/field-group';
|
|
|
|
export type FormOptions = {
|
|
isNew?: boolean;
|
|
};
|
|
|
|
export default class Form<T extends object> {
|
|
declare data: T;
|
|
declare validations: Validations;
|
|
declare isNew: boolean;
|
|
|
|
// used by proxy to determine if the property being accessed is a form field
|
|
// override these in subclasses to define additional/different fields defined on the class
|
|
fieldProps = ['formFields'];
|
|
fieldGroupProps = ['formFieldGroups'];
|
|
|
|
constructor(data: Partial<T> = {}, options: FormOptions = {}, validations?: Validations) {
|
|
this.data = new TrackedObject(data) as T;
|
|
this.isNew = options.isNew || false;
|
|
// typically this would be defined on the subclass
|
|
// if validations are conditional, it may be preferable to define them during instantiation
|
|
if (validations) {
|
|
this.validations = validations;
|
|
}
|
|
// to ease migration from Ember Data Models, return a proxy that forwards get/set to the data object for form field props
|
|
// this allows for form field properties to be accessed directly on the class rather than form.data.someField
|
|
const proxyTarget = (target: this, prop: string) => {
|
|
try {
|
|
// check if the property that is being accessed is a form field
|
|
const fields = this.fieldProps.reduce((fields: FormField[], prop) => {
|
|
const formFields = target[prop as keyof this];
|
|
if (Array.isArray(formFields)) {
|
|
fields.push(...formFields);
|
|
}
|
|
return fields;
|
|
}, []);
|
|
// in the case of formFieldGroups we need extract the fields out into a flat array
|
|
const groupFields = this.fieldGroupProps.reduce((groupFields: FormField[], prop) => {
|
|
const formFieldGroups = target[prop as keyof this];
|
|
if (Array.isArray(formFieldGroups)) {
|
|
const fields = formFieldGroups.reduce((arr: FormField[], group: FormFieldGroup) => {
|
|
const values = Object.values(group)[0] || [];
|
|
return [...arr, ...values];
|
|
}, []);
|
|
groupFields.push(...fields);
|
|
}
|
|
return groupFields;
|
|
}, []);
|
|
// combine the formFields and formGroupFields into a single array
|
|
const allFields = [...fields, ...groupFields];
|
|
const formDataKeys = allFields.map((field) => field.name) || [];
|
|
// if the property is a form field return the data object as the target, otherwise return the original target (this)
|
|
// account for nested form data properties like 'config.maxLeaseTtl' when accessing the object like this.config
|
|
const isDataProp = formDataKeys.some((key) => key === prop || key.split('.').includes(prop));
|
|
return !Reflect.has(target, prop) && isDataProp ? target.data : target;
|
|
} catch (e) {
|
|
// if this fails for any reason return the target object
|
|
return target;
|
|
}
|
|
};
|
|
|
|
return new Proxy(this, {
|
|
get(target, prop: string) {
|
|
return Reflect.get(proxyTarget(target, prop), prop);
|
|
},
|
|
set(target, prop: string, value) {
|
|
return Reflect.set(proxyTarget(target, prop), prop, value);
|
|
},
|
|
});
|
|
}
|
|
|
|
// shim this for now but get away from old Ember patterns!
|
|
set(key = '', val: unknown) {
|
|
set(this, key, val);
|
|
}
|
|
|
|
// when overriding in subclass, data can be passed in if serialization of certain values is required
|
|
// this prevents the underlying data object from being mutated causing potential issues in the view
|
|
toJSON(data = this.data) {
|
|
// validate the form
|
|
// if validations are not defined the util will ignore and return valid state
|
|
const formValidation = validate(data, this.validations);
|
|
return { ...formValidation, data };
|
|
}
|
|
}
|