From 0a82a1ee1ab16b08941011233f534636569d7543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Andra=C5=A1ec?= Date: Tue, 16 May 2023 14:08:39 +0000 Subject: [PATCH] Add `sent_at` to envelope header (#1428) Co-authored-by: Manoel Aranda Neto <5731772+marandaneto@users.noreply.github.com> --- CHANGELOG.md | 4 +++ dart/lib/src/sentry_envelope_header.dart | 12 ++++++++- dart/lib/src/transport/http_transport.dart | 19 ++++--------- dart/test/sentry_envelope_header_test.dart | 4 +++ dart/test/test_utils.dart | 7 ++--- dart/test/transport/http_transport_test.dart | 28 ++++++++++++++++++++ 6 files changed, 54 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bf0924711..e4ff4adb31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- Add `sent_at` to envelope header ([#1428](https://github.com/getsentry/sentry-dart/pull/1428)) + ### Fixes - Fix battery level conversion for iOS 16.4 ([#1433](https://github.com/getsentry/sentry-dart/pull/1433)) diff --git a/dart/lib/src/sentry_envelope_header.dart b/dart/lib/src/sentry_envelope_header.dart index c6b2bb9ead..b7398985cc 100644 --- a/dart/lib/src/sentry_envelope_header.dart +++ b/dart/lib/src/sentry_envelope_header.dart @@ -1,6 +1,7 @@ import 'protocol/sentry_id.dart'; import 'protocol/sdk_version.dart'; import 'sentry_trace_context_header.dart'; +import 'utils.dart'; /// Header containing `SentryId` and `SdkVersion`. class SentryEnvelopeHeader { @@ -9,12 +10,14 @@ class SentryEnvelopeHeader { this.sdkVersion, { this.dsn, this.traceContext, + this.sentAt, }); SentryEnvelopeHeader.newEventId() : eventId = SentryId.newId(), sdkVersion = null, dsn = null, - traceContext = null; + traceContext = null, + sentAt = null; /// The identifier of encoded `SentryEvent`. final SentryId? eventId; @@ -27,6 +30,8 @@ class SentryEnvelopeHeader { /// The `DSN` of the Sentry project. final String? dsn; + DateTime? sentAt; + /// Header encoded as JSON Map toJson() { final json = {}; @@ -49,6 +54,11 @@ class SentryEnvelopeHeader { if (dsn != null) { json['dsn'] = dsn; } + + if (sentAt != null) { + json['sent_at'] = formatDateAsIso8601WithMillisPrecision(sentAt!); + } + return json; } } diff --git a/dart/lib/src/transport/http_transport.dart b/dart/lib/src/transport/http_transport.dart index 1d9c35842d..acf0e6cf64 100644 --- a/dart/lib/src/transport/http_transport.dart +++ b/dart/lib/src/transport/http_transport.dart @@ -46,7 +46,6 @@ class HttpTransport implements Transport { _credentialBuilder = _CredentialBuilder( _dsn, _options.sentryClientName, - _options.clock, ); } @@ -56,6 +55,7 @@ class HttpTransport implements Transport { if (filteredEnvelope == null) { return SentryId.empty(); } + filteredEnvelope.header.sentAt = _options.clock(); final streamedRequest = await _createStreamedRequest(filteredEnvelope); final response = await _options.httpClient @@ -135,23 +135,16 @@ class HttpTransport implements Transport { class _CredentialBuilder { final String _authHeader; - final ClockProvider _clock; + _CredentialBuilder._(String authHeader) : _authHeader = authHeader; - int get timestamp => _clock().millisecondsSinceEpoch; - - _CredentialBuilder._(String authHeader, ClockProvider clock) - : _authHeader = authHeader, - _clock = clock; - - factory _CredentialBuilder( - Dsn dsn, String sdkIdentifier, ClockProvider clock) { + factory _CredentialBuilder(Dsn dsn, String sdkIdentifier) { final authHeader = _buildAuthHeader( publicKey: dsn.publicKey, secretKey: dsn.secretKey, sdkIdentifier: sdkIdentifier, ); - return _CredentialBuilder._(authHeader, clock); + return _CredentialBuilder._(authHeader); } static String _buildAuthHeader({ @@ -172,9 +165,7 @@ class _CredentialBuilder { Map configure(Map headers) { return headers ..addAll( - { - 'X-Sentry-Auth': '$_authHeader, sentry_timestamp=$timestamp' - }, + {'X-Sentry-Auth': _authHeader}, ); } } diff --git a/dart/test/sentry_envelope_header_test.dart b/dart/test/sentry_envelope_header_test.dart index 23b06a1c5c..5dff84aa87 100644 --- a/dart/test/sentry_envelope_header_test.dart +++ b/dart/test/sentry_envelope_header_test.dart @@ -1,5 +1,6 @@ import 'package:sentry/sentry.dart'; import 'package:sentry/src/sentry_envelope_header.dart'; +import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; import 'mocks.dart'; @@ -22,11 +23,13 @@ void main() { 'trace_id': '${SentryId.newId()}', 'public_key': '123', }); + final timestamp = DateTime.utc(2019); final sut = SentryEnvelopeHeader( eventId, sdkVersion, dsn: fakeDsn, traceContext: context, + sentAt: timestamp, ); final expextedSkd = sdkVersion.toJson(); final expected = { @@ -34,6 +37,7 @@ void main() { 'sdk': expextedSkd, 'trace': context.toJson(), 'dsn': fakeDsn, + 'sent_at': formatDateAsIso8601WithMillisPrecision(timestamp), }; expect(sut.toJson(), expected); }); diff --git a/dart/test/test_utils.dart b/dart/test/test_utils.dart index f9c8d354d6..627d19903d 100644 --- a/dart/test/test_utils.dart +++ b/dart/test/test_utils.dart @@ -30,17 +30,14 @@ void testHeaders( 'Content-Type': 'application/x-sentry-envelope', 'X-Sentry-Auth': 'Sentry sentry_version=7, ' 'sentry_client=$sdkName/$sdkVersion, ' - 'sentry_key=public, ' + 'sentry_key=public' }; if (withSecret) { expectedHeaders['X-Sentry-Auth'] = - '${expectedHeaders['X-Sentry-Auth']!}sentry_secret=secret, '; + '${expectedHeaders['X-Sentry-Auth']!}, sentry_secret=secret'; } - expectedHeaders['X-Sentry-Auth'] = - '${expectedHeaders['X-Sentry-Auth']!}sentry_timestamp=${fakeClockProvider().millisecondsSinceEpoch}'; - if (withUserAgent) { expectedHeaders['User-Agent'] = '$sdkName/$sdkVersion'; } diff --git a/dart/test/transport/http_transport_test.dart b/dart/test/transport/http_transport_test.dart index 341681ccab..8319e21b7d 100644 --- a/dart/test/transport/http_transport_test.dart +++ b/dart/test/transport/http_transport_test.dart @@ -159,6 +159,31 @@ void main() { }); }); + group('sent_at', () { + late Fixture fixture; + + setUp(() { + fixture = Fixture(); + }); + + test('capture envelope sets sent_at in header', () async { + final sentryEvent = SentryEvent(); + final envelope = SentryEnvelope.fromEvent( + sentryEvent, + fixture.options.sdk, + dsn: fixture.options.dsn, + ); + + final httpMock = MockClient((http.Request request) async { + return http.Response('{}', 200); + }); + final sut = fixture.getSut(httpMock, MockRateLimiter()); + await sut.send(envelope); + + expect(envelope.header.sentAt, DateTime.utc(2019)); + }); + }); + group('client reports', () { late Fixture fixture; @@ -232,6 +257,9 @@ class Fixture { HttpTransport getSut(http.Client client, RateLimiter rateLimiter) { options.httpClient = client; options.recorder = clientReportRecorder; + options.clock = () { + return DateTime.utc(2019); + }; return HttpTransport(options, rateLimiter); } }