mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
139 lines
3.9 KiB
TypeScript
139 lines
3.9 KiB
TypeScript
import { omitKeys } from "bknd/utils";
|
|
import type { EntityManager } from "data/entities";
|
|
import { TransformPersistFailedException } from "../errors";
|
|
import { baseFieldConfigSchema, Field, type TActionContext, type TRenderContext } from "./Field";
|
|
import type { TFieldTSType } from "data/entities/EntityTypescript";
|
|
import { s } from "bknd/utils";
|
|
|
|
export const enumFieldConfigSchema = s
|
|
.strictObject({
|
|
default_value: s.string(),
|
|
options: s.anyOf([
|
|
s.object({
|
|
type: s.literal("strings"),
|
|
values: s.array(s.string()),
|
|
}),
|
|
s.object({
|
|
type: s.literal("objects"),
|
|
values: s.array(
|
|
s.object({
|
|
label: s.string(),
|
|
value: s.string(),
|
|
}),
|
|
),
|
|
}),
|
|
]),
|
|
...omitKeys(baseFieldConfigSchema.properties, ["default_value"]),
|
|
})
|
|
.partial();
|
|
|
|
export type EnumFieldConfig = s.Static<typeof enumFieldConfigSchema>;
|
|
|
|
export class EnumField<Required extends true | false = false, TypeOverride = string> extends Field<
|
|
EnumFieldConfig,
|
|
TypeOverride,
|
|
Required
|
|
> {
|
|
override readonly type = "enum";
|
|
|
|
constructor(name: string, config: Partial<EnumFieldConfig>) {
|
|
super(name, config);
|
|
|
|
if (this.config.default_value && !this.isValidValue(this.config.default_value)) {
|
|
throw new Error(`Default value "${this.config.default_value}" is not a valid option`);
|
|
}
|
|
}
|
|
|
|
protected getSchema() {
|
|
return enumFieldConfigSchema;
|
|
}
|
|
|
|
getOptions(): { label: string; value: string }[] {
|
|
const options = this.config?.options ?? { type: "strings", values: [] };
|
|
|
|
if (options.type === "strings") {
|
|
return options.values?.map((option) => ({ label: option, value: option }));
|
|
}
|
|
|
|
return options?.values;
|
|
}
|
|
|
|
isValidValue(value: string): boolean {
|
|
const valid_values = this.getOptions().map((option) => option.value);
|
|
return valid_values.includes(value);
|
|
}
|
|
|
|
override getValue(value: any, context: TRenderContext) {
|
|
if (!this.isValidValue(value)) {
|
|
return this.hasDefault() ? this.getDefault() : null;
|
|
}
|
|
|
|
switch (context) {
|
|
case "table":
|
|
return this.getOptions().find((option) => option.value === value)?.label ?? value;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Transform value after retrieving from database
|
|
* @param value
|
|
*/
|
|
override transformRetrieve(value: string | null): string | null {
|
|
const val = super.transformRetrieve(value);
|
|
|
|
if (val === null && this.hasDefault()) {
|
|
return this.getDefault();
|
|
}
|
|
|
|
if (!this.isValidValue(val)) {
|
|
return this.hasDefault() ? this.getDefault() : null;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
override async transformPersist(
|
|
_value: any,
|
|
em: EntityManager<any>,
|
|
context: TActionContext,
|
|
): Promise<string | undefined> {
|
|
const value = await super.transformPersist(_value, em, context);
|
|
if (this.nullish(value)) return value;
|
|
|
|
if (!this.isValidValue(value)) {
|
|
throw new TransformPersistFailedException(
|
|
`Field "${this.name}" must be one of the following values: ${this.getOptions()
|
|
.map((o) => o.value)
|
|
.join(", ")}`,
|
|
);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
override toJsonSchema() {
|
|
const options = this.config?.options ?? { type: "strings", values: [] };
|
|
const values =
|
|
options.values?.map((option) => (typeof option === "string" ? option : option.value)) ??
|
|
[];
|
|
return this.toSchemaWrapIfRequired(
|
|
s.string({
|
|
enum: values,
|
|
default: this.getDefault(),
|
|
}),
|
|
);
|
|
}
|
|
|
|
override toType(): TFieldTSType {
|
|
const union = this.getOptions().map(({ value }) =>
|
|
typeof value === "string" ? `"${value}"` : value,
|
|
);
|
|
return {
|
|
...super.toType(),
|
|
type: union.length > 0 ? union.join(" | ") : "string",
|
|
};
|
|
}
|
|
}
|