-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add docs explaining generated code and its API #39
Merged
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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. | ||
glbrntt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is obvious, but should we clarify that "never" means "unless we tag a new major"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, I think that's worth stating.