1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
use relay_protocol::{Annotated, Array, Empty, FromValue, IntoValue, Object, Value};
use crate::processor::ProcessValue;
use crate::protocol::IpAddr;
/// An installed and loaded package as part of the Sentry SDK.
#[derive(Clone, Debug, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
pub struct ClientSdkPackage {
/// Name of the package.
pub name: Annotated<String>,
/// Version of the package.
pub version: Annotated<String>,
}
/// The SDK Interface describes the Sentry SDK and its configuration used to capture and transmit an event.
#[derive(Clone, Debug, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
#[metastructure(process_func = "process_client_sdk_info", value_type = "ClientSdkInfo")]
pub struct ClientSdkInfo {
/// Unique SDK name. _Required._
///
/// The name of the SDK. The format is `entity.ecosystem[.flavor]` where entity identifies the
/// developer of the SDK, ecosystem refers to the programming language or platform where the
/// SDK is to be used and the optional flavor is used to identify standalone SDKs that are part
/// of a major ecosystem.
///
/// Official Sentry SDKs use the entity `sentry`, as in `sentry.python` or
/// `sentry.javascript.react-native`. Please use a different entity for your own SDKs.
#[metastructure(required = "true", max_chars = 256, max_chars_allowance = 20)]
pub name: Annotated<String>,
/// The version of the SDK. _Required._
///
/// It should have the [Semantic Versioning](https://semver.org/) format `MAJOR.MINOR.PATCH`,
/// without any prefix (no `v` or anything else in front of the major version number).
///
/// Examples: `0.1.0`, `1.0.0`, `4.3.12`
#[metastructure(required = "true", max_chars = 256, max_chars_allowance = 20)]
pub version: Annotated<String>,
/// List of integrations that are enabled in the SDK. _Optional._
///
/// The list should have all enabled integrations, including default integrations. Default
/// integrations are included because different SDK releases may contain different default
/// integrations.
#[metastructure(skip_serialization = "empty_deep")]
pub integrations: Annotated<Array<String>>,
/// List of features that are enabled in the SDK. _Optional._
///
/// A list of feature names identifying enabled SDK features. This list
/// should contain all enabled SDK features. On some SDKs, enabling a feature in the
/// options also adds an integration. We encourage tracking such features with either
/// integrations or features but not both to reduce the payload size.
#[metastructure(skip_serialization = "empty_deep")]
pub features: Annotated<Array<String>>,
/// List of installed and loaded SDK packages. _Optional._
///
/// A list of packages that were installed as part of this SDK or the activated integrations.
/// Each package consists of a name in the format `source:identifier` and `version`. If the
/// source is a Git repository, the `source` should be `git`, the identifier should be a
/// checkout link and the version should be a Git reference (branch, tag or SHA).
#[metastructure(skip_serialization = "empty_deep")]
pub packages: Annotated<Array<ClientSdkPackage>>,
/// IP Address of sender??? Seems unused. Do not send, this only leads to surprises wrt PII, as
/// the value appears nowhere in the UI.
#[metastructure(pii = "true", skip_serialization = "empty", omit_from_schema)]
pub client_ip: Annotated<IpAddr>,
/// Additional arbitrary fields for forwards compatibility.
#[metastructure(additional_properties)]
pub other: Object<Value>,
}
impl ClientSdkInfo {
pub fn has_integration<T: AsRef<str>>(&self, integration: T) -> bool {
if let Some(integrations) = self.integrations.value() {
for x in integrations {
if x.as_str().unwrap_or_default() == integration.as_ref() {
return true;
}
}
};
false
}
}
#[cfg(test)]
mod tests {
use relay_protocol::Map;
use similar_asserts::assert_eq;
use super::*;
#[test]
fn test_client_sdk_roundtrip() {
let json = r#"{
"name": "sentry.rust",
"version": "1.0.0",
"integrations": [
"actix"
],
"features": [
"feature1"
],
"packages": [
{
"name": "cargo:sentry",
"version": "0.10.0"
},
{
"name": "cargo:sentry-actix",
"version": "0.10.0"
}
],
"client_ip": "127.0.0.1",
"other": "value"
}"#;
let sdk = Annotated::new(ClientSdkInfo {
name: Annotated::new("sentry.rust".to_string()),
version: Annotated::new("1.0.0".to_string()),
integrations: Annotated::new(vec![Annotated::new("actix".to_string())]),
features: Annotated::new(vec![Annotated::new("feature1".to_string())]),
packages: Annotated::new(vec![
Annotated::new(ClientSdkPackage {
name: Annotated::new("cargo:sentry".to_string()),
version: Annotated::new("0.10.0".to_string()),
}),
Annotated::new(ClientSdkPackage {
name: Annotated::new("cargo:sentry-actix".to_string()),
version: Annotated::new("0.10.0".to_string()),
}),
]),
client_ip: Annotated::new(IpAddr("127.0.0.1".to_owned())),
other: {
let mut map = Map::new();
map.insert(
"other".to_string(),
Annotated::new(Value::String("value".to_string())),
);
map
},
});
assert_eq!(sdk, Annotated::from_json(json).unwrap());
assert_eq!(json, sdk.to_json_pretty().unwrap());
}
#[test]
fn test_client_sdk_default_values() {
let json = r#"{
"name": "sentry.rust",
"version": "1.0.0",
"client_ip": "127.0.0.1"
}"#;
let sdk = Annotated::new(ClientSdkInfo {
name: Annotated::new("sentry.rust".to_string()),
version: Annotated::new("1.0.0".to_string()),
integrations: Annotated::empty(),
features: Annotated::empty(),
packages: Annotated::empty(),
client_ip: Annotated::new(IpAddr("127.0.0.1".to_owned())),
other: Default::default(),
});
assert_eq!(sdk, Annotated::from_json(json).unwrap());
assert_eq!(json, sdk.to_json_pretty().unwrap());
}
}