diff --git a/CHANGELOG.md b/CHANGELOG.md index 40ddd54b1e2..a3eeacb835d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Improvements + +- Add `itemCount` to `SentryEnvelopeItemHeader` ([#5230](https://github.com/getsentry/sentry-cocoa/pull/5230)) + ### Features - Apps can now manually show and hide the included feedback widget button (#5236) diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 1b5d56f683e..847c157256a 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -805,6 +805,7 @@ 92136D672C9D7660002A9FB8 /* SentryNSURLRequestBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92136D662C9D765D002A9FB8 /* SentryNSURLRequestBuilderTests.swift */; }; 925824C22CB5897700C9B20B /* SentrySessionReplayIntegration-Hybrid.h in Headers */ = {isa = PBXBuildFile; fileRef = D80382BE2C09C6FD0090E048 /* SentrySessionReplayIntegration-Hybrid.h */; settings = {ATTRIBUTES = (Private, ); }; }; 92672BB629C9A2A9006B021C /* SentryBreadcrumb+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 92672BB529C9A2A9006B021C /* SentryBreadcrumb+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 927A5CC42DD7626B00B82404 /* SentryEnvelopeItemHeaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927A5CC32DD7626400B82404 /* SentryEnvelopeItemHeaderTests.swift */; }; 9286059529A5096600F96038 /* SentryGeo.h in Headers */ = {isa = PBXBuildFile; fileRef = 9286059429A5096600F96038 /* SentryGeo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9286059729A5098900F96038 /* SentryGeo.m in Sources */ = {isa = PBXBuildFile; fileRef = 9286059629A5098900F96038 /* SentryGeo.m */; }; 9286059929A50BAB00F96038 /* SentryGeoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9286059829A50BAA00F96038 /* SentryGeoTests.swift */; }; @@ -1978,6 +1979,7 @@ 8FF94DF22B06A24C00BCD650 /* SentryCrash+Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryCrash+Test.h"; sourceTree = ""; }; 92136D662C9D765D002A9FB8 /* SentryNSURLRequestBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryNSURLRequestBuilderTests.swift; sourceTree = ""; }; 92672BB529C9A2A9006B021C /* SentryBreadcrumb+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SentryBreadcrumb+Private.h"; path = "include/HybridPublic/SentryBreadcrumb+Private.h"; sourceTree = ""; }; + 927A5CC32DD7626400B82404 /* SentryEnvelopeItemHeaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryEnvelopeItemHeaderTests.swift; sourceTree = ""; }; 9286059429A5096600F96038 /* SentryGeo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryGeo.h; path = Public/SentryGeo.h; sourceTree = ""; }; 9286059629A5098900F96038 /* SentryGeo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentryGeo.m; sourceTree = ""; }; 9286059829A50BAA00F96038 /* SentryGeoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SentryGeoTests.swift; sourceTree = ""; }; @@ -3120,6 +3122,7 @@ 7B869EBB249B91D8004F4FDB /* SentryDebugMetaEquality.swift */, 7B869EBD249B964D004F4FDB /* SentryThreadEquality.swift */, 7BF536D024BDF3E7004FA6A2 /* SentryEnvelopeTests.swift */, + 927A5CC32DD7626400B82404 /* SentryEnvelopeItemHeaderTests.swift */, 7BC6EBF3255C044A0059822A /* SentryEventTests.swift */, 7B88F30124BC5C6D00ADF90A /* SentrySdkInfoTests.swift */, 7B26BBFA24C0A66D00A79CCC /* SentrySdkInfoNilTests.m */, @@ -5404,6 +5407,7 @@ 62277BBC2DA5183500EF06B7 /* SentryTracer+Test.m in Sources */, 7B58816727FC5D790098B121 /* SentryDiscardReasonMapperTests.swift in Sources */, 63FE720320DA66EC00CDBAE8 /* SentryCrashCPU_Tests.m in Sources */, + 927A5CC42DD7626B00B82404 /* SentryEnvelopeItemHeaderTests.swift in Sources */, 63FE721020DA66EC00CDBAE8 /* SentryCrashCachedData_Tests.m in Sources */, 0A9BF4E928A125390068D266 /* TestSentryViewHierarchy.swift in Sources */, 7BC6EC10255C3F560059822A /* SentryMechanismTests.swift in Sources */, diff --git a/Sources/Sentry/Public/SentryEnvelopeItemHeader.h b/Sources/Sentry/Public/SentryEnvelopeItemHeader.h index 0612432a4ae..89a935e47a5 100644 --- a/Sources/Sentry/Public/SentryEnvelopeItemHeader.h +++ b/Sources/Sentry/Public/SentryEnvelopeItemHeader.h @@ -20,6 +20,11 @@ SENTRY_NO_INIT filenname:(NSString *)filename contentType:(NSString *)contentType; +- (instancetype)initWithType:(NSString *)type + length:(NSUInteger)length + contentType:(NSString *)contentType + itemCount:(NSNumber *)itemCount; + /** * The type of the envelope item. */ @@ -27,6 +32,7 @@ SENTRY_NO_INIT @property (nonatomic, readonly) NSUInteger length; @property (nonatomic, readonly, copy, nullable) NSString *filename; @property (nonatomic, readonly, copy, nullable) NSString *contentType; +@property (nonatomic, readonly, copy, nullable) NSNumber *itemCount; /** * Some envelopes need to report the platform name for enhanced rate limiting functionality in diff --git a/Sources/Sentry/SentryEnvelopeItemHeader.m b/Sources/Sentry/SentryEnvelopeItemHeader.m index 8ba8fff3d43..a94ee64f68f 100644 --- a/Sources/Sentry/SentryEnvelopeItemHeader.m +++ b/Sources/Sentry/SentryEnvelopeItemHeader.m @@ -32,6 +32,17 @@ - (instancetype)initWithType:(NSString *)type return self; } +- (instancetype)initWithType:(NSString *)type + length:(NSUInteger)length + contentType:(NSString *)contentType + itemCount:(NSNumber *)itemCount +{ + if (self = [self initWithType:type length:length contentType:contentType]) { + _itemCount = itemCount; + } + return self; +} + - (NSDictionary *)serialize { @@ -52,6 +63,10 @@ - (NSDictionary *)serialize [target setValue:self.contentType forKey:@"platform"]; } + if (self.itemCount) { + [target setValue:self.itemCount forKey:@"item_count"]; + } + [target setValue:[NSNumber numberWithUnsignedInteger:self.length] forKey:@"length"]; return target; diff --git a/Sources/Sentry/SentrySerialization.m b/Sources/Sentry/SentrySerialization.m index add4536a024..472321e98a4 100644 --- a/Sources/Sentry/SentrySerialization.m +++ b/Sources/Sentry/SentrySerialization.m @@ -182,6 +182,7 @@ + (SentryEnvelope *_Nullable)envelopeWithData:(NSData *)data NSString *filename = [headerDictionary valueForKey:@"filename"]; NSString *contentType = [headerDictionary valueForKey:@"content_type"]; NSString *attachmentType = [headerDictionary valueForKey:@"attachment_type"]; + NSNumber *itemCount = [headerDictionary valueForKey:@"item_count"]; SentryEnvelopeItemHeader *itemHeader; if (nil != filename) { @@ -191,6 +192,11 @@ + (SentryEnvelope *_Nullable)envelopeWithData:(NSData *)data filename:filename contentType:contentType attachmentType:typeForSentryAttachmentName(attachmentType)]; + } else if (nil != itemCount) { + itemHeader = [[SentryEnvelopeItemHeader alloc] initWithType:type + length:bodyLength + contentType:contentType + itemCount:itemCount]; } else { itemHeader = [[SentryEnvelopeItemHeader alloc] initWithType:type length:bodyLength]; } diff --git a/Tests/SentryTests/Helper/SentrySerializationTests.swift b/Tests/SentryTests/Helper/SentrySerializationTests.swift index f0d811ce0d1..6b15c3c496d 100644 --- a/Tests/SentryTests/Helper/SentrySerializationTests.swift +++ b/Tests/SentryTests/Helper/SentrySerializationTests.swift @@ -365,6 +365,39 @@ class SentrySerializationTests: XCTestCase { XCTAssertEqual(Data(payloadAsString.utf8), item.data) } + func testEnvelopeWithData_withLogItems_shouldDeserializeLogItemFields() throws { + let logs = Data(""" + { + \"items\": [ + { + \"timestamp\":\"1969-07-20T20:18:04.000Z\", + \"trace_id\":\"00000000000000000000000000000000\", + \"level\":\"info\", + \"body\":\"foobar\", + \"attributes\":{} + } + ] + } + """.utf8) + + var itemData = Data() + itemData.appendString("{}\n") + itemData.appendString("{\"length\":\(logs.count),\"type\":\"log\",\"item_count\":1,\"content_type\":\"application/vnd.sentry.items.log+json\"}\n") + itemData.append(logs) + + let envelope = try XCTUnwrap(SentrySerialization.envelope(with: itemData), "Failed to deserialize envelope") + + XCTAssertEqual(1, envelope.items.count) + let item = try XCTUnwrap(envelope.items.first) + + let header = try XCTUnwrap(item.header) + XCTAssertEqual(UInt(logs.count), header.length) + XCTAssertEqual("log", header.type) + XCTAssertEqual(1, header.itemCount?.intValue) + XCTAssertEqual("application/vnd.sentry.items.log+json", header.contentType) + XCTAssertEqual(logs, item.data) + } + func testEnvelopeWithData_EmptyEnvelope_ReturnsNil() throws { XCTAssertNil(SentrySerialization.envelope(with: Data())) } diff --git a/Tests/SentryTests/Protocol/SentryEnvelopeItemHeaderTests.swift b/Tests/SentryTests/Protocol/SentryEnvelopeItemHeaderTests.swift new file mode 100644 index 00000000000..d17439213e8 --- /dev/null +++ b/Tests/SentryTests/Protocol/SentryEnvelopeItemHeaderTests.swift @@ -0,0 +1,51 @@ +@testable import Sentry +import SentryTestUtils +import XCTest + +class SentryEnvelopeItemHeaderTests: XCTestCase { + + func test_SentryEnvelopeItemHeaderSerialization_DefaultInit() { + let header = SentryEnvelopeItemHeader(type: "SomeType", length: 10) + + let data = header.serialize() + XCTAssertEqual(data["type"] as? String, "SomeType") + XCTAssertEqual(data["length"] as? Int, 10) + XCTAssertNil(data["filename"]) + XCTAssertNil(data["content_type"]) + XCTAssertEqual(data.count, 2) + } + + func test_SentryEnvelopeItemHeaderSerialization_WithContentType() { + let header = SentryEnvelopeItemHeader(type: "SomeType", length: 10, contentType: "text/html") + + let data = header.serialize() + XCTAssertEqual(data["type"] as? String, "SomeType") + XCTAssertEqual(data["length"] as? Int, 10) + XCTAssertNil(data["filename"]) + XCTAssertEqual(data["content_type"] as? String, "text/html") + XCTAssertEqual(data.count, 3) + } + + func test_SentryEnvelopeItemHeaderSerialization_WithItemCount() { + let header = SentryEnvelopeItemHeader(type: "SomeType", length: 10, contentType: "text/html", itemCount: NSNumber(value: 3)) + + let data = header.serialize() + XCTAssertEqual(data["type"] as? String, "SomeType") + XCTAssertEqual(data["length"] as? Int, 10) + XCTAssertNil(data["filename"]) + XCTAssertEqual(data["content_type"] as? String, "text/html") + XCTAssertEqual(data["item_count"] as? NSNumber, NSNumber(value: 3)) + XCTAssertEqual(data.count, 4) + } + + func test_SentryEnvelopeItemHeaderSerialization_WithFilename() { + let header = SentryEnvelopeItemHeader(type: "SomeType", length: 10, filenname: "SomeFileName", contentType: "text/html") + + let data = header.serialize() + XCTAssertEqual(data["type"] as? String, "SomeType") + XCTAssertEqual(data["length"] as? Int, 10) + XCTAssertEqual(data["filename"] as? String, "SomeFileName") + XCTAssertEqual(data["content_type"] as? String, "text/html") + XCTAssertEqual(data.count, 4) + } +} diff --git a/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift b/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift index 792c43d4545..640710c92f7 100644 --- a/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift +++ b/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift @@ -309,40 +309,6 @@ class SentryEnvelopeTests: XCTestCase { XCTAssertEqual(data2["attachment_type"] as? String, "event.attachment") XCTAssertEqual(data2.count, 3) } - - func test_SentryEnvelopeItemHeaderSerialization_DefaultInit() { - let header = SentryEnvelopeItemHeader(type: "SomeType", length: 10) - - let data = header.serialize() - XCTAssertEqual(data.count, 2) - XCTAssertEqual(data.count, 2) - XCTAssertEqual(data["type"] as? String, "SomeType") - XCTAssertEqual(data["length"] as? Int, 10) - XCTAssertNil(data["filename"]) - XCTAssertNil(data["content_type"]) - } - - func test_SentryEnvelopeItemHeaderSerialization_WithoutFileName() { - let header = SentryEnvelopeItemHeader(type: "SomeType", length: 10, contentType: "text/html") - - let data = header.serialize() - XCTAssertEqual(data["type"] as? String, "SomeType") - XCTAssertEqual(data["length"] as? Int, 10) - XCTAssertNil(data["filename"]) - XCTAssertEqual(data["content_type"] as? String, "text/html") - XCTAssertEqual(data.count, 3) - } - - func test_SentryEnvelopeItemHeaderSerialization_AllParameters() { - let header = SentryEnvelopeItemHeader(type: "SomeType", length: 10, filenname: "SomeFileName", contentType: "text/html") - - let data = header.serialize() - XCTAssertEqual(data["type"] as? String, "SomeType") - XCTAssertEqual(data["length"] as? Int, 10) - XCTAssertEqual(data["filename"] as? String, "SomeFileName") - XCTAssertEqual(data["content_type"] as? String, "text/html") - XCTAssertEqual(data.count, 4) - } func testInitWithDataAttachment_MaxAttachmentSize() { let attachmentTooBig = Attachment(data: fixture.dataTooBig, filename: "")