Skip to content
This repository was archived by the owner on Oct 31, 2024. It is now read-only.

Commit e46146f

Browse files
committed
feat: wip
1 parent b03647e commit e46146f

9 files changed

+3975
-0
lines changed

.editorconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[*.yml]
2+
indent_size = 2
3+
indent_style = space
4+
5+
[*]
6+
indent_size = 4
7+
indent_style = space

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules
2+
dist
3+
.DS_Store
4+
.idea
5+
.vscode
6+
yarn-error.log
7+
package-lock.json

.prettierrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
trailingComma: "es5"
2+
semi: true
3+
printWidth: 120
4+
tabWidth: 4
5+
singleQuote: true
6+
arrowParens: "always"
7+
jsxSingleQuote: true

notebooks/genson-test.ipynb

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"metadata": {
3+
"language_info": {
4+
"codemirror_mode": {
5+
"name": "ipython",
6+
"version": 3
7+
},
8+
"file_extension": ".py",
9+
"mimetype": "text/x-python",
10+
"name": "python",
11+
"nbconvert_exporter": "python",
12+
"pygments_lexer": "ipython3",
13+
"version": "3.7.7-final"
14+
},
15+
"orig_nbformat": 2,
16+
"kernelspec": {
17+
"name": "python_defaultSpec_1598516446951",
18+
"display_name": "Python 3.7.7 64-bit"
19+
}
20+
},
21+
"nbformat": 4,
22+
"nbformat_minor": 2,
23+
"cells": [
24+
{
25+
"cell_type": "code",
26+
"execution_count": 47,
27+
"metadata": {
28+
"tags": []
29+
},
30+
"outputs": [
31+
{
32+
"output_type": "stream",
33+
"name": "stdout",
34+
"text": "{\n \"$schema\": \"http://json-schema.org/schema#\",\n \"properties\": {\n \"hi\": {\n \"anyOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"items\": {\n \"anyOf\": [\n {\n \"type\": [\n \"integer\",\n \"string\"\n ]\n },\n {\n \"properties\": {\n \"test\": {\n \"type\": \"string\"\n },\n \"three\": {\n \"type\": [\n \"array\",\n \"string\"\n ]\n },\n \"two\": {\n \"anyOf\": [\n {\n \"type\": [\n \"integer\",\n \"string\"\n ]\n },\n {\n \"items\": {\n \"type\": \"integer\"\n },\n \"type\": \"array\"\n }\n ]\n }\n },\n \"type\": \"object\"\n }\n ]\n },\n \"type\": \"array\"\n }\n ]\n }\n },\n \"required\": [\n \"hi\"\n ],\n \"type\": \"object\"\n}\n"
35+
}
36+
],
37+
"source": [
38+
"import pprint\n",
39+
"import genson\n",
40+
"import json\n",
41+
"pp = pprint.PrettyPrinter(indent=4,depth=None)\n",
42+
"b1 = genson.SchemaBuilder()\n",
43+
"b1.add_object({\"hi\": \"there\"})\n",
44+
"s1 = b1.to_schema()\n",
45+
"\n",
46+
"b2 = genson.SchemaBuilder()\n",
47+
"b2.add_object({\"hi\": [{\"test\": \"value\"}, 10, \"string\", {\"two\": \"val\"}, {\"two\": 2}, {\"two\": [1]}, {\"three\": []}, {\"three\": \"s\"}]})\n",
48+
"s2 = b2.to_schema()\n",
49+
"\n",
50+
"\n",
51+
"b3 = genson.SchemaBuilder()\n",
52+
"b3.add_schema(s1)\n",
53+
"b3.add_schema(s2)\n",
54+
"s3 = b3.to_json()\n",
55+
"\n",
56+
"parsed = json.loads(s3)\n",
57+
"print(json.dumps(parsed, indent=4, sort_keys=True))\n",
58+
"\n"
59+
]
60+
}
61+
]
62+
}

package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "genson-js",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"license": "MIT",
6+
"devDependencies": {
7+
"@types/jest": "^26.0.10",
8+
"@types/node": "^14.6.0",
9+
"jest": "^26.4.2",
10+
"typescript": "^4.0.2"
11+
}
12+
}

src/index.ts

Whitespace-only changes.

