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

add core validator generator for Object-type props #8640

Closed
indirectlylit opened this issue Nov 9, 2021 · 10 comments · Fixed by #8902
Closed

add core validator generator for Object-type props #8640

indirectlylit opened this issue Nov 9, 2021 · 10 comments · Fixed by #8902
Assignees
Labels
TAG: dev experience Build performance, linting, debugging... TAG: tech update / debt Change not visible to user

Comments

@indirectlylit
Copy link
Contributor

indirectlylit commented Nov 9, 2021

Observed behavior

There are a large number of Object-type props with no validator function. (Search the codebase using a multi-line regex to help find them: {[^}]*type: Object[^}]*})

Normal component props are well-documented using type, required, and sometimes validator. The keys of Object props are as much a part of the component API as the other ones, but they are often under-documented.

Even when validation of Object props is done in Kolibri, it is performed in an ad-hoc manner with little consistency and the logging is less useful because usually Vue will simply say the validator failed without specifying why. Some examples:

device: {
type: Object,
required: true,
validator(val) {
return val.name && val.id && val.baseurl;
},
},

device: {
type: Object,
required: true,
validator(val) {
return isString(val.name) && isString(val.id) && isString(val.baseurl);
},
},

value: {
type: Object,
required: true,
validator(value) {
const inputKeys = ['channels', 'accessibility_labels', 'languages', 'grade_levels'];
return inputKeys.every(k => Object.prototype.hasOwnProperty.call(value, k));
},
},

Expected behavior

  • The expected schema of Object props should be documented and enforced similar to other props. This includes the ability to specify type, required, and validator for object properties
  • Logging from failed validators should produce helpful results for developers
  • Object validators should be easy to construct using syntax similar to normal prop definitions
  • Nice-to-have: add a new linting rule which requires that all Object-type props have a validator function

User-facing consequences

  • Developers who want to leverage Object-based props in new components do not have sufficient support from our built-in tooling for quickly writing effective and useful validators
  • Developers who are working on existing components that use Object-based props do not have as much insight and assurance into what data might exist in the object without looking at parent components that pass the data in

Proposal

Add a new core function objectValidator to kolibri.utils.validators.

  • Input: takes an object similar to the Vue props object, where each key is the name of an expected property, and each value may contain information about the type, required, and validator for that property.
  • Output: returns a new validator function which itself takes an object as an input and returns true or false depending on whether the object conforms to the schema. Additionally, when the value is false the function should log a helpful error message specific to the mode of failure.

Then this can be used to define validators on Object-type properties. For example:

    props: {
      user: {
        type: Object,
        required: true,
        validator: objectValidator({
          name: {
            type: String,
            required: true,
          },
          age: {
            type: Number,
            required: true,
            validator(value) {
              return value > 0;
            },
          },
        }),
      },
    },

Context

0.15

@hamzamunaf
Copy link
Contributor

is this issue still open? Can I contribute to this issue?

@rtibbles
Copy link
Member

Yes, please do!

@hamzamunaf
Copy link
Contributor

Can you assign it to me and provide me any other relevant information/tips?

@rtibbles
Copy link
Member

Yes, I have assigned you.

There is an example of a function that does some of what is described above here: https://github.com/learningequality/kolibri-design-system/blob/main/lib/content/mixin.js#L50, however, this does not do full checking against the Vue js prop specification, so to meet the specs layed out above, it would need to be extended.

Worth looking at the VueJS source code for how they handle validation there, as essentially the spec is just asking for prop validation to be done in a nested way inside objects: https://github.com/vuejs/vue/blob/2.6/src/core/util/props.js#L21

@indirectlylit
Copy link
Contributor Author

indirectlylit commented Nov 21, 2021

A few simplifying assumptions can be made:

  1. Vue contains logic preventing validators from running in production. objectValidator does not need this because it's already handled by Vue.
  2. Vue prop definitions accept various shorthand forms like [ "name", "age" ] and { name: String, age: Number }. For objectValidator we do not need to handle these forms, and can always require the long-form:
    {
      name: { type: String, default: "" },
      age: { type: Number, required: true },
    }
  3. objectValidator should not be recursive in situations where an Object prop itself contains other Object-type properties. Instead, if the sub-object is only one more level deep, this can be handled with a second, nested reference to objectValidator:
    props: {
      user: {
        type: Object,
        required: true,
        validator: objectValidator({
          name: {
            type: String,
            required: true,
          },
          userAttributes: {
            type: Object,
            required: true,
            validator: objectValidator({
              age: {
                type: Number,
                required: true,
              },
              isActive: {
                type: Boolean,
                default: true,
              },
            }),
          },
        }),
      },
    },
    On the other hand, if there is an object with a homogenous and recursively-nested structure like a resource topic tree, the engineer would need to write a custom validator for that schema:
    props: {
      topicTree: {
        type: Object,
        required: true,
        validator: topicTreeObjectValidator,
      },
    },

@hamzamunaf
Copy link
Contributor

Working on it and soon I will make a PR for it

@indirectlylit
Copy link
Contributor Author

Thank you. Please target the develop branch.

@hamzamunaf
Copy link
Contributor

Still working on it, should be ready by tomorrow. Sorry for the delay

@rtibbles
Copy link
Member

rtibbles commented Dec 7, 2021

Thanks for keeping us updated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
TAG: dev experience Build performance, linting, debugging... TAG: tech update / debt Change not visible to user
Projects
None yet
4 participants