diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f2234f1b32..822f3210930 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Add `lock` attribute to the `SentryStackFrame` protocol to better highlight offending frames in the UI ([#2761](https://github.com/getsentry/sentry-java/pull/2761)) - Enrich database spans with blocked main thread info ([#2760](https://github.com/getsentry/sentry-java/pull/2760)) +- Add `api_target` to `Request` and `data` to `Response` Protocols ([#2760](https://github.com/getsentry/sentry-java/pull/2760)) ## 6.21.0 diff --git a/sentry/src/main/java/io/sentry/protocol/Request.java b/sentry/src/main/java/io/sentry/protocol/Request.java index 2e51a231979..188a17c3453 100644 --- a/sentry/src/main/java/io/sentry/protocol/Request.java +++ b/sentry/src/main/java/io/sentry/protocol/Request.java @@ -109,6 +109,15 @@ public final class Request implements JsonUnknown, JsonSerializable { /** The fragment (anchor) of the request URL. */ private @Nullable String fragment; + /** + * The API target/specification that made the request. + *

+ * Values can be `graphql`, `rest`, etc. + *

+ * The data field should contain the request and response bodies based on its target specification. + * */ + private @Nullable String apiTarget; + @SuppressWarnings("unused") private @Nullable Map unknown; @@ -126,6 +135,7 @@ public Request(final @NotNull Request request) { this.data = request.data; this.fragment = request.fragment; this.bodySize = request.bodySize; + this.apiTarget = request.apiTarget; } public @Nullable String getUrl() { @@ -220,12 +230,13 @@ public boolean equals(Object o) { && Objects.equals(headers, request.headers) && Objects.equals(env, request.env) && Objects.equals(bodySize, request.bodySize) - && Objects.equals(fragment, request.fragment); + && Objects.equals(fragment, request.fragment) + && Objects.equals(apiTarget, request.getApiTarget()); } @Override public int hashCode() { - return Objects.hash(url, method, queryString, cookies, headers, env, bodySize, fragment); + return Objects.hash(url, method, queryString, cookies, headers, env, bodySize, fragment, apiTarget); } // region json @@ -241,6 +252,14 @@ public void setUnknown(@Nullable Map unknown) { this.unknown = unknown; } + public @Nullable String getApiTarget() { + return apiTarget; + } + + public void setApiTarget(final @Nullable String apiTarget) { + this.apiTarget = apiTarget; + } + public static final class JsonKeys { public static final String URL = "url"; public static final String METHOD = "method"; @@ -252,6 +271,7 @@ public static final class JsonKeys { public static final String OTHER = "other"; public static final String FRAGMENT = "fragment"; public static final String BODY_SIZE = "body_size"; + public static final String API_TARGET = "api_target"; } @Override @@ -286,7 +306,10 @@ public void serialize(@NotNull JsonObjectWriter writer, @NotNull ILogger logger) writer.name(JsonKeys.FRAGMENT).value(logger, fragment); } if (bodySize != null) { - writer.name(Response.JsonKeys.BODY_SIZE).value(logger, bodySize); + writer.name(JsonKeys.BODY_SIZE).value(logger, bodySize); + } + if (apiTarget != null) { + writer.name(JsonKeys.API_TARGET).value(logger, apiTarget); } if (unknown != null) { for (String key : unknown.keySet()) { @@ -346,9 +369,12 @@ public static final class Deserializer implements JsonDeserializer { case JsonKeys.FRAGMENT: request.fragment = reader.nextStringOrNull(); break; - case Response.JsonKeys.BODY_SIZE: + case JsonKeys.BODY_SIZE: request.bodySize = reader.nextLongOrNull(); break; + case JsonKeys.API_TARGET: + request.apiTarget = reader.nextStringOrNull(); + break; default: if (unknown == null) { unknown = new ConcurrentHashMap<>(); diff --git a/sentry/src/main/java/io/sentry/protocol/Response.java b/sentry/src/main/java/io/sentry/protocol/Response.java index d34b605d2d0..f46ad369a35 100644 --- a/sentry/src/main/java/io/sentry/protocol/Response.java +++ b/sentry/src/main/java/io/sentry/protocol/Response.java @@ -37,6 +37,14 @@ public final class Response implements JsonUnknown, JsonSerializable { /** The body size in bytes */ private @Nullable Long bodySize; + /** + * Response data in any format that makes sense. + *

+ * SDKs should discard large and binary bodies by default. Can be given as a string or + * structural data of any format. + */ + private @Nullable Object data; + @SuppressWarnings("unused") private @Nullable Map unknown; @@ -48,6 +56,7 @@ public Response(final @NotNull Response response) { this.unknown = CollectionUtils.newConcurrentHashMap(response.unknown); this.statusCode = response.statusCode; this.bodySize = response.bodySize; + this.data = response.data; } public @Nullable String getCookies() { @@ -93,6 +102,14 @@ public void setBodySize(final @Nullable Long bodySize) { this.bodySize = bodySize; } + public @Nullable Object getData() { + return data; + } + + public void setData(final @Nullable Object data) { + this.data = data; + } + // region json public static final class JsonKeys { @@ -100,6 +117,7 @@ public static final class JsonKeys { public static final String HEADERS = "headers"; public static final String STATUS_CODE = "status_code"; public static final String BODY_SIZE = "body_size"; + public static final String DATA = "data"; } @Override @@ -119,7 +137,9 @@ public void serialize(final @NotNull JsonObjectWriter writer, final @NotNull ILo if (bodySize != null) { writer.name(JsonKeys.BODY_SIZE).value(logger, bodySize); } - + if (data != null) { + writer.name(JsonKeys.DATA).value(logger, data); + } if (unknown != null) { for (final String key : unknown.keySet()) { final Object value = unknown.get(key); @@ -157,6 +177,9 @@ public static final class Deserializer implements JsonDeserializer { case JsonKeys.BODY_SIZE: response.bodySize = reader.nextLongOrNull(); break; + case JsonKeys.DATA: + response.data = reader.nextObjectOrNull(); + break; default: if (unknown == null) { unknown = new ConcurrentHashMap<>(); diff --git a/sentry/src/test/java/io/sentry/protocol/RequestSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/RequestSerializationTest.kt index 973c661b336..4aaa5423c34 100644 --- a/sentry/src/test/java/io/sentry/protocol/RequestSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/RequestSerializationTest.kt @@ -29,6 +29,7 @@ class RequestSerializationTest { ) bodySize = 1000 fragment = "fragment" + apiTarget = "graphql" } } private val fixture = Fixture() diff --git a/sentry/src/test/java/io/sentry/protocol/RequestTest.kt b/sentry/src/test/java/io/sentry/protocol/RequestTest.kt index 2acb3316d66..5cf1d68aaef 100644 --- a/sentry/src/test/java/io/sentry/protocol/RequestTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/RequestTest.kt @@ -35,6 +35,7 @@ class RequestTest { assertEquals("unknown", clone.unknown!!["unknown"]) assertEquals(1000, clone.bodySize) assertEquals("fragment", clone.fragment) + assertEquals("graphql", clone.apiTarget) } @Test @@ -52,6 +53,7 @@ class RequestTest { request.unknown = newUnknown request.bodySize = 1001 request.fragment = "fragment2" + request.apiTarget = "graphql" assertEquals("get", clone.method) assertEquals("http://localhost:8080", clone.url) @@ -64,6 +66,7 @@ class RequestTest { assertEquals(1, clone.unknown!!.size) assertEquals(1000, clone.bodySize) assertEquals("fragment", clone.fragment) + assertEquals("graphql", clone.apiTarget) } @Test @@ -119,6 +122,7 @@ class RequestTest { setUnknown(unknown) bodySize = 1000 fragment = "fragment" + apiTarget = "graphql" } } } diff --git a/sentry/src/test/java/io/sentry/protocol/ResponseSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/ResponseSerializationTest.kt index 203b697aec1..e278813d8df 100644 --- a/sentry/src/test/java/io/sentry/protocol/ResponseSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/ResponseSerializationTest.kt @@ -14,6 +14,9 @@ class ResponseSerializationTest { headers = mapOf("content-type" to "text/html") statusCode = 500 bodySize = 1000 + data = mapOf( + "d9d709db-b666-40cc-bcbb-093bb12aad26" to "1631d0e6-96b7-4632-85f8-ef69e8bcfb16" + ) unknown = mapOf("arbitrary_field" to "arbitrary") } } diff --git a/sentry/src/test/resources/json/request.json b/sentry/src/test/resources/json/request.json index d0eb6d9735e..d744deac62a 100644 --- a/sentry/src/test/resources/json/request.json +++ b/sentry/src/test/resources/json/request.json @@ -20,5 +20,6 @@ "669ff1c1-517b-46dc-a889-131555364a56": "89043294-f6e1-4e2e-b152-1fdf9b1102fc" }, "fragment": "fragment", - "body_size": 1000 + "body_size": 1000, + "api_target": "graphql" } diff --git a/sentry/src/test/resources/json/response.json b/sentry/src/test/resources/json/response.json index 9faf2b41838..af7d4ab7670 100644 --- a/sentry/src/test/resources/json/response.json +++ b/sentry/src/test/resources/json/response.json @@ -5,5 +5,9 @@ }, "status_code": 500, "body_size": 1000, + "data": + { + "d9d709db-b666-40cc-bcbb-093bb12aad26": "1631d0e6-96b7-4632-85f8-ef69e8bcfb16" + }, "arbitrary_field": "arbitrary" }