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

Allow extending for arbitrary keywords #134

Closed
charypar opened this issue Apr 11, 2016 · 16 comments
Closed

Allow extending for arbitrary keywords #134

charypar opened this issue Apr 11, 2016 · 16 comments

Comments

@charypar
Copy link

Rather than opening a PR out of the blue, I thought I'd ask first:

In our project, we use json-schema-faker to generate fake data according to a schema, however, because the data are used as React component props, we have some extensions in the schema itself for objects that are not strictly JSON.
In order to extend the basic JSON schema, because extending with custom type is against the spec, we define custom keywords for custom things (e.g. { isFunction: true } instead of { type: 'function' }).

While I know this is controversial and we probably shouldn't, the overwhelming majority of prop data is covered by JSON schema and the interoperability of it is extremely appealing.

JSON schema faker currently doesn't support extending for custom keywords, but the support is "almost there". If external type detection looked at available extensions in container and the external type then delegated to any extension here (probably in order from last one defined to first one defined), it should be quite easy to do this. Unless I'm missing something.

Is that something you'd be willing to accept as a PR?

@pateketrueke
Copy link
Member

Do you have some JSON examples so far?

@charypar
Copy link
Author

For the schema or data? Not sure what you're after, sorry.

Currently, we've worked around it defining the function and element nodes like this:

export const func = {
  'isFunction': true, // for validation
  'faker': { // for faking
    'type': 'function'
  }
};

export const element = {
  'type': 'object',
  'isReactElement': true,
  'faker': {
    'type': 'element'
  }
};

@pateketrueke
Copy link
Member

I'd mean, you want to produce some object using json-schema right?

You can't use isSomething: true as is, you must wrap it into a valid schema:

{
  "type": "object",
  "properties": {
    "isSomething": { "enum": [true] }
  },
  "required": ["isSomething"]
}

I don't like the idea of doing non-standar stuff...

@charypar
Copy link
Author

In case of a function I want to produce an actual function. Which I understand is not a valid JSON object, but it would be really useful to be able to describe it.

As fare as schema example, one would be:

{
  type: 'object',
  properties: {
    children: {
      type: 'object',
      isElement: true // any React element
    },
    callback: {
      isFunction: true // any function
    }
  }
}

Does that make sense?

@charypar
Copy link
Author

I suppose the use-case I'm after is not a particularly canonical one, but I suppose a different example where this could be useful is something like:

{
   "type": "number",
   "isPrime": true
}

@pateketrueke
Copy link
Member

I'm still confused about isPrime, isFunction, isElement properties, do you want jsf understand those?

@charypar
Copy link
Author

I don't want it to understand them, but I would like to be able to extend jsf (using jsf.extend) to understand them in a similar way it's possible to currently extend faker and chance.

In fact it would be useful in order to eventually get rid of those optional dependencies altogether, because you could just use them like any other custom data generator.

@charypar
Copy link
Author

I imagine using the the extend API after the change would look something like this:

jsf.extend('isPrime', function(input) {
  return generateMeAPrime();
});

// backwards compatibility for custom.statement
faker.custom = {
  statement: function(length) {
    return faker.name.firstName() + " has " + faker.finance.amount() + " on " + faker.finance.account(length) + ".";
  }
};

jsf.extend('faker', function(input) { // example input = { custom: { statement: [16] }}
  var path = Object.keys(input)[0];
  var generator = getIn(faker, path); // get the generator function

  return generator(input[path]));
});

Very roughly...

@pateketrueke
Copy link
Member

I got it, but IMO stuff like isPrime is already covered by json-schema, but I understand that functions aren't even JSON...

As long as I know, other JSON-Schema validators can be extended to validate custom types but not custom properties, I guess { "type": "function" } is more clear and standard.

What do you think?

@charypar
Copy link
Author

Well, we tried suggesting that extension to ajv here, they pointed out that we should do it via keywords. In any case, jsf doesn't support type extensions either (plus semantically prime type would still be a { type: 'number' }).

For the isPrime case, instead of targeting the isPrime keyword itself, in the current state of jsf you'd need to add a faker declaration next to it and include faker in your package.json, which is a bit cumbersome and forces you to use faker, even though you don't really need it (writing a prime generation function isn't that big a deal).

I can have a go at the PR if you're interested in this. It should make it a bit more clear where I'm going with it, unless you're absolutely against the idea.

@charypar
Copy link
Author

@pateketrueke What do you think? Are you interested enough for me to open a PR?

@pateketrueke
Copy link
Member

@charypar I'm still interested on this topic, do you want to discuss about it? 🤔

@charypar
Copy link
Author

I will have to refresh my memory, it's been a while, but yes, I'd still like to look at it.

@pateketrueke
Copy link
Member

Well, now will be possible to support any keyword:

jsf.define('isSomething', (value, schema) => {
  return { complexValueOrSomethingWith: value, or: schema };
});

Any registered function will receive the property value and the whole schema where is defined.

@goto100
Copy link

goto100 commented Mar 2, 2017

Really a nice feature, hope it can be published soon.

@pateketrueke
Copy link
Member

pateketrueke commented Oct 29, 2017

@goto100 that feature was shipped already on previous RC releases, just I wanted to let you know. ;)

A nice example of this is the built-in keyword pattern which relies only on randexp.

container.define('pattern', utils.randexp);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants