Skip to content

Introducing Plotly.validate(data, layout) #697

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

Merged
merged 14 commits into from
Jul 13, 2016
Merged

Introducing Plotly.validate(data, layout) #697

merged 14 commits into from
Jul 13, 2016

Conversation

etpinard
Copy link
Contributor

@etpinard etpinard commented Jun 27, 2016

resolves #598

based off plotly-validate and lib-validate.

Example

var data = [{
  type: 'bar',
  y: [2, 1, 2],
  orientation: 'horizontal'
}];

var out = Plotly.validate(data);

console.log(out[0].msg));
// "In data trace 0, key orientation is set to an invalid value (horizontal)"

In brief

This PR expose a top-level method allowing users to validate their input data and layout.

Plotly.validate calls Plots.supplyDefaults and PlotSchema.get and a new Lib.validate method internally to determine if:

  • [code 'object'] a container key isn't linked to an object
  • [code 'array'] an array container key isn't linked to an array
  • [code 'schema'] a key was found in a trace or layout object that is not part of the plot schema
  • [code 'unused'] a key was found in user input but isn't used in the plotting code (i.e. the key isn't found fullData or fullLayout )
  • [code 'value'] a value is invalid
  • [code 'invisible'] a trace was made visible = false by an invalid set of attributes

Some remaining questions

  • How to handle non-array data and non-object layout input? Error out?, Output with different other error code?
  • When no validate errors found, should Plotly.validate return true? More generally, should Plotly.validate always return the a { data: [], layout: [] } object?

Those questions ⏫ were resolved in 54a6ed0

assertErrorShape(out, [], 4);
assertErrorContent(out.layout[0], 'schema', ['annotations[1]', 'arrowSymbol'], '');
assertErrorContent(out.layout[1], 'unused', ['xaxis', 'rangeselector', 'buttons[0]', 'count'], '');
assertErrorContent(out.layout[2], 'schema', ['xaxis', 'rangeselector', 'buttons[1]', 'title'], '');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mdtusz Plotly.validate returns an array of nested keys, as you suggested.

Maybe we should also return the .join('.') version for folks using Lib.nestedProperty ?

Moreover, I thought also about writing paths to _isLinkedToArray attributes as e.g.

['xaxis', 'rangeselector', 'buttons', 1 'title']

// instead of
['xaxis', 'rangeselector', 'buttons[1]', 'title']

Thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Moreover, I thought also about writing paths to _isLinkedToArray attributes as e.g.

['xaxis', 'rangeselector', 'buttons', 1, 'title']

// instead of
['xaxis', 'rangeselector', 'buttons[1]', 'title']

I added your missing comma, but yes, if you're going to return an array like that, integers for array indices is the way to go so you could just loop through with container = container[path[i]]. I would think though that, aside from our own internal uses (which can certainly use Lib.nestedProperty unless we export these results to another language), folks will rarely want programmatic access to these items. Mostly they'd just want something easy to look at and spot their error, in which case I'd think the single string version is nicer.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd say go with the more split-up integer notation.

folks will rarely want programmatic access to these items

Chelsea will attest that they always want programmatic access. I don't think that the non-string version is really any more difficult to grok, and the all string version just adds more complexity to programatic usage for when the case arises.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why not output both then?

I think the workspace (cc @chriddyp) uses Lib.nestedProperty (or some in-house version of it), so outputing an attribute string might be best for them.

But, I agree, an array of path parts is best for users just wanting to spot their errors.

Copy link
Contributor Author

@etpinard etpinard Jul 13, 2016

Choose a reason for hiding this comment

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

I decided to output both an array path and an attribute string astr field. The path field should be more readable while the astr field will be useful for compatibility with Plotly.restyle and Plotly.relayout.

See tests here.

- make Plotly.validate return a 1-level deep array of
  error objects
- add 'container' and 'trace' key in each error object
  to locate the error in data trace or layout.
- add 'astr' to error object (for easy plug into restyle and relayout)

// 'failed' just something mutable that won't be === anything else

valObject.coerceFunction(value, propMock, failed, opts);
Copy link
Member

@bpostlethwaite bpostlethwaite Jul 13, 2016

Choose a reason for hiding this comment

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

so the

failed = {}
out = failed
coerce(() => out = v)
out !== failed

dance is just checking to see if if propMock is fired by checking to see if the exising out === failed reference is no longer valid?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Exactly.

Copy link
Member

Choose a reason for hiding this comment

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

would something like

var called = false;
var propMock = {set: function() { called = true }

 valObject.coerceFunction(value, propMock, failed, opts);

return called;

also work?

Copy link
Contributor Author

@etpinard etpinard Jul 13, 2016

Choose a reason for hiding this comment

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

No. Because we want coerced values to be considered valid.

For example,

Lib.validate('1', { valType: 'number' });  // => true

even though 1 will be inserted in fullData.

In other words, out and failed will be linked to different refs when the coerce function makes propMock execute propMock.set(dflt).

Copy link
Member

Choose a reason for hiding this comment

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

right makes sense

@bpostlethwaite
Copy link
Member

Looks good from my view! 💃

@etpinard etpinard merged commit 926bfd5 into master Jul 13, 2016
@etpinard etpinard deleted the validate branch July 13, 2016 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature something new
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Expose info about attribute coercion and/or way to validate user data/layout
4 participants