Skip to content

Commit 5ab919e

Browse files
committed
TypeScript generator refactor
Signed-off-by: Clemens Vasters <clemens@vasters.com>
1 parent 142044e commit 5ab919e

8 files changed

+427
-330
lines changed

avrotize/avrotots.py

+189-328
Large diffs are not rendered by default.

avrotize/avrotots/class_core.ts.jinja

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/** {{ class_name }} class. */
2+
{%- if typed_json_annotation %}
3+
import 'reflect-metadata';
4+
import { jsonObject, jsonMember, TypedJSON } from 'typedjson';
5+
{%- endif %}
6+
{%- if avro_annotation %}
7+
import { Type } from 'avro-js';
8+
{%- endif %}
9+
{%- for import_type, import_path in import_types.items() %}
10+
import { {{ import_type }} } from '{{ import_path }}';
11+
{%- endfor %}
12+
{%- if avro_annotation or typed_json_annotation %}
13+
import pako from 'pako';
14+
{%- endif %}
15+
16+
@jsonObject
17+
export class {{ class_name }} {
18+
{%- if avro_annotation %}
19+
public static AvroType: Type = Type.forSchema({{ avro_schema_json }});
20+
{%- endif %}
21+
22+
{%- for field in fields %}
23+
/** {{ field.docstring }} */
24+
{%- if typed_json_annotation %}
25+
@jsonMember
26+
{%- endif %}
27+
public {{ field.name }}: {{ field.type }};
28+
{%- endfor %}
29+
30+
constructor({%- for field in fields %}{{ field.name }}: {{ field.type }}{%- if not loop.last %}, {%- endif %}{%- endfor %}) {
31+
{%- for field in fields %}
32+
{%- if field.is_enum %}
33+
if ( typeof {{ field.name }} === 'number' ) {
34+
this.{{ field.name }} = {{ field.type }}Utils.fromOrdinal({{ field.name }});
35+
} else {
36+
this.{{ field.name }} = {{ field.name }};
37+
}
38+
{%- else %}
39+
this.{{ field.name }} = {{ field.name }};
40+
{%- endif %}
41+
{%- endfor %}
42+
}
43+
44+
{%- if avro_annotation or typed_json_annotation %}
45+
public toByteArray(contentTypeString: string): Uint8Array {
46+
const contentType = contentTypeString.split(';')[0].trim();
47+
let result: Uint8Array | null = null;
48+
49+
{%- if avro_annotation %}
50+
if (contentType.startsWith('avro/binary') || contentType.startsWith('application/vnd.apache.avro+avro')) {
51+
result = ({{ class_name }}.AvroType.toBuffer(this) as unknown) as Uint8Array;
52+
}
53+
{%- endif %}
54+
55+
{%- if typed_json_annotation %}
56+
if (contentType.startsWith('application/json')) {
57+
const serializer = new TypedJSON({{ class_name }});
58+
const jsonString = serializer.stringify(this);
59+
result = new TextEncoder().encode(jsonString);
60+
}
61+
{%- endif %}
62+
63+
if (result && contentTypeString.endsWith('+gzip')) {
64+
result = pako.gzip(result);
65+
}
66+
67+
if (result) {
68+
return result;
69+
} else {
70+
throw new Error(`Unsupported media type: ${contentTypeString}`);
71+
}
72+
}
73+
74+
public static fromData(data: any, contentTypeString: string): {{ class_name }} {
75+
const contentType = contentTypeString.split(';')[0].trim();
76+
77+
if (contentTypeString.endsWith('+gzip')) {
78+
data = pako.ungzip(data);
79+
}
80+
81+
{%- if avro_annotation %}
82+
if (contentType.startsWith('avro/binary') || contentType.startsWith('application/vnd.apache.avro+avro')) {
83+
return {{ class_name }}.AvroType.fromBuffer(data);
84+
}
85+
{%- endif %}
86+
87+
{%- if typed_json_annotation %}
88+
if (contentType.startsWith('application/json')) {
89+
const serializer = new TypedJSON({{ class_name }});
90+
const retval = serializer.parse(new TextDecoder().decode(data));
91+
if (!(retval instanceof {{ class_name }})) {
92+
throw new Error(`Deserialized object is not an instance of {{ class_name }}`);
93+
}
94+
return retval;
95+
}
96+
{%- endif %}
97+
98+
throw new Error(`Unsupported media type: ${contentTypeString}`);
99+
}
100+
101+
public static isJsonMatch(element: any): boolean {
102+
{%- if fields|length == 0 %}
103+
return true;
104+
{%- else %}
105+
return (
106+
{%- for field in fields %}
107+
{{ get_is_json_match_clause(field.name, field.type, field.is_enum) }}{%- if not loop.last %} &&
108+
{%- endif %}
109+
{%- endfor %}
110+
);
111+
{%- endif %}
112+
}
113+
{%- endif %}
114+
}

