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

Enhance Union Type Error Details #181

Open
2 tasks done
itizarsa opened this issue Oct 25, 2024 · 3 comments
Open
2 tasks done

Enhance Union Type Error Details #181

itizarsa opened this issue Oct 25, 2024 · 3 comments

Comments

@itizarsa
Copy link

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the feature has not already been requested

🚀 Feature Proposal

Problem

When using fastify-type-provider-typebox, union type validation failures provide minimal error details:

{ 
  message: 'Expected union value', 
  instancePath: '' 
}

This lacks the detailed validation information that TypeBox provides through its errors property in the ValueError interface, making it difficult for developers to debug union-type validation failures.

Background

TypeBox's ValueError interface contains rich error information:

export interface ValueError {
    type: ValueErrorType;
    schema: TSchema;
    path: string;
    value: unknown;
    message: string;
    errors: ValueErrorIterator[]; // Contains detailed validation results for each union member
}

Current error mapping in fastify-type-provider-typebox:

error: errors.map((error) => ({
  message: `${error.message}`,
  instancePath: error.path
}))

Proposal

Enhance the error mapping to include detailed union validation information:

import { TypeCompiler, ValueError, ValueErrorIterator } from '@sinclair/typebox/compiler';
import { Type } from '@sinclair/typebox';

const CreateActionDelayBody = Type.Object({
	action: Type.Literal('DELAY'),
	days: Type.Number(),
});

const CreateActionSMSBody = Type.Object({
	action: Type.Literal('SMS'),
	phone: Type.String(),
});

const CreateActionEmailBody = Type.Object({
	action: Type.Literal('EMAIL'),
	email: Type.String({ format: 'email' }),
});

const CreateActionBody = Type.Union([
	CreateActionDelayBody,
	CreateActionSMSBody,
	CreateActionEmailBody,
]);

const C = TypeCompiler.Compile(CreateActionBody);

type ReducedError = [{ message: string; path: string; errors: ReducedError[] }];

const MapValueIterator = (iterator: ValueErrorIterator): ReducedError[] =>
	[...iterator].map((error) => MapValueError(error)) as never;

const MapValueError = (error: ValueError) => ({
	message: error.message,
	path: error.path,
	errors: error.errors.map((iterator) => MapValueIterator(iterator)),
});

const value = { action: 'ONE' };

const R = MapValueIterator(C.Errors(value));

console.dir(R, { depth: 100 });

Example Output

[
	{
		"message": "Expected union value",
		"path": "",
		"errors": [
			[
				{
					"message": "Expected required property",
					"path": "/days",
					"errors": []
				},
				{ "message": "Expected 'DELAY'", "path": "/action", "errors": [] },
				{ "message": "Expected number", "path": "/days", "errors": [] }
			],
			[
				{
					"message": "Expected required property",
					"path": "/phone",
					"errors": []
				},
				{ "message": "Expected 'SMS'", "path": "/action", "errors": [] },
				{ "message": "Expected string", "path": "/phone", "errors": [] }
			],
			[
				{
					"message": "Expected required property",
					"path": "/email",
					"errors": []
				},
				{ "message": "Expected 'EMAIL'", "path": "/action", "errors": [] },
				{ "message": "Expected string", "path": "/email", "errors": [] }
			]
		]
	}
]

I understand we need to stick to FastifySchemaValidationError. I'm raising this here to discuss how to better support it.

Motivation

No response

Example

No response

@itizarsa
Copy link
Author

For Reference, below is the fastify-type-provider-zod error

When empty JSON is provided

[
	{
		"keyword": "invalid_union_discriminator",
		"instancePath": "/action",
		"schemaPath": "#/action/invalid_union_discriminator",
		"params": {
			"issue": {
				"code": "invalid_union_discriminator",
				"options": ["DELAY", "SMS", "EMAIL"],
				"path": ["action"],
				"message": "Invalid discriminator value. Expected 'DELAY' | 'SMS' | 'EMAIL'"
			}
		},
		"message": "Invalid discriminator value. Expected 'DELAY' | 'SMS' | 'EMAIL'",
		"[Symbol(ZodFastifySchemaValidationError)]": true
	}
]

When action is provided, but days aren't

[
	{
		"keyword": "invalid_type",
		"instancePath": "/days",
		"schemaPath": "#/days/invalid_type",
		"params": {
			"issue": {
				"code": "invalid_type",
				"expected": "number",
				"received": "undefined",
				"path": ["days"],
				"message": "Required"
			}
		},
		"message": "Required",
		"[Symbol(ZodFastifySchemaValidationError)]": true
	}
]

@mcollina
Copy link
Member

Thanks for reporting! Would you like to send a Pull Request to address this issue? Remember to add unit tests.

1 similar comment
@mcollina
Copy link
Member

Thanks for reporting! Would you like to send a Pull Request to address this issue? Remember to add unit tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants