Skip to content

Commit

Permalink
Fixing parsing bugs in sub-schema detection.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbrich committed May 10, 2024
1 parent 5d232fd commit b5af3ba
Show file tree
Hide file tree
Showing 10 changed files with 394 additions and 158 deletions.
18 changes: 13 additions & 5 deletions src/custom/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ import {Schema} from '../schema';
import {type SchemaData} from '../schema/data';
import {type CustomTypesInit} from './types/init';
import {type CustomTypesData} from './types/data';
import {CustomTypeVerifier} from './type/verifier';
import {type CustomTypeVerifier} from './type/verifier';
import {Fate} from '@toreda/fate';
import {schemaError} from '../schema/error';

/**
* @category Schemas - Custom Types
Expand Down Expand Up @@ -138,18 +139,25 @@ export class CustomTypes<DataT, InputT extends SchemaData<DataT>, VerifiedT = In
return schema;
}

public async verifyValue(type: string, value: unknown): Promise<Fate<DataT>> {
public async verifyValue(type: string, value: unknown, base: Log): Promise<Fate<DataT>> {
const fate = new Fate<DataT>();

zzs
const verifier = this.getVerifier(type);
return fate;
}

public async verifySchema(
type: string,
value: unknown | SchemaData<unknown>
value: SchemaData<DataT>,
base: Log
): Promise<Fate<SchemaData<unknown>>> {
const fate = new Fate<SchemaData<unknown>>();
const schema = this.getSchema(type);

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

return schema.verify(value, base);
}
}
1 change: 0 additions & 1 deletion src/custom/types/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
*/

import {Schema} from '../../schema';
import {type SchemaData} from '../../schema/data';

