feat(infra): orm f.enum() support (#9323)

This commit is contained in:
CatsJuice
2024-12-31 03:44:58 +00:00
parent 9c119e6505
commit 7c41775c7f
4 changed files with 36 additions and 6 deletions
@@ -61,6 +61,7 @@ describe('Entity validations', () => {
id: f.string().primaryKey().default(nanoid), id: f.string().primaryKey().default(nanoid),
name: f.string(), name: f.string(),
color: f.string(), color: f.string(),
status: f.enum('active', 'inactive').optional(),
}, },
}); });
@@ -130,4 +131,15 @@ describe('Entity validations', () => {
expect(tag.info).toBe(null); expect(tag.info).toBe(null);
}); });
}); });
test('should throw when trying to create entity with invalid enum value', () => {
const client = createTagsClient();
expect(() =>
// @ts-expect-error test
client.tags.create({ name: 'test', status: 'not-active' })
).toThrow(
"[Table(tags)]: Field 'status' value 'not-active' is not valid. Expected one of [active, inactive]."
);
});
}); });
+8 -3
View File
@@ -1,10 +1,11 @@
export type FieldType = 'string' | 'number' | 'boolean' | 'json'; export type FieldType = 'string' | 'number' | 'boolean' | 'json' | 'enum';
export interface FieldSchema<Type = unknown> { export interface FieldSchema<Type = unknown> {
type: FieldType; type: FieldType;
optional: boolean; optional: boolean;
isPrimaryKey: boolean; isPrimaryKey: boolean;
default?: () => Type; default?: () => Type;
values?: Type[];
} }
export type TableSchema = Record<string, FieldSchema>; export type TableSchema = Record<string, FieldSchema>;
@@ -28,10 +29,12 @@ export class FieldSchemaBuilder<
optional: false, optional: false,
isPrimaryKey: false, isPrimaryKey: false,
default: undefined, default: undefined,
values: undefined,
}; };
constructor(type: FieldType) { constructor(type: FieldType, values?: string[]) {
this.schema.type = type; this.schema.type = type;
this.schema.values = values;
} }
optional() { optional() {
@@ -56,7 +59,9 @@ export const f = {
number: () => new FieldSchemaBuilder<number>('number'), number: () => new FieldSchemaBuilder<number>('number'),
boolean: () => new FieldSchemaBuilder<boolean>('boolean'), boolean: () => new FieldSchemaBuilder<boolean>('boolean'),
json: <T = any>() => new FieldSchemaBuilder<T>('json'), json: <T = any>() => new FieldSchemaBuilder<T>('json'),
} satisfies Record<FieldType, () => FieldSchemaBuilder<any>>; enum: <T extends string>(...values: T[]) =>
new FieldSchemaBuilder<T>('enum', values),
} as const;
export const t = { export const t = {
document: <T extends TableSchemaBuilder>(schema: T) => { document: <T extends TableSchemaBuilder>(schema: T) => {
@@ -70,7 +70,14 @@ export const dataValidators = {
} }
const typeGet = inputType(val); const typeGet = inputType(val);
if (!typeMatches(field.type, typeGet)) {
if (field.type === 'enum') {
if (!field.values?.includes(val)) {
throw new Error(
`[Table(${table.name})]: Field '${key}' value '${val}' is not valid. Expected one of [${field.values?.join(', ')}].`
);
}
} else if (!typeMatches(field.type, typeGet)) {
throw new Error( throw new Error(
`[Table(${table.name})]: Field '${key}' type mismatch. Expected ${field.type} got ${typeGet}.` `[Table(${table.name})]: Field '${key}' type mismatch. Expected ${field.type} got ${typeGet}.`
); );
@@ -103,7 +110,13 @@ export const dataValidators = {
} }
const typeGet = inputType(val); const typeGet = inputType(val);
if (!typeMatches(field.type, typeGet)) { if (field.type === 'enum') {
if (!field.values?.includes(val)) {
throw new Error(
`[Table(${table.name})]: Field '${key}' value '${val}' is not valid. Expected one of [${field.values?.join(', ')}].`
);
}
} else if (!typeMatches(field.type, typeGet)) {
throw new Error( throw new Error(
`[Table(${table.name})]: Field '${key}' type mismatch. Expected type '${field.type}' but got '${typeGet}'.` `[Table(${table.name})]: Field '${key}' type mismatch. Expected type '${field.type}' but got '${typeGet}'.`
); );
@@ -26,7 +26,7 @@ export const AFFiNE_WORKSPACE_DB_SCHEMA = {
id: f.string().primaryKey().optional().default(nanoid), id: f.string().primaryKey().optional().default(nanoid),
name: f.string().optional(), name: f.string().optional(),
type: f.string(), type: f.string(),
show: f.string().optional(), show: f.enum('always-show', 'always-hide', 'hide-when-empty').optional(),
index: f.string().optional(), index: f.string().optional(),
icon: f.string().optional(), icon: f.string().optional(),
additionalData: f.json().optional(), additionalData: f.json().optional(),