From 95b00a3dd8b0697f7916789a6515699cc4edabae Mon Sep 17 00:00:00 2001
From: Martin Walsh <martin.walsh@gmail.com>
Date: Mon, 28 Jan 2019 10:51:30 +0000
Subject: [PATCH] Updated Auth0 Telemetry Format (#256)

* Updated Auth0 Telemetry Format
* Updated Tests
* Added Platform OS, Version
* Add Telemetry
---
 Auth0/Telemetry.swift          | 61 ++++++++++++++++++++++++++++------
 Auth0Tests/TelemetrySpec.swift | 43 +++++++++++++++++++-----
 2 files changed, 86 insertions(+), 18 deletions(-)

diff --git a/Auth0/Telemetry.swift b/Auth0/Telemetry.swift
index df2947b0..9d92685b 100644
--- a/Auth0/Telemetry.swift
+++ b/Auth0/Telemetry.swift
@@ -26,7 +26,8 @@ public struct Telemetry {
 
     static let NameKey = "name"
     static let VersionKey = "version"
-    static let WrappedVersion = "lib_version"
+    static let WrappedVersion = "core"
+    static let EnvironmentKey = "env"
 
     static let NoVersion = "0.0.0"
     static let LibraryName = "Auth0.swift"
@@ -45,10 +46,16 @@ public struct Telemetry {
 
     mutating func wrapped(inLibrary name: String, version: String) {
         let info = Telemetry.versionInformation()
-        let wrapped = [
+        var env = Telemetry.generateEnviroment()
+        if let libVersion = info[Telemetry.VersionKey] as? String {
+            env[Telemetry.WrappedVersion] = libVersion
+        } else {
+            env[Telemetry.WrappedVersion] =  Telemetry.NoVersion
+        }
+        let wrapped: [String: Any] = [
             Telemetry.NameKey: name,
             Telemetry.VersionKey: version,
-            Telemetry.WrappedVersion: info[Telemetry.VersionKey] ?? Telemetry.NoVersion
+            Telemetry.EnvironmentKey: env
         ]
         self.info = Telemetry.generateValue(fromInfo: wrapped)
     }
@@ -69,20 +76,54 @@ public struct Telemetry {
         return items
     }
 
-    static func versionInformation(bundle: Bundle = Bundle(for: Credentials.classForCoder())) -> [String: String] {
+    static func versionInformation(bundle: Bundle = Bundle(for: Credentials.classForCoder())) -> [String: Any] {
         let version = bundle.infoDictionary?["CFBundleShortVersionString"] as? String ?? Telemetry.NoVersion
-        let dict = [
+        let dict: [String: Any] = [
             Telemetry.NameKey: Telemetry.LibraryName,
             Telemetry.VersionKey: version,
-            "swift-version": "3.0"
-            ]
+            Telemetry.EnvironmentKey: Telemetry.generateEnviroment()
+        ]
         return dict
     }
 
-    static func generateValue(fromInfo info: [String: String] = Telemetry.versionInformation()) -> String? {
+    static func generateEnviroment() -> [String: String] {
+        let platform = Telemetry.osInfo()
+        let env = [ "swift": Telemetry.swiftVersion(),
+                    platform.0: platform.1
+        ]
+        return env
+    }
+
+    static func generateValue(fromInfo info: [String: Any] = Telemetry.versionInformation()) -> String? {
         let data = try? JSONSerialization.data(withJSONObject: info, options: [])
         return data?.a0_encodeBase64URLSafe()
     }
+
+    static func swiftVersion() -> String {
+        #if swift(>=4.0)
+        return "4.x"
+        #elseif swift(>=3.0)
+        return "3.x"
+        #endif
+    }
+
+    static func osPlatform() -> String {
+        #if os(iOS)
+        return "iOS"
+        #elseif os(OSX)
+        return "macOS"
+        #elseif os(tvOS)
+        return "tvOS"
+        #elseif os(watchOS)
+        return "watchOS"
+        #else
+        return "unknown"
+        #endif
+    }
+
+    static func osInfo() -> (String, String) {
+        return (self.osPlatform(), "\(ProcessInfo().operatingSystemVersion.majorVersion).\(ProcessInfo().operatingSystemVersion.minorVersion)")
+    }
 }
 
 public protocol Trackable {
@@ -95,7 +136,7 @@ extension Trackable {
     /**
      Avoid Auth0.swift sending its version on every request to Auth0 API.
      By default we collect our libraries and SDKs versions to help us during support and evaluate usage.
-
+     
      - parameter enabled: if Auth0.swift should send it's version on every request.
      */
     public mutating func tracking(enabled: Bool) {
@@ -104,7 +145,7 @@ extension Trackable {
 
     /**
      Send the library/framework, that has Auth0.swift as dependency, when sending telemetry information
-
+     
      - parameter name:    name of library or framework that uses Auth0.swift
      - parameter version: version of library or framework
      */
diff --git a/Auth0Tests/TelemetrySpec.swift b/Auth0Tests/TelemetrySpec.swift
index a070e55a..7d5083a9 100644
--- a/Auth0Tests/TelemetrySpec.swift
+++ b/Auth0Tests/TelemetrySpec.swift
@@ -66,21 +66,36 @@ class TelemetrySpec: QuickSpec {
                 bundle.version = nil
             }
 
-            var subject: [String: String] {
+            var subject: [String: Any] {
                 return Telemetry.versionInformation(bundle: bundle)
             }
 
             pending("should return bundle default version if nil") {
-                expect(subject["version"]) == "0.0.0"
+                expect(subject["version"] as? String) == "0.0.0"
             }
 
             pending("should return bundle version") {
                 bundle.version = "1.0.0"
-                expect(subject["version"]) == "1.0.0"
+                expect(subject["version"] as? String) == "1.0.0"
             }
 
             it("should return lib name") {
-                expect(subject["name"]) == "Auth0.swift"
+                expect(subject["name"] as? String) == "Auth0.swift"
+            }
+            
+            it("should have env info") {
+                let env = subject["env"] as! [String : String]
+                #if os(iOS)
+                expect(env["iOS"]).toNot(beNil())
+                #elseif os(OSX)
+                expect(env["macOS"]).toNot(beNil())
+                #elseif os(tvOS)
+                expect(env["tvOS"]).toNot(beNil())
+                #elseif os(watchOS)
+                expect(env["watchOS"]).toNot(beNil())
+                #else
+                expect(env["unknown"]).toNot(beNil())
+                #endif
             }
 
         }
@@ -112,10 +127,22 @@ class TelemetrySpec: QuickSpec {
                 let padded = value.padding(toLength: paddedLength, withPad: "=", startingAt: 0)
 
                 let data = Data(base64Encoded: padded, options: [NSData.Base64DecodingOptions.ignoreUnknownCharacters])
-                let info = try! JSONSerialization.jsonObject(with: data!, options: []) as! [String: String]
-                expect(info["version"]) == "1.0.0-alpha.4"
-                expect(info["lib_version"]) == version
-                expect(info["name"]) == "Lock.iOS"
+                let info = try! JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
+                expect(info["version"] as? String) == "1.0.0-alpha.4"
+                expect(info["name"] as? String) == "Lock.iOS"
+                let env = info["env"] as! [String : String]
+                expect(env["core"]) == version as? String
+                #if os(iOS)
+                expect(env["iOS"]).toNot(beNil())
+                #elseif os(OSX)
+                expect(env["macOS"]).toNot(beNil())
+                #elseif os(tvOS)
+                expect(env["tvOS"]).toNot(beNil())
+                #elseif os(watchOS)
+                expect(env["watchOS"]).toNot(beNil())
+                #else
+                expect(env["unknown"]).toNot(beNil())
+                #endif
             }
         }