diff --git a/Changelog.md b/Changelog.md index f8af3e2..dc11802 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ # Changelog +## 1.8.1 +- added functionality to accept internal metadata from cross-platform agents + ## 1.8.0 - add to mobile feature list if `autoCaptureScreenNames` is enabled - add Privacy Manifest File PrivacyInfo.xcprivacy to iOSAgent diff --git a/InstanaAgent.podspec b/InstanaAgent.podspec index 26a0e39..d97f78f 100644 --- a/InstanaAgent.podspec +++ b/InstanaAgent.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "InstanaAgent" - s.version = "1.8.0" + s.version = "1.8.1" s.summary = "Instana iOS agent." # This description is used to generate tags and improve search results. diff --git a/Sources/InstanaAgent/Beacons/Beacons Types/ViewChange.swift b/Sources/InstanaAgent/Beacons/Beacons Types/ViewChange.swift index c202a77..947cad0 100644 --- a/Sources/InstanaAgent/Beacons/Beacons Types/ViewChange.swift +++ b/Sources/InstanaAgent/Beacons/Beacons Types/ViewChange.swift @@ -9,10 +9,14 @@ class ViewChange: Beacon { var navigationItemTitle: String? var className: String? + // Internal Meta only to be consumed by Flutter/React agents + var viewInternalCPMetaMap: [String: String] + init(timestamp: Instana.Types.Milliseconds = Date().millisecondsSince1970, viewName: String? = nil, accessibilityLabel: String? = nil, navigationItemTitle: String? = nil, - className: String? = nil, isSwiftUI: Bool = false) { + className: String? = nil, isSwiftUI: Bool = false, viewInternalCPMetaMap: [String: String] = [:]) { + self.viewInternalCPMetaMap = [:] var canonicalName: String? = viewName var prefix = "" if accessibilityLabel != nil, !accessibilityLabel!.isEmpty { @@ -32,6 +36,9 @@ class ViewChange: Beacon { canonicalName = prefix.isEmpty ? "@\(self.className!)" : prefix } } + for (key, value) in viewInternalCPMetaMap { + self.viewInternalCPMetaMap[key] = ViewChange.validate(viewName: value) + } super.init(timestamp: timestamp, viewName: canonicalName) } diff --git a/Sources/InstanaAgent/Beacons/CoreBeacon/CoreBeaconFactory.swift b/Sources/InstanaAgent/Beacons/CoreBeacon/CoreBeaconFactory.swift index 642d9fe..924834b 100644 --- a/Sources/InstanaAgent/Beacons/CoreBeacon/CoreBeaconFactory.swift +++ b/Sources/InstanaAgent/Beacons/CoreBeacon/CoreBeaconFactory.swift @@ -109,6 +109,11 @@ extension CoreBeacon { if beacon.className != nil { im![internalMetaDataKeyView_className] = beacon.className! } + if !beacon.viewInternalCPMetaMap.isEmpty { + for (key, value) in beacon.viewInternalCPMetaMap { + im![key] = value + } + } if im!.isEmpty { im = nil } diff --git a/Sources/InstanaAgent/Configuration/Defines.swift b/Sources/InstanaAgent/Configuration/Defines.swift index 436dad6..02408e1 100644 --- a/Sources/InstanaAgent/Configuration/Defines.swift +++ b/Sources/InstanaAgent/Configuration/Defines.swift @@ -56,6 +56,8 @@ let internalMetaDataKeyView_accbltyLabel = "view.accLabel" // accessibilityLabel let internalMetaDataKeyView_navItemTitle = "view.navItemTitle" // navigationItemTitle let internalMetaDataKeyView_className = "view.clsName" // className +let internalMetaFlutterKeys = ["settings.route.name", "widget.name", "child.widget.name", "child.widget.title", "go.router.path"] // keys given from flutter agent + /// /// Mobile Features /// diff --git a/Sources/InstanaAgent/Configuration/VersionConfig.swift b/Sources/InstanaAgent/Configuration/VersionConfig.swift index 9020aaf..2572fd7 100644 --- a/Sources/InstanaAgent/Configuration/VersionConfig.swift +++ b/Sources/InstanaAgent/Configuration/VersionConfig.swift @@ -1,3 +1,3 @@ struct VersionConfig { - static let agentVersion = "1.8.0" + static let agentVersion = "1.8.1" } diff --git a/Sources/InstanaAgent/Instana.swift b/Sources/InstanaAgent/Instana.swift index 7cd5c07..638d524 100644 --- a/Sources/InstanaAgent/Instana.swift +++ b/Sources/InstanaAgent/Instana.swift @@ -412,11 +412,35 @@ import Foundation Instana.current?.setViewInternal(name: name) } + /// Set the screen name and its meta details from crossplatfrom services (Flutter & React Native agents) + /// + /// ONLY TO BE USED BY FLUTTER/REACT NATIVE AGENTS! + /// + /// This is exposed for internal use, for the communication between the instana agents, not to be consumed by developers + /// Call this method from instana cross platform agents when need to provide auto capture of screen names and its meta details + /// - Parameters: + /// - name: The screen name identified from cross platform agents + /// - viewInternalCPMetaMap: Dictionary of keys and values of meta details from CROSS PLATFORM agents + @objc + public static func setViewMetaCPInternal(name: String, viewInternalCPMetaMap: [String: String] = [:]) { + var isAllKeysValid = true + for key in viewInternalCPMetaMap.keys { + if !internalMetaFlutterKeys.contains(key) { + isAllKeysValid = false + break + } + } + if isAllKeysValid { + Instana.current?.setViewInternal(name: name, viewInternalCPMetaMap: viewInternalCPMetaMap) + } + } + public func setViewInternal(name: String?, accessibilityLabel: String? = nil, navigationItemTitle: String? = nil, className: String? = nil, - isSwiftUI: Bool = false) { + isSwiftUI: Bool = false, + viewInternalCPMetaMap: [String: String] = [:]) { guard let propertyHandler = Instana.current?.session.propertyHandler else { return } let isIdentical = propertyHandler.properties.view?.isSame(name: name, accessibilityLabel: accessibilityLabel, @@ -427,7 +451,8 @@ import Foundation accessibilityLabel: accessibilityLabel, navigationItemTitle: navigationItemTitle, className: className, - isSwiftUI: isSwiftUI) + isSwiftUI: isSwiftUI, + viewInternalCPMetaMap: viewInternalCPMetaMap) propertyHandler.properties.view = view guard view.viewName != nil else { return } diff --git a/Tests/InstanaAgentTests/Beacons/Beacon Types/ViewChangeBeaconTests.swift b/Tests/InstanaAgentTests/Beacons/Beacon Types/ViewChangeBeaconTests.swift index 1010a37..1bbd40e 100644 --- a/Tests/InstanaAgentTests/Beacons/Beacon Types/ViewChangeBeaconTests.swift +++ b/Tests/InstanaAgentTests/Beacons/Beacon Types/ViewChangeBeaconTests.swift @@ -13,6 +13,7 @@ class ViewChangeBeaconTests: InstanaTestCase { let testViewName = "test view name 0" let testAcsbLabel = "test accessibilityLabel 1" let testNavItemTitle = "test navigationItemTitle 2" + let testViewInternalMetaMap = ["key1": "value1", "key2": "value2"] override func setUp() { super.setUp() @@ -113,4 +114,18 @@ class ViewChangeBeaconTests: InstanaTestCase { vcAfterSwizzle.viewDidAppear(false) Thread.sleep(forTimeInterval: 1) } + + func test_init_viewInternalMetaMap() { + let vcBeacon = ViewChange(timestamp: testTimestamp, viewName: testViewName, + accessibilityLabel: nil, navigationItemTitle: nil, className: nil,viewInternalCPMetaMap: testViewInternalMetaMap) + XCTAssertEqual(vcBeacon.viewInternalCPMetaMap,testViewInternalMetaMap) + } + + func test_init_viewInternalMetaMap_empty() { + let vcBeacon = ViewChange(timestamp: testTimestamp, viewName: testViewName, + accessibilityLabel: nil, navigationItemTitle: nil, className: nil) + XCTAssertEqual(vcBeacon.viewInternalCPMetaMap,[:]) + } + + } diff --git a/Tests/InstanaAgentTests/Configuration/InstanaSystemUtilsTests.swift b/Tests/InstanaAgentTests/Configuration/InstanaSystemUtilsTests.swift index 831dacb..cb5556c 100644 --- a/Tests/InstanaAgentTests/Configuration/InstanaSystemUtilsTests.swift +++ b/Tests/InstanaAgentTests/Configuration/InstanaSystemUtilsTests.swift @@ -5,7 +5,7 @@ class InstanaSystemUtilsTests: InstanaTestCase { func test_AgentVersion() { // Then - AssertTrue(InstanaSystemUtils.agentVersion == "1.8.0") + AssertTrue(InstanaSystemUtils.agentVersion == "1.8.1") } func test_systemVersion() { diff --git a/Tests/InstanaAgentTests/InstanaTests.swift b/Tests/InstanaAgentTests/InstanaTests.swift index 490f3a9..8f70985 100644 --- a/Tests/InstanaAgentTests/InstanaTests.swift +++ b/Tests/InstanaAgentTests/InstanaTests.swift @@ -827,4 +827,91 @@ class InstanaTests: InstanaTestCase { Instana.current = nil AssertFalse(Instana.collectionEnabled) } + + func test_setViewMetaCPInternal_called_negative(){ + // Given + let session = InstanaSession.mock(configuration: .mock()) + + var viewChangeBeaconCount = 0 + var capturedViewInternalMetaMap: [String: String]? = nil + let reporter = MockReporter { + if let viewChange = $0 as? ViewChange { + capturedViewInternalMetaMap = viewChange.viewInternalCPMetaMap + viewChangeBeaconCount += 1 + } + } + Instana.current = Instana(session: session, monitors: Monitors(session, reporter: reporter)) + + let testViewInternalMetaMap = ["key1": "value1", "key2": "value2"] + // When + Instana.setViewMetaCPInternal(name: "ScreenName", viewInternalCPMetaMap:testViewInternalMetaMap) + + Instana.current?.setViewInternal(name: nil) // nil view name should not trigger ViewChange beacon + Thread.sleep(forTimeInterval: 1) + + // Then + AssertEqualAndNotNil(viewChangeBeaconCount, 0) + AssertEqualAndNotNil(capturedViewInternalMetaMap, nil) + } + + func test_setViewMetaCPInternal_called_positive(){ + // Given + let session = InstanaSession.mock(configuration: .mock()) + + var viewChangeBeaconCount = 0 + var capturedViewInternalMetaMap: [String: String]? = nil + let reporter = MockReporter { + if let viewChange = $0 as? ViewChange { + capturedViewInternalMetaMap = viewChange.viewInternalCPMetaMap + viewChangeBeaconCount += 1 + } + } + Instana.current = Instana(session: session, monitors: Monitors(session, reporter: reporter)) + + let testViewInternalMetaMap = ["settings.route.name": "value1", "widget.name": "value2"] + + + // When + Instana.setViewMetaCPInternal(name: "ScreenName", viewInternalCPMetaMap:testViewInternalMetaMap) + + Instana.current?.setViewInternal(name: nil) // nil view name should not trigger ViewChange beacon + Thread.sleep(forTimeInterval: 1) + + // Then + AssertEqualAndNotNil(viewChangeBeaconCount, 1) + AssertEqualAndNotNil(capturedViewInternalMetaMap, testViewInternalMetaMap) + } + + func test_setViewMetaCPInternal_called_with_max_value_length(){ + // Given + let session = InstanaSession.mock(configuration: .mock()) + + var viewChangeBeaconCount = 0 + var capturedViewInternalMetaMap: [String: String]? = nil + let reporter = MockReporter { + if let viewChange = $0 as? ViewChange { + capturedViewInternalMetaMap = viewChange.viewInternalCPMetaMap + viewChangeBeaconCount += 1 + } + } + Instana.current = Instana(session: session, monitors: Monitors(session, reporter: reporter)) + var randomString = "" + for _ in 0..