-
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
Added ReflectionService initialiser using file paths #1699
Conversation
Motivation: Initializing the Reflection Service using file descriptor protos is too laborious for users, so simply passing the file paths of the binary files containing the serialized file descriptor protos and letting the Service extract the data from those paths is more convenient. Modifications: Added a new initializer that takes in file paths and extracts and converts to file descriptor proto format the Data from the files. Also modified the integration tests to create temporary files containing the serialized file descriptor protos. Result: GRPCUsers can add the ReflectionService to their Server in a more convenient way.
@@ -26,6 +26,10 @@ public final class ReflectionService: CallHandlerProvider, Sendable { | |||
self.reflectionService.serviceName | |||
} | |||
|
|||
public init(protoFilePaths: [String]) throws { |
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.
We aren't loading proto files (i.e. .proto
) we're loading serialised file descriptor protos:
public init(serializedFileDescriptorProtoFilePaths: [String]) throws {
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.
This also needs documentation
@@ -207,6 +211,27 @@ internal struct ReflectionServiceData: Sendable { | |||
internal final class ReflectionServiceProvider: Grpc_Reflection_V1_ServerReflectionAsyncProvider { | |||
private let protoRegistry: ReflectionServiceData | |||
|
|||
internal init(protoFilePaths: [String]) throws { | |||
|
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.
nit: unnecessary blank line
let binaryData = binaryFilesURLs.map { Data(base64Encoded: try! Data(contentsOf: $0)) } | ||
let fileDescriptorProtos = binaryData.map { | ||
try! Google_Protobuf_FileDescriptorProto(serializedData: $0!) | ||
} |
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.
We can't try!
these, they may legitimately fail and this would cause a crash.
We should just do the basic thing:
var fileDescriptorProtos = [Google_Protobuf_FileDescriptorProto]()
for path in paths {
// ...
fileDescriptorProtos.append(...)
}
#else | ||
let temporaryDirectory = fileManager.temporaryDirectory.path() | ||
#endif | ||
let filePath = temporaryDirectory + fileDescriptorProto.name |
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.
I don't think this is sufficiently unique (especially on linux): if we ran these tests in parallel we could end up removing files which another test is expecting to be there. One workaround which is probably fine is to add some randomness to the name.
let reflectionServiceProvider = try ReflectionService( | ||
fileDescriptors: self.protos + [self.independentProto] | ||
) | ||
var filePaths: [String] = [] |
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.
We actually don't need to do this in the integration tests: we can do it in the unit tests because the actual work is done by the registry rather than the service. This also means we can do this in a separate test as well.
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.
At the moment, the ReflectionServiceProvider (not the wrapper, the class that contains the registry) is doing the conversion of Data from the file paths to serialised file descriptor protos and then initialises the registry (using the file descriptor ports). This is why I implemented the testing in the integration tests. So if I'm moving it in the Unit tests I should also move the conversion in a Proto initialiser. Should I do this?
And my second question if I do this: Should I only implement unit tests for this conversion from binary data to file descriptor photos and leave all the other tests as they were before (using directly file descriptor protos)?
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.
Oh, my mistake. Thinking about this some more, I think we can implement this as a couple of helpers on the ReflectionService
which would be called by the init
:
extension ReflectionService {
static func readSerializedFileDescriptorProto(atPath path: String) throws -> Google_Protobuf_FileDescriptorProto {
// ...
}
static func readSerializedFileDescriptorProtos(atPaths paths: [String]) throws -> [Google_Protobuf_FileDescriptorProto] {
// ...
}
}
This way we can test the loading independently of the registry and operation of the service. I.e. we can test that these functions can load and deserialize the serialized descriptors correctly.
/// Receives as parameter an array of strings representing the file paths to the binary files containing | ||
/// the serialised file descriptor protos describing the services supported by the Server and | ||
/// discovarable through Server 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.
Docs should follow a particular format, take a look at https://www.swift.org/documentation/api-design-guidelines/ in particular the "More detail" part of "Write a documentation comment".
The documentation should start with a brief description of what the function does:
Creates a
ReflectionService
by loading serialized reflection data created byprotoc-gen-grpc-swift
.
Then you can go into more information about what the expectations are etc. and how they can generate this data:
You can generate serialized reflection data using the
protoc-gen-grpc-swift
plugin forprotoc
by
setting the toTrue
. The paths provided should be absolute or relative
to the current working directory.
Then you can detail the parameters:
- Parameter filePaths: The paths to files containing serialized reflection data.
And when the function throws:
- Throws: When a file can't be read from disk or parsed.
/// Receives as parameter an array of strings representing the file paths to the binary files containing | ||
/// the serialised file descriptor protos describing the services supported by the Server and | ||
/// discovarable through Server Reflection. | ||
public init(serializedFileDescriptorProtoFilePaths: [String]) throws { |
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.
We should use a shorter label internally:
public init(serializedFileDescriptorProtoFilePaths: [String]) throws { | |
public init(serializedFileDescriptorProtoFilePaths filePaths: [String]) throws { |
code: .invalidArgument, | ||
message: | ||
""" | ||
The \(fileURL.lastPathComponent) file contents could not be transformed \ |
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.
We should include the whole file path. The user could have two files with the same name in different directories which would make this error unclear.
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 needs fixing but looks fine otherwise
/// Creates a `ReflectionService` by loading serialized reflection data created by `protoc-gen-grpc-swift`. | ||
/// | ||
/// You can generate serialized reflection data using the `protoc-gen-grpc-swift` plugin for `protoc` by | ||
/// setting the to `True`. The paths provided should be absolute or relative to the current working directory. |
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.
"setting the to True
" ...?
Motivation:
Initialising the Reflection Service using file descriptor protos is too laborious for users, so simply passing the file paths of the binary files containing the serialised file descriptor protos and letting the Service extract the data from those paths is more convenient.
Modifications:
Added a new initializer that takes in file paths and extracts and converts to file descriptor proto format the Data from the files. Also modified the integration tests to create temporary files containing the serialized file descriptor protos.
Result:
GRPC users can add the ReflectionService to their Server in a more convenient way.