Skip to content

Commit 30d301b

Browse files
committed
Add graceful shutdown to OFREP provider
1 parent c78668a commit 30d301b

File tree

3 files changed

+122
-2
lines changed

3 files changed

+122
-2
lines changed
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift OpenFeature open source project
4+
//
5+
// Copyright (c) 2025 the Swift OpenFeature project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
//
10+
// SPDX-License-Identifier: Apache-2.0
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
import OpenAPIRuntime
15+
16+
public protocol OFREPClientTransport: ClientTransport {
17+
func shutdownGracefully() async throws
18+
}

Sources/OFREP/OFREPProvider.swift

+31-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,41 @@
22
//
33
// This source file is part of the Swift OpenFeature open source project
44
//
5-
// Copyright (c) 2024 the Swift OpenFeature project authors
5+
// Copyright (c) 2025 the Swift OpenFeature project authors
66
// Licensed under Apache License v2.0
77
//
88
// See LICENSE.txt for license information
99
//
1010
// SPDX-License-Identifier: Apache-2.0
1111
//
1212
//===----------------------------------------------------------------------===//
13+
14+
import Logging
15+
import OpenFeature
16+
import ServiceLifecycle
17+
18+
public struct OFREPProvider<Transport: OFREPClientTransport>: OpenFeatureProvider, CustomStringConvertible {
19+
public let metadata = OpenFeatureProviderMetadata(name: "OpenFeature Remote Evaluation Protocol Provider")
20+
public let description = "OFREPProvider"
21+
private let transport: Transport
22+
private let logger = Logger(label: "OFREPProvider")
23+
24+
package init(transport: Transport) {
25+
self.transport = transport
26+
}
27+
28+
public func resolution(
29+
of flag: String,
30+
defaultValue: Bool,
31+
context: OpenFeatureEvaluationContext?
32+
) async -> OpenFeatureResolution<Bool> {
33+
OpenFeatureResolution(value: defaultValue)
34+
}
35+
36+
public func run() async throws {
37+
try await gracefulShutdown()
38+
logger.debug("Shutting down.")
39+
try await transport.shutdownGracefully()
40+
logger.debug("Shut down.")
41+
}
42+
}

Tests/OFREPTests/OFREPProviderTests.swift

+73-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,83 @@
22
//
33
// This source file is part of the Swift OpenFeature open source project
44
//
5-
// Copyright (c) 2024 the Swift OpenFeature project authors
5+
// Copyright (c) 2025 the Swift OpenFeature project authors
66
// Licensed under Apache License v2.0
77
//
88
// See LICENSE.txt for license information
99
//
1010
// SPDX-License-Identifier: Apache-2.0
1111
//
1212
//===----------------------------------------------------------------------===//
13+
14+
import Foundation
15+
import HTTPTypes
16+
import OFREP
17+
import OpenAPIRuntime
18+
import Testing
19+
import ServiceLifecycle
20+
@testable import Logging
21+
22+
@Suite("OFREP Provider")
23+
final class OFREPProviderTests {
24+
init() {
25+
LoggingSystem.bootstrapInternal { label in
26+
var handler = StreamLogHandler.standardOutput(label: label)
27+
handler.logLevel = .debug
28+
return handler
29+
}
30+
}
31+
32+
deinit {
33+
LoggingSystem.bootstrapInternal(SwiftLogNoOpLogHandler.init)
34+
}
35+
36+
@Test("Graceful shutdown")
37+
func shutsDownTransport() async throws {
38+
/// A no-op service which is used to shut down the service group upon successful termination.
39+
struct ShutdownTrigger: Service, CustomStringConvertible {
40+
let description = "ShutdownTrigger"
41+
42+
func run() async throws {}
43+
}
44+
45+
let transport = RecordingOFREPClientTransport()
46+
let provider = OFREPProvider(transport: transport)
47+
48+
await #expect(transport.numberOfShutdownCalls == 0)
49+
50+
let group = ServiceGroup(
51+
configuration: .init(
52+
services: [
53+
.init(service: provider),
54+
.init(service: ShutdownTrigger(), successTerminationBehavior: .gracefullyShutdownGroup),
55+
],
56+
logger: Logger(label: "test")
57+
)
58+
)
59+
60+
try await group.run()
61+
62+
await #expect(transport.numberOfShutdownCalls == 1)
63+
}
64+
}
65+
66+
private actor RecordingOFREPClientTransport: OFREPClientTransport {
67+
var numberOfShutdownCalls = 0
68+
69+
func send(
70+
_ request: HTTPRequest,
71+
body: HTTPBody?,
72+
baseURL: URL,
73+
operationID: String
74+
) async throws -> (
75+
HTTPResponse,
76+
HTTPBody?
77+
) {
78+
(HTTPResponse(status: 418), nil)
79+
}
80+
81+
func shutdownGracefully() async throws {
82+
numberOfShutdownCalls += 1
83+
}
84+
}

0 commit comments

Comments
 (0)