jsonary-js / jsonary

Quickly assemble clients for JSON-based APIs (powered by JSON Schema)
http://jsonary.com/
MIT License
122 stars 14 forks source link

validation with TV4 #69

Closed Ognian closed 11 years ago

Ognian commented 11 years ago

the tv4 validator is great too. Wouldn't it be nice to enable the errors reported by tv4 to be "displayed" in a rendered JSON. What do you think? What would be the easiest way to do it?

geraintluff commented 11 years ago

My first thought would be to add a new type of render component.

There are already separate components in the rendering system for adding/removing, type switching, schema switching and actually rendering the data - one renderer of each type is used for every data instance being rendered. To add a new component, you can make a call like:

Jsonary.render.Components.add('VALIDATION');

Then, you register a renderer with that component like this:

Jsonary.render.register({
    component: Jsonary.render.Components.VALIDATION,
    renderHtml: function (data, context) {
        return "(validation info)" + context.renderHtml(data);
    }
});

That renderer will now be used for every node in the data tree - so you'll see "(validation info)" inserted in front of every object, string, undefined, etc.

At that point, all that remains is to get the appropriate error messages. One way to do that would be to attach something to the Document object, like this:

    renderHtml: function (data, context) {
        if (!data.document.validationResult) {
            data.document.validationResult = tv4.validateResult(data.document.raw.value(), <<schema>>);
        }
        ...
    },
    update: function (element, data, context, operation) {
        // Force an update of the validation result
        data.document.validationResult = null;
    }

All that remains at that point is to assemble a suitable value for <<schema>>, and then determine which errors to display at which data nodes - tv4 includes the relevant data path in any validation results, so you should be able to compare that with data.pointerPath().

Hope that helps!

Ognian commented 11 years ago

Thanks a lot. This opened my eyes... Actually I'm doing the following now:

this is the extension:

Jsonary.render.Components.add('VALIDATION');

Jsonary.render.register({
    component: Jsonary.render.Components.VALIDATION,
    renderHtml: function (data, context) {
        var result = "";
        var rootData = data.document.root;
        if (!!rootData.validation) {
            if (data.pointerPath() != "") {
                for (i in rootData.validation.errors) {
                    if (rootData.validation.errors[i].dataPath == data.pointerPath()) {
                        //result += "<span class='validationError'>(validation message: " + rootData.validation.errors[i].message+" code: "+rootData.validation.errors[i].code+" schemaKey: "+rootData.validation.errors[i].schemaKey+")</span>";
                        result += "<span class='validationError'>"+rootData.validation.errors[i].message+"</span>";
                        //remove i entry in errors
                        rootData.validation.errors.splice(i,1);
                    }
                    ;
                }
                ;
            }
            //else {
                // result += "<span class='validationError'>(validation info: " + JSON.stringify(rootData.validation.errors, undefined, 2) + ")</span>";
                // it is not a good idea to display something here since we do not know which errors can be displayed by subsequent nodes
                // therefore the remaining errors are displayed after renderTo
            //}
            ;
        }
        ;
        return context.renderHtml(data) + result;
    }
});

so thank you very much and maybe someone else can use this too... Ognian

geraintluff commented 11 years ago

So when you render a particular error, you then pop it off from the list?

That works if you only render the item once. If it's a read-only item and it's guaranteed to be rendered exactly once in only one place, that's probably reasonable.

(I feel it should be possible to somehow make it work with editable items all over the place, though... OK, this is an interesting problem!)