Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 36 additions & 11 deletions Sources/DataConnect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public class DataConnect {
private(set) var grpcClient: GrpcClient
private var operationsManager: OperationsManager

private var callerSDKType: CallerSDKType = .base

private static var instanceStore = InstanceStore()

public enum EmulatorDefaults {
Expand All @@ -38,13 +40,20 @@ public class DataConnect {

public class func dataConnect(app: FirebaseApp? = FirebaseApp.app(),
connectorConfig: ConnectorConfig,
settings: DataConnectSettings = DataConnectSettings())
settings: DataConnectSettings = DataConnectSettings(),
callerSDKType: CallerSDKType = .base)
-> DataConnect {
guard let app = app else {
fatalError("No Firebase App present")
}

return instanceStore.instance(for: app, config: connectorConfig, settings: settings)
return instanceStore
.instance(
for: app,
config: connectorConfig,
settings: settings,
callerSDKType: callerSDKType
)
}

// MARK: Emulator
Expand All @@ -66,29 +75,33 @@ public class DataConnect {
settings: settings,
connectorConfig: connectorConfig,
auth: Auth.auth(app: app),
appCheck: AppCheck.appCheck(app: app)
appCheck: AppCheck.appCheck(app: app),
callerSDKType: callerSDKType
)

operationsManager = OperationsManager(grpcClient: grpcClient)
}

// MARK: Init

init(app: FirebaseApp, connectorConfig: ConnectorConfig, settings: DataConnectSettings) {
self.app = app
self.settings = settings
self.connectorConfig = connectorConfig

init(app: FirebaseApp, connectorConfig: ConnectorConfig, settings: DataConnectSettings,
callerSDKType: CallerSDKType = .base) {
guard app.options.projectID != nil else {
fatalError("Firebase DataConnect requires the projectID to be set in the app options")
}

self.app = app
self.settings = settings
self.connectorConfig = connectorConfig
self.callerSDKType = callerSDKType

grpcClient = GrpcClient(
app: self.app,
settings: settings,
connectorConfig: connectorConfig,
auth: Auth.auth(app: app),
appCheck: AppCheck.appCheck(app: app)
appCheck: AppCheck.appCheck(app: app),
callerSDKType: self.callerSDKType
)
operationsManager = OperationsManager(grpcClient: grpcClient)
}
Expand All @@ -114,6 +127,13 @@ public class DataConnect {
}
}

// This enum is public so the gen sdk can access it
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public enum CallerSDKType {
case base // base sdk is directly used
case generated // generated sdk is calling the base
}

// MARK: Instance Creation

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

func instance(for app: FirebaseApp, config: ConnectorConfig,
settings: DataConnectSettings) -> DataConnect {
settings: DataConnectSettings, callerSDKType: CallerSDKType) -> DataConnect {
accessQ.sync {
let key = InstanceKey(app: app, config: config)
if let inst = instances[key] {
return inst
} else {
let dc = DataConnect(app: app, connectorConfig: config, settings: settings)
let dc = DataConnect(
app: app,
connectorConfig: config,
settings: settings,
callerSDKType: callerSDKType
)
instances[key] = dc
return dc
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Internal/Component.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ import Foundation
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@objc(FIRDataConnectComponent) class DataConnectComponent: NSObject {
@objc class func sdkVersion() -> String {
return Version.version
return Version.sdkVersion
}
}
21 changes: 20 additions & 1 deletion Sources/Internal/GrpcClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,14 @@ actor GrpcClient: CustomStringConvertible {

private let appCheck: AppCheck?

private let callerSDKType: CallerSDKType

enum RequestHeaders {
static let googRequestParamsHeader = "x-goog-request-params"
static let authorizationHeader = "x-firebase-auth-token"
static let appCheckHeader = "X-Firebase-AppCheck"
static let firebaseAppId = "x-firebase-gmpid"
static let googApiClient = "x-goog-api-client"
}

private let googRequestHeaderValue: String
Expand All @@ -72,9 +75,23 @@ actor GrpcClient: CustomStringConvertible {
}
}()

private lazy var googApiClientHeaderValue: String = {
let header =
"gl-swift/\(Version.swiftMajorVersion()) fire/\(Version.sdkVersion) \(Version.platformVersionHeader()) grpc-swift/"

switch self.callerSDKType {
case .base:
return header
case .generated:
return "\(header) swift/gen"
}

}()

init(app: FirebaseApp, settings: DataConnectSettings, connectorConfig: ConnectorConfig,
auth: Auth,
appCheck: AppCheck?) {
appCheck: AppCheck?,
callerSDKType: CallerSDKType) {
self.app = app

guard let projectId = app.options.projectID else {
Expand All @@ -86,6 +103,7 @@ actor GrpcClient: CustomStringConvertible {
self.connectorConfig = connectorConfig
self.auth = auth
self.appCheck = appCheck
self.callerSDKType = callerSDKType

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

headers.add(name: RequestHeaders.googRequestParamsHeader, value: googRequestHeaderValue)
headers.add(name: RequestHeaders.firebaseAppId, value: app.options.googleAppID)
headers.add(name: RequestHeaders.googApiClient, value: googApiClientHeaderValue)

// Add Auth token if available
do {
Expand Down
31 changes: 30 additions & 1 deletion Sources/Internal/Version.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,36 @@

import Foundation

import GoogleUtilities_Environment

@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
struct Version {
static let version = "11.3.0-beta"
static let sdkVersion = "11.3.0-beta"

// returns value of form gl-PLATFORM_NAME/PLATFORM_VERSION
static func platformVersionHeader() -> String {
let versionString = GULAppEnvironmentUtil.systemVersion()
let platformName = GULAppEnvironmentUtil.applePlatform()

return "gl-\(platformName)/\(versionString)"
}

// returns the build time major version of swift
static func swiftMajorVersion() -> String {
#if swift(>=6)
return "6"
#elseif swift(>=5)
return "5"
#elseif swift(>=4)
return "4"
#elseif swift(>=3)
return "3"
#elseif swift(>=2)
return "2"
#elseif swift(>=1)
return "1"
#else
return ""
#endif
}
}
37 changes: 37 additions & 0 deletions Tests/Unit/HeaderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ final class HeaderTests: XCTestCase {
connector: "kitchensink"
)

var fakeConnectorConfigTwo = ConnectorConfig(
serviceId: "dataconnect",
location: "us-east1",
connector: "kitchensinkgen"
)

override class func setUp() {
FirebaseApp.configure(options: options)
defaultApp = FirebaseApp.app()
Expand All @@ -50,4 +56,35 @@ final class HeaderTests: XCTestCase {
let contains = values.contains { $0 == HeaderTests.defaultApp!.options.googleAppID }
XCTAssertTrue(contains)
}

func testGoogApiClientHeaderBaseSdk() async throws {
let dcOne = DataConnect.dataConnect(connectorConfig: fakeConnectorConfigOne)
let callOptions = await dcOne.grpcClient.createCallOptions()
let values = callOptions.customMetadata.values(
forHeader: GrpcClient.RequestHeaders.googApiClient, canonicalForm: false
)
let contains = values
.contains {
$0 ==
"gl-swift/\(Version.swiftMajorVersion()) fire/\(Version.sdkVersion) \(Version.platformVersionHeader()) grpc-swift/"
}
XCTAssertTrue(contains)
}

func testGoogleApiClientHeaderGenSdk() async throws {
let dcOne = DataConnect.dataConnect(
connectorConfig: fakeConnectorConfigTwo,
callerSDKType: .generated
)
let callOptions = await dcOne.grpcClient.createCallOptions()
let values = callOptions.customMetadata.values(
forHeader: GrpcClient.RequestHeaders.googApiClient, canonicalForm: false
)
let contains = values
.contains {
$0 ==
"gl-swift/\(Version.swiftMajorVersion()) fire/\(Version.sdkVersion) \(Version.platformVersionHeader()) grpc-swift/ swift/gen"
}
XCTAssertTrue(contains)
}
}
2 changes: 1 addition & 1 deletion Tests/Unit/UserAgentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ final class UserAgentTests: XCTestCase {
/// Confirm that Data Connect gets added to the user agent.
func testUserAgent() {
let userAgent = FirebaseApp.firebaseUserAgent()
let version = Version.version
let version = Version.sdkVersion
XCTAssertTrue(userAgent.contains("fire-dc/\(version)"))
}
}