-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add docs explaining generated code and its API
Motivation: Users should be able to understand how the generated code is structured and how it may change over time. Modifications: Add two docs: 1. Explain the structure of the generate code and how to navigate it. 2. Explain how the generated code may change and hot to ensure it doesn't cause API breakages. Result: Better docs
- Loading branch information
Showing
3 changed files
with
223 additions
and
0 deletions.
There are no files selected for viewing
78 changes: 78 additions & 0 deletions
78
...ces/GRPCProtobuf/Documentation.docc/Articles/API-stability-of-generated-code.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# API stability of generated code | ||
|
||
Understand the impact of changes you make to your Protocol Buffers files on the | ||
generated Swift code. | ||
|
||
## Overview | ||
|
||
The API of the generated code depends on three factors: | ||
|
||
- The contents of the source `.proto` file. | ||
- The options you use when generating the code. | ||
- The code generator (the `protoc-gen-grpc-swift` plugin for `protoc`). | ||
|
||
While this document applies specifically to the gRPC code generated and *not* | ||
code for messages used as inputs and outputs of each method, the concepts still | ||
broadly apply. | ||
|
||
Some of the concepts used in this document are described in more detail in | ||
<doc:Understanding-the-generated-code>. | ||
|
||
## The source .proto file | ||
|
||
The source `.proto` file defines the API of your service. You'll likely provide | ||
it to users so that they can generate clients from it. In order to maintain API | ||
stability for your service and for clients you must adhere to the following | ||
rules: | ||
|
||
1. You mustn't change the `package` the service is defined in. | ||
2. You mustn't change or add the `swift_prefix` option. | ||
3. You mustn't remove or change the name of any services in your `.proto` file. | ||
4. You mustn't remove or change the name of any RPCs in your `.proto` file. | ||
5. You mustn't change the message types used by any RPCs in your `.proto` file. | ||
|
||
Failure to follow these will result in changes in the generated code which can | ||
result in build failures for users. | ||
|
||
Whilst this sounds restrictive you may do the following: | ||
|
||
1. You may add a new RPC to an existing service in your `.proto` file. | ||
2. You may add a new service to your `.proto` file (however it is recommended | ||
that you define a single service per `.proto` file). | ||
|
||
## The options you use for generating code | ||
|
||
Code you generate into your Swift Package becomes part of the API of your | ||
package. You must therefore ensure that downstream users of your package aren't | ||
impacted by the options you use when generating code. | ||
|
||
By default code is generated at the `internal` access level and therefore not | ||
part of the public API. You must explicitly opt in to generating code at the | ||
`public` access level. If you do this then you must be aware that changing what | ||
is generated (clients, servers) affects the public API, as does the access level | ||
of the generated code. | ||
|
||
If you need to validate whether your API has changed you can use tools like | ||
Swift Package Manager's API breakage diagnostic (`swift package | ||
diagnose-api-breaking-changes`.) In general you should prefer providing users | ||
with the service's `.proto` file so that they can generate clients, or provide a | ||
library which wraps the client to hide the API of the generated code. | ||
|
||
## The code generator | ||
|
||
The gRPC Swift maintainers may need to evolve the generated code over time. This | ||
will be done in a source-compatible way. | ||
|
||
If APIs are no longer suitable then they may be deprecated in favour of new | ||
ones. Existing API will never be removed and deprecated APIs will continue to | ||
function. | ||
|
||
If the generator introduces new ways to generate code which are incompatible | ||
with the previously generated code then they will require explicit opt-in via an | ||
option. | ||
|
||
As gRPC Swift is developed the generated code may need to rely on newer | ||
functionality from its runtime counterparts (`GRPCCore` and `GRPCProtobuf`). | ||
This means that you should use the versions of `protoc-gen-grpc-swift` and | ||
`protoc-gen-swift` resolved with your package rather than getting them from an | ||
out-of-band (such as `homebrew`). |
143 changes: 143 additions & 0 deletions
143
...es/GRPCProtobuf/Documentation.docc/Articles/Understanding-the-generated-code.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
# Understanding the generated code | ||
|
||
Understand what code is generated by `protoc-gen-grpc-swift` from a `.proto` | ||
file and how to use it. | ||
|
||
## Overview | ||
|
||
The gRPC Swift Protobuf package provides a plugin to the Protocol Buffers | ||
Compiler (`protoc`) called `protoc-gen-grpc-swift`. The plugin is responsible | ||
for generating the gRPC specific code for services defined in a `.proto` file. | ||
|
||
### Package namespace | ||
|
||
Most `.proto` files contain a `package` directive near the start of the file | ||
describing the namespace it belongs to. Here's an example: | ||
|
||
```proto | ||
package foo.bar.v1; | ||
``` | ||
|
||
The package name "foo.bar.v1" is important as it is used as a prefix for | ||
generated types. The default behaviour is to replace periods with underscores | ||
and to capitalize each word and add a trailing underscore. For this package the | ||
prefix is "Foo\_Bar\_V1\_". If you don't declare a package then the prefix will be | ||
the empty string. | ||
|
||
You can override the prefix by setting the `swift_prefix` option: | ||
|
||
```proto | ||
option swift_prefix = "FooBarV1"; | ||
package foo.bar.v1; | ||
``` | ||
|
||
The prefix for types in this file would be "FooBarV1" instead of "Foo\_Bar\_V1\_". | ||
|
||
### Service namespace | ||
|
||
For each service declared in your `.proto` file, gRPC will generate a caseless | ||
`enum` which is a namespace holding the generated protocols and types. The name | ||
of this `enum` is `{PREFIX}{SERVICE}` where `{PREFIX}` is as described in the | ||
previous section and `{SERVICE}` is the name of the service as declared in the | ||
`.proto` file. | ||
|
||
As an example the following definition creates a service namespace `enum` called | ||
`Foo_Bar_V1_BazService` (the `{PREFIX}` is "Foo_Bar_V1_" and `{SERVICE}` is | ||
"BazService"): | ||
|
||
```proto | ||
package foo.bar.v1; | ||
service BazService { | ||
// ... | ||
} | ||
``` | ||
|
||
Code generated for each service falls into three categories: | ||
|
||
1. Service metadata, | ||
2. Service code, and | ||
3. Client code. | ||
|
||
#### Service metadata | ||
|
||
gRPC generates metadata for each service including a descriptor identifying the | ||
fully qualified name of the service and information about each method in the | ||
service. You likely won't need to interact directly with this information but | ||
it's available should you need to. | ||
|
||
#### Service code | ||
|
||
Within a service namespace gRPC generates three service protocols: | ||
|
||
1. `StreamingServiceProtocol`, | ||
2. `ServiceProtocol`, and | ||
3. `SimpleServiceProtocol`. | ||
|
||
The full name of each protocol includes the service namespace. | ||
|
||
> Example: | ||
> | ||
> For the `BazService` in the `foo.bar.v1` package the protocols would be: | ||
> | ||
> - `Foo_Bar_V1_BazService.StreamingServiceProtocol`, | ||
> - `Foo_Bar_V1_BazService.ServiceProtocol`, and | ||
> - `Foo_Bar_V1_BazService.SimpleServiceProtocol`. | ||
Each of these protocols mirror the `service` defined in your `.proto` file with | ||
one requirement per RPC. To implement your service you must implement one of | ||
these protocols. | ||
|
||
The protocols form a hierarchy with `StreamingServiceProtocol` at the bottom and | ||
`SimpleServiceProtocol` at the top. `ServiceProtocol` refines | ||
`StreamingServiceProtocol`, and `SimpleServiceProtocol` refines | ||
`ServiceProtocol` (and `StreamingServiceProtocol` in turn). | ||
|
||
The `StreamingServiceProtocol` implements each RPC as if it were a bidirectional | ||
streaming RPC. This gives you the most flexibility at the cost of a harder to | ||
implement API. It also puts the responsibility on you to ensure that each RPC | ||
sends and receives the correct number of messages. | ||
|
||
The `ServiceProtocol` enforces that the correct number of messages are sent and | ||
received via its API. It also allows you to read request metadata and send both | ||
initial and trailing metadata. The request and response types for these | ||
requirements are in terms of `ServerRequest` and `ServerResponse`. | ||
|
||
The `SimpleServiceProtocol` also enforces the correct number of messages are | ||
sent and received via its API. However, it isn't defined in terms of | ||
`ServerRequest` or `ServerResponse` so it doesn't allow you access metadata. | ||
This limitation allows it to have the simplest API and is preferred if you don't | ||
need access to metadata. | ||
|
||
| Protocol | Enforces number of messages | Access to metadata | ||
|----------------------------|-----------------------------|------------------- | ||
| `StreamingServiceProtocol` | ✗ | ✓ | ||
| `ServiceProtocol` | ✓ | ✓ | ||
| `SimpleServiceProtocol` | ✓ | ✗ | ||
|
||
#### Client code | ||
|
||
gRPC generates two types for the client within a service namespace: | ||
|
||
1. `ClientProtocol`, and | ||
2. `Client`. | ||
|
||
Like the service code, the full name includes the namespace. | ||
|
||
> Example: | ||
> | ||
> For the `BazService` in the `foo.bar.v1` package the client types would be: | ||
> | ||
> - `Foo_Bar_V1_BazService.ClientProtocol`, and | ||
> - `Foo_Bar_V1_BazService.Client`. | ||
The `ClientProtocol` defines one requirement for each RPC in terms of | ||
`ClientRequest` and `ClientResponse`. You don't need to implement the protocol | ||
as `Client` provides a concrete implementation. | ||
|
||
gRPC also generates extensions on `ClientProtocol` to provide more ergonomic | ||
APIs. These include versions which provide default arguments for various | ||
parameters (like the message serializer and deserializers; call options and | ||
response handler) and versions which don't use `ClientRequest` and | ||
`ClientResponse` directly. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters