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

feat: internal to Standard JSON Schema conversion COMPASS-8700 #219

Merged
240 changes: 206 additions & 34 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
".esm-wrapper.mjs"
],
"scripts": {
"test": "nyc mocha --timeout 5000 --colors -r ts-node/register test/*.ts src/**/*.test.ts",
"test": "nyc mocha --timeout 5000 --colors -r ts-node/register test/**/*.ts src/**/*.test.ts",
"test-example-parse-from-file": "ts-node examples/parse-from-file.ts",
"test-example-parse-schema": "ts-node examples/parse-schema.ts",
"test-time": "ts-node ./test/time-testing.ts",
Expand All @@ -60,6 +60,7 @@
"@types/sinon": "^17.0.3",
"@typescript-eslint/eslint-plugin": "^5.47.1",
"@typescript-eslint/parser": "^5.47.1",
"ajv": "^8.17.1",
"bson": "^6.7.0",
"coveralls": "^3.1.1",
"depcheck": "^1.4.3",
Expand Down
10 changes: 10 additions & 0 deletions src/schema-accessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,24 @@ export class InternalSchemaBasedAccessor implements SchemaAccessor {
return this.internalSchema;
}

/**
* Get standard JSON Schema - as per
* https://json-schema.org/draft/2020-12/schema
*/
async getStandardJsonSchema(options: Options = {}): Promise<StandardJSONSchema> {
return this.standardJSONSchema ??= await convertors.internalSchemaToStandard(this.internalSchema, options);
}

/**
* Get MongoDB's $jsonSchema
*/
async getMongoDBJsonSchema(options: Options = {}): Promise<MongoDBJSONSchema> {
return this.mongodbJSONSchema ??= await convertors.internalSchemaToMongoDB(this.internalSchema, options);
}

/**
* Get expanded JSON Schema - with additional properties
*/
async getExpandedJSONSchema(options: Options = {}): Promise<ExpandedJSONSchema> {
return this.ExpandedJSONSchema ??= await convertors.internalSchemaToExpanded(this.internalSchema, options);
}
Expand Down
25 changes: 12 additions & 13 deletions src/schema-convertors/internalToMongoDB.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/**
* Transforms the internal schema to $jsonSchema
*/
import { ArraySchemaType, DocumentSchemaType, Schema as InternalSchema, SchemaType } from '../schema-analyzer';
import { MongoDBJSONSchema } from '../types';
import { allowAbort } from './util';

const InternalTypeToBsonTypeMap: Record<
export const InternalTypeToBsonTypeMap: Record<
SchemaType['name'] | 'Double' | 'BSONSymbol',
string
> = {
Expand Down Expand Up @@ -36,15 +40,6 @@ const convertInternalType = (type: string) => {
return bsonType;
};

async function allowAbort(signal?: AbortSignal) {
return new Promise<void>((resolve, reject) =>
setTimeout(() => {
if (signal?.aborted) return reject(signal?.reason || new Error('Operation aborted'));
resolve();
})
);
}

async function parseType(type: SchemaType, signal?: AbortSignal): Promise<MongoDBJSONSchema> {
await allowAbort(signal);
const schema: MongoDBJSONSchema = {
Expand All @@ -64,6 +59,10 @@ async function parseType(type: SchemaType, signal?: AbortSignal): Promise<MongoD
return schema;
}

function isPlainTypesOnly(types: MongoDBJSONSchema[]): types is { bsonType: string }[] {
return types.every(definition => !!definition.bsonType && Object.keys(definition).length === 1);
}

async function parseTypes(types: SchemaType[], signal?: AbortSignal): Promise<MongoDBJSONSchema> {
await allowAbort(signal);
const definedTypes = types.filter(type => type.bsonType.toLowerCase() !== 'undefined');
Expand All @@ -72,13 +71,13 @@ async function parseTypes(types: SchemaType[], signal?: AbortSignal): Promise<Mo
return parseType(definedTypes[0], signal);
}
const parsedTypes = await Promise.all(definedTypes.map(type => parseType(type, signal)));
if (definedTypes.some(type => ['Document', 'Array'].includes(type.bsonType))) {
if (isPlainTypesOnly(parsedTypes)) {
return {
anyOf: parsedTypes
bsonType: parsedTypes.map(({ bsonType }) => bsonType)
};
}
return {
bsonType: definedTypes.map((type) => convertInternalType(type.bsonType))
anyOf: parsedTypes
};
}

Expand Down
Loading