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

[Feature Request] Is it possible to add validated input parameters and rule values in the error Violation? #17

Closed
shuqingzai opened this issue Jun 8, 2023 · 6 comments
Assignees
Labels
Feature New feature or request

Comments

@shuqingzai
Copy link

Feature description:

Example Proto

syntax = "proto3";

package project.api.v1;

import "buf/validate/validate.proto";

option go_package = "project/api/v1;v1";

// 分页参数
message PageParams {
  // 当前页, 从1开始(默认: 1, 最大: 1000)
  uint32 page = 1 [
    (buf.validate.field).uint32 = {gt: 0, lte: 1000}
  ];

  // 每页数量(默认: 20, 最大: 500)
  uint32 pageSize = 2 [
    (buf.validate.field).uint32 = {gt: 0, lte: 500}
  ];
}

go validate

func main() {
	msg := &pb.PageParams{
		Page:     0,
		PageSize: 10,
	}

	v, err := protovalidate.New()
	if err != nil {
		fmt.Println("failed to initialize validator:", err)
	}

	if err = v.Validate(msg); err != nil {
		var valErr *protovalidate.ValidationError
		if ok := errors.As(err, &valErr); ok {
			errPb := valErr.ToProto()
			fmt.Printf("\n%+v\n\n", errPb.GetViolations())
		}
	}
}

// ouput
//
// [field_path:"page"  constraint_id:"uint32.gt_lte"  message:"value must be greater than 0 and less than or equal to 1000"]
//

When I validate, the error returned by page is *validate.Violation, but I can’t get the request value 0 and the rule values: 0 and 1000, can the package add new fields (value and attributes ?? ) return?

Problem it solves or use case:

Users can get these values for more custom operations

I need to perform i18n now, because the semantics of each language are different when translating, and I need to customize the translation, but I can’t get these values, which makes the work very difficult

Proposed implementation or solution:

Contribution:

Examples or references:

Additional context:

@shuqingzai shuqingzai added the Feature New feature or request label Jun 8, 2023
@rodaine
Copy link
Member

rodaine commented Jun 8, 2023

Hi @shuqingzai, and thanks for the feedback! If you only need to support a single language, for now I'd recommend creating a custom constraint so that you can control the message. Will explore options with the team (exposing the FieldConstraints shouldn't be too difficult).

@MarcusMogg
Copy link

you can get it, but with ugly reflection code

func main() {

	msg := &pb.PageParams{
		Page:     0,
		PageSize: 10,
	}

	v, err := protovalidate.New()
	if err != nil {
		fmt.Println("failed to initialize validator:", err)
	}

	if err = v.Validate(msg); err != nil {
		var valErr *protovalidate.ValidationError
		if ok := errors.As(err, &valErr); ok {
			errPb := valErr.ToProto()
			fmt.Printf("\n%+v\n\n", errPb.GetViolations())
			a := errPb.GetViolations()[0]

			fieldDesc := msg.ProtoReflect().Descriptor().Fields().ByTextName(a.FieldPath)
			// import "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
			typeDesc := (*validate.E_Field).TypeDescriptor()
			x := fieldDesc.Options().ProtoReflect().Get(typeDesc).Message().Interface().(*validate.FieldConstraints)

			fmt.Println(x.Type) // type is oneof
			fmt.Println(*(x.Type.(*validate.FieldConstraints_Uint32).Uint32.Lte))
		}
	}
}

@shuqingzai
Copy link
Author

you can get it, but with ugly reflection code

func main() {

	msg := &pb.PageParams{
		Page:     0,
		PageSize: 10,
	}

	v, err := protovalidate.New()
	if err != nil {
		fmt.Println("failed to initialize validator:", err)
	}

	if err = v.Validate(msg); err != nil {
		var valErr *protovalidate.ValidationError
		if ok := errors.As(err, &valErr); ok {
			errPb := valErr.ToProto()
			fmt.Printf("\n%+v\n\n", errPb.GetViolations())
			a := errPb.GetViolations()[0]

			fieldDesc := msg.ProtoReflect().Descriptor().Fields().ByTextName(a.FieldPath)
			// import "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
			typeDesc := (*validate.E_Field).TypeDescriptor()
			x := fieldDesc.Options().ProtoReflect().Get(typeDesc).Message().Interface().(*validate.FieldConstraints)

			fmt.Println(x.Type) // type is oneof
			fmt.Println(*(x.Type.(*validate.FieldConstraints_Uint32).Uint32.Lte))
		}
	}
}

@MarcusMogg

Your approach is possible, but when v.Validate() is used, reflection has already been performed. If protovalidate natively supports it, it will definitely be better for performance and scalability. Other languages can also enjoy such convenience

@MarcusMogg
Copy link

you can get it, but with ugly reflection code

func main() {

	msg := &pb.PageParams{
		Page:     0,
		PageSize: 10,
	}

	v, err := protovalidate.New()
	if err != nil {
		fmt.Println("failed to initialize validator:", err)
	}

	if err = v.Validate(msg); err != nil {
		var valErr *protovalidate.ValidationError
		if ok := errors.As(err, &valErr); ok {
			errPb := valErr.ToProto()
			fmt.Printf("\n%+v\n\n", errPb.GetViolations())
			a := errPb.GetViolations()[0]

			fieldDesc := msg.ProtoReflect().Descriptor().Fields().ByTextName(a.FieldPath)
			// import "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
			typeDesc := (*validate.E_Field).TypeDescriptor()
			x := fieldDesc.Options().ProtoReflect().Get(typeDesc).Message().Interface().(*validate.FieldConstraints)

			fmt.Println(x.Type) // type is oneof
			fmt.Println(*(x.Type.(*validate.FieldConstraints_Uint32).Uint32.Lte))
		}
	}
}

@MarcusMogg

Your approach is possible, but when v.Validate() is used, reflection has already been performed. If protovalidate natively supports it, it will definitely be better for performance and scalability. Other languages can also enjoy such convenience

Using reflection is inevitable when you append fields (so many rules here). Perhaps a custom error message formatter interface for rules would suit your needs better?

pkwarren pushed a commit that referenced this issue Jul 18, 2023
@m-d-z-z
Copy link

m-d-z-z commented Aug 25, 2023

I18n for fields validating is very necessary. I hope this package can handle it gracefully.

igor-tsiglyar pushed a commit to igor-tsiglyar/protovalidate that referenced this issue Apr 16, 2024
…ufbuild#17)

Supplies a fallback mechanism for the deprecated extension index.
Essentially we search for the index that we want, if its not found we
look for the old one.

depends on bufbuild#43
@nicksnyder
Copy link
Member

We addressed this in #265 and in various implementations.

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

No branches or pull requests

6 participants