diff --git a/CHANGELOG.md b/CHANGELOG.md index cd8e16b..ab37d1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 The changes documented here do not include those from the original repository. +## 5.0.0-OS16 + +- Fix(Android & iOS): Convert values to double if necessary (https://outsystemsrd.atlassian.net/browse/RMET-3822 & https://outsystemsrd.atlassian.net/browse/RMET-3823). + ## 5.0.0-OS15 - Feat(Android & iOS): Add setConsent functionality to allow users to set consent for analytics (https://outsystemsrd.atlassian.net/browse/RMET-3677 & https://outsystemsrd.atlassian.net/browse/RMET-3678). diff --git a/package.json b/package.json index 136ba9a..b2b9468 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-plugin-firebase-analytics", - "version": "5.0.0-OS15", + "version": "5.0.0-OS16", "description": "Cordova plugin for Firebase Analytics", "cordova": { "id": "cordova-plugin-firebase-analytics", diff --git a/plugin.xml b/plugin.xml index 7851680..9b7d2dd 100644 --- a/plugin.xml +++ b/plugin.xml @@ -1,7 +1,7 @@ + version="5.0.0-OS16"> FirebaseAnalyticsPlugin Cordova plugin for Firebase Analytics diff --git a/src/android/com/outsystems/firebase/analytics/validator/OSFANLEventParameterValidator.kt b/src/android/com/outsystems/firebase/analytics/validator/OSFANLEventParameterValidator.kt index 3cd61de..ab6e954 100644 --- a/src/android/com/outsystems/firebase/analytics/validator/OSFANLEventParameterValidator.kt +++ b/src/android/com/outsystems/firebase/analytics/validator/OSFANLEventParameterValidator.kt @@ -1,6 +1,7 @@ package com.outsystems.firebase.analytics.validator import android.os.Bundle +import android.util.Log import com.outsystems.firebase.analytics.model.OSFANLError import com.outsystems.firebase.analytics.model.OSFANLInputDataFieldKey import com.outsystems.firebase.analytics.model.OSFANLInputDataFieldKey.CURRENCY @@ -8,6 +9,8 @@ import com.outsystems.firebase.analytics.model.OSFANLInputDataFieldKey.EVENT_PAR import com.outsystems.firebase.analytics.model.OSFANLInputDataFieldKey.KEY import com.outsystems.firebase.analytics.model.OSFANLInputDataFieldKey.TYPE_NUMBER import com.outsystems.firebase.analytics.model.OSFANLInputDataFieldKey.VALUE +import com.outsystems.firebase.analytics.model.OSFANLInputDataFieldKey.TAX +import com.outsystems.firebase.analytics.model.OSFANLInputDataFieldKey.SHIPPING import com.outsystems.firebase.analytics.model.putAny import org.json.JSONArray @@ -85,7 +88,22 @@ class OSFANLEventParameterValidator private constructor( hasCurrency = hasCurrency || key == CURRENCY.json parameterKeySet.add(key) - result.putAny(key, value) + + try { + // value, tax, and shipping are actually decimal values, not strings + if (key == VALUE.json || key == TAX.json || key == SHIPPING.json) { + result.putAny(key, value.toDouble()) // can throw NumberFormatException + } else { + result.putAny(key, value) + } + } catch (e: NumberFormatException) { + Log.d( + "OSFANLEventParameterValidator", + "Tried to convert non-number to double. Exception: ${e.message}" + ) + // if conversion isn't successful, we still try to send the event + result.putAny(key, value) + } } // validate value / currency diff --git a/src/ios/Common/Extensions/StringConvertable.swift b/src/ios/Common/Extensions/StringConvertable.swift index b4de8fc..e8fe580 100644 --- a/src/ios/Common/Extensions/StringConvertable.swift +++ b/src/ios/Common/Extensions/StringConvertable.swift @@ -1,20 +1,11 @@ protocol StringConvertable { static var variableType: String { get } - init?(value: String) } extension Decimal: StringConvertable { static var variableType: String { "Decimal" } - - init?(value: String) { - self.init(string: value) - } } extension String: StringConvertable { static var variableType: String { "Text" } - - init?(value: String) { - self.init(stringLiteral: value) - } } diff --git a/src/ios/Common/OSFANLInputDataFieldKey.swift b/src/ios/Common/OSFANLInputDataFieldKey.swift index b177865..86aee1c 100644 --- a/src/ios/Common/OSFANLInputDataFieldKey.swift +++ b/src/ios/Common/OSFANLInputDataFieldKey.swift @@ -15,3 +15,11 @@ enum OSFANLInputDataFieldKey: String { case transactionId = "transaction_id" case value } + +extension OSFANLInputDataFieldKey { + private static var decimalDataFields: [OSFANLInputDataFieldKey] { [.shipping, .tax, .value] } + + var isDecimalType: Bool { + return OSFANLInputDataFieldKey.decimalDataFields.contains(self) + } +} diff --git a/src/ios/InputTransformer/OSFANLInputTransformer.swift b/src/ios/InputTransformer/OSFANLInputTransformer.swift index 89a7f6c..8a7d579 100644 --- a/src/ios/InputTransformer/OSFANLInputTransformer.swift +++ b/src/ios/InputTransformer/OSFANLInputTransformer.swift @@ -1,22 +1,35 @@ struct OSFANLInputTransformer: OSFANLInputTransformable { func transform(_ eventParameterArray: [InputParameterData]?, _ itemArray: [InputItemData]?) throws -> OSFANLInputTransformableModel { - var eventParameterData: InputParameterData? - if let eventParameterArray { - guard let flatResult = try? self.flat(keyValueMapArray: eventParameterArray) else { - throw OSFANLError.duplicateItemsIn(parameter: OSFANLInputDataFieldKey.eventParameters.rawValue) + do { + var eventParameterData: InputParameterData? + if let eventParameterArray { + eventParameterData = try self.flat(keyValueMapArray: eventParameterArray) } - eventParameterData = flatResult + let itemArray = try self.transform(itemArray) + + return .init(eventParameterData, itemArray) + } catch OSFANLError.duplicateKeys { + throw OSFANLError.duplicateItemsIn(parameter: OSFANLInputDataFieldKey.eventParameters.rawValue) } - let itemArray = try self.transform(itemArray) - - return .init(eventParameterData, itemArray) } } private extension OSFANLInputTransformer { func flat(keyValueMapArray array: [InputParameterData]) throws -> InputParameterData { - let flatKeyValueArray = array.map { - [$0[OSFANLInputDataFieldKey.key.rawValue, default: ""]: $0[OSFANLInputDataFieldKey.value.rawValue, default: ""]] + let flatKeyValueArray = try array.reduce(into: [InputParameterData]()) { partialResult, current in + guard let dataFieldKey = current[OSFANLInputDataFieldKey.key.rawValue] as? String, + let dataValue = current[OSFANLInputDataFieldKey.value.rawValue] as? String + else { return } + + let value: StringConvertable + if let dataField = OSFANLInputDataFieldKey(rawValue: dataFieldKey), dataField.isDecimalType { + guard let decimalValue = Decimal(string: dataValue) else { throw OSFANLError.invalidType(dataFieldKey, type: Decimal.variableType) } + value = decimalValue + } else { + value = dataValue + } + + partialResult.append([dataFieldKey: value]) } let flatKeyValueDictionary = self.flat(dictionaryArray: flatKeyValueArray) diff --git a/src/ios/Manager/OSFANLManageable.swift b/src/ios/Manager/OSFANLManageable.swift index 714579e..7bedc73 100644 --- a/src/ios/Manager/OSFANLManageable.swift +++ b/src/ios/Manager/OSFANLManageable.swift @@ -8,7 +8,7 @@ extension DefaultKeyValueData { } } -typealias InputParameterData = [String: String] +typealias InputParameterData = [String: Any] typealias InputItemData = DefaultKeyValueData typealias OutputParameterData = DefaultKeyValueData diff --git a/src/ios/Manager/OSFANLManager+EventValidationRules.swift b/src/ios/Manager/OSFANLManager+EventValidationRules.swift index d72bb68..28a14be 100644 --- a/src/ios/Manager/OSFANLManager+EventValidationRules.swift +++ b/src/ios/Manager/OSFANLManager+EventValidationRules.swift @@ -49,19 +49,17 @@ private extension OSFANLManager { // Validate parameters first if let parameters { if parameters.contains(.currencyAndValue) { - let valueContainsValue = try self.validate(parameter: OSFANLInputDataFieldKey.value.rawValue, ofType: Decimal.self, eventData) - try self.validate( - parameter: OSFANLInputDataFieldKey.currency.rawValue, ofType: String.self, isRequired: valueContainsValue, eventData - ) + let valueContainsValue = try self.validate(dataField: .value, eventData) + try self.validate(dataField: .currency, isRequired: valueContainsValue, eventData) } if parameters.contains(.transactionId) { - try self.validate(parameter: OSFANLInputDataFieldKey.transactionId.rawValue, ofType: String.self, isRequired: true, eventData) + try self.validate(dataField: .transactionId, isRequired: true, eventData) } if parameters.contains(.shipping) { - try self.validate(parameter: OSFANLInputDataFieldKey.shipping.rawValue, ofType: Decimal.self, eventData) + try self.validate(dataField: .shipping, eventData) } if parameters.contains(.tax) { - try self.validate(parameter: OSFANLInputDataFieldKey.tax.rawValue, ofType: Decimal.self, eventData) + try self.validate(dataField: .tax, eventData) } } @@ -112,15 +110,18 @@ private extension OSFANLManager { } @discardableResult - private static func validate(parameter: String, ofType type: T.Type, isRequired: Bool = false, _ eventData: InputParameterData?) throws -> Bool { + private static func validate(dataField: OSFANLInputDataFieldKey, isRequired: Bool = false, _ eventData: InputParameterData?) throws -> Bool { func espace(_ isRequired: Bool, parameterMissing parameter: String) throws -> Bool { if isRequired { throw OSFANLError.missing(parameter) } return false // indicates that there's no value associated to `parameter` } guard let eventData, !eventData.isEmpty else { return try espace(isRequired, parameterMissing: "eventParameters") } - guard let parameterStringValue = eventData[parameter] else { return try espace(isRequired, parameterMissing: parameter) } - if T(value: parameterStringValue) == nil { throw OSFANLError.invalidType(parameter, type: T.variableType) } + let parameter = dataField.rawValue + guard let parameterValue = eventData[parameter] else { return try espace(isRequired, parameterMissing: parameter) } + + let dataField: (type: StringConvertable.Type, value: StringConvertable?) = dataField.isDecimalType ? (Decimal.self, parameterValue as? Decimal) : (String.self, parameterValue as? String) + if dataField.value == nil { throw OSFANLError.invalidType(parameter, type: dataField.type.variableType) } return true // indicates that there's a value associated to `parameter` } } diff --git a/src/ios/Manager/OSFANLManager.swift b/src/ios/Manager/OSFANLManager.swift index 7059f0f..18861bf 100644 --- a/src/ios/Manager/OSFANLManager.swift +++ b/src/ios/Manager/OSFANLManager.swift @@ -15,17 +15,17 @@ extension OSFANLManager: OSFANLManageable { } func createEventModel(for inputArgument: InputParameterData) throws -> OSFANLOutputModel { - guard let eventName = inputArgument[OSFANLInputDataFieldKey.event.rawValue], !eventName.isEmpty else { + guard let eventName = inputArgument[OSFANLInputDataFieldKey.event.rawValue] as? String, !eventName.isEmpty else { throw OSFANLError.missing(OSFANLInputDataFieldKey.event.rawValue) } var eventParameterArray: [InputParameterData]? - if let eventParameterString = inputArgument[OSFANLInputDataFieldKey.eventParameters.rawValue] { + if let eventParameterString = inputArgument[OSFANLInputDataFieldKey.eventParameters.rawValue] as? String { eventParameterArray = self.convert(jsonString: eventParameterString) } var itemArray: [InputItemData]? - if let itemString = inputArgument[OSFANLInputDataFieldKey.items.rawValue] { + if let itemString = inputArgument[OSFANLInputDataFieldKey.items.rawValue] as? String { itemArray = self.convert(jsonString: itemString) }