longshotlabs / simpl-schema

A JavaScript schema validation package that supports direct validation of MongoDB update modifier objects
https://www.npmjs.com/package/simpl-schema
MIT License
560 stars 114 forks source link

JSON Schema / IETF support #72

Open awatson1978 opened 7 years ago

awatson1978 commented 7 years ago

So, apparently JSON Schema is an IETF standard (Informational), that is used in products like Swagger, and is some sort of alternative to node-simple-schema.

http://json-schema.org/ http://json-schema.org/documentation.html http://json-schema.org/latest/json-schema-core.html

Has there been any thought given to support json-schema objects and syntax? Perhaps through toSimpleJson() and fromSimpleJson() methods?

aldeed commented 7 years ago

Yes, it's been proposed many times and considered quite a bit. The main issue is all of the support for functions in SS, which JSON schema doesn't have, and by definition can't have since it's a text format.

That said, going FROM jsonschema should be fine and going TO jsonschema could print warnings maybe whenever unsupported features are found.

I would prefer for this to be a separate package, though. I think someone may have attempted such a package awhile back, but I don't remember where or what it is offhand.

gerwinbrunner commented 7 years ago

Sounds like a good idea.

Here is one use case to consider: Having a (public) json schema that you want/have to use. This would be the base (and could be updated later) for the simple schema. The simple schema that gets created should be extendable as before with custom stuff. ie.: autoform.

awatson1978 commented 4 years ago

Hi! Glad to see 1.5.7 released. :)

I've recently finished a gnarly refactor, and the resulting library is a big step closer to needing JsonSchema support. I have a much clearer idea of what we need, and it's just the minimal case where we initialize with a JsonSchema which then hydrates the SimpleSchema. No need to worry about functions or warnings; can punt on those.

Anyhow, I've been noodling on how to move this forward, and coming up with something like the following:


Persons = new Mongo.Collection('Persons');

let personJsonSchema = {
  "$id": "https://example.com/person.schema.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Person",
  "type": "object",
  "properties": {
    "givenName": {
      "type": "string",
      "description": "The person's given name, commonly the first listed name"
    },
    "familyName": {
      "type": "string",
      "description": "The person's family name, commonly the last listed name."
    },
    "age": {
      "description": "Age in years which must be equal to or greater than zero.",
      "type": "integer",
      "minimum": 0
    }
  }
}

// alternatively, maybe import it
import { personJsonSchema } from 'lib-with-my-schemas'

// Idea A - Adjust the constructor, so SimpleSchema accepts a JsonSchema input
let PersonSimpleSchema = new SimpleSchema(personJsonSchema)

// Idea B - Initialize some sort of null schema, 
// and add a function to parse and add the JsonSchema
let PersonSimpleSchema = new SimpleSchema()
PersonSimpleSchema.fromJsonSchema(personJsonSchema);

// Finally, attach the schema
Persons.attachSchema(PersonSimpleSchema);

Any thoughts on which approach would be better? Have any thoughts on best practices on converting from JsonSchema to SimpleSchema?

We have a library of a few hundred highly curated JsonSchemas we need to parse into SimpleSchemas, so we can attach to server/client cursors via the Meteor build tool. They get updated every few years, so we're going to have them in a separate library file. But it looks like I need this functionality, and it's the next big refactor (scheduled for this summer).

coagmano commented 4 years ago

Between those approaches, I prefer a static factory function that returns an instance:

let PersonSimpleSchema = SimpleSchema.fromJsonSchema(personJsonSchema);

maybe SimpleSchema.newFromJsonSchema would make it more obvious that it returns an instace?

aldeed commented 4 years ago

@awatson1978 I agree with @coagmano that some sort of factory function is best, but I'd add that it should be a separate package with a dependency on simpl-schema. So rather than SimpleSchema.fromJsonSchema, it would be its own thing you import:

import buildSimpleSchemaFromJson from "some-pkg";

let PersonSimpleSchema = buildSimpleSchemaFromJson(personJsonSchema);

If nobody else is interested in creating and owning this package, I can do it. But I do think it's best to keep all of that separate. (The existing package should already be broken into a few different packages, IMO.)

Out of curiosity, are you using MongoDB and have you looked into their newish built-in JSONSchema validation? If you attach those to the mongo collections directly in Mongo, then .attachSchema may not even be necessary, although you'd have to parse mongo errors.

awatson1978 commented 4 years ago

Hi, I don't have a horse in the race regarding how it's package. The one thing I'll say though, is don't underestimate the value of having a well curated API.

// maybe export both the function as well as attaching it to the SimpleSchema object?  
// lodash and moment provide similar flexibility
import { buildSimpleSchemaFromJson, SimpleSchema} from "some-pkg";

// for people who want want to keep their namespaces flat
let PersonSimpleSchemaA = buildSimpleSchemaFromJson(personJsonSchema);

// for people who want curated API namespaces and Intellisense functionality in their editors 
let PersonSimpleSchemaB = SimpleSchema.buildSimpleSchemaFromJson(personJsonSchema);

As for MongoDB's newish built-in JSONSchema validation - yes, I have! We're very excited about the new functionality, and very much looking forward to using it.

However, maybe 80% or 90% of the work we do is with iOS apps and using Meteor as an application server. In the healthcare industry, there's an industry wide initiative to enable standardized HTTP interfaces at all the hospitals and laboratories in the US. So we focus a lot of our time building client side apps that don't actually use Mongo or DDP all that much. Rather, we tend use the Meteor build pipeline, and then fetch data from lots of different servers using HTTP. The hospital endpoints are suppose to be all the same, but there are implementation bugs, differences in interpretation, different versions of the standard, etc. So, we need the JSONSchemas on the client to do quality control.

We're interested in someday landing on an NPM based simpl-schema + minimongo + ajv solution, and minimizing our overall usage of Atmosphere. But that's probably a year away, and requires at least two more major refactors.

Until then, SimpleSchema/Minimongo is the isomorphic solution we know works. Better to use it to work the JSONSchemas over to the client. Then we can lock down some acceptance tests and integration tests, and then refactor to the NPM packages.

aldeed commented 4 years ago

Thanks for the context @awatson1978 .

I'll have to think about the best place for the function. An in between solution would be having the implementation in a separate package, and also export wrapper functions from the main pkg like your example. The new package would be a peer dep that is dynamically imported. Therefore you'd only need to install it if you plan to call that function. I might be leaning toward this approach for this as well as other things like clean that I'd like to move to separate packages to reduce the package size further.

awatson1978 commented 1 year ago

Hi! A year or few later, and I've find myself digging into that refactor I was talking about. I've also found an example of an external factory package that tried to do the conversion that we were discussing:

https://github.com/bshamblen/meteor-json-simple-schema

The following file was last updated in 2017, but sort of attempts to do the conversion: https://github.com/bshamblen/meteor-json-simple-schema/blob/master/json-simple-schema.js

Avivbens commented 5 months ago

Hi all 👋

I have to say, really cool package! 🎉

Me Thinking With You All

I'm using NestJS, and kind of tied to the class-validator style. I was wondering if converting class-validator to JSON Schema, and then importing it to simpl-schema might be a good idea. I like the way this library handles MongoDB operators and edge-cases.

What do you think? Is that an overhead?