Skip to content

Commit bc2dd54

Browse files
committed
feat: union support
1 parent f721822 commit bc2dd54

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+6070
-6884
lines changed

.npmignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.github/
2+
.vscode/
3+
coverage/
4+
example/
5+
node_modules/
6+
src/
7+
test/
8+
.editorconfig
9+
.gitignore
10+
.prettierrc
11+
jest.config.ts
12+
tsconfig.json

README.md

+38-14
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export class Fruit {
1717
@AvroInt()
1818
id: number
1919

20-
@AvroString({ description: 'The name of the fruit' })
20+
@AvroString({ fieldDoc: 'The name of the fruit' })
2121
name: string
2222
}
2323
```
@@ -40,7 +40,7 @@ you need to setup the following
4040
// reference your models here
4141
{ class: Fruit }
4242
],
43-
// each referenced model will be written to a file
43+
// each referenced model will be written to a avsc file
4444
outDir: 'src/example/schemas'
4545
}
4646

@@ -63,9 +63,9 @@ you need to setup the following
6363
## Configuration
6464
`Avro Decorators` requires a configuration file written in TypeScript, to ensure the models have applied the decorators accordingly to read the required metadata.
6565

66-
The `models` array is mandatory. Each model requires `class` - a reference to the TypeScript class, and an optional filename `avscFileName` to name the schema output file.
66+
The `models` array in the config is mandatory. Each model requires `class` - a reference to the TypeScript class, and an optional filename `avscFileName` to name the schema output file.
6767

68-
Additionaly, an output directory `outDir` can be declared. If it is not specified, the generated schemas will be printed to stdout instead.
68+
Additionally, an output directory `outDir` can be declared. If it is not specified, the generated schemas will be printed to stdout instead.
6969

7070
### Locating config not in root
7171
By default, `Avro Decorators` will check the current working directory for the file `avro-decorators.config.ts`. If your config is located in a different folder, pass it to the program using the flag `--config <path>` or `-c <path>`.
@@ -74,7 +74,7 @@ By default, `Avro Decorators` will check the current working directory for the f
7474
### Namespace
7575
Declare a namespace for a record as seen in the following example. If you want to use a model name different than the class name, you can use the `name` property.
7676

77-
For enum fields you can also declare them in the decorator.
77+
For enum and fixed fields you can also declare them in the decorator.
7878
```ts
7979
@Record({
8080
namespace: 'fruits.meta',
@@ -89,14 +89,14 @@ export class Fruit {
8989
fruitType: FruitType
9090
}
9191
```
92-
### Different field or record name
93-
To use a different field name in the schema than in the class, you the decorator property `avroOverrideName`:
92+
### Different field or record name in schema than in class
93+
To use a different field name in the schema than in the class, you can use the decorator property `fieldName`:
9494
```ts
95-
@AvroString({avroOverrideName: 'fieldNameInSchema'})
95+
@AvroString({ fieldName: 'fieldNameInSchema' })
9696
fieldNameInClass: string
9797
```
9898
### Nested Records
99-
To use a record inside another record on a field type, you should declare both records independently and then reference it on the field:
99+
To use a record inside another record on a field type, you should declare both records independently and then reference it on the field. It will then be inlined in the schema avsc file:
100100
```ts
101101
@Record()
102102
export class Address {
@@ -111,7 +111,7 @@ export class User {
111111
}
112112
```
113113
### Reference schema by name
114-
Referencing by name works using the
114+
Referencing by name works using `@AvroReferenceByName`:
115115
```ts
116116
@Record()
117117
export class Fruit {
@@ -134,11 +134,35 @@ This will result in the schema
134134
]
135135
}
136136
```
137-
Note that there is no validation if that referenced type actually exists anywhere in this library.
137+
Note that there is no validation if that referenced type actually exists anywhere.
138+
139+
### Unions
140+
Note that if you just want to add a `null` type to a field, you can always use the `nullable` property:
141+
```ts
142+
@Record()
143+
export class Fruit {
144+
@AvroString({ nullable: true })
145+
field: string | null
146+
}
147+
```
148+
To express more complex unions, use the `@AvroUnion` decorator.
149+
It requires a second argument, which is an array of all referenced union types.
150+
```ts
151+
@Record()
152+
export class Address { /* ... */ }
153+
154+
@Record()
155+
export class Model {
156+
@AvroUnion(
157+
{ fieldDoc: 'Can be an int, a string, an address or null' },
158+
['int', 'string', 'null', () => Address]
159+
)
160+
field: number | string | null | Address
161+
}
162+
```
138163

139164
## Features not supported yet
140-
* Union
141-
* Top-level non-record (e. g. enum)
165+
* Top-level non-record (e. g. enum or fixed)
142166
* Validation of `name` and `namespace` according to [specification](https://avro.apache.org/docs/current/spec.html#names)
143-
* Custom tsconfig for model compilation
167+
* Custom tsconfig for complex model compilation
144168

example/fruit.model.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type FruitType = typeof fruitTypes[number]
1616
namespace: 'fruits.meta',
1717
})
1818
export class Origin {
19-
@AvroString({ description: 'The continent the fruit is originally from' })
19+
@AvroString({ fieldDoc: 'The continent the fruit is originally from' })
2020
continent: string
2121
}
2222

@@ -25,18 +25,18 @@ export class Fruit {
2525
@AvroInt()
2626
id: number
2727

28-
@AvroString({ description: 'The name of the fruit' })
28+
@AvroString({ fieldDoc: 'The name of the fruit' })
2929
name: string
3030

31-
@AvroRecord({ ofType: () => Origin, description: 'The origin of the fruit' })
31+
@AvroRecord({ ofType: () => Origin, fieldDoc: 'The origin of the fruit' })
3232
origin: Origin
3333

3434
@AvroEnum({ name: 'FruitType', symbols: fruitTypes })
3535
fruitType: FruitType
3636

37-
@AvroArray({ ofType: () => 'string', default: [] })
37+
@AvroArray({ ofType: 'string', fieldDefault: [] })
3838
flavours: string[]
3939

40-
@AvroMap({ ofType: () => 'int' })
40+
@AvroMap({ ofType: 'int' })
4141
inventory: Record<string, number>
4242
}

example/schemas/Fruit.avsc

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,27 @@
1616
"type": {
1717
"type": "record",
1818
"name": "Origin",
19-
"namespace": "fruits.meta",
20-
"doc": "An origin location",
2119
"fields": [
2220
{
2321
"name": "continent",
2422
"type": "string",
2523
"doc": "The continent the fruit is originally from"
2624
}
27-
]
25+
],
26+
"namespace": "fruits.meta",
27+
"doc": "An origin location"
2828
},
2929
"doc": "The origin of the fruit"
3030
},
3131
{
3232
"name": "fruitType",
3333
"type": {
3434
"type": "enum",
35+
"name": "FruitType",
3536
"symbols": [
3637
"berry",
3738
"tropical"
38-
],
39-
"name": "FruitType"
39+
]
4040
}
4141
},
4242
{

0 commit comments

Comments
 (0)