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

Use named placeholder for error messages in options.proto #842

Merged
merged 27 commits into from
Dec 13, 2024

Conversation

yevhenii-nadtochii
Copy link
Contributor

@yevhenii-nadtochii yevhenii-nadtochii commented Dec 11, 2024

This PR unifies error messages format for all Spine options. Previously, the default error messages used printf-style format, but the custom ones expected placeholders instead. Now, both the custom and the default messages expect a string, which may contain placeholders in an arbitrary order.

A number of placeholders have been increased to allow specifying more detailed errors.

Copy link

codecov bot commented Dec 11, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 75.16%. Comparing base (57c83eb) to head (eaf8400).
Report is 28 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##             master     #842   +/-   ##
=========================================
  Coverage     75.16%   75.16%           
  Complexity     1178     1178           
=========================================
  Files           190      190           
  Lines          4293     4293           
  Branches        346      346           
=========================================
  Hits           3227     3227           
  Misses          935      935           
  Partials        131      131           

@yevhenii-nadtochii yevhenii-nadtochii self-assigned this Dec 12, 2024
@yevhenii-nadtochii yevhenii-nadtochii marked this pull request as ready for review December 12, 2024 13:37
@yevhenii-nadtochii
Copy link
Contributor Author

yevhenii-nadtochii commented Dec 12, 2024

@armiol @alexander-yevsyukov PTAL

Copy link
Collaborator

@armiol armiol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yevhenii-nadtochii LGTM except for some comments.

@@ -668,19 +676,14 @@ message IfMissingOption {
// double value = 1 [(min) = {
// value: "0.0",
// exclusive: true,
// error_msg: "Temperature cannot reach {other}K, but provided {value}."
// error_msg: "The temperature cannot reach `{min.threshold}K`, but provided `{field.value}`."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would simplify to {min.value}.

//
option (default_message) = "The number must be greater than %s%s.";
// The default error message.
option (default_message) = "The field `{parent.type}.{field.name}` must be {max.comparison} {min.threshold}.";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about {max.operator}?

// 4. `{parent.type}` – the fully qualified name of the field declaring type.
// 5. `{min.threshold}` – the specified minimum threshold `value`.
// 6. `{min.comparison}` – if `exclusive` is set to `true`, this placeholder equals
// to "greater than". Otherwise, "greater than or equal to".
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed today, let's use > and >=.

@yevhenii-nadtochii
Copy link
Contributor Author

@armiol PTAL

Though, we still have (distinct) and (range), for which there are no custom messages, nor the default ones on the level of the option definitions. The default messages for them are still defined as string literals within validation.

Copy link
Collaborator

@armiol armiol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yevhenii-nadtochii LGTM.

But what if we define the corresponding behaviour for (distinct) and (range) in this PR? It would be easier to review all the proposed placeholder names together.

Please consider.

Copy link
Contributor

@alexander-yevsyukov alexander-yevsyukov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see my comments. I'll review it once again after this batch is addressed.

//
message IfMissingOption {

// The default error message.
option (default_message) = "A value must be set.";
option (default_message) = "The field `{parent.type}.{field.name}` of type `{field.type}` must have a value.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it "the type"?

// The default error message for the field.
option (default_message) = "The message must have valid properties.";
// The default error message.
option (default_message) = "The field `{parent.type}.{field.path}` of type `{field.type}` is invalid. The field value: `{field.value}`.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we having here field.path instead of field.name? We refer to the immediate placement of the field in this context.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the combination of {parent.type}.{field.name} above.

// The default error message used when the field value is re-assigned.
option (default_message) = "The field `{fieldName}` already has the value `{currentValue}` and cannot be reassigned to `{proposedValue}`.";
// The default error message.
option (default_message) = "The field `{parent.type}.{field.name}` of `{field.type}` already has the value `{field.value}` and cannot be reassigned to `{field.proposed_value}`.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have "of the type {field.type}" instead of "of {field.type}"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and in similar case above and below, please.

@yevhenii-nadtochii
Copy link
Contributor Author

@armiol @alexander-yevsyukov PTAL

The two remaining options have got the default and custom messages support:

  1. For the boolean (distinct) constraint, I've added a companion option (if_has_duplicates).
  2. (range) option became a message option – RangeOption.

Copy link
Collaborator

@armiol armiol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yevhenii-nadtochii LGTM in general. Please see my comments.

message IfHasDuplicatesOption {

// The default error message.
option (default_message) = "The field `{parent.type}.{field.name}` of the type `{field.type}` must not contain duplicates. The found duplicates: `{field.duplicates}`.";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"The duplicates found:"

//
// message Blizzard {
// repeated Snowflake = 1 [(distinct) = true,
// (if_has_duplicates).error_msg = "Every snowflake must be unique! The found duplicates: `{field.duplicates}`."];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

// 1. `{field.name}` – the field name.
// 2. `{field.type}` – the fully qualified name of the field type.
// 3. `{field.value}` – the field value (the whole collection).
// 4. `{field.duplicates}` – the found duplicates (elements that occur more than once).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

src/main/proto/spine/options.proto Show resolved Hide resolved
// double angle = 4 [(range).value = "(0.0..180.0)"];
// }
//
// NOTE: That definition of ranges must be consistent with the type they constrain.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say "... they limit", or "... they describe".

// (if_invalid).error_msg = "The field is invalid."];
// }
// It is applicable only to message fields marked with `(validate)`.
// Repeated fields are supported.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about maps?

Copy link
Contributor

@alexander-yevsyukov alexander-yevsyukov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@yevhenii-nadtochii
Copy link
Contributor Author

@armiol PTAL

I've updated docs to (validate) and (distinct).

@yevhenii-nadtochii yevhenii-nadtochii merged commit 5388f69 into master Dec 13, 2024
7 checks passed
@yevhenii-nadtochii yevhenii-nadtochii deleted the error-message-placeholders branch December 13, 2024 13:29
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

Successfully merging this pull request may close these issues.

3 participants