Skip to content

Commit d1b3231

Browse files
Insert Goog-Api-Client Header (#14)
1 parent 8e6b8a2 commit d1b3231

File tree

7 files changed

+162
-16
lines changed

7 files changed

+162
-16
lines changed

Package.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ let package = Package(
3434
url: "https://github.com/grpc/grpc-swift.git",
3535
from: "1.19.1" // TODO: Constrain to a range at time of release
3636
),
37+
.package(
38+
url: "https://github.com/google/GoogleUtilities.git",
39+
"8.0.0" ..< "9.0.0"
40+
),
3741
],
3842
targets: [
3943
.target(
@@ -44,7 +48,7 @@ let package = Package(
4448
// TODO: Investigate switching Auth and AppCheck to interop.
4549
.product(name: "FirebaseAuth", package: "firebase-ios-sdk"),
4650
.product(name: "FirebaseAppCheck", package: "firebase-ios-sdk"),
47-
51+
.product(name: "GULEnvironment", package: "GoogleUtilities"),
4852
],
4953
path: "Sources"
5054
),

Sources/DataConnect.swift

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public class DataConnect {
2727
private(set) var grpcClient: GrpcClient
2828
private var operationsManager: OperationsManager
2929

30+
private var callerSDKType: CallerSDKType = .base
31+
3032
private static var instanceStore = InstanceStore()
3133

3234
public enum EmulatorDefaults {
@@ -38,13 +40,20 @@ public class DataConnect {
3840

3941
public class func dataConnect(app: FirebaseApp? = FirebaseApp.app(),
4042
connectorConfig: ConnectorConfig,
41-
settings: DataConnectSettings = DataConnectSettings())
43+
settings: DataConnectSettings = DataConnectSettings(),
44+
callerSDKType: CallerSDKType = .base)
4245
-> DataConnect {
4346
guard let app = app else {
4447
fatalError("No Firebase App present")
4548
}
4649

47-
return instanceStore.instance(for: app, config: connectorConfig, settings: settings)
50+
return instanceStore
51+
.instance(
52+
for: app,
53+
config: connectorConfig,
54+
settings: settings,
55+
callerSDKType: callerSDKType
56+
)
4857
}
4958

5059
// MARK: Emulator
@@ -66,29 +75,33 @@ public class DataConnect {
6675
settings: settings,
6776
connectorConfig: connectorConfig,
6877
auth: Auth.auth(app: app),
69-
appCheck: AppCheck.appCheck(app: app)
78+
appCheck: AppCheck.appCheck(app: app),
79+
callerSDKType: callerSDKType
7080
)
7181

7282
operationsManager = OperationsManager(grpcClient: grpcClient)
7383
}
7484

7585
// MARK: Init
7686

77-
init(app: FirebaseApp, connectorConfig: ConnectorConfig, settings: DataConnectSettings) {
78-
self.app = app
79-
self.settings = settings
80-
self.connectorConfig = connectorConfig
81-
87+
init(app: FirebaseApp, connectorConfig: ConnectorConfig, settings: DataConnectSettings,
88+
callerSDKType: CallerSDKType = .base) {
8289
guard app.options.projectID != nil else {
8390
fatalError("Firebase DataConnect requires the projectID to be set in the app options")
8491
}
8592

93+
self.app = app
94+
self.settings = settings
95+
self.connectorConfig = connectorConfig
96+
self.callerSDKType = callerSDKType
97+
8698
grpcClient = GrpcClient(
8799
app: self.app,
88100
settings: settings,
89101
connectorConfig: connectorConfig,
90102
auth: Auth.auth(app: app),
91-
appCheck: AppCheck.appCheck(app: app)
103+
appCheck: AppCheck.appCheck(app: app),
104+
callerSDKType: self.callerSDKType
92105
)
93106
operationsManager = OperationsManager(grpcClient: grpcClient)
94107
}
@@ -114,6 +127,13 @@ public class DataConnect {
114127
}
115128
}
116129

130+
// This enum is public so the gen sdk can access it
131+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
132+
public enum CallerSDKType {
133+
case base // base sdk is directly used
134+
case generated // generated sdk is calling the base
135+
}
136+
117137
// MARK: Instance Creation
118138

119139
// Support for creating or reusing DataConnect instances.
@@ -145,13 +165,18 @@ private class InstanceStore {
145165
var instances = [InstanceKey: DataConnect]()
146166

147167
func instance(for app: FirebaseApp, config: ConnectorConfig,
148-
settings: DataConnectSettings) -> DataConnect {
168+
settings: DataConnectSettings, callerSDKType: CallerSDKType) -> DataConnect {
149169
accessQ.sync {
150170
let key = InstanceKey(app: app, config: config)
151171
if let inst = instances[key] {
152172
return inst
153173
} else {
154-
let dc = DataConnect(app: app, connectorConfig: config, settings: settings)
174+
let dc = DataConnect(
175+
app: app,
176+
connectorConfig: config,
177+
settings: settings,
178+
callerSDKType: callerSDKType
179+
)
155180
instances[key] = dc
156181
return dc
157182
}

Sources/Internal/Component.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ import Foundation
1818
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1919
@objc(FIRDataConnectComponent) class DataConnectComponent: NSObject {
2020
@objc class func sdkVersion() -> String {
21-
return Version.version
21+
return Version.sdkVersion
2222
}
2323
}

Sources/Internal/GrpcClient.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,14 @@ actor GrpcClient: CustomStringConvertible {
4444

4545
private let appCheck: AppCheck?
4646

47+
private let callerSDKType: CallerSDKType
48+
4749
enum RequestHeaders {
4850
static let googRequestParamsHeader = "x-goog-request-params"
4951
static let authorizationHeader = "x-firebase-auth-token"
5052
static let appCheckHeader = "X-Firebase-AppCheck"
5153
static let firebaseAppId = "x-firebase-gmpid"
54+
static let googApiClient = "x-goog-api-client"
5255
}
5356

5457
private let googRequestHeaderValue: String
@@ -72,9 +75,23 @@ actor GrpcClient: CustomStringConvertible {
7275
}
7376
}()
7477

78+
private lazy var googApiClientHeaderValue: String = {
79+
let header =
80+
"gl-swift/\(Version.swiftVersion()) fire/\(Version.sdkVersion) \(Version.platformVersionHeader()) grpc-swift/"
81+
82+
switch self.callerSDKType {
83+
case .base:
84+
return header
85+
case .generated:
86+
return "\(header) swift/gen"
87+
}
88+
89+
}()
90+
7591
init(app: FirebaseApp, settings: DataConnectSettings, connectorConfig: ConnectorConfig,
7692
auth: Auth,
77-
appCheck: AppCheck?) {
93+
appCheck: AppCheck?,
94+
callerSDKType: CallerSDKType) {
7895
self.app = app
7996

8097
guard let projectId = app.options.projectID else {
@@ -86,6 +103,7 @@ actor GrpcClient: CustomStringConvertible {
86103
self.connectorConfig = connectorConfig
87104
self.auth = auth
88105
self.appCheck = appCheck
106+
self.callerSDKType = callerSDKType
89107

90108
connectorName =
91109
"projects/\(projectId)/locations/\(connectorConfig.location)/services/\(connectorConfig.serviceId)/connectors/\(connectorConfig.connector)"
@@ -187,6 +205,7 @@ actor GrpcClient: CustomStringConvertible {
187205

188206
headers.add(name: RequestHeaders.googRequestParamsHeader, value: googRequestHeaderValue)
189207
headers.add(name: RequestHeaders.firebaseAppId, value: app.options.googleAppID)
208+
headers.add(name: RequestHeaders.googApiClient, value: googApiClientHeaderValue)
190209

191210
// Add Auth token if available
192211
do {

Sources/Internal/Version.swift

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,68 @@
1414

1515
import Foundation
1616

17+
import GoogleUtilities_Environment
18+
1719
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1820
struct Version {
19-
static let version = "11.3.0-beta"
21+
static let sdkVersion = "11.3.0-beta"
22+
23+
// returns value of form gl-PLATFORM_NAME/PLATFORM_VERSION
24+
static func platformVersionHeader() -> String {
25+
let versionString = GULAppEnvironmentUtil.systemVersion()
26+
let platformName = GULAppEnvironmentUtil.applePlatform()
27+
28+
return "gl-\(platformName)/\(versionString)"
29+
}
30+
31+
// returns the build time major version of swift
32+
static func swiftVersion() -> String {
33+
#if swift(>=6)
34+
return "6"
35+
#elseif swift(>=5.10)
36+
return "5.10"
37+
#elseif swift(>=5.9)
38+
return "5.9"
39+
#elseif swift(>=5.8)
40+
return "5.8"
41+
#elseif swift(>=5.7)
42+
return "5.7"
43+
#elseif swift(>=5.6)
44+
return "5.6"
45+
#elseif swift(>=5.5)
46+
return "5.5"
47+
#elseif swift(>=5.4)
48+
return "5.4"
49+
#elseif swift(>=5.2)
50+
return "5.2"
51+
#elseif swift(>=5.1)
52+
return "5.1"
53+
#elseif swift(>=5.0)
54+
return "5.0"
55+
#elseif swift(>=4.2)
56+
return "4.2"
57+
#elseif swift(>=4.1)
58+
return "4.1"
59+
#elseif swift(>=4.0)
60+
return "4.0"
61+
#elseif swift(>=3.1)
62+
return "3.1"
63+
#elseif swift(>=3.0)
64+
return "3.0"
65+
#elseif swift(>=2.2)
66+
return "2.2"
67+
#elseif swift(>=2.1)
68+
return "2.1"
69+
#elseif swift(>=2.0)
70+
return "2.0"
71+
#elseif swift(>=1.2)
72+
return "1.2"
73+
#elseif swift(>=1.1)
74+
return "1.1"
75+
#elseif swift(>=1.0)
76+
return "1.0"
77+
#else
78+
return ""
79+
#endif
80+
}
2081
}

Tests/Unit/HeaderTests.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ final class HeaderTests: XCTestCase {
3636
connector: "kitchensink"
3737
)
3838

39+
var fakeConnectorConfigTwo = ConnectorConfig(
40+
serviceId: "dataconnect",
41+
location: "us-east1",
42+
connector: "kitchensinkgen"
43+
)
44+
3945
override class func setUp() {
4046
FirebaseApp.configure(options: options)
4147
defaultApp = FirebaseApp.app()
@@ -50,4 +56,35 @@ final class HeaderTests: XCTestCase {
5056
let contains = values.contains { $0 == HeaderTests.defaultApp!.options.googleAppID }
5157
XCTAssertTrue(contains)
5258
}
59+
60+
func testGoogApiClientHeaderBaseSdk() async throws {
61+
let dcOne = DataConnect.dataConnect(connectorConfig: fakeConnectorConfigOne)
62+
let callOptions = await dcOne.grpcClient.createCallOptions()
63+
let values = callOptions.customMetadata.values(
64+
forHeader: GrpcClient.RequestHeaders.googApiClient, canonicalForm: false
65+
)
66+
let contains = values
67+
.contains {
68+
$0 ==
69+
"gl-swift/\(Version.swiftVersion()) fire/\(Version.sdkVersion) \(Version.platformVersionHeader()) grpc-swift/"
70+
}
71+
XCTAssertTrue(contains)
72+
}
73+
74+
func testGoogleApiClientHeaderGenSdk() async throws {
75+
let dcOne = DataConnect.dataConnect(
76+
connectorConfig: fakeConnectorConfigTwo,
77+
callerSDKType: .generated
78+
)
79+
let callOptions = await dcOne.grpcClient.createCallOptions()
80+
let values = callOptions.customMetadata.values(
81+
forHeader: GrpcClient.RequestHeaders.googApiClient, canonicalForm: false
82+
)
83+
let contains = values
84+
.contains {
85+
$0 ==
86+
"gl-swift/\(Version.swiftVersion()) fire/\(Version.sdkVersion) \(Version.platformVersionHeader()) grpc-swift/ swift/gen"
87+
}
88+
XCTAssertTrue(contains)
89+
}
5390
}

Tests/Unit/UserAgentTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ final class UserAgentTests: XCTestCase {
3434
/// Confirm that Data Connect gets added to the user agent.
3535
func testUserAgent() {
3636
let userAgent = FirebaseApp.firebaseUserAgent()
37-
let version = Version.version
37+
let version = Version.sdkVersion
3838
XCTAssertTrue(userAgent.contains("fire-dc/\(version)"))
3939
}
4040
}

0 commit comments

Comments
 (0)