avrotize/avrotots/enum_core.ts.jinja

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/** {{ enum_name }} enum. */
2+
export enum {{ enum_name }} {
3+
{%- for symbol in symbols %}
4+
{{ symbol }} = "{{ symbol }}"{%- if not loop.last %},{%- endif %}
5+
{%- endfor %}
6+
}
7+
8+
export class {{ enum_name }}Utils {
9+
/**
10+
* Converts a {{ enum_name }} enum value to its ordinal.
11+
* @param value - The {{ enum_name }} enum value ({{ symbols|join(', ') }}).
12+
* @returns The ordinal number corresponding to the enum value.
13+
*/
14+
static toOrdinal(value: {{ enum_name }}): number {
15+
switch (value) {
16+
{%- for symbol in symbols %}
17+
case {{ enum_name }}.{{ symbol }}:
18+
return {{ loop.index0 }};
19+
{%- endfor %}
20+
default:
21+
throw new Error("Invalid {{ enum_name }} value");
22+
}
23+
}
24+
25+
/**
26+
* Converts an ordinal to its corresponding {{ enum_name }} enum value.
27+
* @param ordinal - The ordinal number of the enum value.
28+
* @returns The corresponding {{ enum_name }} enum value.
29+
*/
30+
static fromOrdinal(ordinal: number): {{ enum_name }} {
31+
switch (ordinal) {
32+
{%- for symbol in symbols %}
33+
case {{ loop.index0 }}:
34+
return {{ enum_name }}.{{ symbol }};
35+
{%- endfor %}
36+
default:
37+
throw new Error("Invalid ordinal value");
38+
}
39+
}
40+
}

avrotize/avrotots/gitignore.jinja

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
8+
# Runtime data
9+
pids
10+
*.pid
11+
*.seed
12+
*.pid.lock
13+
14+
# Dependency directories
15+
node_modules/
16+
jspm_packages/
17+
18+
# Typescript declaration files
19+
typings/
20+
21+
# Optional npm cache directory
22+
.npm
23+
24+
# Output of 'npm pack'
25+
*.tgz
26+
27+
# Yarn Integrity file
28+
.yarn-integrity
29+
30+
# dotenv environment variables file
31+
.env
32+
33+
# Build output
34+
dist/

avrotize/avrotots/index.ts.jinja

Whitespace-only changes.

avrotize/avrotots/package.json.jinja

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "{{ package_name }}",
3+
"version": "1.0.0",
4+
"description": "Generated TypeScript classes from Avro schema",
5+
"main": "dist/index.js",
6+
"type": "module",
7+
"scripts": {
8+
"build": "tsc",
9+
"start": "node dist/index.js"
10+
},
11+
"dependencies": {
12+
"avro-js": "^1.12.0",
13+
"typedjson": "^1.8.0",
14+
"pako": "^2.1.0",
15+
"reflect-metadata": "^0.2.2",
16+
"@types/pako": "^2.0.3"
17+
},
18+
"devDependencies": {
19+
"typescript": "^5.4.5"
20+
},
21+
"author": "",
22+
"license": "ISC"
23+
}

avrotize/avrotots/tsconfig.json.jinja

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2022",
4+
"module": "ES2022",
5+
"strict": true,
6+
"esModuleInterop": true,
7+
"skipLibCheck": true,
8+
"forceConsistentCasingInFileNames": true,
9+
"outDir": "./dist",
10+
"rootDir": "./src",
11+
"experimentalDecorators": true,
12+
"emitDecoratorMetadata": true,
13+
"declaration": true,
14+
"declarationMap": true,
15+
"moduleResolution": "node",
16+
"resolveJsonModule": true
17+
},
18+
"include": [
19+
"src/**/*"
20+
]
21+
}

avrotize/common.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ def snake(string):
477477
return result
478478

479479

480-
def fullname(avro_schema: dict):
480+
def fullname(avro_schema: dict| str, parent_namespace: str = '') -> str:
481481
"""
482482
Constructs the full name of the Avro schema.
483483
@@ -487,8 +487,12 @@ def fullname(avro_schema: dict):
487487
Returns:
488488
str: The full name of the Avro schema.
489489
"""
490+
if isinstance(avro_schema, str):
491+
if not '.' in avro_schema and parent_namespace:
492+
return parent_namespace + '.' + avro_schema
493+
return avro_schema
490494
name = avro_schema.get("name", "")
491-
namespace = avro_schema.get("namespace", "")
495+
namespace = avro_schema.get("namespace", parent_namespace)
492496
return namespace + "." + name if namespace else name
493497

494498

0 commit comments

Comments
 (0)