Commit 9f2bf284 authored by nanahira's avatar nanahira

buggy

parent 950956b7
import { ClassType, SchemaOptions } from '../def/interfaces';
import {
ClassType,
RefSym,
SchemaOptions,
SchemaReference,
SchemaType,
} from '../def/interfaces';
import { Metadata } from '../metadata/metadata';
import {
SetTransformer,
......@@ -55,3 +61,12 @@ export function SchemaProperty(options: SchemaOptions = {}): PropertyDecorator {
// for backward compatibility
export const DefineSchema = SchemaProperty;
export function SchemaRef<S = any, T = S>(
factory: () => SchemaType<S, T>,
): SchemaReference<S, T> {
return {
[RefSym]: true,
factory,
};
}
import {
ClassType,
RefSym,
SchemaClassOptions,
SchemaOptions,
SchemaOptionsDict,
SchemaOrReference,
SchemaReference,
SchemaType,
} from '../def';
import Schema from 'schemastery';
import { reflector } from '../metadata/reflector';
......@@ -10,11 +14,11 @@ import _ from 'lodash';
import { Metadata } from '../metadata/metadata';
import { kSchema } from '../utility/kschema';
function getBasePropertySchemaFromOptions(options: SchemaOptions) {
if (options.schema) {
return options.schema;
function resolveSchemaType(schemaType: SchemaType): SchemaOrReference {
if (schemaType && schemaType[RefSym]) {
return schemaType as SchemaReference;
}
switch (options.type as string) {
switch (schemaType as string) {
case 'any':
return Schema.any();
case 'never':
......@@ -28,7 +32,7 @@ function getBasePropertySchemaFromOptions(options: SchemaOptions) {
case 'object':
return Schema.object({}).default({});
default:
return Schema.from(options.type);
return Schema.from(schemaType);
}
}
......@@ -36,8 +40,14 @@ function applyOptionsToSchema(schema: Schema, options: SchemaClassOptions) {
Object.assign(schema.meta, options);
}
function getPropertySchemaFromOptions<PT>(options: SchemaOptions): Schema<PT> {
let schema = getBasePropertySchemaFromOptions(options);
function getPropertySchemaFromOptions<PT>(
options: SchemaOptions,
): SchemaOrReference<PT> {
const _schema = resolveSchemaType(options.type);
if (_schema[RefSym]) {
return _schema as SchemaReference<PT>;
}
let schema = _schema as Schema;
if (options.dict) {
const skeySchema = (
options.dict === true
......@@ -53,10 +63,61 @@ function getPropertySchemaFromOptions<PT>(options: SchemaOptions): Schema<PT> {
return schema;
}
function resolveSchemaReference<S = any, T = S>(ref: SchemaReference<S, T>) {
const value = ref.factory();
if (typeof value === 'function' && !value[kSchema]) {
return SchemaClass(value as { new (...args: any[]): any });
}
return value;
}
function resolvePropertySchemaFromOptions<PT>(
options: SchemaOptions,
): Schema<PT> {
const schema = getPropertySchemaFromOptions<PT>(options);
if (schema[RefSym]) {
return resolvePropertySchemaFromOptions({
...options,
type: (schema as SchemaReference<PT>).factory(),
});
}
return schema as Schema<PT>;
}
function tempDefGet<T, K extends keyof T>(obj: T, key: K, getter: () => T[K]) {
Object.defineProperty(obj, key, {
get: getter,
set: (value: T[K]) => {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
writable: true,
value,
});
},
enumerable: true,
configurable: true,
});
}
function schemasFromDict<T>(dict: SchemaOptionsDict<T>) {
const schemaDict: {
[P in keyof SchemaOptionsDict<T>]: Schema<SchemaOptionsDict<T>[P]>;
[P in keyof SchemaOptionsDict<T>]: SchemaOrReference<
SchemaOptionsDict<T>[P]
>;
} = _.mapValues(dict, (opt) => getPropertySchemaFromOptions(opt));
for (const _key of Object.keys(schemaDict)) {
const key = _key as keyof T;
if (schemaDict[key][RefSym]) {
const schemaOptions = dict[key];
tempDefGet(schemaDict, key, () =>
resolvePropertySchemaFromOptions({
...schemaOptions,
type: resolveSchemaReference(schemaDict[key] as SchemaReference),
}),
);
}
}
return Schema.object(schemaDict);
}
......
......@@ -10,23 +10,36 @@ export type SchemaNativeType =
| 'any'
| 'never';
export type SchemaSource =
export type SchemaSource<S = any, T = S> =
| null
| undefined
| string
| number
| boolean
| Schema
| Schema<S, T>
// eslint-disable-next-line @typescript-eslint/ban-types
| Function
| { new (...args: any[]): any };
export type SchemaType = SchemaNativeType | SchemaSource;
export const RefSym = Symbol('SchemasteryGenRef');
export interface SchemaReference<S = any, T = S> {
[RefSym]: true;
factory: () => SchemaType<S, T>;
}
export type SchemaOrReference<S = any, T = S> =
| Schema<S, T>
| SchemaReference<S, T>;
export type SchemaType<S = any, T = S> =
| SchemaNativeType
| SchemaSource<S, T>
| SchemaReference<S, T>;
export type SchemaClassOptions = Schema.Meta<any>;
export interface SchemaOptions extends SchemaClassOptions {
schema?: Schema<any>;
dict?: boolean | Schema<any, string> | string;
array?: boolean;
type?: SchemaType;
......
import { DefineSchema, RegisterSchema, SchemaRef } from '../src/decorators';
import { kSchema } from '../src/utility/kschema';
import Schema from 'schemastery';
@RegisterSchema()
class Post {
constructor(_: Partial<Post>) {}
@DefineSchema()
name: string;
getName?() {
return this.name;
}
@DefineSchema({
type: SchemaRef(() => Post),
})
child?: Post;
}
describe('Recurse schema', () => {
it('should be a Schema', () => {
expect(Post[kSchema]).toBeDefined();
});
it('child should be Schema', () => {
const childPost = (Post as Schema<Post>).dict.child;
expect(childPost[kSchema]).toBeDefined();
expect(childPost === Post);
});
it('child of child should be Schema', () => {
const childPost = (Post as Schema<Post>).dict.child.dict.child;
expect(childPost[kSchema]).toBeDefined();
expect(childPost === Post);
});
it('should resolve value', () => {
const post = new Post({
name: 'test',
});
expect(post.getName()).toBe('test');
});
it('should be able to recurse', () => {
const post = new Post({
name: 'test',
child: {
name: 'test',
},
});
expect(post.child.getName()).toBe('test');
});
it('should recurse twice', () => {
const post = new Post({
name: 'test',
child: {
name: 'test',
child: {
name: 'test',
},
},
});
expect(post.getName()).toBe('test');
expect(post.child.getName()).toBe('test');
expect(post.child.child.getName()).toBe('test');
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment