Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(framework): add class-validator support #6945

Open
wants to merge 87 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
369ffaf
feat: add class-validator support
paulwer Nov 4, 2024
19c03c2
Merge branch 'next' into feat-package-class-validator-support
paulwer Nov 4, 2024
a16f147
Merge branch 'next' into feat-package-class-validator-support
paulwer Nov 5, 2024
1016672
Merge branch 'next' into feat-package-class-validator-support
rifont Nov 8, 2024
c2c0512
Update packages/framework/src/validators/class-validator.validator.ts
paulwer Nov 8, 2024
f343a9d
fixes
paulwer Nov 8, 2024
b91fe48
Merge branch 'next' into feat-package-class-validator-support
paulwer Nov 8, 2024
8662b2b
workaround for classValidator defaultStorage
paulwer Nov 8, 2024
df9128e
Update packages/framework/src/validators/class-validator.validator.ts
paulwer Nov 8, 2024
86bd4f9
fixes
paulwer Nov 8, 2024
5d2b8e9
Merge remote-tracking branch 'refs/remotes/origin/feat-package-class-…
paulwer Nov 8, 2024
2b8f000
Merge branch 'next' into feat-package-class-validator-support
paulwer Nov 8, 2024
f74470a
refactor(types): replace any with unknown in T_Controls
rifont Nov 8, 2024
e7f3ef8
Update packages/framework/src/validators/class-validator.validator.ts
rifont Nov 8, 2024
c5daec5
refactor(validator): simplify metadata storage import
rifont Nov 8, 2024
255115c
docs(validator): update TODO comment with @see link
rifont Nov 8, 2024
68e5c9c
test(validation): update tests for validation changes
rifont Nov 8, 2024
ecec05d
refactor(types): update class type inference logic
rifont Nov 8, 2024
836bde3
test: remove JSON stringify from test assertion
rifont Nov 8, 2024
f3decf0
docs(schema.types): update ClassType type description
rifont Nov 8, 2024
ed7c857
refactor(class-validator): improve type checking logic
rifont Nov 8, 2024
322d118
test(validator): extract class-validator fixtures
rifont Nov 8, 2024
7fb6368
Merge branch 'next' into feat-package-class-validator-support
rifont Nov 8, 2024
b432668
refactor: Remove IsOptional from name property
rifont Nov 8, 2024
11210c4
Merge branch 'feat-package-class-validator-support' of https://github…
rifont Nov 8, 2024
76de6fa
refactor(schema): remove zod dependency from schemas
rifont Nov 10, 2024
3eb118a
docs(import.types): clarify dependency import comment
rifont Nov 10, 2024
8049177
refactor(validators): update type imports and schema names
rifont Nov 10, 2024
0d0b31b
refactor(types): update import to use 'type' keyword
rifont Nov 10, 2024
15b9ab4
docs(zod): add comment for ZodSchema type
rifont Nov 10, 2024
ff1577e
fix(framework): Add dependency check import utilities
rifont Nov 10, 2024
dac4ef8
refactor(zod): simplify import and type usage
rifont Nov 11, 2024
34f9add
Merge branch 'import-utils-refactor-schemas' into feat-package-class-…
rifont Nov 11, 2024
d5c304b
Update packages/framework/src/types/schema.types/zod.schema.types.ts
rifont Nov 11, 2024
1281a14
Update packages/framework/src/client.test.ts
rifont Nov 11, 2024
d29556d
Update packages/framework/package.json
rifont Nov 11, 2024
0ab32ec
Update packages/framework/src/validators/class-validator.validator.ts
rifont Nov 11, 2024
5d76360
refactor(json-schema): update JSONSchema imports
rifont Nov 11, 2024
0c8857d
docs(json-schema-dto): update comment for clarity
rifont Nov 11, 2024
dbf050e
Merge branch 'import-utils-refactor-schemas' into feat-package-class-…
rifont Nov 11, 2024
37bc8bc
Update packages/framework/src/validators/validator.test.ts
rifont Nov 11, 2024
2b9c81f
refactor(json-schema-dto): simplify JSON schema types
rifont Nov 11, 2024
522d59a
refactor(api): replace json-schema with JSONSchemaDto
rifont Nov 11, 2024
8c24423
refactor(schema): update JSONSchema to JSONSchemaDefinition
rifont Nov 11, 2024
d35de47
Update apps/dashboard/package.json
rifont Nov 11, 2024
4aaa275
refactor: replace JsonSchema with JSONSchemaDto
rifont Nov 11, 2024
6d56c0a
Merge branch 'import-utils-refactor-schemas' of ssh://github.com/novu…
rifont Nov 11, 2024
2e5c3b5
refactor(json-schema-dto): use readonly types for safety
rifont Nov 11, 2024
6847ef5
Merge branch 'next' into import-utils-refactor-schemas
rifont Nov 11, 2024
7c2736e
refactor(api): remove json-schema-to-ts dependency
rifont Nov 11, 2024
e353b7e
Merge branch 'jsonschema-dto-everywhere' into import-utils-refactor-s…
rifont Nov 11, 2024
0fb4a20
refactor(types): refactor JSON schema type definitions
rifont Nov 11, 2024
ed7ab69
Merge branch 'jsonschema-dto-everywhere' into import-utils-refactor-s…
rifont Nov 11, 2024
c1bf7ff
chore: add 'combinators' to cspell.json dictionary
rifont Nov 11, 2024
fd340d9
Merge branch 'next' into jsonschema-dto-everywhere
rifont Nov 11, 2024
d28d16b
Merge branch 'jsonschema-dto-everywhere' into import-utils-refactor-s…
rifont Nov 11, 2024
b918978
Merge branch 'import-utils-refactor-schemas' into feat-package-class-…
rifont Nov 11, 2024
e663960
Merge branch 'next' into import-utils-refactor-schemas
rifont Nov 11, 2024
7e31412
Merge branch 'import-utils-refactor-schemas' of ssh://github.com/novu…
rifont Nov 11, 2024
4140bda
Merge branch 'import-utils-refactor-schemas' into feat-package-class-…
rifont Nov 11, 2024
d54f009
Merge branch 'next' into feat-package-class-validator-support
rifont Nov 12, 2024
d7ed75f
Update packages/framework/src/resources/workflow/discover-custom-step…
rifont Nov 12, 2024
229b530
Update packages/framework/src/types/schema.types/base.schema.types.ts
rifont Nov 12, 2024
41d8ff3
feat: add enum tests
paulwer Nov 12, 2024
f146791
feat: support nested array structures
paulwer Nov 12, 2024
deb1a7c
Merge branch 'feat-package-class-validator-support' of https://github…
paulwer Nov 12, 2024
c39591f
fixes
paulwer Nov 12, 2024
db76adb
chore: cleanup namings for fixtures & add tests for simple UnionTypes
paulwer Nov 12, 2024
42f85a6
Merge branch 'next' into feat-package-class-validator-support
paulwer Nov 12, 2024
d9c952c
fixes
paulwer Nov 12, 2024
771a6c5
Merge branch 'next' into feat-package-class-validator-support
paulwer Nov 27, 2024
8705bd8
Merge branch 'next' into feat-package-class-validator-support
scopsy Dec 2, 2024
bb0e949
feat: cspell
scopsy Dec 2, 2024
1c7dc73
test: Add tests for enhanced type inference system
rifont Dec 2, 2024
2c1a969
Merge branch 'feat-package-class-validator-support' of https://github…
rifont Dec 2, 2024
52886b1
refactor(workflow): improve data type coercion
rifont Dec 2, 2024
bb552b8
refactor(utils): relocate checkIsResponseError function
rifont Dec 3, 2024
52e7084
refactor(types): update test and stringify logic
rifont Dec 3, 2024
d14be75
build: exclude shared from circular check
rifont Dec 3, 2024
44a390f
Merge branch 'next' into feat-package-class-validator-support
rifont Dec 3, 2024
53dad23
refactor(schema): update SchemaError message wording
rifont Dec 3, 2024
905f6a5
docs: Fix typos in schema inference comments
rifont Dec 3, 2024
49cc45d
style(validator): fix import extensions warnings
rifont Dec 3, 2024
7beb058
Merge branch 'next' into feat-package-class-validator-support
rifont Dec 4, 2024
48a81af
Merge branch 'next' into feat-package-class-validator-support
rifont Dec 6, 2024
d4d8812
Merge branch 'next' of https://github.com/novuhq/novu into feat-packa…
paulwer Jan 30, 2025
f8fd20c
Merge branch 'next' into feat-package-class-validator-support
paulwer Feb 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/framework/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,13 @@
"@sveltejs/kit": ">=1.27.3",
"@vercel/node": ">=2.15.9",
"aws-lambda": ">=1.0.7",
"class-transformer": ">=0.5.1",
"class-validator": ">=0.14.0",
"class-validator-jsonschema": ">=5.0.0",
"express": ">=4.19.2",
"h3": ">=1.8.1",
"next": ">=12.0.0",
"reflect-metadata": ">=0.2.2",
"zod": ">=3.0.0",
"zod-to-json-schema": ">=3.0.0"
},
Expand Down Expand Up @@ -201,6 +205,18 @@
"next": {
"optional": true
},
"class-transformer": {
"optional": true
},
"class-validator": {
"optional": true
},
"class-validator-jsonschema": {
"optional": true
},
"reflect-metadata": {
"optional": true
},
rifont marked this conversation as resolved.
Show resolved Hide resolved
"zod": {
"optional": true
},
Expand All @@ -219,11 +235,15 @@
"@types/sanitize-html": "2.11.0",
"@vercel/node": "^2.15.9",
"aws-lambda": "^1.0.7",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"class-validator-jsonschema": "^5.0.1",
"express": "^4.19.2",
"h3": "^1.11.1",
"madge": "^8.0.0",
"next": "^13.5.4",
"prettier": "^3.2.5",
"reflect-metadata": "^0.2.2",
"ts-node": "^10.9.2",
"tsup": "^8.0.2",
"tsx": "4.16.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
StepType,
StepOutput,
StepOptions,
FromSchema,
rifont marked this conversation as resolved.
Show resolved Hide resolved
Schema,
} from '../../types';
import { transformSchema } from '../../validators';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ describe('FromSchema', () => {

expectTypeOf<FromSchema<typeof testZodSchema>>().toEqualTypeOf<{ foo: string; bar?: string }>();
});

