Skip to content

Commit

Permalink
Type cleanup & fixes for SchemaFieldType combined with custom type keys.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbrich committed May 20, 2024
1 parent 19d15bb commit 2991d83
Show file tree
Hide file tree
Showing 14 changed files with 169 additions and 55 deletions.
44 changes: 23 additions & 21 deletions src/schema/built/ins.ts → src/builtin/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,30 @@
*
*/

import {type SchemaFieldType} from '../field/type';
import {type SchemaFieldType} from '../schema/field/type';

/**
* @category Schemas
*/
export const schemaBuiltIns: SchemaFieldType[] = [
'array',
'bigint',
'BigInt',
'boolean',
'datetime',
'dbl',
'float',
'int',
'iterable',
'json-serialized',
'json',
'null',
'number',
'string',
'time',
'uint',
'undefined',
'url'
];
export function builtinTypes<InputT = unknown>(): SchemaFieldType<InputT>[] {
return [
'array',
'bigint',
'BigInt',
'boolean',
'datetime',
'dbl',
'float',
'int',
'iterable',
'json-serialized',
'json',
'null',
'number',
'string',
'time',
'uint',
'undefined',
'url'
];
}
4 changes: 2 additions & 2 deletions src/custom/schema/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ import {type SchemaVerifyInit} from '../../schema/verify/init';
/**
* @category Schemas - Custom Types
*/
export interface CustomSchemaVerify extends SchemaVerifyInit {
type: string;
export interface CustomSchemaVerify<DataT = unknown> extends SchemaVerifyInit<DataT> {
typeId: string;
}
22 changes: 18 additions & 4 deletions src/custom/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ export class CustomTypes<DataT, InputT extends SchemaData<DataT>, VerifiedT = In
return this.registered.has(id);
}

/**
* Check whether `id` is a registered custom type schema. Doesn't return true
* when `id`is registered with a non-schema.
* @param id
*/
public hasSchema(id: string): boolean {
if (!this.has(id)) {
return false;
Expand All @@ -90,9 +95,14 @@ export class CustomTypes<DataT, InputT extends SchemaData<DataT>, VerifiedT = In
return false;
}

return typeof o?.verify === 'function';
return this.isSchema(o);
}

/**
* Check whether `id` is a registered custom type schema. Doesn't return true
* when `id`is registered with a non-schema.
* @param id
*/
public hasVerifier(id: string): boolean {
if (!this.has(id)) {
return false;
Expand Down Expand Up @@ -173,7 +183,8 @@ export class CustomTypes<DataT, InputT extends SchemaData<DataT>, VerifiedT = In
return false;
}

return true;
const schema = o as Schema<DataT, InputT, VerifiedT>;
return typeof schema?.verify === 'function';
}

public async verifyValue(id: string, type: string, value: unknown, base: Log): Promise<Fate<DataT>> {

Check warning on line 190 in src/custom/types.ts

View workflow job for this annotation

GitHub Actions / build_project (20.x)

'value' is defined but never used. Allowed unused args must match /^_/u

Check warning on line 190 in src/custom/types.ts

View workflow job for this annotation

GitHub Actions / build_project (20.x)

'base' is defined but never used. Allowed unused args must match /^_/u
Expand All @@ -186,15 +197,18 @@ export class CustomTypes<DataT, InputT extends SchemaData<DataT>, VerifiedT = In
public async verifyOnly(init: CustomSchemaVerify): Promise<Fate<VerifiedSchema<DataT>>> {
const fate = new Fate<VerifiedSchema<DataT>>();

const schema = this.getSchema(init.type);
const schema = this.getSchema(init.typeId);

if (!schema) {
return fate.setErrorCode(schemaError('missing_custom_type_schema', init.type));
return fate.setErrorCode(schemaError('missing_schema_typeId', init.typeId));
}

return schema.verifyOnly(init);
}

/**
* Reset all properties to their initial values.
*/
public reset(): void {
this.registered.clear();
}
Expand Down
20 changes: 8 additions & 12 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {isUInt} from './is/uint';
import {isInt} from './is/int';
import {type SchemaFieldData} from './schema/field/data';
import {CustomTypes} from './custom/types';
import {schemaBuiltIns} from './schema/built/ins';
import {builtinTypes} from './builtin/types';
import {valueTypeLabel} from './value/type/label';
import {SchemaPath} from './schema/path';
import {type SchemaVerifyInit} from './schema/verify/init';
Expand Down Expand Up @@ -162,15 +162,11 @@ export class Schema<DataT, InputT extends SchemaData<DataT>, VerifiedT = InputT>
);
}