tests/schema-builder.spec.ts

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
import { isArray } from 'util';
2+
3+
enum ValueType {
4+
Null = 'null',
5+
Boolean = 'boolean',
6+
Number = 'number',
7+
String = 'string',
8+
Object = 'object',
9+
Array = 'array',
10+
}
11+
12+
type Schema = SingleTypeSchema | AnyOfSchema;
13+
type SingleTypeSchema = PrimitiveSchema | ContainerSchema;
14+
type PrimitiveSchema = NullSchema | BooleanSchema | NumberSchema | StringSchema;
15+
type ContainerSchema = ArraySchema | ObjectSchema;
16+
type AnyOfSchema = RegularAnyOfSchema | SimplifiedAnyOfSchema;
17+
18+
type NullSchema = {
19+
type: ValueType.Null;
20+
};
21+
22+
type BooleanSchema = {
23+
type: ValueType.Boolean;
24+
};
25+
26+
type NumberSchema = {
27+
type: ValueType.Number;
28+
};
29+
30+
type StringSchema = {
31+
type: ValueType.String;
32+
};
33+
34+
type ArraySchema = {
35+
type: ValueType.Array;
36+
items?: Schema;
37+
};
38+
39+
type ObjectSchema = {
40+
type: ValueType.Object;
41+
properties?: Record<string, Schema>;
42+
required?: string[];
43+
};
44+
45+
type SimplifiedAnyOfSchema = {
46+
type: ValueType[];
47+
};
48+
49+
type RegularAnyOfSchema = {
50+
anyOf: Schema[];
51+
};
52+
53+
function createSchemaFor(value: any): SingleTypeSchema {
54+
switch (typeof value) {
55+
case 'number':
56+
return { type: ValueType.Number };
57+
case 'boolean':
58+
return { type: ValueType.Boolean };
59+
case 'string':
60+
return { type: ValueType.String };
61+
case 'object':
62+
if (value === null) {
63+
return { type: ValueType.Null };
64+
}
65+
if (Array.isArray(value)) {
66+
return createSchemaForArray(value);
67+
}
68+
return createSchemaForObject(value);
69+
}
70+
}
71+
72+
function createSchemaForArray(arr: Array<any>): SingleTypeSchema {
73+
if (arr.length === 0) {
74+
return { type: ValueType.Array };
75+
}
76+
const elementSchemas = arr.map((value) => createSchemaFor(value));
77+
const items = combineSchemas(elementSchemas);
78+
return { type: ValueType.Array, items };
79+
}
80+
81+
function createSchemaForObject(obj: Object): SingleTypeSchema {
82+
const schemasByProp = Object.entries(obj).reduce((acc, [prop, value]) => {
83+
const existingSchemas = acc[prop];
84+
if (!existingSchemas) {
85+
acc[prop] = [createSchemaFor(value)];
86+
} else {
87+
acc[prop].push(createSchemaFor(value));
88+
}
89+
return acc;
90+
}, {});
91+
return { type: ValueType.Object, properties: schemasByProp };
92+
}
93+
94+
function combineSchemas(schemas: SingleTypeSchema[]): Schema {
95+
const schemasByType: Record<ValueType, SingleTypeSchema[]> = {
96+
[ValueType.Null]: [],
97+
[ValueType.Boolean]: [],
98+
[ValueType.Number]: [],
99+
[ValueType.String]: [],
100+
[ValueType.Array]: [],
101+
[ValueType.Object]: [],
102+
};
103+
104+
for (const schema of schemas) {
105+
const { type } = schema;
106+
if (schemasByType[type].length === 0 || isContainerSchema(schema)) {
107+
schemasByType[type].push(schema);
108+
} else {
109+
continue;
110+
}
111+
}
112+
113+
const resultSchemasByType: Record<ValueType, Schema> = {
114+
[ValueType.Null]: schemasByType[ValueType.Null][0],
115+
[ValueType.Boolean]: schemasByType[ValueType.Boolean][0],
116+
[ValueType.Number]: schemasByType[ValueType.Number][0],
117+
[ValueType.String]: schemasByType[ValueType.String][0],
118+
[ValueType.Array]: combineArraySchemas(schemasByType[ValueType.Array] as ArraySchema[]),
119+
[ValueType.Object]: combineObjectSchemas(schemasByType[ValueType.Object] as ObjectSchema[]),
120+
};
121+
122+
const schemasFound = Object.values(resultSchemasByType).filter(Boolean);
123+
const multiType = schemasFound.length > 1;
124+
if (multiType) return { anyOf: schemasFound };
125+
return schemasFound[0] as Schema;
126+
}
127+
128+
function combineArraySchemas(schemas: ArraySchema[]): ArraySchema {
129+
const itemSchemas: SingleTypeSchema[] = [];
130+
for (const schema of schemas) {
131+
// todo: think on this case
132+
if (!schema.items) continue;
133+
if (isSimplifiedAnyOfSchema(schema.items)) {
134+
} else if (isRegularAnyOfSchema(schema.items)) {
135+
for (const itemSchema of schema.items.anyOf) {
136+
itemSchemas.push(itemSchema);
137+
}
138+
} else {
139+
}
140+
}
141+
}
142+
143+
function combineObjectSchemas(schemas: ObjectSchema[]): ObjectSchema {}
144+
145+
function isContainerSchema(schema: Schema): schema is ContainerSchema {
146+
const type = (schema as SingleTypeSchema).type;
147+
return type === ValueType.Array || type === ValueType.Object;
148+
}
149+
150+
function isSimplifiedAnyOfSchema(schema: Schema): schema is SimplifiedAnyOfSchema {
151+
return Array.isArray((schema as SingleTypeSchema).type);
152+
}
153+
154+
function isRegularAnyOfSchema(schema: Schema): schema is RegularAnyOfSchema {
155+
return typeof (schema as RegularAnyOfSchema).anyOf !== 'undefined';
156+
}
157+
158+
function isAnyOfSchema(schema: Schema): schema is AnyOfSchema {
159+
return isRegularAnyOfSchema(schema) || isSimplifiedAnyOfSchema(schema);
160+
}
161+
162+
function generateSchema(value: any): Schema {
163+
return createSchemaFor(value);
164+
}
165+
166+
function simplifiedAnyOfToRegular(schema: SimplifiedAnyOfSchema): RegularAnyOfSchema {
167+
return {
168+
anyOf: schema.type.map((valueType) => ({
169+
type: valueType,
170+
})),
171+
};
172+
}
173+
174+
function simplifyRegularAnyOfSchema(schema: RegularAnyOfSchema): SimplifiedAnyOfSchema {
175+
return {
176+
type: schema.anyOf.map((s) => {
177+
s.type;
178+
}),
179+
};
180+
}
181+
182+
function simplifySchemas(schemas: SingleTypeSchema[]) {
183+
return {
184+
type: unique([schemas.map((s) => s.type)]),
185+
};
186+
}
187+
188+
function unsimplifySchema(schema: Schema): Schema {
189+
if (isSimplifiedAnyOfSchema(schema))
190+
return { anyOf: (schema as SimplifiedAnyOfSchema).type.map((s) => ({ type: s })) } as Schema;
191+
return schema;
192+
}
193+
194+
function unique(array: any[]) {
195+
const set = new Set(array);
196+
const uniqueValues = [...set.values()];
197+
return uniqueValues;
198+
}
199+
200+
// function mergeComplexSchemas(schema1: Schema, schema2: Schema): Schema {
201+
// if (schema1.type === ValueType.Array && schema2.type === ValueType.Array)
202+
// return mergeArraySchemas(schema1, schema2);
203+
// if (schema1.type === ValueType.Object && schema2.type === ValueType.Object)
204+
// return mergeObjectSchemas(schema1, schema2);
205+
// throw new Error(`Can't merge complex schemas of different types ${schema1.type} and ${schema2.type}`);
206+
// }
207+
208+
// function mergeObjectSchemas(s1: ObjectSchema, s2: ObjectSchema): ObjectSchema {
209+
// const props1 = Object.entries(s1.properties);
210+
// const props2 = Object.entries(s2.properties);
211+
// // if ()
212+
// }
213+
214+
// function mergeArraySchemas(s1: ArraySchema, s2: ArraySchema): ArraySchema {
215+
// const itemSchemaTypes: Record<ValueType, boolean | Schema> = {
216+
// [ValueType.Array]: false,
217+
// [ValueType.Object]: false,
218+
// [ValueType.Boolean]: false,
219+
// [ValueType.Number]: false,
220+
// [ValueType.String]: false,
221+
// [ValueType.Null]: false,
222+
// };
223+
// }
224+
// function getUniqueSchemas(schemas: Schema[]) {
225+
// const uniqueSchemas = [];
226+
// schemas.forEach((schema) => {
227+
// if (!uniqueSchemas.some((s) => areEqual(s, schema))) {
228+
// uniqueSchemas.push(schema);
229+
// }
230+
// });
231+
// return uniqueSchemas;
232+
// }
233+
234+
// function areEqual(s1: Schema, s2: Schema): boolean {
235+
// if (s1.type != s2.type) return false;
236+
// if (s1.type == NodeType.Array && s2.type == NodeType.Array) {
237+
// if (typeof s1.items !== typeof s2.items) return false;
238+
// if (Array.isArray(s1.items) && Array.isArray(s2.items)) {
239+
// if (s1.items?.length !== s2.items?.length) return false;
240+
// return;
241+
// }
242+
// }
243+
// }
244+
245+
// function mergeSchemas(schemas: Schema[]): Schema {}
246+
// function extendSchema(object: any): Schema {}
247+
// function isSuperset(schema: Schema, schema: Schema): boolean {}
248+
249+
describe('SchemaBuilder', () => {
250+
it('should have nice api', async () => {});
251+
});

tsconfig.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2017",
4+
"lib": ["ES2017"],
5+
"rootDir": "./src",
6+
"outDir": "dist",
7+
"module": "commonjs",
8+
"moduleResolution": "node",
9+
"strict": false,
10+
"declaration": true,
11+
"sourceMap": true,
12+
"inlineSources": true,
13+
"types": ["node", "jest"],
14+
"allowSyntheticDefaultImports": true,
15+
"esModuleInterop": true
16+
},
17+
"include": ["src/**/*"],
18+
"exclude": ["**/node_modules/*", "dist"]
19+
}

0 commit comments

Comments
 (0)