microsoft / microsoft/typespec

Emit protovalidate constraints for core TypeSpec decorators

Open
#11,227 2 comments 2 reactions 1 assignee Claimed by @timotheeguerin View on GitHub
emitter:protobuf feature triaged:core
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.