How To Deal with CMS Protovalidation Errors.

What is Protovalidation in General.

While Google Protobuf has long provided consistent structured data schemas across APIs, analytics, and data pipelines, it doesn’t provide a way to ensure the field values within those structures are correct. Protovalidation fixes this by enabling runtime evaluation of data validation rules, represented by message and field level custom options.

Example (one_of message-level data validation rule is applied to SearchRequest message):

message SearchRequest
{
    // Only one optional "request" field is expected.
    option (constraints_1.message).one_of =
    {
        field_set : [FIELD_SET_SELECT_BY_TAG],
        field_tag : ["request"]
    };

    ...
}

Info: All the data validation rules for CMS protocol are currently defined in constraints_1 package in Validation/constraints_1.proto file.

So Protovalidation provides a missing link between the shape and structure of the Google Protobuf as well as the underlying semantic expectations for what a valid data message should be.
## How CMS Reports Protovalidation Errors. Protovalidation errors occur if a data message violates some validation rule, applied to this message as a whole (for message-level validation rules) or concrete message fields (for field-level validation rules). CMS reports these errors in the same way as the other ones: using .shared_1.Text message.

Example of .shared_1.Text message containing a Protovalidation error:

.shared_1.Text: {
   "key": "CMSI97"
   "text": "Request has invalid data: incorrect person's name: John3323 (cmsapi_1.OperationRequest.create_profile.profile.contact_information.first_name)."
   "format": "Request has invalid data: %1% (%2%) [%3%]."
   "param": [
     "incorrect person's name:",
     "cmsapi_1.OperationRequest.create_profile.profile.contact_information.first_name",
     "str_06",
     "John3323"
   ]
}

Such messages (representing Protovalidation errors) have: * key field always set to CMSI97; * text field composed of four parts: 1. prefix, always Request has invalid data:, 2. rule violation message (incorrect person’s name: in the example), 3. field value which caused the error, i.e. invalid value (John3323 in the example), 4. enclosed in parentheses, fully qualified field name containing the invalid value
(cmsapi_1.OperationRequest...first_name in the example).

In most cases, the text field provides enough information to resolve the error. If not, that is, it remains uncertain why a value was treated as invalid, then the examination of other fields in a Protovalidation error message should help.

What Else in shared_1.Text Protovalidation Errors Messages.

Another notable information provided by the messages is unique validation rule ID (meaning a rule which was violated) in param[2] field (str_06 in the example). An acquired rule ID allows quick navigation to the rule description in Validation/constraints_1.proto file. Rules descriptions, represented as Protobuf comments, contain additional information about the data expected.

Example (the description of str_06 rule in Validation/constraints_1.proto file):

// - `person_name_part` specifies that the field contains a part of a person's name -
// a free-form text, which must consist of characters from categories
// `Lu`, `Ll`, `Pd`, `Po` and scripts `Latin`, `Common` (see `FreeFormTextRule`
// for the categories/scripts description).
optional bool person_name_part = 6
    [(md_1.rule).specs =
    {
        id: "str_06",
        violation_message: "incorrect person's name: %s"
    }];

As it’s seen from the example’s rule description, digits (category Nd) are not allowed in persons’ names, that’s why the rule is violated.

Also shared_1.Text.param contains: * param[0]: rule violation message as it is defined in Validation/constraints_1.proto file; * param[1]: fully qualified name of the field which caused the error; * param[2]: (described above); * param[3]: optional, (invalid) value of the field.

Info: shared_1.Text.param is primarily intended for automating Protovalidation errors processing on the client-side. ### Specifics of The Message-Level Validation Rules. The message-level validation is inherently more complex than the field-level, as the former allows implementing quite sophisticated validation scenarios/algorithms. Because of this, Protovalidation errors originated from message-level validation rules, in case the rules violation, can be not so clear in some cases, so that additional explanations are needed. #### Country-Dependent Fields Validation Rules. These rules are: * international phone number validation (rule ID: msg_02); * country sub-divisions (or states) validation (rule ID: msg_03); * zip code validation (rule ID: msg_04).

All of them require a valid country code to be present in a Protobuf message being validated. The absense or invalid value of the code causes these rules to be violated, but the reason itself will not be reflected explicitly in the Protovalidation error message returned.

Consider an example, where state_code country sub-divisions validation rule is applied to Address message:

message Address
{
    option (constraints_1.message).state_code =
    {
        field_name : "state",
        country_code_field_name : "country",
    };
    ...
    optional string country = 1;
    optional string state = 2;
    ...
}

If a CMS request, having Address as a sub-message, is formed in such a way that the state code is specified, but the country code is not, like:

.cmsapiex_1.framework_request: {
    ...
    "address": {
        ...
        "state": "CA"
        ...
    }
    ...
}

then (after this request is sent) CMS will respond with a Protovalidation error, complaining that the state code is invalid:

.cmsapiex_1.framework_result: {
  ...
  "error_message": {
    "key": "CMSI97",
    "text": "Request has invalid data: invalid state code: CA (framework_1.FrameworkRequest.obtain_demo_login.address.state).",
    "format": "Request has invalid data: %1% (%2%) [%3%].",
    "param": [
      "invalid state code",
      "framework_1.FrameworkRequest.obtain_demo_login.address.state",
      "msg_03",
      "CA"
    ]
  }
}

despite that CA is a valid state code.

Tip: If a phone number (or state code, or zip code) appears to be correct in a request message, but nevertheless CMS responds with a Protovalidation error, then most probably that a country code is missed in this message. #### Field Sets Validation Rules. These are the rules to perform validation of logical sets comprising Protobuf message fields: * one-of fields validation (rule ID: msg_06); * mutual-exclusive fields validation (rule ID: msg_07).

The content of Protovalidation errors messages (i.e. shared_1.Text) raised by these rules is a bit different from what is described at the beginning of this text.

Consider an example, where one-of message-level validation rule is applied to CloneUser message:

message CloneUser
{
    option (constraints_1.message).one_of =
    {
        field_set : [FIELD_SET_SELECT_BY_NAME],
        field_name : ["new_user_contact_information", "target_profile_id"]
    };
    ...
    optional ContactInformation new_user_contact_information = 6;
    optional string target_profile_id = 8;
    ...
}

In this example, a logical set consists of two fields: new_user_contact_information and target_profile_id, and the one-of rule checks if only one of these fields is present in a CloneUser message being validated.

If both fields are present, then CMS will respond with a Protovalidation error, like:

operation_result: {
  ...
  "error_message": {
    "key": "CMSI97",
    "text": "Request has invalid data: too many/too few fields are present: common_1.CloneUser.new_user_contact_information common_1.CloneUser.target_profile_id (cmsapi_1.OperationRequest.clone_user).",
    "format": "Request has invalid data: %1% (%2%) [%3%].",
    "param": [
      "too many/too few fields are present",
      "cmsapi_1.OperationRequest.clone_user",
      "msg_06",
      "common_1.CloneUser.new_user_contact_information common_1.CloneUser.target_profile_id"
    ]
  }
}

The list of fully qualified field names which caused the rule violation (common_1.CloneUser.new_user_contact_information common_1.CloneUser.target_profile_id) is provided as the third (not fourth) part of .shared_1.Text.text field.

Tip: Also this list (the field names delimited by spaces) can be obtained via shared_1.Text.param[3] field.

<< The End >>