-
Notifications
You must be signed in to change notification settings - Fork 420
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 reflection support to plugin #1835
Conversation
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.
Thanks for taking the time to look at this and opening a PR. However, your PR has a number of issues. The biggest of which is related to how the data is generated needs some more thought / discussion.
As proposed, the reflection data is generated for each service and is the data for the whole file containing that service.
The issue with this approach is that it is incomplete: messages can be declared in different proto files.
Another issue is duplication: a file may contain many services, each of which would contain the same generated data. This also creates a surprising API: if a users adds FooService
to their list of reflected services, then they may expose BarService
without realising.
Ah I see. I was assuming one service per proto file but that is obviously not a restriction of the specification. Currently if What do you think about this approach: For each invocation/file with
I wondering what the downsides of putting that much data into a string would be and if there are other approaches for reading the |
If you create one Swift file per input file then you need a way to find all of those inputs at runtime. If they are generated as part of Swift structs then users would need to find and add each provider manually (i.e. user is responsible for finding all dependencies of a service), so from an ergonomics point of view I don't think it's a good solution. This is much less of an issue with the current implementation because it's easy to list all files with a given extension or in a given directory. |
I agree - the ergonomics aren't great. I explored some other solutions for this. If there was a way to have some file such as:
then for every invocation and each file that reflection is enabled for another file is created:
but Swift does not allow for extensions on enums in this way. Another approach is to have a file thats:
but each invocation of the plugin could potentially generate this file. My next idea was to flatMap all the invocations with reflections enabled and generate the above file for the protofiles that want reflection enabled. |
In terms or ergonomics, I think your original proposal is good (i.e. registering a service directly with the reflection service). It's very clear to the user. The issue is that it needs to include all transitive dependencies in there too. I would probably take that as a starting point and work out how to make that possible. |
ace4c59
to
195030c
Compare
I tried a different approach and would like your thoughts on it. I've just added the generated reflection file as an output file for the plugin. This way the same files are generated when you use the plugin or protoc directly. By including the As far as CI is concerned, I'm not sure if installing protoc via apt is the best approach or if that should be included in a base image from somewhere else. |
f853986
to
8177506
Compare
Oh, this is great! I didn't realise non-Swift outputs can be included in the
This is a bit of an annoying one: we don't want to add a build target which relies on the plugin because it'll force everyone who tries to build it locally to have |
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.
The overall shape of this is good but we need to remove the example and CI changes unfortunately, see #1835 (comment).
Plugins/GRPCSwiftPlugin/plugin.swift
Outdated
@@ -205,6 +211,12 @@ struct GRPCSwiftPlugin { | |||
|
|||
// Add the outputPath as an output file | |||
outputFiles.append(protobufOutputPath) | |||
|
|||
if invocation.reflection == true { |
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.
No need to compare against true
, just if invocation.reflection { ... }
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.
invocation.reflection
is an optional bool so if invocation.reflection { ... }
was giving me an error.
Plugins/GRPCSwiftPlugin/plugin.swift
Outdated
@@ -205,6 +211,12 @@ struct GRPCSwiftPlugin { | |||
|
|||
// Add the outputPath as an output file | |||
outputFiles.append(protobufOutputPath) | |||
|
|||
if invocation.reflection == true { | |||
let reflectionFile = file.replacingOccurrences(of: "grpc.swift", with: "grpc.reflection") |
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.
The code above deliberately doesn't use this pattern because it's not necessarily correct. The filename could be "grpc.swift.foo.bar.grpc.swift" and this would incorrectly rename it to "grpc.reflection.foo.bar.grpc.reflection".
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.
Changed the logic here so drop the last swift
and add reflection
similar to how proto
is dropped and `swift is appended.
8177506
to
4cc7004
Compare
Pushed some commits responding to the comments above and added some comments to existing documentation. |
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.
Doc nit, looks good otherwise!
@@ -70,6 +70,25 @@ You can use Swift Package Manager [resources][swiftpm-resources] to add the gene | |||
In our example the reflection data is written into the "Generated" directory within the target | |||
so we include the `.copy("Generated")` rule in our target's resource list. | |||
|
|||
#### SPM Plugin |
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.
#### SPM Plugin | |
#### Swift Package Manager Plugin |
@@ -70,6 +70,25 @@ You can use Swift Package Manager [resources][swiftpm-resources] to add the gene | |||
In our example the reflection data is written into the "Generated" directory within the target | |||
so we include the `.copy("Generated")` rule in our target's resource list. | |||
|
|||
#### SPM Plugin | |||
|
|||
Reflection data can also be generated via the SPM plugin by including `"reflectionData": true` in `grpc-swift-config.json`. This will generate the same data as running `protoc` above. The generated data will be in your `.build` folder (or `DerivedData` if running from Xcode) and included in the build process. More about [spm-plugin][spm-plugin] can be found here. |
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.
The generated data will be in your
.build
folder (orDerivedData
if running from Xcode) and included in the build process.
We should avoid wording it like this: the location isn't stable and users shouldn't search out these files. Instead we should tell them how to access them via the Bundle
.
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.
Updated docs to refer to Bundle
3c0d6f6
to
cf107a9
Compare
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.
Great, thanks @samisuteria!
Tested by running
grpcurl -plaintext localhost:1234 list
.