Skip to content

Commit 36edab9

Browse files
committed
docs(protobuf): document exporter functionality
Signed-off-by: Ahmed Mohamed <[email protected]>
1 parent 653a8f4 commit 36edab9

File tree

1 file changed

+371
-0
lines changed
  • docs-gen/content/docs/tools

1 file changed

+371
-0
lines changed

docs-gen/content/docs/tools/cli.md

Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ query Selection {
7373
```
7474

7575
The composed schema will include:
76+
7677
- Only the selected types: `vehicle`, `adas`, `abs`
7778
- Only the selected fields within each type
7879
- Types referenced by field arguments (e.g., enums used in field arguments)
@@ -109,6 +110,376 @@ type InCabinArea2x2 @instanceTag @reference(source: "S2DM Spec") {
109110

110111
## Export Commands
111112

113+
### Protocol Buffers (Protobuf)
114+
115+
This exporter translates the given GraphQL schema to [Protocol Buffers](https://protobuf.dev/) (`.proto`) format.
116+
117+
#### Key Features
118+
119+
- **Complete GraphQL Type Support**: Handles all GraphQL types including scalars, objects, enums, unions, interfaces, and lists
120+
- **Root Type Filtering**: Use the `--root-type` flag to export only a specific type and its dependencies
121+
- **Flatten Naming Mode**: Use the `--flatten-naming` flag to flatten nested structures into a single message with prefixed field names
122+
- **Expanded Instance Tags**: Use the `--expanded-instances` flag to transform instance tag arrays into nested message structures
123+
- **Directive Support**: Converts S2DM directives like `@cardinality`, `@range`, and `@noDuplicates` to protovalidate constraints
124+
- **Package Name Support**: Use the `--package-name` flag to specify a protobuf package namespace
125+
126+
#### Example Transformation
127+
128+
Consider the following GraphQL schema:
129+
130+
```graphql
131+
type Cabin {
132+
doors: [Door]
133+
temperature: Float
134+
}
135+
136+
type Door {
137+
isLocked: Boolean
138+
instanceTag: DoorPosition
139+
}
140+
141+
type DoorPosition @instanceTag {
142+
row: RowEnum
143+
side: SideEnum
144+
}
145+
146+
enum RowEnum {
147+
ROW1
148+
ROW2
149+
}
150+
151+
enum SideEnum {
152+
DRIVERSIDE
153+
PASSENGERSIDE
154+
}
155+
```
156+
157+
The Protobuf exporter produces:
158+
159+
```protobuf
160+
syntax = "proto3";
161+
162+
import "google/protobuf/descriptor.proto";
163+
import "buf/validate/validate.proto";
164+
165+
extend google.protobuf.MessageOptions {
166+
string source = 50001;
167+
}
168+
169+
message RowEnum {
170+
option (source) = "RowEnum";
171+
172+
enum Enum {
173+
ROWENUM_UNSPECIFIED = 0;
174+
ROW1 = 1;
175+
ROW2 = 2;
176+
}
177+
}
178+
179+
message SideEnum {
180+
option (source) = "SideEnum";
181+
182+
enum Enum {
183+
SIDEENUM_UNSPECIFIED = 0;
184+
DRIVERSIDE = 1;
185+
PASSENGERSIDE = 2;
186+
}
187+
}
188+
189+
message DoorPosition {
190+
option (source) = "DoorPosition";
191+
192+
RowEnum.Enum row = 1;
193+
SideEnum.Enum side = 2;
194+
}
195+
196+
message Cabin {
197+
option (source) = "Cabin";
198+
199+
repeated Door doors = 1;
200+
float temperature = 2;
201+
}
202+
203+
message Door {
204+
option (source) = "Door";
205+
206+
bool isLocked = 1;
207+
DoorPosition instanceTag = 2;
208+
}
209+
```
210+
211+
#### Root Type Filtering
212+
213+
Use the `--root-type` flag to export only a specific type and its dependencies:
214+
215+
```bash
216+
s2dm export protobuf --schema schema.graphql --output vehicle.proto --root-type Vehicle
217+
```
218+
219+
This will include only the `Vehicle` type and all types transitively referenced by it.
220+
221+
#### Flatten Naming Mode
222+
223+
Use the `--flatten-naming` flag to flatten nested object structures into a single message with prefixed field names. This mode requires `--root-type` to be set:
224+
225+
```bash
226+
s2dm export protobuf --schema schema.graphql --output vehicle.proto --root-type Vehicle --flatten-naming
227+
```
228+
229+
**Example transformation:**
230+
231+
Given a GraphQL schema:
232+
233+
```graphql
234+
type Vehicle {
235+
adas: ADAS
236+
}
237+
238+
type ADAS {
239+
abs: ABS
240+
}
241+
242+
type ABS {
243+
isEngaged: Boolean
244+
}
245+
```
246+
247+
Flatten mode produces:
248+
249+
```protobuf
250+
syntax = "proto3";
251+
252+
import "google/protobuf/descriptor.proto";
253+
import "buf/validate/validate.proto";
254+
255+
extend google.protobuf.MessageOptions {
256+
string source = 50001;
257+
}
258+
259+
message Message {
260+
bool Vehicle_adas_abs_isEngaged = 1;
261+
}
262+
263+
```
264+
265+
#### Expanded Instance Tags
266+
267+
The `--expanded-instances` flag transforms instance tag objects into nested message structures instead of repeated fields. This provides compile-time type safety for accessing specific instances.
268+
269+
```bash
270+
s2dm export protobuf --schema schema.graphql --output cabin.proto --expanded-instances
271+
```
272+
273+
**Default behavior (without flag):**
274+
275+
Given a GraphQL schema with instance tags:
276+
277+
```graphql
278+
type Cabin {
279+
doors: [Door]
280+
}
281+
282+
type Door {
283+
isLocked: Boolean
284+
instanceTag: DoorPosition
285+
}
286+
287+
type DoorPosition @instanceTag {
288+
row: RowEnum
289+
side: SideEnum
290+
}
291+
292+
enum RowEnum {
293+
ROW1
294+
ROW2
295+
}
296+
297+
enum SideEnum {
298+
DRIVERSIDE
299+
PASSENGERSIDE
300+
}
301+
```
302+
303+
Default output uses repeated fields and includes the instanceTag field:
304+
305+
```protobuf
306+
syntax = "proto3";
307+
308+
import "google/protobuf/descriptor.proto";
309+
import "buf/validate/validate.proto";
310+
311+
extend google.protobuf.MessageOptions {
312+
string source = 50001;
313+
}
314+
315+
message RowEnum {
316+
option (source) = "RowEnum";
317+
318+
enum Enum {
319+
ROWENUM_UNSPECIFIED = 0;
320+
ROW1 = 1;
321+
ROW2 = 2;
322+
}
323+
}
324+
325+
message SideEnum {
326+
option (source) = "SideEnum";
327+
328+
enum Enum {
329+
SIDEENUM_UNSPECIFIED = 0;
330+
DRIVERSIDE = 1;
331+
PASSENGERSIDE = 2;
332+
}
333+
}
334+
335+
message Door {
336+
option (source) = "Door";
337+
338+
bool isLocked = 1;
339+
DoorPosition instanceTag = 2;
340+
}
341+
342+
343+
message Cabin {
344+
option (source) = "Cabin";
345+
346+
repeated Door doors = 1;
347+
}
348+
349+
350+
message DoorPosition {
351+
option (source) = "DoorPosition";
352+
353+
RowEnum.Enum row = 1;
354+
SideEnum.Enum side = 2;
355+
}
356+
```
357+
358+
**With `--expanded-instances` flag:**
359+
360+
The same schema produces nested messages representing the cartesian product of instance tag values:
361+
362+
```protobuf
363+
syntax = "proto3";
364+
365+
import "google/protobuf/descriptor.proto";
366+
import "buf/validate/validate.proto";
367+
368+
extend google.protobuf.MessageOptions {
369+
string source = 50001;
370+
}
371+
372+
message Door {
373+
option (source) = "Door";
374+
375+
bool isLocked = 1;
376+
}
377+
378+
379+
message Cabin {
380+
option (source) = "Cabin";
381+
382+
message Cabin_Door {
383+
message Cabin_Door_ROW1 {
384+
Door DRIVERSIDE = 1;
385+
Door PASSENGERSIDE = 2;
386+
}
387+
388+
message Cabin_Door_ROW2 {
389+
Door DRIVERSIDE = 1;
390+
Door PASSENGERSIDE = 2;
391+
}
392+
393+
Cabin_Door_ROW1 ROW1 = 1;
394+
Cabin_Door_ROW2 ROW2 = 2;
395+
}
396+
397+
Cabin_Door Door = 1;
398+
}
399+
```
400+
401+
**Key differences:**
402+
403+
- Instance tag enums (`RowEnum`, `SideEnum`) are excluded from the output when using expanded instances
404+
- Types with `@instanceTag` directive (`DoorPosition`) are excluded from the output
405+
- The `instanceTag` field is excluded from the Door message
406+
- Nested messages are created inside the parent message
407+
- Field names use the GraphQL type name (`Door` not `doors`)
408+
409+
#### Directive Support
410+
411+
S2DM directives are converted to [protovalidate](https://github.com/bufbuild/protovalidate) constraints:
412+
413+
- `@range(min: 0, max: 100)``[(buf.validate.field).int32 = {gte: 0, lte: 100}]`
414+
- `@noDuplicates``[(buf.validate.field).repeated = {unique: true}]`
415+
- `@cardinality(min: 1, max: 5)``[(buf.validate.field).repeated = {min_items: 1, max_items: 5}]`
416+
417+
Example:
418+
419+
```graphql
420+
type Vehicle {
421+
speed: Int @range(min: 0, max: 300)
422+
tags: [String] @noDuplicates @cardinality(min: 1, max: 10)
423+
}
424+
```
425+
426+
Produces:
427+
428+
```protobuf
429+
syntax = "proto3";
430+
431+
import "google/protobuf/descriptor.proto";
432+
import "buf/validate/validate.proto";
433+
434+
extend google.protobuf.MessageOptions {
435+
string source = 50001;
436+
}
437+
438+
message Vehicle {
439+
option (source) = "Vehicle";
440+
441+
int32 speed = 1 [(buf.validate.field).int32 = {gte: 0, lte: 300}];
442+
repeated string tags = 2 [(buf.validate.field).repeated = {unique: true, min_items: 1, max_items: 10}];
443+
}
444+
```
445+
446+
#### Type Mappings
447+
448+
GraphQL types are mapped to protobuf types as follows:
449+
450+
| GraphQL Type | Protobuf Type |
451+
|--------------|---------------|
452+
| `String` | `string` |
453+
| `Int` | `int32` |
454+
| `Float` | `float` |
455+
| `Boolean` | `bool` |
456+
| `ID` | `string` |
457+
| `Int8` | `int32` |
458+
| `UInt8` | `uint32` |
459+
| `Int16` | `int32` |
460+
| `UInt16` | `uint32` |
461+
| `UInt32` | `uint32` |
462+
| `Int64` | `int64` |
463+
| `UInt64` | `uint64` |
464+
465+
**List types** are converted to `repeated` fields:
466+
467+
- `[String]``repeated string`
468+
- `[Int]``repeated int32`
469+
470+
**Enums** are converted to protobuf enums wrapped in a message:
471+
472+
- Each GraphQL enum becomes a protobuf message with the same name
473+
- Inside the message, an `Enum` nested enum is created
474+
- An `UNSPECIFIED` value is added at position 0
475+
- References use the `.Enum` suffix (e.g., `LockStatus.Enum`)
476+
477+
You can call the help for usage reference:
478+
479+
```bash
480+
s2dm export protobuf --help
481+
```
482+
112483
### Naming Configuration
113484

114485
All export commands support a global naming configuration feature that allows you to transform element names during the export process using the `[--naming-config | -n]` flag.

0 commit comments

Comments
 (0)