Skip to content

Commit d79dce2

Browse files
Hongyan JiangGitHub Enterprise
authored andcommitted
Support hybrid Agent Id and version if iOSAgent is invoked by flutter-agent or react-native-agent
1 parent 987936f commit d79dce2

12 files changed

Lines changed: 176 additions & 17 deletions

File tree

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Add more exceptionType processing to crash beacon
66
- Display crash terminationReason as meta data rather than error message
77
- Add Objective-C target ObjectiveCAppExample to InstanaAgentExample
8+
- Support hybrid agent id and version (if invoked by flutter-agent/react-native-agent)
89

910
## 1.6.7
1011
- Add more raw crash payload info to stackTrace

Sources/InstanaAgent/Beacons/CoreBeacon/CoreBeacon.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,17 @@ struct CoreBeacon: Codable {
422422
func isCrashPayloadField(fieldKey: String) -> Bool {
423423
return fieldKey == "st" || fieldKey == "ast"
424424
}
425+
426+
// If invoked by flutter-agent(f) or react-native-agent(r),
427+
// put calling agent's id and version after iOSAgent version
428+
static func getInstanaAgentVersion(hybridAgentId: String?, hybridAgentVersion: String?) -> String {
429+
let iOSAgentVersion = InstanaSystemUtils.agentVersion
430+
guard let hybridAgentId = hybridAgentId, !hybridAgentId.isEmpty,
431+
let hybridAgentVersion = hybridAgentVersion, !hybridAgentVersion.isEmpty else {
432+
return iOSAgentVersion
433+
}
434+
return "\(iOSAgentVersion):\(hybridAgentId):\(hybridAgentVersion)"
435+
}
425436
}
426437