public isBuiltIn(type: SchemaFieldType): boolean {
if (typeof type !== 'string') {
return false;
}

return schemaBuiltIns.includes(type);
public isBuiltIn(type: SchemaFieldType<InputT>): boolean {
return builtinTypes<InputT>().includes(type);
}

public schemaSupportsType(type: SchemaFieldType): boolean {
public schemaSupportsType(type: SchemaFieldType<InputT>): boolean {
if (this.isBuiltIn(type)) {
return true;
}
Expand All @@ -183,7 +179,7 @@ export class Schema<DataT, InputT extends SchemaData<DataT>, VerifiedT = InputT>
* @param type
* @param value
*/
public valueIsBuiltInType(type: SchemaFieldType, value: unknown): value is DataT {
public valueHasBuiltinType(type: SchemaFieldType<InputT>, value: unknown): value is DataT {
if (typeof type !== 'string') {
return false;
}
Expand Down Expand Up @@ -224,11 +220,11 @@ export class Schema<DataT, InputT extends SchemaData<DataT>, VerifiedT = InputT>
* @param type
* @param value
*/
public async verifyValue(init: SchemaVerifyValue): Promise<Fate<VerifiedField<DataT>>> {
public async verifyValue(init: SchemaVerifyValue<DataT, InputT>): Promise<Fate<VerifiedField<DataT>>> {
const fate = new Fate<VerifiedField<DataT>>();

if (this.isBuiltIn(init.fieldType)) {
if (this.valueIsBuiltInType(init.fieldType, init.value)) {
if (this.valueHasBuiltinType(init.fieldType, init.value)) {
// TODO: Add validation here. Type match does not automatically prove valid content.
fate.data = init.value;
return fate.setSuccess(true);
Expand All @@ -245,7 +241,7 @@ export class Schema<DataT, InputT extends SchemaData<DataT>, VerifiedT = InputT>
if (this.customTypes.hasSchema(init.fieldType) && typeof init.value === 'object') {
return this.customTypes.verifyOnly({
id: init.fieldId,
type: init.fieldType,
typeId: init.fieldType,
data: init.value as SchemaData<DataT>,
path: init.path,
base: init.base,
Expand Down
10 changes: 9 additions & 1 deletion src/schema/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*
*/

import {Ruleset} from '../ruleset';
import {type SchemaFieldData} from './field/data';
import {type SchemaFieldType} from './field/type';

Expand All @@ -32,12 +33,19 @@ import {type SchemaFieldType} from './field/type';
export class SchemaField<InputT = unknown> {
public readonly name: string;
public readonly key: keyof InputT;
public readonly types: SchemaFieldType[];
public readonly types: SchemaFieldType<InputT>[];
public readonly defaultValue: unknown;
public readonly ruleset: Ruleset<InputT>;

constructor(data: SchemaFieldData<InputT>) {
this.key = data.name;
this.name = data.name.toString();
this.ruleset = new Ruleset<InputT>();

if (Array.isArray(data.rules)) {
this.ruleset.add(...data.rules);
}

if (Array.isArray(data.types)) {
this.types = data.types;
} else if (typeof data.types === 'string') {
Expand Down
7 changes: 5 additions & 2 deletions src/schema/field/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@
*
*/

import type {SchemaFieldType} from './type';
import {Block} from '../../block';
import {Statement} from '../../statement';
import {type SchemaFieldType} from './type';

/**
* @category Schemas
*/
export interface SchemaFieldData<InputT = unknown> {
name: keyof InputT;
types: SchemaFieldType | (SchemaFieldType | keyof InputT)[] | keyof InputT[];
types: SchemaFieldType<InputT> | SchemaFieldType<InputT>[];
defaultValue?: unknown;
rules?: Block<Statement<InputT>>[];
}
4 changes: 2 additions & 2 deletions src/schema/field/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
/**
* @category Schemas
*/
export type SchemaFieldType<CustomT = unknown> =
export type SchemaFieldType<InputT = unknown> =
| 'array'
| 'bigint'
| 'BigInt'
Expand All @@ -47,4 +47,4 @@ export type SchemaFieldType<CustomT = unknown> =
| 'undefined'
| 'url'
| 'time'
| keyof CustomT;
| Extract<keyof InputT, string>;
4 changes: 2 additions & 2 deletions src/schema/output/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@

import {Fate} from '@toreda/fate';
import {Log} from '@toreda/log';
import {type VerifiedResult} from '../../verified/schema';
import {type VerifiedSchema} from '../../verified/schema';

/**
* Transforms schema parser output.
*
* @category Schemas
*/
export type SchemaOutputTransformer<DataT, VerifiedT> = (
mapped: VerifiedResult<DataT>,
mapped: VerifiedSchema<DataT>,
base: Log
) => Promise<Fate<VerifiedT | null>>;
4 changes: 2 additions & 2 deletions src/schema/verify/value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ import {type SchemaData} from '../data';
/**
* @category Schemas
*/
export interface SchemaVerifyValue<DataT = unknown> {
export interface SchemaVerifyValue<DataT = unknown, InputT = unknown> {
fieldId: string;
fieldType: SchemaFieldType;
fieldType: SchemaFieldType<InputT>;
path: SchemaPath;
value: unknown | SchemaData<DataT>;
base: Log;
Expand Down
26 changes: 25 additions & 1 deletion tests/_data/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Log} from '@toreda/log';
import {Schema} from '../../src/schema';
import {Primitive} from '@toreda/types';
import {SchemaData} from '../../src/schema/data';
import {SchemaInit} from '../../src';
import {SchemaInit} from '../../src/schema/init';

export interface SampleData extends SchemaData<Primitive> {
str1: string;
Expand Down Expand Up @@ -91,3 +91,27 @@ export class SampleSchema extends Schema<Primitive, SampleData, SampleData> {
});
}
}

export class SampleRulesetSchema extends Schema<Primitive, SampleData, SampleData> {
constructor(init: SchemaInit<Primitive, SampleData, SampleData>) {
super({
name: 'SampleSchema',
fields: [
{
name: 'str1',
types: ['string']
},
{
name: 'int1',
types: ['number']
},
{
name: 'bool1',
types: ['boolean', 'null']
}
],
options: init?.options,
base: init.base
});
}
}
30 changes: 30 additions & 0 deletions tests/custom/types.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import {type Primitive} from '@toreda/types';
import {type CustomTypeVerifier} from '../../src/custom/type/verifier';
import {Fate} from '@toreda/fate';
import {type SchemaInit} from '../../src/schema/init';
import {CustomSchemaVerify} from '../../src/custom/schema/verify';
import {schemaError} from '../../src';

describe('CustomTypes', () => {
let base: Log;
let init: SchemaInit<Primitive, SampleData, SampleData>;
let instance: CustomTypes<Primitive, SampleData, SampleData>;
let sampleSchema: SampleSchema;
let typeVerifier: CustomTypeVerifier<any>;
let verifyInit: CustomSchemaVerify<any>;

beforeAll(() => {
typeVerifier = async (): Promise<Fate<any>> => {
Expand Down Expand Up @@ -51,6 +54,12 @@ describe('CustomTypes', () => {
}
]
};

verifyInit = {
data: {},
base: base,
typeId: 'sampleSchema'
};
});

describe('Constructor', () => {
Expand Down Expand Up @@ -197,5 +206,26 @@ describe('CustomTypes', () => {
expect(result).toEqual(verifier);
});
});

describe('verifyOnly', () => {
it(`should fail with code when init.type is undefined`, async () => {
verifyInit.type = undefined as any;
const result = await instance.verifyOnly(verifyInit);

expect(result.ok()).toBe(false);
expect(result.errorCode()).toBe(schemaError('missing_schema_typeId', 'sampleSchema'));
});
});
});

describe('reset', () => {
it(`should clear all registered types`, () => {
instance.registered.set('aa3', sampleSchema);
instance.registered.set('aa4', sampleSchema);
expect(instance.registered.size).toBe(2);

instance.reset();
expect(instance.registered.size).toBe(0);
});
});
});
6 changes: 3 additions & 3 deletions tests/schema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import {Levels, Log} from '@toreda/log';
import {schemaError} from '../src/schema/error';
import {SchemaField} from '../src/schema/field';
import {valueTypeLabel} from '../src/value/type/label';
import {SampleData, SampleSchema} from './_data/schema';
import {type SampleData, SampleSchema} from './_data/schema';
import {SchemaPath} from '../src/schema/path';
import {SchemaInit} from '../src';
import {Primitive} from '@toreda/types';
import {type SchemaInit} from '../src';
import {type Primitive} from '@toreda/types';

const EMPTY_OBJECT = {};
const EMPTY_STRING = '';
Expand Down
Loading

0 comments on commit 2991d83

Please sign in to comment.