it('should infer a Class Schema type', () => {
class TestSchema {
foo: string = 'bar';
bar?: string;
}

expectTypeOf<FromSchema<typeof TestSchema>>().toEqualTypeOf<{ foo: string; bar?: string }>();
});
});

describe('FromSchemaUnvalidated', () => {
Expand Down Expand Up @@ -74,4 +83,13 @@ describe('FromSchemaUnvalidated', () => {

expectTypeOf<FromSchemaUnvalidated<typeof testZodSchema>>().toEqualTypeOf<{ foo?: string; bar?: string }>();
});

it('should infer a Class Schema type', () => {
class TestClassSchema {
foo?: string = 'bar';
bar?: string;
}

expectTypeOf<FromSchemaUnvalidated<typeof TestClassSchema>>().toEqualTypeOf<{ foo?: string; bar?: string }>();
});
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { InferJsonSchema, JsonSchemaMinimal } from './json.schema.types';
import type { InferZodSchema, ZodSchemaMinimal } from './zod.schema.types';
import type { InferClassValidatorSchema, ClassValidatorSchema } from './class.schema.types';

/**
* A schema used to validate a JSON object.
*/
export type Schema = JsonSchemaMinimal | ZodSchemaMinimal;
export type Schema = JsonSchemaMinimal | ZodSchemaMinimal | ClassValidatorSchema;

/**
* Main utility type for schema inference
Expand All @@ -14,6 +15,8 @@ export type Schema = JsonSchemaMinimal | ZodSchemaMinimal;
*/
type InferSchema<T extends Schema, Options extends { validated: boolean }> =
| InferJsonSchema<T, Options>
| InferZodSchema<T, Options>
rifont marked this conversation as resolved.
Show resolved Hide resolved
| InferClassValidatorSchema<T, Options>
| InferZodSchema<T, Options>;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { describe, expectTypeOf, it } from 'vitest';
import { InferClassValidatorSchema } from './class.schema.types';

describe('ClassSchema types', () => {
class TestSchema {
foo: string = 'bar';
bar?: string;
}

describe('validated data', () => {
it('should compile when the expected properties are provided', () => {
expectTypeOf<InferClassValidatorSchema<typeof TestSchema, { validated: true }>>().toEqualTypeOf<{
foo: string;
bar?: string;
}>();
});

it('should not compile when the schema is not a ClassSchema', () => {
expectTypeOf<InferClassValidatorSchema<string, { validated: true }>>().toEqualTypeOf<never>();
});

it('should not compile when a property does not match the expected type', () => {
// @ts-expect-error - Type 'number' is not assignable to type 'string'.
expectTypeOf<InferClassValidatorSchema<typeof TestSchema, { validated: true }>>().toEqualTypeOf<{
foo: number;
}>();
});
});

describe('unvalidated data', () => {
/**
* TODO: Support accessing defaulted properties when Typescript supports it.
*/
it.skip('should keep the defaulted properties optional', () => {
// @ts-expect-error - Type 'undefined' is not assignable to type 'string'.
expectTypeOf<InferClassValidatorSchema<typeof TestSchema, { validated: false }>>().toEqualTypeOf<{
foo?: string;
bar?: string;
}>();
});
});
});
44 changes: 44 additions & 0 deletions packages/framework/src/types/schema.types/class.schema.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Prettify } from '../util.types';

/**
* A type that represents a class.
*/
export type ClassValidatorSchema<T = unknown> = new (...args: unknown[]) => T;

/**
* Extract the properties of a class type.
*/
export type ClassPropsInfer<T extends ClassValidatorSchema> =
T extends ClassValidatorSchema<infer R> ? Prettify<R> : never;

/**
* Infer the data type of a ClassValidatorSchema.
*
* @param T - The ClassValidatorSchema to infer the data type of.
* @param Options - Configuration options for the type inference. The `validated` flag determines whether the schema has been validated. If `validated` is true, all properties are required unless specified otherwise. If false, properties with default values are optional.
*
* @returns The inferred type.
*
* @example
* ```ts
* class MySchema {
* @IsString()
* @IsNotEmpty()
* name: string;
*
* @IsEmail()
* @IsOptional()
* email?: string;
* }
*
* // has type { name: string, email?: string }
* type MySchema = InferClassValidatorSchema<typeof MySchema, { validated: true }>;
* ```
*/
export type InferClassValidatorSchema<T, Options extends { validated: boolean }> = T extends ClassValidatorSchema
? Options['validated'] extends true
? ClassPropsInfer<T>
: // ClassSchema doesn't support default properties, so the resulting type
// will not have default properties set to optional.
ClassPropsInfer<T>
: never;
1 change: 1 addition & 0 deletions packages/framework/src/types/schema.types/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export type { JsonSchema } from './json.schema.types';
export type { ZodSchemaMinimal, ZodSchema } from './zod.schema.types';
export type { ClassValidatorSchema } from './class.schema.types';
export type { Schema, FromSchema, FromSchemaUnvalidated } from './base.schema.types';
15 changes: 14 additions & 1 deletion packages/framework/src/validators/base.validator.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import type { FromSchema, FromSchemaUnvalidated, Schema, JsonSchema, ZodSchema } from '../types/schema.types';
import type {
FromSchema,
FromSchemaUnvalidated,
Schema,
JsonSchema,
ZodSchema,
ClassValidatorSchema,
} from '../types/schema.types';
import type { ValidateResult } from '../types/validator.types';
import { JsonSchemaValidator } from './json-schema.validator';
import { ZodValidator } from './zod.validator';
import { ClassValidatorValidator } from './class-validator.validator';

const zodValidator = new ZodValidator();
const classValidatorValidator = new ClassValidatorValidator();
const jsonSchemaValidator = new JsonSchemaValidator();

/**
Expand All @@ -28,6 +37,8 @@ export const validateData = async <
*/
if (await zodValidator.canHandle(schema)) {
return zodValidator.validate(data, schema as ZodSchema);
} else if (await classValidatorValidator.canHandle(schema)) {
return classValidatorValidator.validate(data, schema as ClassValidatorSchema);
} else if (await jsonSchemaValidator.canHandle(schema)) {
return jsonSchemaValidator.validate(data, schema as JsonSchema);
}
Expand All @@ -44,6 +55,8 @@ export const validateData = async <
export const transformSchema = async (schema: Schema): Promise<JsonSchema> => {
if (await zodValidator.canHandle(schema)) {
return zodValidator.transformToJsonSchema(schema as ZodSchema);
} else if (await classValidatorValidator.canHandle(schema)) {
return classValidatorValidator.transformToJsonSchema(schema as ClassValidatorSchema);
} else if (await jsonSchemaValidator.canHandle(schema)) {
return jsonSchemaValidator.transformToJsonSchema(schema as JsonSchema);
}
Expand Down
Loading
Loading