Skip to content

Commit a69ba75

Browse files
authored
Merge pull request #57 from Exabyte-io/update/SOF-6535
SOF-6535: new JSONSchema logic
2 parents 66e64c9 + ba8ebb8 commit a69ba75

12 files changed

+5367
-202
lines changed

package-lock.json

+269-16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
"./dist/utils": "./dist/utils/index.js",
2929
"./dist/constants": "./dist/constants.js",
3030
"./dist/math": "./dist/math.js",
31-
"./dist/JSONSchemasInterface": "./dist/JSONSchemasInterface.js"
31+
"./dist/utils/schemas": "./dist/utils/schemas.js",
32+
"./dist/JSONSchemasInterface": "./dist/JSONSchemasInterface.js",
33+
"./dist/bin/compileTS": "./dist/bin/compileTS.js"
3234
},
3335
"files": [
3436
"/dist",
@@ -42,6 +44,7 @@
4244
"license": "Apache-2.0",
4345
"homepage": "https://github.com/Exabyte-io/code.js",
4446
"dependencies": {
47+
"json-schema-deref-sync": "0.14.0",
4548
"@babel/cli": "7.16.0",
4649
"@babel/core": "7.16.0",
4750
"@babel/eslint-parser": "7.16.3",
@@ -50,11 +53,12 @@
5053
"@babel/preset-react": "7.16.7",
5154
"@babel/register": "^7.16.0",
5255
"@babel/runtime-corejs3": "7.16.8",
53-
"@exabyte-io/esse.js": "2023.7.14-0",
56+
"@exabyte-io/esse.js": "2023.7.28-0",
5457
"ajv": "4.1.7",
5558
"crypto-js": "^4.1.1",
5659
"js-yaml": "^4.1.0",
5760
"json-schema-merge-allof": "^0.8.1",
61+
"json-schema-to-typescript": "^13.0.2",
5862
"lodash": "^4.17.21",
5963
"mathjs": "^3.9.0",
6064
"mixwith": "^0.1.1",

src/JSONSchemasInterface.js

+30-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import { schemas } from "@exabyte-io/esse.js/schemas";
1+
import baseSchema from "@exabyte-io/esse.js/schema";
22
import Ajv from "ajv";
3+
import deref from "json-schema-deref-sync";
34
import mergeAllOf from "json-schema-merge-allof";
45

6+
export const esseSchema = baseSchema;
7+
58
const schemasCache = new Map();
69

710
/**
@@ -41,20 +44,16 @@ function removeSchemaIdsAfterAllOf(schema, clean = false) {
4144
}
4245

4346
export class JSONSchemasInterface {
47+
_schema = null;
48+
4449
/**
4550
*
4651
* @param {string} schemaId id of JSON schema from ESSE
4752
* @returns {Object.<string, any>} resolved JSON schema
4853
*/
4954
static schemaById(schemaId) {
50-
if (!schemasCache.has(schemaId)) {
51-
const originalSchema = schemas.find((schema) => schema.schemaId === schemaId);
52-
53-
if (!originalSchema) {
54-
throw new Error(`Schema not found: ${schemaId}`);
55-
}
56-
57-
this.registerSchema(originalSchema);
55+
if (schemasCache.size === 0) {
56+
JSONSchemasInterface.registerGlobalSchema(esseSchema);
5857
}
5958

6059
return schemasCache.get(schemaId);
@@ -64,16 +63,28 @@ export class JSONSchemasInterface {
6463
*
6564
* @param {Object} - external schema
6665
*/
67-
static registerSchema(originalSchema) {
68-
const schema = mergeAllOf(removeSchemaIdsAfterAllOf(originalSchema), {
69-
resolvers: {
70-
defaultResolver: mergeAllOf.options.resolvers.title,
71-
},
72-
});
66+
static registerGlobalSchema(globalSchema) {
67+
if (JSONSchemasInterface._schema === globalSchema) {
68+
// performance optimization:
69+
// skip resolving as we already did it for the same globalSchema object
70+
return;
71+
}
7372

74-
schemasCache.set(schema.schemaId, schema);
73+
JSONSchemasInterface._schema = globalSchema;
7574

76-
return schema;
75+
const { definitions } = deref(globalSchema);
76+
77+
schemasCache.clear();
78+
79+
Object.values(definitions).forEach((originalSchema) => {
80+
const schema = mergeAllOf(removeSchemaIdsAfterAllOf(originalSchema), {
81+
resolvers: {
82+
defaultResolver: mergeAllOf.options.resolvers.title,
83+
},
84+
});
85+
86+
schemasCache.set(schema.schemaId, schema);
87+
});
7788
}
7889

7990
/**
@@ -99,7 +110,8 @@ export class JSONSchemasInterface {
99110
*/
100111
static matchSchema(query) {
101112
const searchFields = Object.keys(query);
102-
return schemas.find((schema) => {
113+
114+
return Array.from(schemasCache.values()).find((schema) => {
103115
return searchFields.every((field) => {
104116
const { $regex } = query[field];
105117
return new RegExp($regex).test(schema[field]);

src/bin/compileTS.js

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { makeFlatSchemaKey, makeFlatSchemaRef } from "@exabyte-io/esse.js/lib/js/esse/schemaUtils";
2+
import fs from "fs/promises";
3+
import { compile } from "json-schema-to-typescript";
4+
5+
/**
6+
* Compiles ESSE JSON schemas to TypeScript interfaces/types
7+
* @param {Object} globalSchema
8+
* @param {String} savePath
9+
* @returns {Promise<void>}
10+
* @example
11+
* await compileTS(esseSchema, "./dist/types.ts");
12+
*/
13+
export async function compileTS(globalSchema, savePath) {
14+
const preparedDefinitions = Object.entries(globalSchema.definitions).reduce(
15+
(newDefinitions, [key, schema]) => {
16+
if (schema.allOf && schema.properties) {
17+
/**
18+
* The current version of json-schema-to-typescript ignores properties if there is allOf array in the schema.
19+
* To fix the issue here we are creating a separate schema from properties and add it to the allOf array
20+
*/
21+
return [
22+
...newDefinitions,
23+
[
24+
key,
25+
{
26+
...schema,
27+
allOf: [
28+
...schema.allOf,
29+
makeFlatSchemaRef(`${schema.schemaId}-properties`),
30+
],
31+
properties: null,
32+
},
33+
],
34+
[
35+
makeFlatSchemaKey(`${schema.schemaId}-properties`),
36+
{
37+
schemaId: `${schema.schemaId}-properties`,
38+
type: "object",
39+
properties: schema.properties,
40+
},
41+
],
42+
];
43+
}
44+
45+
return [...newDefinitions, [key, schema]];
46+
},
47+
[],
48+
);
49+
50+
const compiled = await compile(
51+
{
52+
...globalSchema,
53+
definitions: Object.fromEntries(preparedDefinitions),
54+
},
55+
"",
56+
{
57+
unreachableDefinitions: true,
58+
},
59+
);
60+
61+
await fs.writeFile(savePath, compiled);
62+
}

src/entity/in_memory.js

+1-34
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import lodash from "lodash";
22

3-
// import { ESSE } from "@exabyte-io/esse.js";
43
import { clone, deepClone } from "../utils/clone";
54
import { getSchemaByClassName } from "../utils/schemas";
65

@@ -191,45 +190,13 @@ export class InMemoryEntity {
191190
return filtered[0];
192191
}
193192

194-
/**
195-
* @summary If there any nested in-memory entities, first resolve them
196-
* and then mix with original schema in baseJSONSchema()
197-
* @returns {Object.<string,InMemoryEntity>|null}
198-
* @example
199-
* class Workflow extends InMemoryEntity {
200-
* get customJsonSchemaProperties() {
201-
* return {
202-
* subworkflows: {
203-
* type: 'array',
204-
* items: Subworkflow.jsonSchema
205-
* }
206-
* };
207-
* }
208-
* }
209-
*/
210-
static get customJsonSchemaProperties() {
211-
return null;
212-
}
213-
214193
/**
215194
* Returns class JSON schema
216195
* @returns {Object} schema
217196
*/
218197
static get jsonSchema() {
219198
try {
220-
if (!this.customJsonSchemaProperties) {
221-
return getSchemaByClassName(this.name);
222-
}
223-
224-
const { properties, ...schema } = getSchemaByClassName(this.name);
225-
226-
return {
227-
...schema,
228-
properties: {
229-
...properties,
230-
...this.customJsonSchemaProperties,
231-
},
232-
};
199+
return getSchemaByClassName(this.name);
233200
} catch (e) {
234201
console.error(e.stack);
235202
throw e;

0 commit comments

Comments
 (0)