-
-
Notifications
You must be signed in to change notification settings - Fork 341
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
48 changed files
with
2,447 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -210,4 +210,8 @@ - (BOOL)isAppHangEvent | |
|
||
@end | ||
|
||
@implementation SentryEventDecodable | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,21 @@ | ||
#import "SentryLevelHelper.h" | ||
#import "SentryBreadcrumb+Private.h" | ||
#import "SentryEvent.h" | ||
|
||
@implementation SentryLevelBridge : NSObject | ||
+ (NSUInteger)breadcrumbLevel:(SentryBreadcrumb *)breadcrumb | ||
{ | ||
return breadcrumb.level; | ||
} | ||
|
||
+ (void)setBreadcrumbLevel:(SentryBreadcrumb *)breadcrumb level:(NSUInteger)level | ||
{ | ||
breadcrumb.level = level; | ||
} | ||
|
||
+ (void)setBreadcrumbLevelOnEvent:(SentryEvent *)event level:(NSUInteger)level | ||
{ | ||
event.level = level; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 134 additions & 0 deletions
134
Sources/Swift/Protocol/Codable/DecodeArbitraryData.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
@_implementationOnly import _SentryPrivate | ||
|
||
/// Represents arbitrary data that can be decoded from JSON with Decodable. | ||
/// | ||
/// - Note: Some classes on the protocol allow adding extra data in a dictionary of type String:Any. | ||
/// Users can put anything in there that can be serialized to JSON. The SDK uses JSONSerialization to | ||
/// serialize these dictionaries. At first glance, you could assume that we can use JSONSerialization.jsonObject(with:options) | ||
/// to deserialize these dictionaries, but we can't. When using Decodable, you don't have access to the raw | ||
/// data of the JSON. The Decoder and the DecodingContainers don't offer methods to access the underlying | ||
/// data. The Swift Decodable converts the raw data to a JSON object and then casts the JSON object to the | ||
/// class that implements the Decodable protocol, see: | ||
/// https://github.com/swiftlang/swift-foundation/blob/e9d59b6065ad909fee15f174bd5ca2c580490388/Sources/FoundationEssentials/JSON/JSONDecoder.swift#L360-L386 | ||
/// https://github.com/swiftlang/swift-foundation/blob/e9d59b6065ad909fee15f174bd5ca2c580490388/Sources/FoundationEssentials/JSON/JSONScanner.swift#L343-L383 | ||
|
||
/// Therefore, we have to implement decoding the arbitrary dictionary manually. | ||
/// | ||
/// A discarded option is to decode the JSON raw data twice: once with Decodable and once with the JSONSerialization. | ||
/// This has two significant downsides: First, we deserialize the JSON twice, which is a performance overhead. Second, | ||
/// we don't conform to the Decodable protocol, which could lead to unwanted hard-to-detect problems in the future. | ||
enum ArbitraryData: Decodable { | ||
case string(String) | ||
case int(Int) | ||
case number(Double) | ||
case boolean(Bool) | ||
case date(Date) | ||
case dict([String: ArbitraryData]) | ||
case array([ArbitraryData]) | ||
case null | ||
|
||
init(from decoder: Decoder) throws { | ||
let container = try decoder.singleValueContainer() | ||
|
||
// The order here matters as we're dealing with arbitrary data. | ||
// We have to check the double before the Date, because otherwise | ||
// a double value could turn into a Date. So only ISO 8601 string formatted | ||
// dates work, which sanitizeArray and sentry_sanitize use. | ||
// We must check String after Date, because otherwise we would turn a ISO 8601 | ||
// string into a string and not a date. | ||
if let intValue = try? container.decode(Int.self) { | ||
self = .int(intValue) | ||
} else if let numberValue = try? container.decode(Double.self) { | ||
self = .number(numberValue) | ||
} else if let boolValue = try? container.decode(Bool.self) { | ||
self = .boolean(boolValue) | ||
} else if let dateValue = try? container.decode(Date.self) { | ||
self = .date(dateValue) | ||
} else if let stringValue = try? container.decode(String.self) { | ||
self = .string(stringValue) | ||
} else if let objectValue = try? container.decode([String: ArbitraryData].self) { | ||
self = .dict(objectValue) | ||
} else if let arrayValue = try? container.decode([ArbitraryData].self) { | ||
self = .array(arrayValue) | ||
} else if container.decodeNil() { | ||
self = .null | ||
} else { | ||
throw DecodingError.dataCorruptedError( | ||
in: container, | ||
debugDescription: "Invalid JSON value" | ||
) | ||
} | ||
} | ||
} | ||
|
||
func decodeArbitraryData(decode: () throws -> [String: ArbitraryData]?) -> [String: Any]? { | ||
do { | ||
let rawData = try decode() | ||
if rawData == nil { | ||
return nil | ||
} | ||
|
||
return unwrapArbitraryDict(rawData) | ||
} catch { | ||
SentryLog.error("Failed to decode raw data: \(error)") | ||
return nil | ||
} | ||
} | ||
|
||
func decodeArbitraryData(decode: () throws -> [String: [String: ArbitraryData]]?) -> [String: [String: Any]]? { | ||
do { | ||
let rawData = try decode() | ||
if rawData == nil { | ||
return nil | ||
} | ||
|
||
var newData = [String: [String: Any]]() | ||
for (key, value) in rawData ?? [:] { | ||
newData[key] = unwrapArbitraryDict(value) | ||
} | ||
|
||
return newData | ||
} catch { | ||
SentryLog.error("Failed to decode raw data: \(error)") | ||
return nil | ||
} | ||
} | ||
|
||
private func unwrapArbitraryDict(_ dict: [String: ArbitraryData]?) -> [String: Any]? { | ||
guard let nonNullDict = dict else { | ||
return nil | ||
} | ||
|
||
return nonNullDict.mapValues { unwrapArbitraryValue($0) as Any } | ||
} | ||
|
||
private func unwrapArbitraryArray(_ array: [ArbitraryData]?) -> [Any]? { | ||
guard let nonNullArray = array else { | ||
return nil | ||
} | ||
|
||
return nonNullArray.map { unwrapArbitraryValue($0) as Any } | ||
} | ||
|
||
private func unwrapArbitraryValue(_ value: ArbitraryData?) -> Any? { | ||
switch value { | ||
case .string(let stringValue): | ||
return stringValue | ||
case .number(let numberValue): | ||
return numberValue | ||
case .int(let intValue): | ||
return intValue | ||
case .boolean(let boolValue): | ||
return boolValue | ||
case .date(let dateValue): | ||
return dateValue | ||
case .dict(let dictValue): | ||
return unwrapArbitraryDict(dictValue) | ||
case .array(let arrayValue): | ||
return unwrapArbitraryArray(arrayValue) | ||
case .null: | ||
return NSNull() | ||
case .none: | ||
return nil | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
Sources/Swift/Protocol/Codable/NSNumberDecodableWrapper.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
struct NSNumberDecodableWrapper: Decodable { | ||
let value: NSNumber? | ||
|
||
init(from decoder: Decoder) throws { | ||
let container = try decoder.singleValueContainer() | ||
if let intValue = try? container.decode(Int.self) { | ||
value = NSNumber(value: intValue) | ||
} | ||
// On 32-bit platforms UInt is UInt32, so we use UInt64 to cover all platforms. | ||
// We don't need UInt128 because NSNumber doesn't support it. | ||
else if let uint64Value = try? container.decode(UInt64.self) { | ||
value = NSNumber(value: uint64Value) | ||
} else if let doubleValue = try? container.decode(Double.self) { | ||
value = NSNumber(value: doubleValue) | ||
} else if let boolValue = try? container.decode(Bool.self) { | ||
value = NSNumber(value: boolValue) | ||
} else { | ||
SentryLog.warning("Failed to decode NSNumber from container for key: \(container.codingPath.last?.stringValue ?? "unknown")") | ||
value = nil | ||
} | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
Sources/Swift/Protocol/Codable/SentryBreadcrumbCodable.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
@_implementationOnly import _SentryPrivate | ||
import Foundation | ||
|
||
extension Breadcrumb: Decodable { | ||
|
||
private enum CodingKeys: String, CodingKey { | ||
case level | ||
case category | ||
case timestamp | ||
case type | ||
case message | ||
case data | ||
case origin | ||
} | ||
|
||
required convenience public init(from decoder: any Decoder) throws { | ||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||
|
||
self.init() | ||
|
||
let rawLevel = try container.decode(String.self, forKey: .level) | ||
let level = SentryLevelHelper.levelForName(rawLevel) | ||
SentryLevelBridge.setBreadcrumbLevel(self, level: level.rawValue) | ||
|
||
self.category = try container.decode(String.self, forKey: .category) | ||
self.timestamp = try container.decodeIfPresent(Date.self, forKey: .timestamp) | ||
self.type = try container.decodeIfPresent(String.self, forKey: .type) | ||
self.message = try container.decodeIfPresent(String.self, forKey: .message) | ||
self.origin = try container.decodeIfPresent(String.self, forKey: .origin) | ||
|
||
self.data = decodeArbitraryData { | ||
try container.decodeIfPresent([String: ArbitraryData].self, forKey: .data) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
@_implementationOnly import _SentryPrivate | ||
import Foundation | ||
|
||
func decodeFromJSONData<T: Decodable>(jsonData: Data) -> T? { | ||
if jsonData.isEmpty { | ||
return nil | ||
} | ||
|
||
do { | ||
let decoder = JSONDecoder() | ||
decoder.dateDecodingStrategy = .custom { decoder in | ||
let container = try decoder.singleValueContainer() | ||
|
||
// We prefer a Double/TimeInterval because it allows nano second precision. | ||
// The ISO8601 formatter only supports millisecond precision. | ||
if let timeIntervalSince1970 = try? container.decode(Double.self) { | ||
return Date(timeIntervalSince1970: timeIntervalSince1970) | ||
} | ||
|
||
if let dateString = try? container.decode(String.self) { | ||
let formatter = sentryGetIso8601FormatterWithMillisecondPrecision() | ||
guard let date = formatter.date(from: dateString) else { | ||
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date format. The following string doesn't represent a valid ISO 8601 date string: '\(dateString)'") | ||
} | ||
|
||
return date | ||
} | ||
|
||
throw DecodingError.typeMismatch(Date.self, DecodingError.Context( | ||
codingPath: decoder.codingPath, | ||
debugDescription: "Invalid date format. The Date must either be a Double/TimeInterval representing the timeIntervalSince1970 or it can be a ISO 8601 formatted String." | ||
)) | ||
|
||
} | ||
return try decoder.decode(T.self, from: jsonData) | ||
} catch { | ||
SentryLog.error("Could not decode object of type \(T.self) from JSON data due to error: \(error)") | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.