From 3b35905f1895d30d36d4c4d5fb8e9b3e940e8180 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 15 Jan 2025 17:27:35 +0000 Subject: [PATCH 1/2] Bring HealthService API in line with ReflectionService Motivation: The reflection service is a single type called `ReflectionService`. The Health service is a single type (called `Health`) holding an instance of the `HealthService` and a type to provide values to the service. This indirection isn't necessary, the health service can just hold it instead. This makes the API more like the reflection service and removes indirection. Modifications: - Remove indirection from health service and rename from 'Health' to 'HealthService'. - Make the reflection service a struct (it was a class for no good reason). Result: More consistent API --- Sources/GRPCHealthService/Health.swift | 74 ++++++++----------- .../{HealthService.swift => Service.swift} | 10 ++- .../Service/ReflectionService.swift | 6 +- .../GRPCHealthServiceTests/HealthTests.swift | 6 +- 4 files changed, 45 insertions(+), 51 deletions(-) rename Sources/GRPCHealthService/{HealthService.swift => Service.swift} (95%) diff --git a/Sources/GRPCHealthService/Health.swift b/Sources/GRPCHealthService/Health.swift index c2cfbc5..1cf1468 100644 --- a/Sources/GRPCHealthService/Health.swift +++ b/Sources/GRPCHealthService/Health.swift @@ -16,64 +16,54 @@ public import GRPCCore -/// ``Health`` is gRPC’s mechanism for checking whether a server is able to handle RPCs. Its semantics are documented in -/// https://github.com/grpc/grpc/blob/5011420f160b91129a7baebe21df9444a07896a6/doc/health-checking.md. +/// ``HealthService`` is gRPC's mechanism for checking whether a server is able to handle RPCs. +/// Its semantics are documented in the [gRPC repository]( https://github.com/grpc/grpc/blob/5011420f160b91129a7baebe21df9444a07896a6/doc/health-checking.md). /// -/// `Health` initializes a new ``Health/Service-swift.struct`` and ``Health/Provider-swift.struct``. -/// - `Health.Service` implements the Health service from the `grpc.health.v1` package and can be registered with a server -/// like any other service. -/// - `Health.Provider` provides status updates to `Health.Service`. `Health.Service` doesn't know about the other -/// services running on a server so it must be provided with status updates via `Health.Provider`. To make specifying the service -/// being updated easier, the generated code for services includes an extension to `ServiceDescriptor`. +/// `HealthService` implements the `grpc.health.v1` service and can be registered with a server +/// like any other service. It holds a ``HealthService/Provider-swift.struct`` which provides +/// status updates to the service. The service doesn't know about the other services running on the +/// server so it must be provided with status updates via the ``Provider-swift.struct``. To make +/// specifying the service being updated easier, the generated code for services includes an +/// extension to `ServiceDescriptor`. /// -/// The following shows an example of initializing a Health service and updating the status of the `Foo` service in the `bar` package. +/// The following shows an example of initializing a Health service and updating the status of +/// the `Foo` service in the `bar` package. /// /// ```swift -/// let health = Health() -/// let server = GRPCServer( +/// let health = HealthService() +/// try await withGRPCServer( /// transport: transport, -/// services: [health.service, FooService()] -/// ) +/// services: [health, FooService()] +/// ) { server in +/// // Update the status of the 'bar.Foo' service. +/// health.provider.updateStatus(.serving, forService: .bar_Foo) /// -/// health.provider.updateStatus( -/// .serving, -/// forService: .bar_Foo -/// ) +/// // ... +/// } /// ``` -public struct Health: Sendable { +public struct HealthService: Sendable, RegistrableRPCService { /// An implementation of the `grpc.health.v1.Health` service. - public let service: Health.Service + private let service: Service /// Provides status updates to the Health service. - public let provider: Health.Provider + public let provider: HealthService.Provider - /// Constructs a new ``Health``, initializing a ``Health/Service-swift.struct`` and a - /// ``Health/Provider-swift.struct``. + /// Constructs a new ``HealthService``. public init() { - let healthService = HealthService() - - self.service = Health.Service(healthService: healthService) - self.provider = Health.Provider(healthService: healthService) + let healthService = Service() + self.service = healthService + self.provider = HealthService.Provider(healthService: healthService) } -} -extension Health { - /// An implementation of the `grpc.health.v1.Health` service. - public struct Service: RegistrableRPCService, Sendable { - private let healthService: HealthService - - public func registerMethods(with router: inout RPCRouter) { - self.healthService.registerMethods(with: &router) - } - - fileprivate init(healthService: HealthService) { - self.healthService = healthService - } + public func registerMethods(with router: inout RPCRouter) { + self.service.registerMethods(with: &router) } +} - /// Provides status updates to ``Health/Service-swift.struct``. +extension HealthService { + /// Provides status updates to ``HealthService``. public struct Provider: Sendable { - private let healthService: HealthService + private let healthService: Service /// Updates the status of a service. /// @@ -107,7 +97,7 @@ extension Health { ) } - fileprivate init(healthService: HealthService) { + fileprivate init(healthService: Service) { self.healthService = healthService } } diff --git a/Sources/GRPCHealthService/HealthService.swift b/Sources/GRPCHealthService/Service.swift similarity index 95% rename from Sources/GRPCHealthService/HealthService.swift rename to Sources/GRPCHealthService/Service.swift index f548840..165d1c8 100644 --- a/Sources/GRPCHealthService/HealthService.swift +++ b/Sources/GRPCHealthService/Service.swift @@ -17,9 +17,13 @@ internal import GRPCCore private import Synchronization -internal struct HealthService: Grpc_Health_V1_Health.ServiceProtocol { - private let state = HealthService.State() +extension HealthService { + internal struct Service: Grpc_Health_V1_Health.ServiceProtocol { + private let state = Self.State() + } +} +extension HealthService.Service { func check( request: ServerRequest, context: ServerContext @@ -65,7 +69,7 @@ internal struct HealthService: Grpc_Health_V1_Health.ServiceProtocol { } } -extension HealthService { +extension HealthService.Service { private final class State: Sendable { // The state of each service keyed by the fully qualified service name. private let lockedStorage = Mutex([String: ServiceState]()) diff --git a/Sources/GRPCReflectionService/Service/ReflectionService.swift b/Sources/GRPCReflectionService/Service/ReflectionService.swift index fc0eebe..cb77a0b 100644 --- a/Sources/GRPCReflectionService/Service/ReflectionService.swift +++ b/Sources/GRPCReflectionService/Service/ReflectionService.swift @@ -33,13 +33,13 @@ public import struct Foundation.Data /// /// The service will offer information to clients about any registered services. You can register /// a service by providing its descriptor set to the service. -public final class ReflectionService: Sendable { +public struct ReflectionService: Sendable { private let service: ReflectionService.V1 /// Create a new instance of the reflection service from a list of descriptor set file URLs. /// /// - Parameter fileURLs: A list of file URLs containing serialized descriptor sets. - public convenience init( + public init( descriptorSetFileURLs fileURLs: [URL] ) throws { let fileDescriptorProtos = try Self.readDescriptorSets(atURLs: fileURLs) @@ -49,7 +49,7 @@ public final class ReflectionService: Sendable { /// Create a new instance of the reflection service from a list of descriptor set file paths. /// /// - Parameter filePaths: A list of file paths containing serialized descriptor sets. - public convenience init( + public init( descriptorSetFilePaths filePaths: [String] ) throws { let fileDescriptorProtos = try Self.readDescriptorSets(atPaths: filePaths) diff --git a/Tests/GRPCHealthServiceTests/HealthTests.swift b/Tests/GRPCHealthServiceTests/HealthTests.swift index e628031..8571557 100644 --- a/Tests/GRPCHealthServiceTests/HealthTests.swift +++ b/Tests/GRPCHealthServiceTests/HealthTests.swift @@ -22,11 +22,11 @@ import XCTest final class HealthTests: XCTestCase { private func withHealthClient( - _ body: @Sendable (Grpc_Health_V1_Health.Client, Health.Provider) async throws -> Void + _ body: @Sendable (Grpc_Health_V1_Health.Client, HealthService.Provider) async throws -> Void ) async throws { - let health = Health() + let health = HealthService() let inProcess = InProcessTransport() - let server = GRPCServer(transport: inProcess.server, services: [health.service]) + let server = GRPCServer(transport: inProcess.server, services: [health]) let client = GRPCClient(transport: inProcess.client) let healthClient = Grpc_Health_V1_Health.Client(wrapping: client) From 8b8ad2276b6b15a46e79ac37eaeb6e05906538c9 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 15 Jan 2025 17:35:03 +0000 Subject: [PATCH 2/2] Rename --- .../{Service.swift => HealthService+Service.swift} | 0 Sources/GRPCHealthService/{Health.swift => HealthService.swift} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Sources/GRPCHealthService/{Service.swift => HealthService+Service.swift} (100%) rename Sources/GRPCHealthService/{Health.swift => HealthService.swift} (100%) diff --git a/Sources/GRPCHealthService/Service.swift b/Sources/GRPCHealthService/HealthService+Service.swift similarity index 100% rename from Sources/GRPCHealthService/Service.swift rename to Sources/GRPCHealthService/HealthService+Service.swift diff --git a/Sources/GRPCHealthService/Health.swift b/Sources/GRPCHealthService/HealthService.swift similarity index 100% rename from Sources/GRPCHealthService/Health.swift rename to Sources/GRPCHealthService/HealthService.swift