From 4c2fccf6a7b41d76a2a7551c0f318a5bb3ee1bf9 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 8 Jul 2025 13:30:25 +0200 Subject: [PATCH 01/14] feat: implement the new User Feedback API https://develop.sentry.dev/sdk/data-model/envelope-items/#user-feedback --- examples/example.c | 10 ++--- include/sentry.h | 31 +++++++++++-- src/sentry_core.c | 48 +++++++++++++++++---- src/sentry_envelope.c | 32 ++++++++++++-- src/sentry_envelope.h | 12 ++++-- src/sentry_value.c | 47 ++++++++++++++++++++ tests/__init__.py | 8 +++- tests/assertions.py | 8 ++-- tests/test_integration_http.py | 7 +-- tests/unit/test_envelopes.c | 79 ++++++++++++++++++++++++++++++++-- tests/unit/tests.inc | 3 +- 11 files changed, 243 insertions(+), 42 deletions(-) diff --git a/examples/example.c b/examples/example.c index 2bda9a3399..7cc2c0c127 100644 --- a/examples/example.c +++ b/examples/example.c @@ -556,14 +556,10 @@ main(int argc, char **argv) sentry_capture_event(event); } if (has_arg(argc, argv, "capture-user-feedback")) { - sentry_value_t event = sentry_value_new_message_event( - SENTRY_LEVEL_INFO, "my-logger", "Hello user feedback!"); - sentry_uuid_t event_id = sentry_capture_event(event); - - sentry_value_t user_feedback = sentry_value_new_user_feedback( - &event_id, "some-name", "some-email", "some-comment"); + sentry_value_t user_feedback = sentry_value_new_feedback( + "some-message", "some-email", "some-name", NULL, NULL, NULL); - sentry_capture_user_feedback(user_feedback); + sentry_capture_feedback(user_feedback); } if (has_arg(argc, argv, "capture-transaction")) { diff --git a/include/sentry.h b/include/sentry.h index 688e16f4ea..4bdff149dc 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -2419,11 +2419,13 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_set_name_n( sentry_transaction_t *transaction, const char *name, size_t name_len); /** - * Creates a new User Feedback with a specific name, email and comments. + * Creates a deprecated User Report with a specific name, email and comments. + * Deprecated: Please use `sentry_value_new_feedback` instead. * - * See https://develop.sentry.dev/sdk/envelopes/#user-feedback + * See + * https://develop.sentry.dev/sdk/data-model/envelope-items/#user-report---deprecated * - * User Feedback has to be associated with a specific event that has been + * User Report has to be associated with a specific event that has been * sent to Sentry earlier. */ SENTRY_API sentry_value_t sentry_value_new_user_feedback( @@ -2434,10 +2436,31 @@ SENTRY_API sentry_value_t sentry_value_new_user_feedback_n( const char *email, size_t email_len, const char *comments, size_t comments_len); +/** + * Captures a deprecated User Report and sends it to Sentry. + * Deprecated: Please use `sentry_capture_feedback` instead. + */ +SENTRY_API void sentry_capture_user_feedback(sentry_value_t user_report); + +/** + * Creates a new User Feedback with a message (required), and a number of + * optional attributes: contact email, name, url, associated event ID, and + * replay ID. + * + * See https://develop.sentry.dev/sdk/data-model/envelope-items/#user-feedback + */ +SENTRY_API sentry_value_t sentry_value_new_feedback(const char *message, + const char *contact_email, const char *name, const char *url, + const sentry_uuid_t *associated_event_id, const sentry_uuid_t *replay_id); +SENTRY_API sentry_value_t sentry_value_new_feedback_n(const char *message, + size_t message_len, const char *contact_email, size_t contact_email_len, + const char *name, size_t name_len, const char *url, size_t url_len, + const sentry_uuid_t *associated_event_id, const sentry_uuid_t *replay_id); + /** * Captures a manually created User Feedback and sends it to Sentry. */ -SENTRY_API void sentry_capture_user_feedback(sentry_value_t user_feedback); +SENTRY_API void sentry_capture_feedback(sentry_value_t feedback); /** * The status of a Span or Transaction. diff --git a/src/sentry_core.c b/src/sentry_core.c index 29ccbbf11f..6672a24989 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -659,22 +659,40 @@ sentry__prepare_transaction(const sentry_options_t *options, } static sentry_envelope_t * -prepare_user_feedback(sentry_value_t user_feedback) +prepare_feedback(sentry_value_t feedback) { sentry_envelope_t *envelope = NULL; envelope = sentry__envelope_new(); - if (!envelope - || !sentry__envelope_add_user_feedback(envelope, user_feedback)) { + if (!envelope || !sentry__envelope_add_feedback(envelope, feedback)) { goto fail; } return envelope; fail: - SENTRY_WARN("dropping user feedback"); + SENTRY_WARN("dropping feedback"); sentry_envelope_free(envelope); - sentry_value_decref(user_feedback); + sentry_value_decref(feedback); + return NULL; +} + +static sentry_envelope_t * +prepare_user_report(sentry_value_t user_report) +{ + sentry_envelope_t *envelope = NULL; + + envelope = sentry__envelope_new(); + if (!envelope || !sentry__envelope_add_user_report(envelope, user_report)) { + goto fail; + } + + return envelope; + +fail: + SENTRY_WARN("dropping user report"); + sentry_envelope_free(envelope); + sentry_value_decref(user_report); return NULL; } @@ -1335,17 +1353,31 @@ sentry_span_finish_ts(sentry_span_t *opaque_span, uint64_t timestamp) } void -sentry_capture_user_feedback(sentry_value_t user_feedback) +sentry_capture_feedback(sentry_value_t feedback) +{ + sentry_envelope_t *envelope = NULL; + + SENTRY_WITH_OPTIONS (options) { + envelope = prepare_feedback(feedback); + if (envelope) { + sentry__capture_envelope(options->transport, envelope); + } + } + sentry_value_decref(feedback); +} + +void +sentry_capture_user_feedback(sentry_value_t user_report) { sentry_envelope_t *envelope = NULL; SENTRY_WITH_OPTIONS (options) { - envelope = prepare_user_feedback(user_feedback); + envelope = prepare_user_report(user_report); if (envelope) { sentry__capture_envelope(options->transport, envelope); } } - sentry_value_decref(user_feedback); + sentry_value_decref(user_report); } int diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index 1de810d0a8..0436dae9f2 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -297,8 +297,32 @@ sentry__envelope_add_transaction( } sentry_envelope_item_t * -sentry__envelope_add_user_feedback( - sentry_envelope_t *envelope, sentry_value_t user_feedback) +sentry__envelope_add_feedback( + sentry_envelope_t *envelope, sentry_value_t feedback) +{ + sentry_value_t event = sentry_value_new_event(); + sentry_value_t contexts = sentry_value_get_by_key(event, "contexts"); + if (sentry_value_is_null(contexts)) { + contexts = sentry_value_new_object(); + } + sentry_value_set_by_key(contexts, "feedback", feedback); + sentry_value_set_by_key(event, "contexts", contexts); + + sentry_envelope_item_t *item = sentry__envelope_add_event(envelope, event); + if (!item) { + sentry_value_decref(event); + return NULL; + } + + sentry__envelope_item_set_header( + item, "type", sentry_value_new_string("feedback")); + + return item; +} + +sentry_envelope_item_t * +sentry__envelope_add_user_report( + sentry_envelope_t *envelope, sentry_value_t user_report) { sentry_envelope_item_t *item = envelope_add_item(envelope); if (!item) { @@ -310,9 +334,9 @@ sentry__envelope_add_user_feedback( return NULL; } - sentry_value_t event_id = sentry__ensure_event_id(user_feedback, NULL); + sentry_value_t event_id = sentry__ensure_event_id(user_report, NULL); - sentry__jsonwriter_write_value(jw, user_feedback); + sentry__jsonwriter_write_value(jw, user_report); item->payload = sentry__jsonwriter_into_string(jw, &item->payload_len); sentry__envelope_item_set_header( diff --git a/src/sentry_envelope.h b/src/sentry_envelope.h index 8febb75d58..5172bd1783 100644 --- a/src/sentry_envelope.h +++ b/src/sentry_envelope.h @@ -44,10 +44,16 @@ sentry_envelope_item_t *sentry__envelope_add_transaction( sentry_envelope_t *envelope, sentry_value_t transaction); /** - * Add a user feedback to this envelope. + * Add a feedback to this envelope. */ -sentry_envelope_item_t *sentry__envelope_add_user_feedback( - sentry_envelope_t *envelope, sentry_value_t user_feedback); +sentry_envelope_item_t *sentry__envelope_add_feedback( + sentry_envelope_t *envelope, sentry_value_t feedback); + +/** + * Add a user report to this envelope. + */ +sentry_envelope_item_t *sentry__envelope_add_user_report( + sentry_envelope_t *envelope, sentry_value_t user_report); /** * Add a session to this envelope. diff --git a/src/sentry_value.c b/src/sentry_value.c index 9e573fe6fd..2bfa8b3c37 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -1346,6 +1346,53 @@ sentry_value_new_stacktrace(void **ips, size_t len) return stacktrace; } +sentry_value_t +sentry_value_new_feedback(const char *message, const char *contact_email, + const char *name, const char *url, const sentry_uuid_t *associated_event_id, + const sentry_uuid_t *replay_id) +{ + return sentry_value_new_feedback_n(message, sentry__guarded_strlen(message), + contact_email, sentry__guarded_strlen(contact_email), name, + sentry__guarded_strlen(name), url, sentry__guarded_strlen(url), + associated_event_id, replay_id); +} + +sentry_value_t +sentry_value_new_feedback_n(const char *message, size_t message_len, + const char *contact_email, size_t contact_email_len, const char *name, + size_t name_len, const char *url, size_t url_len, + const sentry_uuid_t *associated_event_id, const sentry_uuid_t *replay_id) +{ + sentry_value_t rv = sentry_value_new_object(); + + if (message) { + sentry_value_set_by_key( + rv, "message", sentry_value_new_string_n(message, message_len)); + } + if (contact_email) { + sentry_value_set_by_key(rv, "contact_email", + sentry_value_new_string_n(contact_email, contact_email_len)); + } + if (name) { + sentry_value_set_by_key( + rv, "name", sentry_value_new_string_n(name, name_len)); + } + if (url) { + sentry_value_set_by_key( + rv, "url", sentry_value_new_string_n(url, url_len)); + } + if (associated_event_id) { + sentry_value_set_by_key(rv, "associated_event_id", + sentry__value_new_uuid(associated_event_id)); + } + if (replay_id) { + sentry_value_set_by_key( + rv, "replay_id", sentry__value_new_uuid(replay_id)); + } + + return rv; +} + sentry_value_t sentry_value_new_user_feedback(const sentry_uuid_t *uuid, const char *name, const char *email, const char *comments) diff --git a/tests/__init__.py b/tests/__init__.py index 6f9bd1a425..255ce8ff32 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -278,7 +278,13 @@ def deserialize_from( headers = json.loads(line) length = headers["length"] payload = f.read(length) - if headers.get("type") in ["event", "session", "transaction", "user_report"]: + if headers.get("type") in [ + "event", + "feedback", + "session", + "transaction", + "user_report", + ]: rv = cls(headers=headers, payload=PayloadRef(json=json.loads(payload))) else: rv = cls(headers=headers, payload=payload) diff --git a/tests/assertions.py b/tests/assertions.py index ef1dbfcee7..22aa222bc5 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -42,13 +42,13 @@ def assert_session(envelope, extra_assertion=None): def assert_user_feedback(envelope): user_feedback = None for item in envelope: - if item.headers.get("type") == "user_report" and item.payload.json is not None: - user_feedback = item.payload.json + if item.headers.get("type") == "feedback" and item.payload.json is not None: + user_feedback = item.payload.json["contexts"]["feedback"] assert user_feedback is not None assert user_feedback["name"] == "some-name" - assert user_feedback["email"] == "some-email" - assert user_feedback["comments"] == "some-comment" + assert user_feedback["contact_email"] == "some-email" + assert user_feedback["message"] == "some-message" def assert_meta( diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 809d4506aa..ae2b7a3d1c 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -168,15 +168,10 @@ def test_user_feedback_http(cmake, httpserver): env=env, ) - assert len(httpserver.log) == 2 + assert len(httpserver.log) == 1 output = httpserver.log[0][0].get_data() envelope = Envelope.deserialize(output) - assert_event(envelope, "Hello user feedback!") - - output = httpserver.log[1][0].get_data() - envelope = Envelope.deserialize(output) - assert_user_feedback(envelope) diff --git a/tests/unit/test_envelopes.c b/tests/unit/test_envelopes.c index 2805064158..4621745d61 100644 --- a/tests/unit/test_envelopes.c +++ b/tests/unit/test_envelopes.c @@ -1,4 +1,6 @@ +#include "sentry.h" #include "sentry_envelope.h" +#include "sentry_json.h" #include "sentry_path.h" #include "sentry_testsupport.h" #include "sentry_transport.h" @@ -78,16 +80,85 @@ SENTRY_TEST(basic_http_request_preparation_for_transaction) sentry__dsn_decref(dsn); } -SENTRY_TEST(basic_http_request_preparation_for_user_feedback) +SENTRY_TEST(basic_http_request_preparation_for_feedback) { SENTRY_TEST_DSN_NEW_DEFAULT(dsn); sentry_uuid_t event_id = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); sentry_envelope_t *envelope = sentry__envelope_new(); - sentry_value_t user_feedback = sentry_value_new_user_feedback( + sentry_value_t feedback = sentry_value_new_feedback( + "some-message", "some-email", "some-name", NULL, &event_id, NULL); + sentry__envelope_add_feedback(envelope, feedback); + + sentry_prepared_http_request_t *req + = sentry__prepare_http_request(envelope, dsn, NULL, NULL); + TEST_CHECK_STRING_EQUAL(req->method, "POST"); + TEST_CHECK_STRING_EQUAL( + req->url, "https://sentry.invalid:443/api/42/envelope/"); +#ifndef SENTRY_TRANSPORT_COMPRESSION + char *line1 = req->body; + char *line1_end = strchr(line1, '\n'); + line1_end[0] = '\0'; + TEST_CHECK_STRING_EQUAL( + line1, "{\"event_id\":\"4c035723-8638-4c3a-923f-2ab9d08b4018\"}"); + + char *line2 = line1_end ? line1_end + 1 : NULL; + char *line2_end = line2 ? strchr(line2, '\n') : NULL; + line2_end[0] = '\0'; + TEST_CHECK_STRING_EQUAL(line2, "{\"type\":\"feedback\",\"length\":273}"); + + char *line3 = line2_end ? line2_end + 1 : NULL; + char *line3_end = line3 ? strchr(line3, '\n') : NULL; + sentry_value_t line3_json = sentry__value_from_json(line3, strlen(line3)); + TEST_CHECK(!sentry_value_is_null(line3_json)); + + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(line3_json, "event_id")), + "4c035723-8638-4c3a-923f-2ab9d08b4018"); + TEST_CHECK(!sentry_value_is_null( + sentry_value_get_by_key(line3_json, "timestamp"))); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(line3_json, "platform")), + "native"); + + sentry_value_t contexts = sentry_value_get_by_key(line3_json, "contexts"); + TEST_CHECK(!sentry_value_is_null(contexts)); + + sentry_value_t actual = sentry_value_get_by_key(contexts, "feedback"); + TEST_CHECK(!sentry_value_is_null(actual)); + + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(actual, "message")), + "some-message"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + actual, "contact_email")), + "some-email"); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(actual, "name")), + "some-name"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + actual, "associated_event_id")), + "c993afb6-b4ac-48a6-b61b-2558e601d65d"); + sentry_value_decref(line3_json); +#endif + sentry__prepared_http_request_free(req); + sentry_value_decref(feedback); + sentry_envelope_free(envelope); + + sentry__dsn_decref(dsn); +} + +SENTRY_TEST(basic_http_request_preparation_for_user_report) +{ + SENTRY_TEST_DSN_NEW_DEFAULT(dsn); + + sentry_uuid_t event_id + = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); + sentry_envelope_t *envelope = sentry__envelope_new(); + sentry_value_t user_report = sentry_value_new_user_feedback( &event_id, "some-name", "some-email", "some-comment"); - sentry__envelope_add_user_feedback(envelope, user_feedback); + sentry__envelope_add_user_report(envelope, user_report); sentry_prepared_http_request_t *req = sentry__prepare_http_request(envelope, dsn, NULL, NULL); @@ -103,7 +174,7 @@ SENTRY_TEST(basic_http_request_preparation_for_user_feedback) "\"some-comment\"}"); #endif sentry__prepared_http_request_free(req); - sentry_value_decref(user_feedback); + sentry_value_decref(user_report); sentry_envelope_free(envelope); sentry__dsn_decref(dsn); diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 4daa747398..0239476b45 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -13,9 +13,10 @@ XX(basic_function_transport_transaction) XX(basic_function_transport_transaction_ts) XX(basic_http_request_preparation_for_event) XX(basic_http_request_preparation_for_event_with_attachment) +XX(basic_http_request_preparation_for_feedback) XX(basic_http_request_preparation_for_minidump) XX(basic_http_request_preparation_for_transaction) -XX(basic_http_request_preparation_for_user_feedback) +XX(basic_http_request_preparation_for_user_report) XX(basic_spans) XX(basic_tracing_context) XX(basic_transaction) From d3de644e26d867f49c64c2db107046e6a7b64c00 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 8 Jul 2025 15:04:26 +0200 Subject: [PATCH 02/14] Fix heap-use-after-free --- src/sentry_envelope.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index 0436dae9f2..f8cb88abb6 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -305,7 +305,8 @@ sentry__envelope_add_feedback( if (sentry_value_is_null(contexts)) { contexts = sentry_value_new_object(); } - sentry_value_set_by_key(contexts, "feedback", feedback); + sentry_value_set_by_key( + contexts, "feedback", sentry__value_clone(feedback)); sentry_value_set_by_key(event, "contexts", contexts); sentry_envelope_item_t *item = sentry__envelope_add_event(envelope, event); From 0feda423174ea84bed1817da99745a86020437e6 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 8 Jul 2025 15:05:40 +0200 Subject: [PATCH 03/14] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b845c86c2..1901be4cff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Add `sentry_clear_attachments()` to allow clearing all previously added attachments in the global scope. ([#1290](https://github.com/getsentry/sentry-native/pull/1290)) - Compiles also on Xbox One ([#1294](https://github.com/getsentry/sentry-native/pull/1294)) - Provide `sentry_regenerate_trace()` to allow users to set manual trace boundaries. ([#1293](https://github.com/getsentry/sentry-native/pull/1293)) +- Implement the new [User Feedback API](https://develop.sentry.dev/sdk/data-model/envelope-items/#user-feedback). ([#1304](https://github.com/getsentry/sentry-native/pull/1304)) ## 0.9.1 From 0bb0f3fc9a5fa2a51e971cbe95bdb9052d12d5e4 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 8 Jul 2025 15:33:37 +0200 Subject: [PATCH 04/14] remove unused line3_end --- tests/unit/test_envelopes.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_envelopes.c b/tests/unit/test_envelopes.c index 4621745d61..639a206ad9 100644 --- a/tests/unit/test_envelopes.c +++ b/tests/unit/test_envelopes.c @@ -99,17 +99,18 @@ SENTRY_TEST(basic_http_request_preparation_for_feedback) #ifndef SENTRY_TRANSPORT_COMPRESSION char *line1 = req->body; char *line1_end = strchr(line1, '\n'); + TEST_CHECK(line1_end != NULL); line1_end[0] = '\0'; TEST_CHECK_STRING_EQUAL( line1, "{\"event_id\":\"4c035723-8638-4c3a-923f-2ab9d08b4018\"}"); - char *line2 = line1_end ? line1_end + 1 : NULL; - char *line2_end = line2 ? strchr(line2, '\n') : NULL; + char *line2 = line1_end + 1; + char *line2_end = strchr(line2, '\n'); + TEST_CHECK(line2_end != NULL); line2_end[0] = '\0'; TEST_CHECK_STRING_EQUAL(line2, "{\"type\":\"feedback\",\"length\":273}"); - char *line3 = line2_end ? line2_end + 1 : NULL; - char *line3_end = line3 ? strchr(line3, '\n') : NULL; + char *line3 = line2_end + 1; sentry_value_t line3_json = sentry__value_from_json(line3, strlen(line3)); TEST_CHECK(!sentry_value_is_null(line3_json)); From 581b017ca972517f31350c18094d1718d7160294 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 8 Jul 2025 17:12:35 +0200 Subject: [PATCH 05/14] what if we reused the old API? --- examples/example.c | 6 ++-- include/sentry.h | 37 ++++---------------- src/sentry_core.c | 48 +++++--------------------- src/sentry_envelope.c | 60 +++++++++++++++++--------------- src/sentry_envelope.h | 12 ++----- src/sentry_value.c | 69 +++++++------------------------------ tests/unit/test_envelopes.c | 42 +++------------------- tests/unit/test_value.c | 45 +++++++++++++----------- tests/unit/tests.inc | 3 +- 9 files changed, 95 insertions(+), 227 deletions(-) diff --git a/examples/example.c b/examples/example.c index 7cc2c0c127..9dc6eeb5a0 100644 --- a/examples/example.c +++ b/examples/example.c @@ -556,10 +556,10 @@ main(int argc, char **argv) sentry_capture_event(event); } if (has_arg(argc, argv, "capture-user-feedback")) { - sentry_value_t user_feedback = sentry_value_new_feedback( - "some-message", "some-email", "some-name", NULL, NULL, NULL); + sentry_value_t user_feedback = sentry_value_new_user_feedback( + NULL, "some-name", "some-email", "some-message"); - sentry_capture_feedback(user_feedback); + sentry_capture_user_feedback(user_feedback); } if (has_arg(argc, argv, "capture-transaction")) { diff --git a/include/sentry.h b/include/sentry.h index 4bdff149dc..102719e05a 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -2419,48 +2419,25 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_set_name_n( sentry_transaction_t *transaction, const char *name, size_t name_len); /** - * Creates a deprecated User Report with a specific name, email and comments. - * Deprecated: Please use `sentry_value_new_feedback` instead. + * Creates a new User Feedback with a specific name, email and message. * - * See - * https://develop.sentry.dev/sdk/data-model/envelope-items/#user-report---deprecated + * See https://develop.sentry.dev/sdk/data-model/envelope-items/#user-feedback * - * User Report has to be associated with a specific event that has been + * User Feedback can be associated with a specific event that has been * sent to Sentry earlier. */ SENTRY_API sentry_value_t sentry_value_new_user_feedback( const sentry_uuid_t *uuid, const char *name, const char *email, - const char *comments); + const char *message); SENTRY_API sentry_value_t sentry_value_new_user_feedback_n( const sentry_uuid_t *uuid, const char *name, size_t name_len, - const char *email, size_t email_len, const char *comments, - size_t comments_len); - -/** - * Captures a deprecated User Report and sends it to Sentry. - * Deprecated: Please use `sentry_capture_feedback` instead. - */ -SENTRY_API void sentry_capture_user_feedback(sentry_value_t user_report); - -/** - * Creates a new User Feedback with a message (required), and a number of - * optional attributes: contact email, name, url, associated event ID, and - * replay ID. - * - * See https://develop.sentry.dev/sdk/data-model/envelope-items/#user-feedback - */ -SENTRY_API sentry_value_t sentry_value_new_feedback(const char *message, - const char *contact_email, const char *name, const char *url, - const sentry_uuid_t *associated_event_id, const sentry_uuid_t *replay_id); -SENTRY_API sentry_value_t sentry_value_new_feedback_n(const char *message, - size_t message_len, const char *contact_email, size_t contact_email_len, - const char *name, size_t name_len, const char *url, size_t url_len, - const sentry_uuid_t *associated_event_id, const sentry_uuid_t *replay_id); + const char *email, size_t email_len, const char *message, + size_t message_len); /** * Captures a manually created User Feedback and sends it to Sentry. */ -SENTRY_API void sentry_capture_feedback(sentry_value_t feedback); +SENTRY_API void sentry_capture_user_feedback(sentry_value_t user_feedback); /** * The status of a Span or Transaction. diff --git a/src/sentry_core.c b/src/sentry_core.c index 6672a24989..29ccbbf11f 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -659,40 +659,22 @@ sentry__prepare_transaction(const sentry_options_t *options, } static sentry_envelope_t * -prepare_feedback(sentry_value_t feedback) +prepare_user_feedback(sentry_value_t user_feedback) { sentry_envelope_t *envelope = NULL; envelope = sentry__envelope_new(); - if (!envelope || !sentry__envelope_add_feedback(envelope, feedback)) { + if (!envelope + || !sentry__envelope_add_user_feedback(envelope, user_feedback)) { goto fail; } return envelope; fail: - SENTRY_WARN("dropping feedback"); + SENTRY_WARN("dropping user feedback"); sentry_envelope_free(envelope); - sentry_value_decref(feedback); - return NULL; -} - -static sentry_envelope_t * -prepare_user_report(sentry_value_t user_report) -{ - sentry_envelope_t *envelope = NULL; - - envelope = sentry__envelope_new(); - if (!envelope || !sentry__envelope_add_user_report(envelope, user_report)) { - goto fail; - } - - return envelope; - -fail: - SENTRY_WARN("dropping user report"); - sentry_envelope_free(envelope); - sentry_value_decref(user_report); + sentry_value_decref(user_feedback); return NULL; } @@ -1353,31 +1335,17 @@ sentry_span_finish_ts(sentry_span_t *opaque_span, uint64_t timestamp) } void -sentry_capture_feedback(sentry_value_t feedback) -{ - sentry_envelope_t *envelope = NULL; - - SENTRY_WITH_OPTIONS (options) { - envelope = prepare_feedback(feedback); - if (envelope) { - sentry__capture_envelope(options->transport, envelope); - } - } - sentry_value_decref(feedback); -} - -void -sentry_capture_user_feedback(sentry_value_t user_report) +sentry_capture_user_feedback(sentry_value_t user_feedback) { sentry_envelope_t *envelope = NULL; SENTRY_WITH_OPTIONS (options) { - envelope = prepare_user_report(user_report); + envelope = prepare_user_feedback(user_feedback); if (envelope) { sentry__capture_envelope(options->transport, envelope); } } - sentry_value_decref(user_report); + sentry_value_decref(user_feedback); } int diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index f8cb88abb6..93ae6dd7d0 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -296,34 +296,8 @@ sentry__envelope_add_transaction( return item; } -sentry_envelope_item_t * -sentry__envelope_add_feedback( - sentry_envelope_t *envelope, sentry_value_t feedback) -{ - sentry_value_t event = sentry_value_new_event(); - sentry_value_t contexts = sentry_value_get_by_key(event, "contexts"); - if (sentry_value_is_null(contexts)) { - contexts = sentry_value_new_object(); - } - sentry_value_set_by_key( - contexts, "feedback", sentry__value_clone(feedback)); - sentry_value_set_by_key(event, "contexts", contexts); - - sentry_envelope_item_t *item = sentry__envelope_add_event(envelope, event); - if (!item) { - sentry_value_decref(event); - return NULL; - } - - sentry__envelope_item_set_header( - item, "type", sentry_value_new_string("feedback")); - - return item; -} - -sentry_envelope_item_t * -sentry__envelope_add_user_report( - sentry_envelope_t *envelope, sentry_value_t user_report) +static sentry_envelope_item_t * +add_legacy_user_report(sentry_envelope_t *envelope, sentry_value_t user_report) { sentry_envelope_item_t *item = envelope_add_item(envelope); if (!item) { @@ -351,6 +325,36 @@ sentry__envelope_add_user_report( return item; } +sentry_envelope_item_t * +sentry__envelope_add_user_feedback( + sentry_envelope_t *envelope, sentry_value_t user_feedback) +{ + if (!sentry_value_is_null( + sentry_value_get_by_key(user_feedback, "event_id"))) { + return add_legacy_user_report(envelope, user_feedback); + } + + sentry_value_t event = sentry_value_new_event(); + sentry_value_t contexts = sentry_value_get_by_key(event, "contexts"); + if (sentry_value_is_null(contexts)) { + contexts = sentry_value_new_object(); + } + sentry_value_set_by_key( + contexts, "feedback", sentry__value_clone(user_feedback)); + sentry_value_set_by_key(event, "contexts", contexts); + + sentry_envelope_item_t *item = sentry__envelope_add_event(envelope, event); + if (!item) { + sentry_value_decref(event); + return NULL; + } + + sentry__envelope_item_set_header( + item, "type", sentry_value_new_string("feedback")); + + return item; +} + sentry_envelope_item_t * sentry__envelope_add_session( sentry_envelope_t *envelope, const sentry_session_t *session) diff --git a/src/sentry_envelope.h b/src/sentry_envelope.h index 5172bd1783..8febb75d58 100644 --- a/src/sentry_envelope.h +++ b/src/sentry_envelope.h @@ -44,16 +44,10 @@ sentry_envelope_item_t *sentry__envelope_add_transaction( sentry_envelope_t *envelope, sentry_value_t transaction); /** - * Add a feedback to this envelope. + * Add a user feedback to this envelope. */ -sentry_envelope_item_t *sentry__envelope_add_feedback( - sentry_envelope_t *envelope, sentry_value_t feedback); - -/** - * Add a user report to this envelope. - */ -sentry_envelope_item_t *sentry__envelope_add_user_report( - sentry_envelope_t *envelope, sentry_value_t user_report); +sentry_envelope_item_t *sentry__envelope_add_user_feedback( + sentry_envelope_t *envelope, sentry_value_t user_feedback); /** * Add a session to this envelope. diff --git a/src/sentry_value.c b/src/sentry_value.c index 2bfa8b3c37..c3447e0e05 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -1347,21 +1347,18 @@ sentry_value_new_stacktrace(void **ips, size_t len) } sentry_value_t -sentry_value_new_feedback(const char *message, const char *contact_email, - const char *name, const char *url, const sentry_uuid_t *associated_event_id, - const sentry_uuid_t *replay_id) +sentry_value_new_user_feedback(const sentry_uuid_t *uuid, const char *name, + const char *email, const char *message) { - return sentry_value_new_feedback_n(message, sentry__guarded_strlen(message), - contact_email, sentry__guarded_strlen(contact_email), name, - sentry__guarded_strlen(name), url, sentry__guarded_strlen(url), - associated_event_id, replay_id); + return sentry_value_new_user_feedback_n(uuid, name, + sentry__guarded_strlen(name), email, sentry__guarded_strlen(email), + message, sentry__guarded_strlen(message)); } sentry_value_t -sentry_value_new_feedback_n(const char *message, size_t message_len, - const char *contact_email, size_t contact_email_len, const char *name, - size_t name_len, const char *url, size_t url_len, - const sentry_uuid_t *associated_event_id, const sentry_uuid_t *replay_id) +sentry_value_new_user_feedback_n(const sentry_uuid_t *uuid, const char *name, + size_t name_len, const char *email, size_t email_len, const char *message, + size_t message_len) { sentry_value_t rv = sentry_value_new_object(); @@ -1369,59 +1366,17 @@ sentry_value_new_feedback_n(const char *message, size_t message_len, sentry_value_set_by_key( rv, "message", sentry_value_new_string_n(message, message_len)); } - if (contact_email) { - sentry_value_set_by_key(rv, "contact_email", - sentry_value_new_string_n(contact_email, contact_email_len)); - } - if (name) { - sentry_value_set_by_key( - rv, "name", sentry_value_new_string_n(name, name_len)); - } - if (url) { - sentry_value_set_by_key( - rv, "url", sentry_value_new_string_n(url, url_len)); - } - if (associated_event_id) { - sentry_value_set_by_key(rv, "associated_event_id", - sentry__value_new_uuid(associated_event_id)); - } - if (replay_id) { + if (email) { sentry_value_set_by_key( - rv, "replay_id", sentry__value_new_uuid(replay_id)); + rv, "contact_email", sentry_value_new_string_n(email, email_len)); } - - return rv; -} - -sentry_value_t -sentry_value_new_user_feedback(const sentry_uuid_t *uuid, const char *name, - const char *email, const char *comments) -{ - return sentry_value_new_user_feedback_n(uuid, name, - sentry__guarded_strlen(name), email, sentry__guarded_strlen(email), - comments, sentry__guarded_strlen(comments)); -} - -sentry_value_t -sentry_value_new_user_feedback_n(const sentry_uuid_t *uuid, const char *name, - size_t name_len, const char *email, size_t email_len, const char *comments, - size_t comments_len) -{ - sentry_value_t rv = sentry_value_new_object(); - - sentry_value_set_by_key(rv, "event_id", sentry__value_new_uuid(uuid)); - if (name) { sentry_value_set_by_key( rv, "name", sentry_value_new_string_n(name, name_len)); } - if (email) { - sentry_value_set_by_key( - rv, "email", sentry_value_new_string_n(email, email_len)); - } - if (comments) { + if (uuid) { sentry_value_set_by_key( - rv, "comments", sentry_value_new_string_n(comments, comments_len)); + rv, "associated_event_id", sentry__value_new_uuid(uuid)); } return rv; diff --git a/tests/unit/test_envelopes.c b/tests/unit/test_envelopes.c index 639a206ad9..6b27dc5acb 100644 --- a/tests/unit/test_envelopes.c +++ b/tests/unit/test_envelopes.c @@ -1,4 +1,3 @@ -#include "sentry.h" #include "sentry_envelope.h" #include "sentry_json.h" #include "sentry_path.h" @@ -80,16 +79,16 @@ SENTRY_TEST(basic_http_request_preparation_for_transaction) sentry__dsn_decref(dsn); } -SENTRY_TEST(basic_http_request_preparation_for_feedback) +SENTRY_TEST(basic_http_request_preparation_for_user_feedback) { SENTRY_TEST_DSN_NEW_DEFAULT(dsn); sentry_uuid_t event_id = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); sentry_envelope_t *envelope = sentry__envelope_new(); - sentry_value_t feedback = sentry_value_new_feedback( - "some-message", "some-email", "some-name", NULL, &event_id, NULL); - sentry__envelope_add_feedback(envelope, feedback); + sentry_value_t user_feedback = sentry_value_new_user_feedback( + &event_id, "some-name", "some-email", "some-message"); + sentry__envelope_add_user_feedback(envelope, user_feedback); sentry_prepared_http_request_t *req = sentry__prepare_http_request(envelope, dsn, NULL, NULL); @@ -144,38 +143,7 @@ SENTRY_TEST(basic_http_request_preparation_for_feedback) sentry_value_decref(line3_json); #endif sentry__prepared_http_request_free(req); - sentry_value_decref(feedback); - sentry_envelope_free(envelope); - - sentry__dsn_decref(dsn); -} - -SENTRY_TEST(basic_http_request_preparation_for_user_report) -{ - SENTRY_TEST_DSN_NEW_DEFAULT(dsn); - - sentry_uuid_t event_id - = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); - sentry_envelope_t *envelope = sentry__envelope_new(); - sentry_value_t user_report = sentry_value_new_user_feedback( - &event_id, "some-name", "some-email", "some-comment"); - sentry__envelope_add_user_report(envelope, user_report); - - sentry_prepared_http_request_t *req - = sentry__prepare_http_request(envelope, dsn, NULL, NULL); - TEST_CHECK_STRING_EQUAL(req->method, "POST"); - TEST_CHECK_STRING_EQUAL( - req->url, "https://sentry.invalid:443/api/42/envelope/"); -#ifndef SENTRY_TRANSPORT_COMPRESSION - TEST_CHECK_STRING_EQUAL(req->body, - "{\"event_id\":\"c993afb6-b4ac-48a6-b61b-2558e601d65d\"}\n" - "{\"type\":\"user_report\",\"length\":117}\n" - "{\"event_id\":\"c993afb6-b4ac-48a6-b61b-2558e601d65d\",\"name\":" - "\"some-name\",\"email\":\"some-email\",\"comments\":" - "\"some-comment\"}"); -#endif - sentry__prepared_http_request_free(req); - sentry_value_decref(user_report); + sentry_value_decref(user_feedback); sentry_envelope_free(envelope); sentry__dsn_decref(dsn); diff --git a/tests/unit/test_value.c b/tests/unit/test_value.c index 00258475d7..fd83fd6221 100644 --- a/tests/unit/test_value.c +++ b/tests/unit/test_value.c @@ -819,18 +819,21 @@ SENTRY_TEST(user_feedback_is_valid) sentry_uuid_t event_id = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); sentry_value_t user_feedback = sentry_value_new_user_feedback( - &event_id, "some-name", "some-email", "some-comment"); + &event_id, "some-name", "some-email", "some-message"); TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(user_feedback, "name")), "some-name"); - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key(user_feedback, "email")), + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + user_feedback, "contact_email")), "some-email"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - user_feedback, "comments")), - "some-comment"); + user_feedback, "message")), + "some-message"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + user_feedback, "associated_event_id")), + "c993afb6-b4ac-48a6-b61b-2558e601d65d"); sentry_value_decref(user_feedback); } @@ -845,40 +848,40 @@ SENTRY_TEST(user_feedback_with_null_args) TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK( sentry_value_is_null(sentry_value_get_by_key(user_feedback, "name"))); - TEST_CHECK( - sentry_value_is_null(sentry_value_get_by_key(user_feedback, "email"))); TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(user_feedback, "comments"))); + sentry_value_get_by_key(user_feedback, "contact_email"))); + TEST_CHECK(sentry_value_is_null( + sentry_value_get_by_key(user_feedback, "message"))); sentry_value_decref(user_feedback); user_feedback = sentry_value_new_user_feedback( - &event_id, NULL, "some-email", "some-comment"); + &event_id, NULL, "some-email", "some-message"); TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK( sentry_value_is_null(sentry_value_get_by_key(user_feedback, "name"))); - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key(user_feedback, "email")), + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + user_feedback, "contact_email")), "some-email"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - user_feedback, "comments")), - "some-comment"); + user_feedback, "message")), + "some-message"); sentry_value_decref(user_feedback); user_feedback = sentry_value_new_user_feedback( - &event_id, "some-name", NULL, "some-comment"); + &event_id, "some-name", NULL, "some-message"); TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(user_feedback, "name")), "some-name"); - TEST_CHECK( - sentry_value_is_null(sentry_value_get_by_key(user_feedback, "email"))); + TEST_CHECK(sentry_value_is_null( + sentry_value_get_by_key(user_feedback, "contact_email"))); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - user_feedback, "comments")), - "some-comment"); + user_feedback, "message")), + "some-message"); sentry_value_decref(user_feedback); @@ -890,11 +893,11 @@ SENTRY_TEST(user_feedback_with_null_args) TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(user_feedback, "name")), "some-name"); - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key(user_feedback, "email")), + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + user_feedback, "contact_email")), "some-email"); TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(user_feedback, "comments"))); + sentry_value_get_by_key(user_feedback, "message"))); sentry_value_decref(user_feedback); } diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 0239476b45..4daa747398 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -13,10 +13,9 @@ XX(basic_function_transport_transaction) XX(basic_function_transport_transaction_ts) XX(basic_http_request_preparation_for_event) XX(basic_http_request_preparation_for_event_with_attachment) -XX(basic_http_request_preparation_for_feedback) XX(basic_http_request_preparation_for_minidump) XX(basic_http_request_preparation_for_transaction) -XX(basic_http_request_preparation_for_user_report) +XX(basic_http_request_preparation_for_user_feedback) XX(basic_spans) XX(basic_tracing_context) XX(basic_transaction) From 5ac15ef5ed581036861c6caacbda053b786c0479 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 9 Jul 2025 11:07:34 +0200 Subject: [PATCH 06/14] use internal id to fix event association in Sentry Web UI --- src/sentry_value.c | 2 +- tests/unit/test_envelopes.c | 4 ++-- tests/unit/test_value.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sentry_value.c b/src/sentry_value.c index c3447e0e05..24e8eebb40 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -1376,7 +1376,7 @@ sentry_value_new_user_feedback_n(const sentry_uuid_t *uuid, const char *name, } if (uuid) { sentry_value_set_by_key( - rv, "associated_event_id", sentry__value_new_uuid(uuid)); + rv, "associated_event_id", sentry__value_new_internal_uuid(uuid)); } return rv; diff --git a/tests/unit/test_envelopes.c b/tests/unit/test_envelopes.c index 6b27dc5acb..77d7b55903 100644 --- a/tests/unit/test_envelopes.c +++ b/tests/unit/test_envelopes.c @@ -107,7 +107,7 @@ SENTRY_TEST(basic_http_request_preparation_for_user_feedback) char *line2_end = strchr(line2, '\n'); TEST_CHECK(line2_end != NULL); line2_end[0] = '\0'; - TEST_CHECK_STRING_EQUAL(line2, "{\"type\":\"feedback\",\"length\":273}"); + TEST_CHECK_STRING_EQUAL(line2, "{\"type\":\"feedback\",\"length\":269}"); char *line3 = line2_end + 1; sentry_value_t line3_json = sentry__value_from_json(line3, strlen(line3)); @@ -139,7 +139,7 @@ SENTRY_TEST(basic_http_request_preparation_for_user_feedback) "some-name"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( actual, "associated_event_id")), - "c993afb6-b4ac-48a6-b61b-2558e601d65d"); + "c993afb6b4ac48a6b61b2558e601d65d"); sentry_value_decref(line3_json); #endif sentry__prepared_http_request_free(req); diff --git a/tests/unit/test_value.c b/tests/unit/test_value.c index fd83fd6221..e7fc1330f7 100644 --- a/tests/unit/test_value.c +++ b/tests/unit/test_value.c @@ -833,7 +833,7 @@ SENTRY_TEST(user_feedback_is_valid) "some-message"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( user_feedback, "associated_event_id")), - "c993afb6-b4ac-48a6-b61b-2558e601d65d"); + "c993afb6b4ac48a6b61b2558e601d65d"); sentry_value_decref(user_feedback); } From e3ac17077d92219cce9cc0b700b28dd88412b534 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 9 Jul 2025 20:43:03 +0200 Subject: [PATCH 07/14] restore integration test for legacy user report --- CONTRIBUTING.md | 2 ++ examples/example.c | 18 ++++++++++++++++++ tests/assertions.py | 12 ++++++++++++ tests/test_integration_http.py | 30 ++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a4372bf2e8..e7bd13c9c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -189,6 +189,8 @@ The example currently supports the following commands: - `capture-with-scope`: Captures an event with a local scope. - `attach-to-scope`: Same as `attachment` but attaches the file to the local scope. - `clear-attachments`: Clears all attachments from the global scope. +- `capture-user-feedback`: Captures a user feedback event. +- `capture-user-report`: Captures an event together with a legacy user report. Only on Linux using crashpad: - `crashpad-wait-for-upload`: Couples application shutdown to complete the upload in the `crashpad_handler`. diff --git a/examples/example.c b/examples/example.c index 9dc6eeb5a0..7e4163a057 100644 --- a/examples/example.c +++ b/examples/example.c @@ -561,6 +561,24 @@ main(int argc, char **argv) sentry_capture_user_feedback(user_feedback); } + if (has_arg(argc, argv, "capture-user-report")) { + sentry_value_t event = sentry_value_new_message_event( + SENTRY_LEVEL_INFO, "my-logger", "Hello user feedback!"); + sentry_uuid_t event_id = sentry_capture_event(event); + char buf[37]; + sentry_uuid_as_string(&event_id, buf); + + sentry_value_t user_report = sentry_value_new_object(); + sentry_value_set_by_key( + user_report, "event_id", sentry_value_new_string(buf)); + sentry_value_set_by_key( + user_report, "name", sentry_value_new_string("some-name")); + sentry_value_set_by_key( + user_report, "email", sentry_value_new_string("some-email")); + sentry_value_set_by_key( + user_report, "comments", sentry_value_new_string("some-comment")); + sentry_capture_user_feedback(user_report); + } if (has_arg(argc, argv, "capture-transaction")) { sentry_transaction_context_t *tx_ctx diff --git a/tests/assertions.py b/tests/assertions.py index 22aa222bc5..1c63dedeb4 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -51,6 +51,18 @@ def assert_user_feedback(envelope): assert user_feedback["message"] == "some-message" +def assert_user_report(envelope): + user_report = None + for item in envelope: + if item.headers.get("type") == "user_report" and item.payload.json is not None: + user_feedback = item.payload.json + + assert user_feedback is not None + assert user_feedback["name"] == "some-name" + assert user_feedback["email"] == "some-email" + assert user_feedback["comments"] == "some-comment" + + def assert_meta( envelope, release="test-example-release", diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index ae2b7a3d1c..a3b53d72a1 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -30,6 +30,7 @@ assert_inproc_crash, assert_session, assert_user_feedback, + assert_user_report, assert_minidump, assert_breakpad_crash, assert_gzip_content_encoding, @@ -175,6 +176,35 @@ def test_user_feedback_http(cmake, httpserver): assert_user_feedback(envelope) +def test_user_report_http(cmake, httpserver): + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"}) + + httpserver.expect_request( + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, + ).respond_with_data("OK") + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + + run( + tmp_path, + "sentry_example", + ["log", "capture-user-report"], + check=True, + env=env, + ) + + assert len(httpserver.log) == 2 + output = httpserver.log[0][0].get_data() + envelope = Envelope.deserialize(output) + + assert_event(envelope, "Hello user feedback!") + + output = httpserver.log[1][0].get_data() + envelope = Envelope.deserialize(output) + + assert_user_report(envelope) + + def test_exception_and_session_http(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"}) From 3986c5b08465d0b3944b28751b17061d404a4e27 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 10 Jul 2025 09:12:46 +0200 Subject: [PATCH 08/14] convert a deprecated "user report" to a new "user feedback" --- examples/example.c | 2 +- src/sentry_envelope.c | 41 +++++++++++++--------------------- tests/assertions.py | 12 ---------- tests/test_integration_http.py | 3 +-- 4 files changed, 17 insertions(+), 41 deletions(-) diff --git a/examples/example.c b/examples/example.c index 7e4163a057..b1905dfac3 100644 --- a/examples/example.c +++ b/examples/example.c @@ -576,7 +576,7 @@ main(int argc, char **argv) sentry_value_set_by_key( user_report, "email", sentry_value_new_string("some-email")); sentry_value_set_by_key( - user_report, "comments", sentry_value_new_string("some-comment")); + user_report, "comments", sentry_value_new_string("some-message")); sentry_capture_user_feedback(user_report); } diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index 93ae6dd7d0..c63f6e867a 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -296,43 +296,32 @@ sentry__envelope_add_transaction( return item; } -static sentry_envelope_item_t * -add_legacy_user_report(sentry_envelope_t *envelope, sentry_value_t user_report) +static void +rename_key(sentry_value_t object, const char *from, const char *to) { - sentry_envelope_item_t *item = envelope_add_item(envelope); - if (!item) { - return NULL; + sentry_value_t old_value = sentry_value_get_by_key(object, from); + if (sentry_value_is_null(old_value)) { + return; } - sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL); - if (!jw) { - return NULL; + sentry_value_t new_value = sentry_value_get_by_key(object, to); + if (!sentry_value_is_null(new_value)) { + return; } - sentry_value_t event_id = sentry__ensure_event_id(user_report, NULL); - - sentry__jsonwriter_write_value(jw, user_report); - item->payload = sentry__jsonwriter_into_string(jw, &item->payload_len); - - sentry__envelope_item_set_header( - item, "type", sentry_value_new_string("user_report")); - sentry_value_t length = sentry_value_new_int32((int32_t)item->payload_len); - sentry__envelope_item_set_header(item, "length", length); - - sentry_value_incref(event_id); - sentry__envelope_set_header(envelope, "event_id", event_id); - - return item; + sentry_value_incref(old_value); + sentry_value_set_by_key(object, to, old_value); + sentry_value_remove_by_key(object, from); } sentry_envelope_item_t * sentry__envelope_add_user_feedback( sentry_envelope_t *envelope, sentry_value_t user_feedback) { - if (!sentry_value_is_null( - sentry_value_get_by_key(user_feedback, "event_id"))) { - return add_legacy_user_report(envelope, user_feedback); - } + // convert a deprecated "user report" to a new "user feedback" + rename_key(user_feedback, "event_id", "associated_event_id"); + rename_key(user_feedback, "email", "contact_email"); + rename_key(user_feedback, "comments", "message"); sentry_value_t event = sentry_value_new_event(); sentry_value_t contexts = sentry_value_get_by_key(event, "contexts"); diff --git a/tests/assertions.py b/tests/assertions.py index 1c63dedeb4..22aa222bc5 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -51,18 +51,6 @@ def assert_user_feedback(envelope): assert user_feedback["message"] == "some-message" -def assert_user_report(envelope): - user_report = None - for item in envelope: - if item.headers.get("type") == "user_report" and item.payload.json is not None: - user_feedback = item.payload.json - - assert user_feedback is not None - assert user_feedback["name"] == "some-name" - assert user_feedback["email"] == "some-email" - assert user_feedback["comments"] == "some-comment" - - def assert_meta( envelope, release="test-example-release", diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index a3b53d72a1..d7a254acb8 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -30,7 +30,6 @@ assert_inproc_crash, assert_session, assert_user_feedback, - assert_user_report, assert_minidump, assert_breakpad_crash, assert_gzip_content_encoding, @@ -202,7 +201,7 @@ def test_user_report_http(cmake, httpserver): output = httpserver.log[1][0].get_data() envelope = Envelope.deserialize(output) - assert_user_report(envelope) + assert_user_feedback(envelope) def test_exception_and_session_http(cmake, httpserver): From 36ce410127e197d63308bc6b0ba8dcdd3c52ce88 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 10 Jul 2025 10:30:25 +0200 Subject: [PATCH 09/14] conversion: note in docs & log info message --- include/sentry.h | 5 +++++ src/sentry_envelope.c | 19 +++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/include/sentry.h b/include/sentry.h index 102719e05a..8ffd5e02ad 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -2436,6 +2436,11 @@ SENTRY_API sentry_value_t sentry_value_new_user_feedback_n( /** * Captures a manually created User Feedback and sends it to Sentry. + * + * Note: This function automatically converts old deprecated User Report objects + * to the new User Feedback format. See + * https://develop.sentry.dev/sdk/data-model/envelope-items/#user-report---deprecated + * and https://develop.sentry.dev/sdk/data-model/envelope-items/#user-feedback. */ SENTRY_API void sentry_capture_user_feedback(sentry_value_t user_feedback); diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index c63f6e867a..31d055cc93 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -296,22 +296,23 @@ sentry__envelope_add_transaction( return item; } -static void +static bool rename_key(sentry_value_t object, const char *from, const char *to) { sentry_value_t old_value = sentry_value_get_by_key(object, from); if (sentry_value_is_null(old_value)) { - return; + return false; } sentry_value_t new_value = sentry_value_get_by_key(object, to); if (!sentry_value_is_null(new_value)) { - return; + return false; } sentry_value_incref(old_value); sentry_value_set_by_key(object, to, old_value); sentry_value_remove_by_key(object, from); + return true; } sentry_envelope_item_t * @@ -319,9 +320,15 @@ sentry__envelope_add_user_feedback( sentry_envelope_t *envelope, sentry_value_t user_feedback) { // convert a deprecated "user report" to a new "user feedback" - rename_key(user_feedback, "event_id", "associated_event_id"); - rename_key(user_feedback, "email", "contact_email"); - rename_key(user_feedback, "comments", "message"); + // https://develop.sentry.dev/sdk/data-model/envelope-items/#user-report---deprecated + bool converted + = rename_key(user_feedback, "event_id", "associated_event_id"); + converted |= rename_key(user_feedback, "email", "contact_email"); + converted |= rename_key(user_feedback, "comments", "message"); + if (converted) { + SENTRY_INFO( + "converted a deprecated user report to a new user feedback object"); + } sentry_value_t event = sentry_value_new_event(); sentry_value_t contexts = sentry_value_get_by_key(event, "contexts"); From 1be41aec580b6ca0c1249379feb30609e27f0195 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 10 Jul 2025 10:44:50 +0200 Subject: [PATCH 10/14] let sentry__envelope_add_user_feedback take ownership --- src/sentry_core.c | 3 ++- src/sentry_envelope.c | 3 +-- tests/unit/test_envelopes.c | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sentry_core.c b/src/sentry_core.c index 29ccbbf11f..045b8e742d 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -1343,9 +1343,10 @@ sentry_capture_user_feedback(sentry_value_t user_feedback) envelope = prepare_user_feedback(user_feedback); if (envelope) { sentry__capture_envelope(options->transport, envelope); + } else { + sentry_value_decref(user_feedback); } } - sentry_value_decref(user_feedback); } int diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index 31d055cc93..7cb19798f9 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -335,8 +335,7 @@ sentry__envelope_add_user_feedback( if (sentry_value_is_null(contexts)) { contexts = sentry_value_new_object(); } - sentry_value_set_by_key( - contexts, "feedback", sentry__value_clone(user_feedback)); + sentry_value_set_by_key(contexts, "feedback", user_feedback); sentry_value_set_by_key(event, "contexts", contexts); sentry_envelope_item_t *item = sentry__envelope_add_event(envelope, event); diff --git a/tests/unit/test_envelopes.c b/tests/unit/test_envelopes.c index 77d7b55903..ab2abd0089 100644 --- a/tests/unit/test_envelopes.c +++ b/tests/unit/test_envelopes.c @@ -143,7 +143,6 @@ SENTRY_TEST(basic_http_request_preparation_for_user_feedback) sentry_value_decref(line3_json); #endif sentry__prepared_http_request_free(req); - sentry_value_decref(user_feedback); sentry_envelope_free(envelope); sentry__dsn_decref(dsn); From be006dcb92aaae5a70a1b60d6ca2c852b5a38017 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 10 Jul 2025 12:20:48 +0200 Subject: [PATCH 11/14] revert back to option 1 --- CHANGELOG.md | 3 +- CONTRIBUTING.md | 1 - examples/example.c | 22 ++++------- include/sentry.h | 47 ++++++++++++++++------- src/sentry_core.c | 35 ++++++++++++++++- src/sentry_envelope.c | 46 +++++++++++------------ src/sentry_envelope.h | 6 +++ src/sentry_value.c | 54 +++++++++++++++++++++----- tests/assertions.py | 12 ++++++ tests/test_integration_http.py | 3 +- tests/unit/test_envelopes.c | 35 ++++++++++++++++- tests/unit/test_value.c | 69 ++++++++++++++++++++++------------ tests/unit/tests.inc | 2 + 13 files changed, 244 insertions(+), 91 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1901be4cff..1f39432c68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ - Add `sentry_clear_attachments()` to allow clearing all previously added attachments in the global scope. ([#1290](https://github.com/getsentry/sentry-native/pull/1290)) - Compiles also on Xbox One ([#1294](https://github.com/getsentry/sentry-native/pull/1294)) - Provide `sentry_regenerate_trace()` to allow users to set manual trace boundaries. ([#1293](https://github.com/getsentry/sentry-native/pull/1293)) -- Implement the new [User Feedback API](https://develop.sentry.dev/sdk/data-model/envelope-items/#user-feedback). ([#1304](https://github.com/getsentry/sentry-native/pull/1304)) +- Add `sentry_value_new_feedback` and `sentry_capture_feedback` to allow capturing [User Feedback](https://develop.sentry.dev/sdk/data-model/envelope-items/#user-feedback). ([#1304](https://github.com/getsentry/sentry-native/pull/1304)) + - Deprecate `sentry_value_new_user_feedback` and `sentry_capture_user_feedback` in favor of the new API. ## 0.9.1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e7bd13c9c9..50cf8bc669 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -190,7 +190,6 @@ The example currently supports the following commands: - `attach-to-scope`: Same as `attachment` but attaches the file to the local scope. - `clear-attachments`: Clears all attachments from the global scope. - `capture-user-feedback`: Captures a user feedback event. -- `capture-user-report`: Captures an event together with a legacy user report. Only on Linux using crashpad: - `crashpad-wait-for-upload`: Couples application shutdown to complete the upload in the `crashpad_handler`. diff --git a/examples/example.c b/examples/example.c index b1905dfac3..ed89a71c3d 100644 --- a/examples/example.c +++ b/examples/example.c @@ -556,28 +556,20 @@ main(int argc, char **argv) sentry_capture_event(event); } if (has_arg(argc, argv, "capture-user-feedback")) { - sentry_value_t user_feedback = sentry_value_new_user_feedback( - NULL, "some-name", "some-email", "some-message"); + sentry_value_t user_feedback = sentry_value_new_feedback( + "some-message", "some-email", "some-name", NULL); - sentry_capture_user_feedback(user_feedback); + sentry_capture_feedback(user_feedback); } if (has_arg(argc, argv, "capture-user-report")) { sentry_value_t event = sentry_value_new_message_event( SENTRY_LEVEL_INFO, "my-logger", "Hello user feedback!"); sentry_uuid_t event_id = sentry_capture_event(event); - char buf[37]; - sentry_uuid_as_string(&event_id, buf); - sentry_value_t user_report = sentry_value_new_object(); - sentry_value_set_by_key( - user_report, "event_id", sentry_value_new_string(buf)); - sentry_value_set_by_key( - user_report, "name", sentry_value_new_string("some-name")); - sentry_value_set_by_key( - user_report, "email", sentry_value_new_string("some-email")); - sentry_value_set_by_key( - user_report, "comments", sentry_value_new_string("some-message")); - sentry_capture_user_feedback(user_report); + sentry_value_t user_feedback = sentry_value_new_user_feedback( + &event_id, "some-name", "some-email", "some-comment"); + + sentry_capture_user_feedback(user_feedback); } if (has_arg(argc, argv, "capture-transaction")) { diff --git a/include/sentry.h b/include/sentry.h index 8ffd5e02ad..50bdc868b1 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -2419,30 +2419,51 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_set_name_n( sentry_transaction_t *transaction, const char *name, size_t name_len); /** - * Creates a new User Feedback with a specific name, email and message. + * Creates a deprecated User Report with a specific name, email and comments. * - * See https://develop.sentry.dev/sdk/data-model/envelope-items/#user-feedback + * See + * https://develop.sentry.dev/sdk/data-model/envelope-items/#user-report---deprecated * - * User Feedback can be associated with a specific event that has been - * sent to Sentry earlier. + * Deprecated: Please use `sentry_value_new_feedback` and + * `sentry_capture_feedback` instead. */ SENTRY_API sentry_value_t sentry_value_new_user_feedback( const sentry_uuid_t *uuid, const char *name, const char *email, - const char *message); + const char *comments); SENTRY_API sentry_value_t sentry_value_new_user_feedback_n( const sentry_uuid_t *uuid, const char *name, size_t name_len, - const char *email, size_t email_len, const char *message, - size_t message_len); + const char *email, size_t email_len, const char *comments, + size_t comments_len); /** - * Captures a manually created User Feedback and sends it to Sentry. + * Captures a deprecated User Report and sends it to Sentry. * - * Note: This function automatically converts old deprecated User Report objects - * to the new User Feedback format. See - * https://develop.sentry.dev/sdk/data-model/envelope-items/#user-report---deprecated - * and https://develop.sentry.dev/sdk/data-model/envelope-items/#user-feedback. + * Deprecated: Please use `sentry_value_new_feedback` and + * `sentry_capture_feedback` instead. + */ +SENTRY_API void sentry_capture_user_feedback(sentry_value_t user_report); + +/** + * Creates a new User Feedback with a specific message (required), and optional + * contact_email, name, message, and associated_event_id. + * + * See https://develop.sentry.dev/sdk/data-model/envelope-items/#user-feedback + * + * User Feedback can be associated with a specific event that has been + * sent to Sentry earlier. + */ +SENTRY_API sentry_value_t sentry_value_new_feedback(const char *message, + const char *contact_email, const char *name, + const sentry_uuid_t *associated_event_id); +SENTRY_API sentry_value_t sentry_value_new_feedback_n(const char *message, + size_t message_len, const char *contact_email, size_t contact_email_len, + const char *name, size_t name_len, + const sentry_uuid_t *associated_event_id); + +/** + * Captures a manually created User Feedback and sends it to Sentry. */ -SENTRY_API void sentry_capture_user_feedback(sentry_value_t user_feedback); +SENTRY_API void sentry_capture_feedback(sentry_value_t user_feedback); /** * The status of a Span or Transaction. diff --git a/src/sentry_core.c b/src/sentry_core.c index 045b8e742d..a76de14628 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -658,6 +658,25 @@ sentry__prepare_transaction(const sentry_options_t *options, return NULL; } +static sentry_envelope_t * +prepare_user_report(sentry_value_t user_report) +{ + sentry_envelope_t *envelope = NULL; + + envelope = sentry__envelope_new(); + if (!envelope || !sentry__envelope_add_user_report(envelope, user_report)) { + goto fail; + } + + return envelope; + +fail: + SENTRY_WARN("dropping user report"); + sentry_envelope_free(envelope); + sentry_value_decref(user_report); + return NULL; +} + static sentry_envelope_t * prepare_user_feedback(sentry_value_t user_feedback) { @@ -1335,7 +1354,21 @@ sentry_span_finish_ts(sentry_span_t *opaque_span, uint64_t timestamp) } void -sentry_capture_user_feedback(sentry_value_t user_feedback) +sentry_capture_user_feedback(sentry_value_t user_report) +{ + sentry_envelope_t *envelope = NULL; + + SENTRY_WITH_OPTIONS (options) { + envelope = prepare_user_report(user_report); + if (envelope) { + sentry__capture_envelope(options->transport, envelope); + } + } + sentry_value_decref(user_report); +} + +void +sentry_capture_feedback(sentry_value_t user_feedback) { sentry_envelope_t *envelope = NULL; diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index 7cb19798f9..5702b42f0f 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -296,40 +296,40 @@ sentry__envelope_add_transaction( return item; } -static bool -rename_key(sentry_value_t object, const char *from, const char *to) +sentry_envelope_item_t * +sentry__envelope_add_user_report( + sentry_envelope_t *envelope, sentry_value_t user_report) { - sentry_value_t old_value = sentry_value_get_by_key(object, from); - if (sentry_value_is_null(old_value)) { - return false; + sentry_envelope_item_t *item = envelope_add_item(envelope); + if (!item) { + return NULL; } - sentry_value_t new_value = sentry_value_get_by_key(object, to); - if (!sentry_value_is_null(new_value)) { - return false; + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL); + if (!jw) { + return NULL; } - sentry_value_incref(old_value); - sentry_value_set_by_key(object, to, old_value); - sentry_value_remove_by_key(object, from); - return true; + sentry_value_t event_id = sentry__ensure_event_id(user_report, NULL); + + sentry__jsonwriter_write_value(jw, user_report); + item->payload = sentry__jsonwriter_into_string(jw, &item->payload_len); + + sentry__envelope_item_set_header( + item, "type", sentry_value_new_string("user_report")); + sentry_value_t length = sentry_value_new_int32((int32_t)item->payload_len); + sentry__envelope_item_set_header(item, "length", length); + + sentry_value_incref(event_id); + sentry__envelope_set_header(envelope, "event_id", event_id); + + return item; } sentry_envelope_item_t * sentry__envelope_add_user_feedback( sentry_envelope_t *envelope, sentry_value_t user_feedback) { - // convert a deprecated "user report" to a new "user feedback" - // https://develop.sentry.dev/sdk/data-model/envelope-items/#user-report---deprecated - bool converted - = rename_key(user_feedback, "event_id", "associated_event_id"); - converted |= rename_key(user_feedback, "email", "contact_email"); - converted |= rename_key(user_feedback, "comments", "message"); - if (converted) { - SENTRY_INFO( - "converted a deprecated user report to a new user feedback object"); - } - sentry_value_t event = sentry_value_new_event(); sentry_value_t contexts = sentry_value_get_by_key(event, "contexts"); if (sentry_value_is_null(contexts)) { diff --git a/src/sentry_envelope.h b/src/sentry_envelope.h index 8febb75d58..a41985dce6 100644 --- a/src/sentry_envelope.h +++ b/src/sentry_envelope.h @@ -43,6 +43,12 @@ sentry_envelope_item_t *sentry__envelope_add_event( sentry_envelope_item_t *sentry__envelope_add_transaction( sentry_envelope_t *envelope, sentry_value_t transaction); +/** + * Add a deprecated user report to this envelope. + */ +sentry_envelope_item_t *sentry__envelope_add_user_report( + sentry_envelope_t *envelope, sentry_value_t user_report); + /** * Add a user feedback to this envelope. */ diff --git a/src/sentry_value.c b/src/sentry_value.c index 24e8eebb40..47d1a12cd4 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -1348,35 +1348,69 @@ sentry_value_new_stacktrace(void **ips, size_t len) sentry_value_t sentry_value_new_user_feedback(const sentry_uuid_t *uuid, const char *name, - const char *email, const char *message) + const char *email, const char *comments) { return sentry_value_new_user_feedback_n(uuid, name, sentry__guarded_strlen(name), email, sentry__guarded_strlen(email), - message, sentry__guarded_strlen(message)); + comments, sentry__guarded_strlen(comments)); } sentry_value_t sentry_value_new_user_feedback_n(const sentry_uuid_t *uuid, const char *name, - size_t name_len, const char *email, size_t email_len, const char *message, - size_t message_len) + size_t name_len, const char *email, size_t email_len, const char *comments, + size_t comments_len) { sentry_value_t rv = sentry_value_new_object(); - if (message) { + sentry_value_set_by_key(rv, "event_id", sentry__value_new_uuid(uuid)); + + if (name) { sentry_value_set_by_key( - rv, "message", sentry_value_new_string_n(message, message_len)); + rv, "name", sentry_value_new_string_n(name, name_len)); } if (email) { sentry_value_set_by_key( - rv, "contact_email", sentry_value_new_string_n(email, email_len)); + rv, "email", sentry_value_new_string_n(email, email_len)); + } + if (comments) { + sentry_value_set_by_key( + rv, "comments", sentry_value_new_string_n(comments, comments_len)); + } + + return rv; +} + +sentry_value_t +sentry_value_new_feedback(const char *message, const char *contact_email, + const char *name, const sentry_uuid_t *associated_event_id) +{ + return sentry_value_new_feedback_n(message, sentry__guarded_strlen(message), + contact_email, sentry__guarded_strlen(contact_email), name, + sentry__guarded_strlen(name), associated_event_id); +} + +sentry_value_t +sentry_value_new_feedback_n(const char *message, size_t message_len, + const char *contact_email, size_t contact_email_len, const char *name, + size_t name_len, const sentry_uuid_t *associated_event_id) +{ + sentry_value_t rv = sentry_value_new_object(); + + if (message) { + sentry_value_set_by_key( + rv, "message", sentry_value_new_string_n(message, message_len)); + } + if (contact_email) { + sentry_value_set_by_key(rv, "contact_email", + sentry_value_new_string_n(contact_email, contact_email_len)); } if (name) { sentry_value_set_by_key( rv, "name", sentry_value_new_string_n(name, name_len)); } - if (uuid) { - sentry_value_set_by_key( - rv, "associated_event_id", sentry__value_new_internal_uuid(uuid)); + if (associated_event_id) { + sentry_value_set_by_key(rv, "associated_event_id", + sentry__value_new_internal_uuid(associated_event_id)); } return rv; diff --git a/tests/assertions.py b/tests/assertions.py index 22aa222bc5..8fd340f8ee 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -51,6 +51,18 @@ def assert_user_feedback(envelope): assert user_feedback["message"] == "some-message" +def assert_user_report(envelope): + user_report = None + for item in envelope: + if item.headers.get("type") == "user_report" and item.payload.json is not None: + user_report = item.payload.json + + assert user_report is not None + assert user_report["name"] == "some-name" + assert user_report["email"] == "some-email" + assert user_report["comments"] == "some-comment" + + def assert_meta( envelope, release="test-example-release", diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index d7a254acb8..a3b53d72a1 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -30,6 +30,7 @@ assert_inproc_crash, assert_session, assert_user_feedback, + assert_user_report, assert_minidump, assert_breakpad_crash, assert_gzip_content_encoding, @@ -201,7 +202,7 @@ def test_user_report_http(cmake, httpserver): output = httpserver.log[1][0].get_data() envelope = Envelope.deserialize(output) - assert_user_feedback(envelope) + assert_user_report(envelope) def test_exception_and_session_http(cmake, httpserver): diff --git a/tests/unit/test_envelopes.c b/tests/unit/test_envelopes.c index ab2abd0089..e02077b72b 100644 --- a/tests/unit/test_envelopes.c +++ b/tests/unit/test_envelopes.c @@ -79,6 +79,37 @@ SENTRY_TEST(basic_http_request_preparation_for_transaction) sentry__dsn_decref(dsn); } +SENTRY_TEST(basic_http_request_preparation_for_user_report) +{ + SENTRY_TEST_DSN_NEW_DEFAULT(dsn); + + sentry_uuid_t event_id + = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); + sentry_envelope_t *envelope = sentry__envelope_new(); + sentry_value_t user_report = sentry_value_new_user_feedback( + &event_id, "some-name", "some-email", "some-comment"); + sentry__envelope_add_user_report(envelope, user_report); + + sentry_prepared_http_request_t *req + = sentry__prepare_http_request(envelope, dsn, NULL, NULL); + TEST_CHECK_STRING_EQUAL(req->method, "POST"); + TEST_CHECK_STRING_EQUAL( + req->url, "https://sentry.invalid:443/api/42/envelope/"); +#ifndef SENTRY_TRANSPORT_COMPRESSION + TEST_CHECK_STRING_EQUAL(req->body, + "{\"event_id\":\"c993afb6-b4ac-48a6-b61b-2558e601d65d\"}\n" + "{\"type\":\"user_report\",\"length\":117}\n" + "{\"event_id\":\"c993afb6-b4ac-48a6-b61b-2558e601d65d\",\"name\":" + "\"some-name\",\"email\":\"some-email\",\"comments\":" + "\"some-comment\"}"); +#endif + sentry__prepared_http_request_free(req); + sentry_value_decref(user_report); + sentry_envelope_free(envelope); + + sentry__dsn_decref(dsn); +} + SENTRY_TEST(basic_http_request_preparation_for_user_feedback) { SENTRY_TEST_DSN_NEW_DEFAULT(dsn); @@ -86,8 +117,8 @@ SENTRY_TEST(basic_http_request_preparation_for_user_feedback) sentry_uuid_t event_id = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); sentry_envelope_t *envelope = sentry__envelope_new(); - sentry_value_t user_feedback = sentry_value_new_user_feedback( - &event_id, "some-name", "some-email", "some-message"); + sentry_value_t user_feedback = sentry_value_new_feedback( + "some-message", "some-email", "some-name", &event_id); sentry__envelope_add_user_feedback(envelope, user_feedback); sentry_prepared_http_request_t *req diff --git a/tests/unit/test_value.c b/tests/unit/test_value.c index e7fc1330f7..f85c0dfff7 100644 --- a/tests/unit/test_value.c +++ b/tests/unit/test_value.c @@ -814,26 +814,23 @@ SENTRY_TEST(thread_without_name_still_valid) sentry_value_decref(thread); } -SENTRY_TEST(user_feedback_is_valid) +SENTRY_TEST(user_report_is_valid) { sentry_uuid_t event_id = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); sentry_value_t user_feedback = sentry_value_new_user_feedback( - &event_id, "some-name", "some-email", "some-message"); + &event_id, "some-name", "some-email", "some-comment"); TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(user_feedback, "name")), "some-name"); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - user_feedback, "contact_email")), + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(user_feedback, "email")), "some-email"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - user_feedback, "message")), - "some-message"); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - user_feedback, "associated_event_id")), - "c993afb6b4ac48a6b61b2558e601d65d"); + user_feedback, "comments")), + "some-comment"); sentry_value_decref(user_feedback); } @@ -848,40 +845,40 @@ SENTRY_TEST(user_feedback_with_null_args) TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK( sentry_value_is_null(sentry_value_get_by_key(user_feedback, "name"))); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(user_feedback, "email"))); TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(user_feedback, "contact_email"))); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(user_feedback, "message"))); + sentry_value_get_by_key(user_feedback, "comments"))); sentry_value_decref(user_feedback); user_feedback = sentry_value_new_user_feedback( - &event_id, NULL, "some-email", "some-message"); + &event_id, NULL, "some-email", "some-comment"); TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK( sentry_value_is_null(sentry_value_get_by_key(user_feedback, "name"))); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - user_feedback, "contact_email")), + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(user_feedback, "email")), "some-email"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - user_feedback, "message")), - "some-message"); + user_feedback, "comments")), + "some-comment"); sentry_value_decref(user_feedback); user_feedback = sentry_value_new_user_feedback( - &event_id, "some-name", NULL, "some-message"); + &event_id, "some-name", NULL, "some-comment"); TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(user_feedback, "name")), "some-name"); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(user_feedback, "contact_email"))); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(user_feedback, "email"))); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - user_feedback, "message")), - "some-message"); + user_feedback, "comments")), + "some-comment"); sentry_value_decref(user_feedback); @@ -890,14 +887,38 @@ SENTRY_TEST(user_feedback_with_null_args) TEST_CHECK(!sentry_value_is_null(user_feedback)); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(user_feedback, "name")), + "some-name"); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(user_feedback, "email")), + "some-email"); + TEST_CHECK(sentry_value_is_null( + sentry_value_get_by_key(user_feedback, "comments"))); + + sentry_value_decref(user_feedback); +} + +SENTRY_TEST(user_feedback_is_valid) +{ + sentry_uuid_t event_id + = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); + sentry_value_t user_feedback = sentry_value_new_feedback( + "some-message", "some-email", "some-name", &event_id); + + TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(user_feedback, "name")), "some-name"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( user_feedback, "contact_email")), "some-email"); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(user_feedback, "message"))); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + user_feedback, "message")), + "some-message"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + user_feedback, "associated_event_id")), + "c993afb6b4ac48a6b61b2558e601d65d"); sentry_value_decref(user_feedback); } diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 4daa747398..7be02a45dd 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -16,6 +16,7 @@ XX(basic_http_request_preparation_for_event_with_attachment) XX(basic_http_request_preparation_for_minidump) XX(basic_http_request_preparation_for_transaction) XX(basic_http_request_preparation_for_user_feedback) +XX(basic_http_request_preparation_for_user_report) XX(basic_spans) XX(basic_tracing_context) XX(basic_transaction) @@ -145,6 +146,7 @@ XX(url_parsing_partial) XX(url_parsing_with_path) XX(user_feedback_is_valid) XX(user_feedback_with_null_args) +XX(user_report_is_valid) XX(uuid_api) XX(uuid_v4) XX(value_bool) From e618388da457df0ad6dbd302bd84f0b520743d71 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 10 Jul 2025 18:21:33 +0200 Subject: [PATCH 12/14] deprecate sentry_value_new_user_feedback() & sentry_capture_user_feedback() --- include/sentry.h | 9 +++------ tests/unit/test_value.c | 23 +++++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/sentry.h b/include/sentry.h index 21be2c60c5..d07df5f501 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -2438,13 +2438,12 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_set_name_n( * * See * https://develop.sentry.dev/sdk/data-model/envelope-items/#user-report---deprecated - * - * Deprecated: Please use `sentry_value_new_feedback` and - * `sentry_capture_feedback` instead. */ +SENTRY_DEPRECATED("Use `sentry_value_new_feedback` instead") SENTRY_API sentry_value_t sentry_value_new_user_feedback( const sentry_uuid_t *uuid, const char *name, const char *email, const char *comments); +SENTRY_DEPRECATED("Use `sentry_value_new_feedback_n` instead") SENTRY_API sentry_value_t sentry_value_new_user_feedback_n( const sentry_uuid_t *uuid, const char *name, size_t name_len, const char *email, size_t email_len, const char *comments, @@ -2452,10 +2451,8 @@ SENTRY_API sentry_value_t sentry_value_new_user_feedback_n( /** * Captures a deprecated User Report and sends it to Sentry. - * - * Deprecated: Please use `sentry_value_new_feedback` and - * `sentry_capture_feedback` instead. */ +SENTRY_DEPRECATED("Use `sentry_capture_feedback` instead") SENTRY_API void sentry_capture_user_feedback(sentry_value_t user_report); /** diff --git a/tests/unit/test_value.c b/tests/unit/test_value.c index f85c0dfff7..46fc893a96 100644 --- a/tests/unit/test_value.c +++ b/tests/unit/test_value.c @@ -818,8 +818,10 @@ SENTRY_TEST(user_report_is_valid) { sentry_uuid_t event_id = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); - sentry_value_t user_feedback = sentry_value_new_user_feedback( - &event_id, "some-name", "some-email", "some-comment"); + sentry_value_t user_feedback; + SENTRY_TEST_DEPRECATED( + user_feedback = sentry_value_new_user_feedback( + &event_id, "some-name", "some-email", "some-comment")); TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK_STRING_EQUAL( @@ -839,8 +841,9 @@ SENTRY_TEST(user_feedback_with_null_args) { sentry_uuid_t event_id = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); - sentry_value_t user_feedback - = sentry_value_new_user_feedback(&event_id, NULL, NULL, NULL); + sentry_value_t user_feedback; + SENTRY_TEST_DEPRECATED(user_feedback + = sentry_value_new_user_feedback(&event_id, NULL, NULL, NULL)); TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK( @@ -852,8 +855,8 @@ SENTRY_TEST(user_feedback_with_null_args) sentry_value_decref(user_feedback); - user_feedback = sentry_value_new_user_feedback( - &event_id, NULL, "some-email", "some-comment"); + SENTRY_TEST_DEPRECATED(user_feedback = sentry_value_new_user_feedback( + &event_id, NULL, "some-email", "some-comment")); TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK( @@ -867,8 +870,8 @@ SENTRY_TEST(user_feedback_with_null_args) sentry_value_decref(user_feedback); - user_feedback = sentry_value_new_user_feedback( - &event_id, "some-name", NULL, "some-comment"); + SENTRY_TEST_DEPRECATED(user_feedback = sentry_value_new_user_feedback( + &event_id, "some-name", NULL, "some-comment")); TEST_CHECK(!sentry_value_is_null(user_feedback)); TEST_CHECK_STRING_EQUAL( @@ -882,8 +885,8 @@ SENTRY_TEST(user_feedback_with_null_args) sentry_value_decref(user_feedback); - user_feedback = sentry_value_new_user_feedback( - &event_id, "some-name", "some-email", NULL); + SENTRY_TEST_DEPRECATED(user_feedback = sentry_value_new_user_feedback( + &event_id, "some-name", "some-email", NULL)); TEST_CHECK(!sentry_value_is_null(user_feedback)); From 90a10e1dacd11f1b9782567f153516c185555cc0 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 10 Jul 2025 18:39:05 +0200 Subject: [PATCH 13/14] fix remaining deprecation warnings --- examples/example.c | 17 +++++++++++++++++ tests/unit/test_envelopes.c | 6 ++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/examples/example.c b/examples/example.c index ed89a71c3d..5404b246fc 100644 --- a/examples/example.c +++ b/examples/example.c @@ -58,6 +58,21 @@ get_current_thread_id() } #endif +#if defined(__GNUC__) || defined(__clang__) +# define SUPPRESS_DEPRECATED \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +# define RESTORE_WARNINGS _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) +# define SUPPRESS_DEPRECATED \ + __pragma(warning(push)); \ + __pragma(warning(disable : 4996)) +# define RESTORE_WARNINGS __pragma(warning(pop)) +#else +# define SUPPRESS_DEPRECATED +# define RESTORE_WARNINGS +#endif + static double traces_sampler_callback(const sentry_transaction_context_t *transaction_ctx, sentry_value_t custom_sampling_ctx, const int *parent_sampled) @@ -566,10 +581,12 @@ main(int argc, char **argv) SENTRY_LEVEL_INFO, "my-logger", "Hello user feedback!"); sentry_uuid_t event_id = sentry_capture_event(event); + SUPPRESS_DEPRECATED sentry_value_t user_feedback = sentry_value_new_user_feedback( &event_id, "some-name", "some-email", "some-comment"); sentry_capture_user_feedback(user_feedback); + RESTORE_WARNINGS } if (has_arg(argc, argv, "capture-transaction")) { diff --git a/tests/unit/test_envelopes.c b/tests/unit/test_envelopes.c index e02077b72b..e7ba20fb87 100644 --- a/tests/unit/test_envelopes.c +++ b/tests/unit/test_envelopes.c @@ -86,8 +86,10 @@ SENTRY_TEST(basic_http_request_preparation_for_user_report) sentry_uuid_t event_id = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); sentry_envelope_t *envelope = sentry__envelope_new(); - sentry_value_t user_report = sentry_value_new_user_feedback( - &event_id, "some-name", "some-email", "some-comment"); + sentry_value_t user_report; + SENTRY_TEST_DEPRECATED( + user_report = sentry_value_new_user_feedback( + &event_id, "some-name", "some-email", "some-comment")); sentry__envelope_add_user_report(envelope, user_report); sentry_prepared_http_request_t *req From 80f1f6149ad260e8ad7bb47f9920e99c84b9f3eb Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 10 Jul 2025 20:23:50 +0200 Subject: [PATCH 14/14] msvc --- examples/example.c | 19 ++----------------- include/sentry.h | 15 +++++++++++++++ src/sentry_value.c | 2 ++ tests/unit/sentry_testsupport.h | 25 ++++++------------------- 4 files changed, 25 insertions(+), 36 deletions(-) diff --git a/examples/example.c b/examples/example.c index 5404b246fc..bb4f1cd5ad 100644 --- a/examples/example.c +++ b/examples/example.c @@ -58,21 +58,6 @@ get_current_thread_id() } #endif -#if defined(__GNUC__) || defined(__clang__) -# define SUPPRESS_DEPRECATED \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -# define RESTORE_WARNINGS _Pragma("GCC diagnostic pop") -#elif defined(_MSC_VER) -# define SUPPRESS_DEPRECATED \ - __pragma(warning(push)); \ - __pragma(warning(disable : 4996)) -# define RESTORE_WARNINGS __pragma(warning(pop)) -#else -# define SUPPRESS_DEPRECATED -# define RESTORE_WARNINGS -#endif - static double traces_sampler_callback(const sentry_transaction_context_t *transaction_ctx, sentry_value_t custom_sampling_ctx, const int *parent_sampled) @@ -581,12 +566,12 @@ main(int argc, char **argv) SENTRY_LEVEL_INFO, "my-logger", "Hello user feedback!"); sentry_uuid_t event_id = sentry_capture_event(event); - SUPPRESS_DEPRECATED + SENTRY_SUPPRESS_DEPRECATED sentry_value_t user_feedback = sentry_value_new_user_feedback( &event_id, "some-name", "some-email", "some-comment"); sentry_capture_user_feedback(user_feedback); - RESTORE_WARNINGS + SENTRY_RESTORE_DEPRECATED } if (has_arg(argc, argv, "capture-transaction")) { diff --git a/include/sentry.h b/include/sentry.h index d07df5f501..16d678bcc4 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -119,6 +119,21 @@ extern "C" { # endif #endif +#if defined(__GNUC__) || defined(__clang__) +# define SENTRY_SUPPRESS_DEPRECATED \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +# define SENTRY_RESTORE_DEPRECATED _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) +# define SENTRY_SUPPRESS_DEPRECATED \ + __pragma(warning(push)); \ + __pragma(warning(disable : 4996)) +# define SENTRY_RESTORE_DEPRECATED __pragma(warning(pop)) +#else +# define SENTRY_SUPPRESS_DEPRECATED +# define SENTRY_RESTORE_DEPRECATED +#endif + /* marks a function as experimental api */ #ifndef SENTRY_EXPERIMENTAL_API # define SENTRY_EXPERIMENTAL_API SENTRY_API diff --git a/src/sentry_value.c b/src/sentry_value.c index 47d1a12cd4..2ac33c809c 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -1350,9 +1350,11 @@ sentry_value_t sentry_value_new_user_feedback(const sentry_uuid_t *uuid, const char *name, const char *email, const char *comments) { + SENTRY_SUPPRESS_DEPRECATED return sentry_value_new_user_feedback_n(uuid, name, sentry__guarded_strlen(name), email, sentry__guarded_strlen(email), comments, sentry__guarded_strlen(comments)); + SENTRY_RESTORE_DEPRECATED } sentry_value_t diff --git a/tests/unit/sentry_testsupport.h b/tests/unit/sentry_testsupport.h index e3364f51ac..0fe83d78bd 100644 --- a/tests/unit/sentry_testsupport.h +++ b/tests/unit/sentry_testsupport.h @@ -90,24 +90,11 @@ sentry_dsn_t *Varname = sentry__dsn_new(DSN_URL); \ TEST_ASSERT(!!Varname) -#if defined(__GNUC__) || defined(__clang__) -# define SENTRY_TEST_DEPRECATED(call) \ - do { \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\""); \ - call; \ - _Pragma("GCC diagnostic pop"); \ - } while (0) -#elif defined(_MSC_VER) -# define SENTRY_TEST_DEPRECATED(call) \ - do { \ - __pragma(warning(push)); \ - __pragma(warning(disable : 4996)); \ - call; \ - __pragma(warning(pop)); \ - } while (0) -#else -# define SENTRY_TEST_DEPRECATED(call) call -#endif +#define SENTRY_TEST_DEPRECATED(call) \ + do { \ + SENTRY_SUPPRESS_DEPRECATED \ + call; \ + SENTRY_RESTORE_DEPRECATED \ + } while (0) #endif // SENTRY_TEST_SUPPORT_H_INCLUDED