vault/ui/app/forms/form.ts
Vault Automation 71dee6b2e5
[UI] Ember Data Migration - PKI Configuration (#10328) (#10523)
* [UI] Ember Data Migration - PKI Config Setup (#10320)

* adds api and capabilities services to pki engine

* updates eslintrc to ignore rest siblings for no-unused-vars rule

* adds ember-template-lint to pki engine

* updates check-issuers decorator to use api service

* adds constants for pki capabilities paths

* updates pki configuration route to use api service and fetch capabilities

* [UI] Ember Data Migration - PKI Config Generate Form (#10322)

* updates form class data object to tracked

* adds isNot validator

* updates tsconfig to resolve json modules

* updates open-api form class to use the spec file rather than help response for form field/group generation

* adds pki config generate form

* [UI] Ember Data Migration - PKI Config Create (#10331)

* updates pki configure create route and component

* updates pki generate csr component to use api service and form class

* updates pki generate root component to use api service and form class

* updates pki import bundle component to use api service

* [UI] Ember Data Migration - PKI Config Generate Sub Components (#10332)

* updates pki generate toggle groups component to support form class

* updates pki key parameters component to support form class

* updates pki generate immediate component based on csr component changes

* updates pki generate root component based on root component changes

* more pki config sub component updates

* updates pki issuer rotate root component to use api serivce and form

* updates pki acceptance tests (#10341)

* [UI] Ember Data Migration - PKI Configuration Edit (#10339)

* adds forms for pki config acme, cluster, crl and urls

* updates pki config edit worflow to use api service and forms

* updates pki config details workflow to use api service (#10340)

* updates auth configure section route to pass schema key to OpenApiForm constructor

Co-authored-by: Jordan Reimer <zofskeez@gmail.com>
2025-11-18 09:42:22 -07:00

96 lines
4.0 KiB
TypeScript

/**
* Copyright IBM Corp. 2016, 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 };
}
}