fastify-type-provider-typebox icon indicating copy to clipboard operation
fastify-type-provider-typebox copied to clipboard

Enhance Union Type Error Details

Open itizarsa opened this issue 1 year ago • 3 comments

Prerequisites

  • [X] I have written a descriptive issue title
  • [X] 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 avatar Oct 25 '24 10:10 itizarsa

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
	}
]

itizarsa avatar Oct 25 '24 14:10 itizarsa

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

mcollina avatar Dec 22 '24 10:12 mcollina

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

mcollina avatar Dec 22 '24 10:12 mcollina