sagold / json-schema-library

Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation
MIT License
164 stars 19 forks source link

Is it possible to add a custom "core" for mongodb's json schema variant? #18

Closed taxilian closed 1 year ago

taxilian commented 1 year ago

Hey,

I love your project -- finally gave me the piece I'd been looking for to build a mongodb tool I've been working with. The only issue I'm hitting is that mongoDB adds a lot of custom types -- and they do it using a different field.

{
  "type": "object",
  "title": "Consumer",
  "properties": {
    "_id": {
      "bsonType": "objectId",
      "tsType": "ObjectId",
    },
    "uniqueSlug": { "type": "string" },
    "name": { "type": "string" },
    "count": { "type": "number" },
    "perms": {
      "type": "array",
      "items": {
        "enum": ["read", "admin"]
      }
    },
    "pubKey": { "type": "string", "default": "NONE DEFINED" }
  },
  "required": ["_id", "uniqueSlug", "name", "perms"],
  "additionalProperties": false
}

This is an example of a schema that I'm working with; while most of the types are "normal", there are some mongodb types that you represent by providing {bsonType: '(type'} instead of using {type: '(type)'. Looking through your API docs and your source code I'm not seeing a way to set something which will match based on a different field name.

One of the things I've been using is schemaEach to try to enumerate all the fields that could be on an object (is there a better way?) but in the above schema it does not fire for _id and I can't help thinking there will be other issues as well.

I'm trying to add a validator for those fields:


    addValidator.format(this, 'objectId', (core, schema, value, pointer) => {
      if (schema.bsonType === 'objectId') {
        if (ObjectId.isValid(value)) {
          return;
        }
        return {
          type: 'error',
          name: 'objectId',
          code: 'INVALID_OBJECT_ID',
          message: 'Invalid objectId',
          data: {
            value,
            pointer,
          },
        };
      }
    });
  }

It seems plausible, but also seems like it may well only fire if {type: 'objectId'} which will never be the case.

Any suggestions? =] I don't mind tweaking code and submitting a PR, but I'd rather not reinvent the wheel if I don't have to.

taxilian commented 1 year ago

And digging in more, looks like that should actually be addValidator.type -- except that type isn't the right keyword in this case =]

Sorry, I'm still new to jsonschema as well as to this project.

taxilian commented 1 year ago

... and as long as I'm staying 100% on topic, it would be really fantastic if you could have the distributed version of this use unminified code -- if a user is deploying to a browser they should be minifying it themselves, but in development it's really hard to step through code with the source minified like it is :-(

sagold commented 1 year ago

Hey taxilian,

let me try:

it would be really fantastic if you could have the distributed version of this use unminified code

unminified code (ts-transpiled output) is available in /dist/module/index.js, you can also see this on the current develop branch https://github.com/sagold/json-schema-library/blob/develop/dist/module/index.js

but in the above schema it does not fire for _id

here, you hit the one exception. You need to remove _id from the blacklist by settings.blacklist.length = 0 https://github.com/sagold/json-schema-library/blob/develop/lib/config/settings.ts

represent by providing {bsonType: '(type'} instead of using {type: '(type)'.

json-schema definitions work only on a type or missing a type for dynamic schemas. Adding an additional switch will complicate things and you probably would need to provide a custom step method: https://github.com/sagold/json-schema-library/blob/develop/lib/step.ts#L230. Purpose of the step function is to get the schema for a given property.

To simplify things i would consider a precompilation step for the mongo schema to work on a consistent schema, if possible. If this is not possible, one could rely on the getTypeOf(data) helper, which will retrieve a type by the passed datatype (string, number, object, ...). So, for each datatype i would try to add a validation method like validateBsonType:

addValidator.keyword(core, "string", "bsonType", validateBsonType);
addValidator.keyword(core, "number", "bsonType", validateBsonType);
addValidator.keyword(core, "boolean", "bsonType", validateBsonType);
addValidator.keyword(core, "object", "bsonType", validateBsonType);
// ...

Thank you for you feedback! Cheers