/**
* @category Schemas - Custom Types
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export {simpleOutputTransform} from './simple/output/transform';
export {Statement} from './statement';
export {stringIdVerify} from './string/id/verify';
export {urlVerify} from './url/verify';
export {valueTypeLabel} from './value/type/label';
export {Verified} from './verified';
export {Verifier} from './verifier';
export {VerifierCounts} from './verifier/counts';
Expand Down
60 changes: 36 additions & 24 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {isInt} from './is/int';
import {type SchemaFieldData} from './schema/field/data';
import {CustomTypes} from './custom/types';
import {schemaBuiltIns} from './schema/built/ins';
import {valueTypeLabel} from './value/type/label';

/**
* @category Schemas
Expand Down Expand Up @@ -85,7 +86,8 @@ export class Schema<DataT, InputT extends SchemaData<DataT>, VerifiedT = InputT>
public async verifyField(
name: string,
field: SchemaField<InputT>,
value: unknown
value: unknown,
base: Log
): Promise<Fate<DataT | SchemaData<unknown> | null>> {
const fate = new Fate<DataT | SchemaData<unknown> | null>();

Expand All @@ -97,7 +99,7 @@ export class Schema<DataT, InputT extends SchemaData<DataT>, VerifiedT = InputT>
return fate.setErrorCode(schemaError('missing_field_value', `${this.schemaName}.${name}`));
}

const verified = await this.fieldSupportsValue(field, value);
const verified = await this.fieldSupportsValue(field, value, base);
if (!verified.ok()) {
return fate.setErrorCode(verified.errorCode());
}
Expand All @@ -109,24 +111,25 @@ export class Schema<DataT, InputT extends SchemaData<DataT>, VerifiedT = InputT>

public async fieldSupportsValue(
field: SchemaField<InputT>,
value: unknown
value: unknown,
base: Log
): Promise<Fate<DataT | SchemaData<unknown>>> {
const fate = new Fate<DataT | SchemaData<unknown>>();

if (value === null && !field.types.includes('null')) {
return fate.setErrorCode(
schemaError('unsupported_type:null', `${this.schemaName}.${field.name}`)
schemaError('field_does_not_support_type:null', `${this.schemaName}.${field.name}`)
);
}

for (const type of field.types) {
if (!this.schemaSupportsType(type)) {
return fate.setErrorCode(
schemaError(`unsupported_schema_type:${type}`, `${this.schemaName}.${field.name}`)
schemaError(`field_does_not_support_type:${type}`, `${this.schemaName}.${field.name}`)
);
}

const result = await this.verifyValue(type, value);
const result = await this.verifyValue(type, value, base);

if (result.ok() === true) {
fate.data = result.data;
Expand All @@ -135,7 +138,10 @@ export class Schema<DataT, InputT extends SchemaData<DataT>, VerifiedT = InputT>
}

return fate.setErrorCode(
schemaError(`unsupported_type:${typeof value}`, `${this.schemaName}.${field.name}`)
schemaError(
`field_does_not_support_type:${valueTypeLabel(value)}`,
`${this.schemaName}.${field.name}`
)
);
}

Expand Down Expand Up @@ -203,32 +209,38 @@ export class Schema<DataT, InputT extends SchemaData<DataT>, VerifiedT = InputT>
*/
public async verifyValue(
type: SchemaFieldType,
value: unknown | SchemaData<unknown>
value: unknown | SchemaData<DataT>,
base: Log
): Promise<Fate<DataT | SchemaData<unknown>>> {
const fate = new Fate<DataT | SchemaData<unknown>>();

if (this.valueIsBuiltInType(type, value)) {
// TODO: Add validation here. Type match does not automatically prove valid content.
fate.data = value;
return fate.setSuccess(true);
}

if (!this.customTypes.has(type)) {
return fate.setErrorCode(
schemaError(`unsupported_type:${typeof value}`, `${this.schemaName}.verifyValue`)
);
if (this.isBuiltIn(type)) {
if (this.valueIsBuiltInType(type, value)) {
// TODO: Add validation here. Type match does not automatically prove valid content.
fate.data = value;
return fate.setSuccess(true);
} else {
return fate.setErrorCode(
schemaError(
`field_does_not_support_value_type:${valueTypeLabel(value)}`,
`${this.schemaName}.verifyValue`
)
);
}
}

if (this.customTypes.hasSchema(type)) {
return this.customTypes.verifySchema(type, value);
base.debug(`@@@@@@@@@@@@@@@ TYPE: ${type} // TYPEOF VAL: ${valueTypeLabel(value)}`);
if (this.customTypes.hasSchema(type) && typeof value === 'object') {
return this.customTypes.verifySchema(type, value as SchemaData<DataT>, base);
}

//const custom = await this.customTypes.verify(type, value, this.base);
if (this.customTypes.hasVerifier(type)) {
return this.customTypes.verifyValue(type, value);
return this.customTypes.verifyValue(type, value, base);
}

return fate.setErrorCode(`field_cant_verify_custom_type:${type?.toString()}`);
return fate.setErrorCode(
schemaError(`field_does_not_support_type:${type}`, `${this.schemaName}.verifyValue`)
);
}

public async verify(data: SchemaData<DataT>, base: Log): Promise<Fate<VerifiedT>> {
Expand Down Expand Up @@ -269,7 +281,7 @@ export class Schema<DataT, InputT extends SchemaData<DataT>, VerifiedT = InputT>

for (const [id, field] of this.fields.entries()) {
const name = id.toString();
const verified = await this.verifyField(name, field, data[name]);
const verified = await this.verifyField(name, field, data[name], base);

if (!verified.ok()) {
return fate.setErrorCode(verified.errorCode());
Expand Down
2 changes: 1 addition & 1 deletion src/schema/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@
* @category Schemas
*/
export interface SchemaData<DataT> {
[k: string]: DataT | null;
[k: string]: DataT | SchemaData<DataT> | null;
}
4 changes: 2 additions & 2 deletions src/schema/field/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ import type {SchemaFieldType} from './type';
/**
* @category Schemas
*/
export interface SchemaFieldData<InputT> {
export interface SchemaFieldData<InputT = unknown> {
name: keyof InputT;
types: SchemaFieldType | SchemaFieldType[];
types: SchemaFieldType | (SchemaFieldType | keyof InputT)[] | keyof InputT[];
defaultValue?: unknown;
}
5 changes: 3 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 =
export type SchemaFieldType<CustomT = unknown> =
| 'array'
| 'bigint'
| 'BigInt'
Expand All @@ -46,4 +46,5 @@ export type SchemaFieldType =
| 'uint'
| 'undefined'
| 'url'
| 'time';
| 'time'
| keyof CustomT;
11 changes: 11 additions & 0 deletions src/value/type/label.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export function valueTypeLabel(value: unknown): string {
if (value === null) {
return 'null';
}

if (Array.isArray(value)) {
return 'array';
}

return typeof value;
}
37 changes: 37 additions & 0 deletions tests/schehma/types.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {schemaFieldTypes} from '../../src/schema/field/types';

const EXPECTED_TYPES: string[] = [
'array',
'bigint',
'boolean',
'datetime',
'dbl',
'double',
'float',
'int',
'iterable',
'json-serialized',
'json',
'null',
'number',
'real',
'string',
'time',
'uint',
'undefined',
'url'
];

describe('Schema Field Types (built-in)', () => {
let fieldTypes: Set<string>;

beforeAll(() => {
fieldTypes = new Set<string>(schemaFieldTypes);
});

for (const expectedType of EXPECTED_TYPES) {
it(`should include built-in type '${expectedType}'`, () => {
expect(fieldTypes.has(expectedType)).toBe(true);
});
}
});
Loading

0 comments on commit b5af3ba

Please sign in to comment.