427438
extension CoreBeacon: Hashable {

Sources/InstanaAgent/Beacons/CoreBeacon/CoreBeaconFactory.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ class CoreBeaconFactory {
2424
var cbeacon = CoreBeacon.createDefault(viewName: beacon.viewName, key: conf.key,
2525
timestamp: beacon.timestamp,
2626
sid: session.id, usi: session.usi,
27-
id: beacon.id, mobileFeatures: mobileFeatures)
27+
id: beacon.id, mobileFeatures: mobileFeatures,
28+
hybridAgentId: conf.hybridAgentId,
29+
hybridAgentVersion: conf.hybridAgentVersion)
2830
cbeacon.append(properties)
2931
switch beacon {
3032
case let item as HTTPBeacon:
@@ -185,6 +187,8 @@ extension CoreBeacon {
185187
usi: UUID?,
186188
id: String,
187189
mobileFeatures: String?,
190+
hybridAgentId: String?,
191+
hybridAgentVersion: String?,
188192
connection: NetworkUtility.ConnectionType = InstanaSystemUtils.networkUtility.connectionType,
189193
ect: NetworkUtility.CellularType? = nil)
190194
-> CoreBeacon {
@@ -203,7 +207,8 @@ extension CoreBeacon {
203207
osn: InstanaSystemUtils.systemName,
204208
osv: InstanaSystemUtils.systemVersion,
205209
dmo: InstanaSystemUtils.deviceModel,
206-
agv: InstanaSystemUtils.agentVersion,
210+
agv: CoreBeacon.getInstanaAgentVersion(hybridAgentId: hybridAgentId,
211+
hybridAgentVersion: hybridAgentVersion),
207212
ro: String(InstanaSystemUtils.isDeviceJailbroken),
208213
vw: String(Int(InstanaSystemUtils.screenSize.width)),
209214
vh: String(Int(InstanaSystemUtils.screenSize.height)),

Sources/InstanaAgent/Configuration/InstanaConfiguraton.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,16 @@ class InstanaConfiguration {
7373
var preQueueUsageTime: TimeInterval
7474
var reporterRateLimits: [ReporterRateLimitConfig]
7575
var isValid: Bool { !key.isEmpty && !reportingURL.absoluteString.isEmpty }
76+
// set if iOSAgent is invoked by flutter-agent or react-native-agent
77+
var hybridAgentId: String?
78+
var hybridAgentVersion: String?
7679

7780
required init(reportingURL: URL, key: String, httpCaptureConfig: HTTPCaptureConfig,
7881
enableCrashReporting: Bool, suspendReporting: Set<SuspendReporting>? = nil,
7982
slowSendInterval: Instana.Types.Seconds,
80-
usiRefreshTimeIntervalInHrs: Double) {
83+
usiRefreshTimeIntervalInHrs: Double,
84+
hybridAgentId: String?,
85+
hybridAgentVersion: String?) {
8186
self.reportingURL = reportingURL
8287
self.key = key
8388
self.httpCaptureConfig = httpCaptureConfig
@@ -88,6 +93,8 @@ class InstanaConfiguration {
8893
self.suspendReporting = suspendReporting ?? SuspendReporting.defaults
8994
self.slowSendInterval = slowSendInterval
9095
self.usiRefreshTimeIntervalInHrs = usiRefreshTimeIntervalInHrs
96+
self.hybridAgentId = hybridAgentId
97+
self.hybridAgentVersion = hybridAgentVersion
9198
reporterSendDebounce = Defaults.reporterSendDebounce
9299
reporterSendLowBatteryDebounce = Defaults.reporterSendLowBatteryDebounce
93100
maxRetries = Defaults.maxRetries
@@ -102,12 +109,16 @@ class InstanaConfiguration {
102109
enableCrashReporting: Bool,
103110
suspendReporting: Set<SuspendReporting>? = nil,
104111
slowSendInterval: Instana.Types.Seconds = 0.0,
105-
usiRefreshTimeIntervalInHrs: Double = defaultUsiRefreshTimeIntervalInHrs)
112+
usiRefreshTimeIntervalInHrs: Double = defaultUsiRefreshTimeIntervalInHrs,
113+
hybridAgentId: String? = nil,
114+
hybridAgentVersion: String? = nil)
106115
-> InstanaConfiguration {
107116
self.init(reportingURL: reportingURL, key: key, httpCaptureConfig: httpCaptureConfig,
108117
enableCrashReporting: enableCrashReporting,
109118
suspendReporting: suspendReporting,
110119
slowSendInterval: slowSendInterval,
111-
usiRefreshTimeIntervalInHrs: usiRefreshTimeIntervalInHrs)
120+
usiRefreshTimeIntervalInHrs: usiRefreshTimeIntervalInHrs,
121+
hybridAgentId: hybridAgentId,
122+
hybridAgentVersion: hybridAgentVersion)
112123
}
113124
}

Sources/InstanaAgent/Instana.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ import Foundation
8181
/// - Returns: true on success, false on error
8282
@objc
8383
public static func setup(key: String, reportingURL: URL, options: InstanaSetupOptions?) -> Bool {
84+
return setupInternal(key: key, reportingURL: reportingURL, options: options, hybridOptions: nil)
85+
}
86+
87+
/// Internal use, configures and sets up the Instana agent.
88+
///
89+
/// - Parameters:
90+
/// - hybridOptions: hybrid agent configuration options (set if invoked by Instana flutter-agent or react-native-agent)
91+
///
92+
/// - Returns: true on success, false on error
93+
@objc
94+
public static func setupInternal(key: String, reportingURL: URL, options: InstanaSetupOptions?,
95+
hybridOptions: HybridAgentOptions?) -> Bool {
8496
var httpCaptureConfig = HTTPCaptureConfig.automatic
8597
var collectionEnabled = true
8698
var enableCrashReporting = false
@@ -112,12 +124,22 @@ import Foundation
112124

113125
usiRefreshTimeIntervalInHrs = options.usiRefreshTimeIntervalInHrs
114126
}
127+
128+
var hybridAgentId: String?
129+
var hybridAgentVersion: String?
130+
if let hybridOptions = hybridOptions {
131+
hybridAgentId = hybridOptions.id
132+
hybridAgentVersion = hybridOptions.version
133+
}
134+
115135
let config = InstanaConfiguration.default(key: key, reportingURL: reportingURL,
116136
httpCaptureConfig: httpCaptureConfig,
117137
enableCrashReporting: enableCrashReporting,
118138
suspendReporting: suspendReporting,
119139
slowSendInterval: slowSendInterval,
120-
usiRefreshTimeIntervalInHrs: usiRefreshTimeIntervalInHrs)
140+
usiRefreshTimeIntervalInHrs: usiRefreshTimeIntervalInHrs,
141+
hybridAgentId: hybridAgentId,
142+
hybridAgentVersion: hybridAgentVersion)
121143
let session = InstanaSession(configuration: config, propertyHandler: InstanaPropertyHandler(),
122144
collectionEnabled: collectionEnabled)
123145
Instana.current = Instana(session: session)

Sources/InstanaAgent/InstanaSetupOptions.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,20 @@ import Foundation
3737
self.usiRefreshTimeIntervalInHrs = usiRefreshTimeIntervalInHrs
3838
}
3939
}
40+
41+
// Hybrid Agent options for setup
42+
@objc public class HybridAgentOptions: NSObject {
43+
public private(set) var id: String
44+
public private(set) var version: String
45+
46+
/// - Parameters:
47+
/// - id: flutter-agent or react-native-agent
48+
/// - version: version of flutter-agent or react-native-agent
49+
@objc public
50+
init(id: String, version: String) {
51+
// remove leading and trailing spaces
52+
// truncate if too long
53+
self.id = String(id.trimmingCharacters(in: .whitespaces).prefix(16))
54+
self.version = String(version.trimmingCharacters(in: .whitespaces).prefix(16))
55+
}
56+
}

Tests/InstanaAgentTests/Beacons/CoreBeacon/CoreBeaconTests.swift

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ class CoreBeaconTests: InstanaTestCase {
3333
sid: sessionID,
3434
usi: session.usi,
3535
id: beaconID,
36-
mobileFeatures: "c")
36+
mobileFeatures: "c",
37+
hybridAgentId: nil,
38+
hybridAgentVersion: nil)
3739
coreBeacon.append(props)
3840
wifiCoreBeacon = CoreBeacon.createDefault(viewName: viewName,
3941
key: key,
@@ -42,6 +44,8 @@ class CoreBeaconTests: InstanaTestCase {
4244
usi: session.usi,
4345
id: beaconID,
4446
mobileFeatures: "c",
47+
hybridAgentId: "f",
48+
hybridAgentVersion: "3.0.6",
4549
connection: .wifi,
4650
ect: .fiveG)
4751
wifiCoreBeacon.append(props)
@@ -135,7 +139,9 @@ class CoreBeaconTests: InstanaTestCase {
135139
// Given
136140
let configUsi = InstanaConfiguration(reportingURL: .random, key: "KEY", httpCaptureConfig: .automatic,
137141
enableCrashReporting: false, slowSendInterval: 0.0,
138-
usiRefreshTimeIntervalInHrs: usiTrackingNotAllowed)
142+
usiRefreshTimeIntervalInHrs: usiTrackingNotAllowed,
143+
hybridAgentId: nil,
144+
hybridAgentVersion: nil)
139145
let sessionUsi = InstanaSession.mock(configuration: configUsi,
140146
sessionID: sessionID,
141147
metaData: metaData,
@@ -148,6 +154,8 @@ class CoreBeaconTests: InstanaTestCase {
148154
usi: sessionUsi.usi,
149155
id: beaconID,
150156
mobileFeatures: "c",
157+
hybridAgentId: "nil",
158+
hybridAgentVersion: nil,
151159
connection: .wifi,
152160
ect: .fiveG)
153161
beacon.append(props)
@@ -282,4 +290,31 @@ class CoreBeaconTests: InstanaTestCase {
282290
XCTAssertTrue(sut!.hasSuffix(""))
283291
XCTAssertEqual(CoreBeacon.maxLengthPerField, 16384)
284292
}
293+
294+
func test_getInstanaAgentVersion() {
295+
let sut = CoreBeacon.getInstanaAgentVersion(hybridAgentId: nil, hybridAgentVersion: nil)
296+
let expected = "\(InstanaSystemUtils.agentVersion)"
297+
XCTAssertEqual(sut, expected)
298+
299+
let sutFlutter = CoreBeacon.getInstanaAgentVersion(hybridAgentId: "f", hybridAgentVersion: "3.0.6")
300+
let expectedFlutter = "\(InstanaSystemUtils.agentVersion):f:3.0.6"
301+
XCTAssertEqual(sutFlutter, expectedFlutter)
302+
303+
let sutRn = CoreBeacon.getInstanaAgentVersion(hybridAgentId: "r", hybridAgentVersion: "2.0.3")
304+
let expectedRn = "\(InstanaSystemUtils.agentVersion):r:2.0.3"
305+
XCTAssertEqual(sutRn, expectedRn)
306+
307+
// negative cases
308+
let sutMisConfigVer = CoreBeacon.getInstanaAgentVersion(hybridAgentId: nil, hybridAgentVersion: "misConfigedVersion")
309+
let expectedMisConfigVer = "\(InstanaSystemUtils.agentVersion)"
310+
XCTAssertEqual(sutMisConfigVer, expectedMisConfigVer)
311+
312+
let sutMisConfigId = CoreBeacon.getInstanaAgentVersion(hybridAgentId: "misConfigedId", hybridAgentVersion: nil)
313+
let expectedMisConfigId = "\(InstanaSystemUtils.agentVersion)"
314+
XCTAssertEqual(sutMisConfigId, expectedMisConfigId)
315+
316+
let sutMisConfigEmpty = CoreBeacon.getInstanaAgentVersion(hybridAgentId: "", hybridAgentVersion: " ")
317+
let expectedMisConfigEmpty = "\(InstanaSystemUtils.agentVersion)"
318+
XCTAssertEqual(sutMisConfigEmpty, expectedMisConfigEmpty)
319+
}
285320
}

Tests/InstanaAgentTests/Configuration/InstanaSessionTests.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ class InstanaSessionTests: InstanaTestCase {
2929
// When
3030
let configUsi = InstanaConfiguration(reportingURL: .random, key: "KEY", httpCaptureConfig: .automatic,
3131
enableCrashReporting: false, slowSendInterval: 0.0,
32-
usiRefreshTimeIntervalInHrs: usiTrackingNotAllowed)
32+
usiRefreshTimeIntervalInHrs: usiTrackingNotAllowed,
33+
hybridAgentId: nil,
34+
hybridAgentVersion: nil)
3335
let sut = InstanaSession(configuration: configUsi, propertyHandler: propertyHandler, collectionEnabled: true)
3436

3537
// Then
@@ -44,7 +46,9 @@ class InstanaSessionTests: InstanaTestCase {
4446
// When
4547
let configUsi = InstanaConfiguration(reportingURL: .random, key: "KEY", httpCaptureConfig: .automatic,
4648
enableCrashReporting: false, slowSendInterval: 0.0,
47-
usiRefreshTimeIntervalInHrs: (1.0 / 3600.0))
49+
usiRefreshTimeIntervalInHrs: (1.0 / 3600.0),
50+
hybridAgentId: nil,
51+
hybridAgentVersion: nil)
4852
let sut = InstanaSession(configuration: configUsi, propertyHandler: propertyHandler, collectionEnabled: true)
4953
let oldUsi = sut.usi?.uuidString
5054

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// Copyright © 2024 IBM Corp. All rights reserved.
3+
//
4+
5+
import Foundation
6+
import XCTest
7+
@testable import InstanaAgent
8+
9+
class HybridAgentOptionsTests: XCTestCase {
10+
func test_init() {
11+
let mco = HybridAgentOptions(id:"f", version:"3.0.6")
12+
AssertEqualAndNotNil(mco.id, "f")
13+
AssertEqualAndNotNil(mco.version, "3.0.6")
14+
15+
let mcoTooLong = HybridAgentOptions(id:"react-native-agent", version:"2.0.3")
16+
AssertEqualAndNotNil(mcoTooLong.id, "react-native-age")
17+
AssertEqualAndNotNil(mcoTooLong.version, "2.0.3")
18+
19+
let mcoMisConfiged = HybridAgentOptions(id:"", version:"")
20+
AssertEqualAndNotNil(mcoMisConfiged.id, "")
21+
AssertEqualAndNotNil(mcoMisConfiged.version, "")
22+
}
23+
}

Tests/InstanaAgentTests/InstanaTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,28 @@ class InstanaTests: InstanaTestCase {
119119
AssertEqualAndNotNil(Instana.current?.session.configuration.usiRefreshTimeIntervalInHrs, defaultUsiRefreshTimeIntervalInHrs)
120120
}
121121

122+
func test_setupInternal() {
123+
// Given
124+
let key = "KEY"
125+
let reportingURL = URL(string: "http://www.instana.com")!
126+
127+
let hybridOptions = HybridAgentOptions(id: "f", version:"3.0.6")
128+
_ = Instana.setupInternal(key: key, reportingURL: reportingURL, options: nil, hybridOptions: hybridOptions)
129+
130+
// Then
131+
AssertEqualAndNotNil(Instana.key, key)
132+
AssertTrue(Instana.collectionEnabled)
133+
AssertTrue(Instana.current!.session.collectionEnabled)
134+
AssertEqualAndNotNil(Instana.sessionID, Instana.current?.session.id.uuidString)
135+
AssertEqualAndNotNil(Instana.reportingURL, reportingURL)
136+
AssertEqualAndNotNil(Instana.current?.session.configuration.reportingURL, reportingURL)
137+
AssertEqualAndNotNil(Instana.current?.session.configuration.httpCaptureConfig, .automatic)
138+
XCTAssertNotEqual(Instana.current?.session.configuration,
139+
.default(key: key, reportingURL: reportingURL, enableCrashReporting: false))
140+
AssertEqualAndNotNil(Instana.current?.session.configuration.slowSendInterval, 0.0)
141+
AssertEqualAndNotNil(Instana.current?.session.configuration.usiRefreshTimeIntervalInHrs, defaultUsiRefreshTimeIntervalInHrs)
142+
}
143+
122144
func test_setup_disabled() {
123145
// Given
124146
let key = "KEY"

0 commit comments

Comments
 (0)