microsoft / microsoft/typespec
Emit protovalidate constraints for core TypeSpec decorators
- Dominant language
- Java
- Stars
- 5.9k
- Forks
- 394
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 104
Description
Following up on #4090 and #7838 (cc @rhodee & @witemple-msft) I put together a working implementation and wanted to check whether it covers what you're after before opening a PR.
The idea: the protobuf emitter currently drops the constraint information people already put on their models. If you write `@minLength`, `@maxValue`, `@pattern`, `@format`, etc., none of it makes it into the generated `.proto`. Those decorators are core TypeSpec and the OpenAPI emitter already honours them, so rather than invent anything protobuf-specific, the emitter can translate them into the ecosystem-standard `buf.validate` field options.
It's behind an opt-in emitter option (`emit-protovalidate`). When it's on, any file that emits at least one constraint also gets `import "buf/validate/validate.proto";`.
e.g.:
```typespec
model User {
@field(1) @minLength(3) @maxLength(32) @pattern("^[a-z0-9_]+$") username: string;
@field(2) @format("email") email: string;
@field(3) @minValue(0) @maxValue(150) age: int32;
@field(4) @minItems(1) @maxItems(10) roles: string[];
}
```
produces:
```proto
import "buf/validate/validate.proto";
message User {
string username = 1 [(buf.validate.field).string.min_len = 3, (buf.validate.field).string.max_len = 32, (buf.validate.field).string.pattern = "^[a-z0-9_]+$"];
string email = 2 [(buf.validate.field).string.email = true];
int32 age = 3 [(buf.validate.field).int32.gte = 0, (buf.validate.field).int32.lte = 150];
repeated string roles = 4 [(buf.validate.field).repeated.min_items = 1, (buf.validate.field).repeated.max_items = 10];
}
```
Currently mapped:
- `@minValue` -> `.gte`, `@maxValue` -> `.lte`, `@minValueExclusive` -> `.gt`, `@maxValueExclusive` -> `.lt` (where `` is the field's numeric proto type, e.g. `int32`, `sint64`, `double`)
- `@minLength` -> `string.min_len`, `@maxLength` -> `string.max_len`
- `@pattern` -> `string.pattern`
- `@minItems` -> `repeated.min_items`, `@maxItems` -> `repeated.max_items`
- `@format` -> the matching `string` rule for the values with a direct `buf.validate` analogue: `email`, `uuid`, `hostname`, `ip`, `ipv4`, `ipv6`, `uri`
I didn't add anything that isn't already expressable by TypeSpec decorators, even if it had a protovalidate representation (CEL cross-field expressions, `required`/`ignore` semantics, well-known-type rules). Those would need new protobuf-specific decorators, which felt like a separate conversation (and closer to #4090).
@rhodee -- does this cover your use case, or were you relying on any of the out-of-scope pieces above? And for the maintainers: if the general direction is acceptable, I'm happy to open the PR.
Contributor guide
Assessment
This issue has not been assessed yet.