Closed gurumaxi closed 2 years ago
Heh, I just hit this same issue =] Would love to see this!
So here is one possibility -- I haven't tested it yet with mongodb but it would be easy to hack it a bit to make it work.
const ConsumerPerms = [
'create',
'read',
'admin',
] as const;
type ConsumerPerms = typeof ConsumerPerms[number];
const consumerSchema = {
type: "object",
properties: {
user: {
type: "string",
bsonType: 'objectId',
},
uniqueSlug: { type: "string" },
name: { type: "string" },
perms: {
type: "array",
items: { enum: ConsumerPerms },
},
},
required: ["user", "uniqueSlug", "name", "perms"],
additionalProperties: false,
} as const;
export type consumerFromSchema = FromSchema<typeof consumerSchema, {
deserialize: [{
pattern: {
type: 'string',
bsonType: 'objectId',
},
output: ObjectId,
}],
}>;
The above seems to work perfectly with json-schema-to-ts
-- it doesn't care if you add bsonType
, you just still need to have type
. Now what I don't know is if mongodb is going to puke if you define the above (with both bsonType
and type
) -- however, if it does then you could write a simple wrapper which would just recursively search through the schema object and remove the type
field from anything which also has bsonType
-- not the most elegant solution, but it would give you what you want without needing to wait for an update which allows customizing the type.
If you define both type
and bsonType
it gives you the following error: Cannot specify both $jsonSchema keywords 'type' and 'bsonType'
. For now I indeed wrote my own very minimal wrapper. It isn't the most beautiful way to solve this issue, but it works. I also needed to make sure that these objectId fields can also be nullable (for foreign references). So my deserialize options look like this:
{
deserialize: [{
pattern: {type: 'string', bsonType: 'objectId'},
output: ObjectId,
}, {
pattern: {type: 'string', bsonType: ['objectId', 'null']},
output: ObjectId,
}, {
pattern: {type: 'string', bsonType: ['null', 'objectId']},
output: ObjectId,
}],
}
and my wrapper method, which transforms my Json schema to bson:
function toBsonSchema(schema) {
const bsonSchema = JSON.parse(JSON.stringify(schema));
Object.values(bsonSchema.properties).forEach(obj => {
if (obj.bsonType?.includes('objectId')) {
delete obj.type;
}
});
return bsonSchema;
}
I am not totally satisfied, but it works for the moment. But I would definitely appreciate if json-schema-to-ts
would give as the possibility to specify bsonTypes.
Hi @gurumaxi and @taxilian,
The JSONSchema
type constraint accepts custom keywords (like bsonType
), but it throws if the schema has no property in common with JSONSchema
. You don't need to have type
, another keyword like examples
would have worked :)
Anyway, this shouldn't be an issue since v2.5.5 which I just released to fix https://github.com/ThomasAribart/json-schema-to-ts/issues/84
Now the JSONSchema
constraints has { [key: string]: unknown }
in it, so { bsonType: "objectId" }
should work just fine. Can you confirm this ?
@gurumaxi two notes:
You can use unions in the pattern
keyword instead of using several patterns, it should work just fine:
type test = FromSchema<
{ bsonType: "objectId" },
{
deserialize: [
{
pattern: {
bsonType: "objectId" | ["objectId", "null"] | ["null", "objectId"];
};
output: ObjectId;
}
];
}
>;
// => ObjectId
This is a bit better, but still doesn't scale very well. Sadly, I don't think you can check with an extend
type ternary (which is how the deserialize
feature works) that an array contains a certain value. But you can still use anyOf
keyword instead of using an array in the type
keyword:
type test = FromSchema<
{ anyOf: [{ bsonType: "objectId" }, { bsonType: "somethingElse" }] },
{
deserialize: [
{
pattern: { bsonType: "objectId" };
output: ObjectId;
},
{
pattern: { bsonType: "somethingElse" };
output: SomethingElse;
}
];
}
>;
// => ObjectId | SomethingElse
This will scale properly.
Can confirm, the bsonType attribute now works without any JSONSchema property. Also, the union approach for the deserialize pattern is much better. Thank you very much!
I use json schemas to validate my mongodb collections, and convert them to typescript types with
json-schema-to-ts
. Everything works fantastic.The problem I have is that mongodb saves its ID fields as
ObjectId
objects, not plain strings. With the help of the deserialize option I could easily map the value to theObjectId
type like this:The problem is that in order to be a valid validation schema, I cannot assign the type string to an id field. Mongodb forces all id fields to have the type
bsonType: 'objectId'
and to not be just simple string fields. Since the attributebsonType
is not part of the valid json schema definition, the FromSchema<> conversion obviously throws an error. Is there a way I can circumvent this problem? Maybe extending the JSONSchema type definitions?I hope there is a solutions, since I really want to work with
json-schema-to-ts
because of its many advantages.