阶段1接入AGC标记解析
在统一 API tracking middleware 中解析 X-Genarrative-Client。 将有效 marker 传入 TrackingEventDraft,供后续 metadata 合并使用。 增加大小写、空白、未知值和非法文本的定向测试。
This commit is contained in:
@@ -24,7 +24,7 @@ use crate::{
|
||||
response_headers::propagate_request_id_header,
|
||||
state::{AppState, BackpressureState},
|
||||
telemetry::record_http_observability,
|
||||
tracking::record_route_tracking_event_after_success,
|
||||
tracking::{record_route_tracking_event_after_success, resolve_tracking_client_marker},
|
||||
wechat::pay::{
|
||||
handle_wechat_pay_notify, handle_wechat_pay_refund_notify,
|
||||
handle_wechat_virtual_payment_message_push_verify, handle_wechat_virtual_payment_notify,
|
||||
@@ -241,6 +241,7 @@ async fn record_api_tracking_after_success(
|
||||
) -> Response {
|
||||
let method = request.method().clone();
|
||||
let path = request.uri().path().to_string();
|
||||
let client_marker = resolve_tracking_client_marker(request.headers());
|
||||
let response = next.run(request).await;
|
||||
let authenticated = response
|
||||
.extensions()
|
||||
@@ -253,6 +254,7 @@ async fn record_api_tracking_after_success(
|
||||
&path,
|
||||
response.status(),
|
||||
authenticated.as_ref(),
|
||||
client_marker,
|
||||
)
|
||||
.await;
|
||||
response
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use axum::http::{Method, StatusCode};
|
||||
use axum::http::{HeaderMap, Method, StatusCode};
|
||||
#[cfg(not(test))]
|
||||
use module_auth::AuthLoginMethod;
|
||||
use module_runtime::RuntimeTrackingScopeKind;
|
||||
@@ -8,6 +8,22 @@ use uuid::Uuid;
|
||||
|
||||
use crate::{auth::AuthenticatedAccessToken, request_context::RequestContext, state::AppState};
|
||||
|
||||
const AGC_CLIENT_MARKER_HEADER: &str = "x-genarrative-client";
|
||||
const AGC_CLIENT_MARKER_VALUE: &str = "agc";
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum TrackingClientMarker {
|
||||
Agc,
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_tracking_client_marker(headers: &HeaderMap) -> Option<TrackingClientMarker> {
|
||||
headers
|
||||
.get(AGC_CLIENT_MARKER_HEADER)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.filter(|value| value.trim() == AGC_CLIENT_MARKER_VALUE)
|
||||
.map(|_| TrackingClientMarker::Agc)
|
||||
}
|
||||
|
||||
/// 后端用户行为埋点入口统一走这里:写入失败只记录日志,不反向阻断主业务。
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TrackingEventDraft {
|
||||
@@ -19,6 +35,7 @@ pub struct TrackingEventDraft {
|
||||
pub profile_id: Option<String>,
|
||||
pub module_key: Option<&'static str>,
|
||||
pub metadata: Value,
|
||||
pub(crate) client_marker: Option<TrackingClientMarker>,
|
||||
}
|
||||
|
||||
impl TrackingEventDraft {
|
||||
@@ -32,6 +49,7 @@ impl TrackingEventDraft {
|
||||
profile_id: None,
|
||||
module_key: Some(module_key),
|
||||
metadata: json!({}),
|
||||
client_marker: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +128,7 @@ pub async fn record_route_tracking_event_after_success(
|
||||
path: &str,
|
||||
status: StatusCode,
|
||||
authenticated: Option<&AuthenticatedAccessToken>,
|
||||
client_marker: Option<TrackingClientMarker>,
|
||||
) {
|
||||
if !status.is_success() {
|
||||
return;
|
||||
@@ -130,6 +149,7 @@ pub async fn record_route_tracking_event_after_success(
|
||||
draft.scope_kind = spec.scope_kind;
|
||||
draft.scope_id = scope_id;
|
||||
draft.user_id = user_id;
|
||||
draft.client_marker = client_marker;
|
||||
draft.metadata = build_route_tracking_metadata(&spec, request_context, method, path, status);
|
||||
if draft.user_id.is_some() {
|
||||
draft.owner_user_id = draft.user_id.clone();
|
||||
@@ -836,9 +856,42 @@ fn runtime_profile_beijing_day_key(occurred_at_micros: i64) -> i64 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use axum::http::Method;
|
||||
use axum::http::{HeaderMap, HeaderValue, Method};
|
||||
|
||||
use super::{is_route_tracking_excluded, normalize_route_path, resolve_route_tracking_spec};
|
||||
use super::{
|
||||
TrackingClientMarker, is_route_tracking_excluded, normalize_route_path,
|
||||
resolve_route_tracking_spec, resolve_tracking_client_marker,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn tracking_client_marker_accepts_only_trimmed_lowercase_agc() {
|
||||
let cases = [
|
||||
(None, None),
|
||||
(Some(""), None),
|
||||
(Some("AGC"), None),
|
||||
(Some("other"), None),
|
||||
(Some(" agc "), Some(TrackingClientMarker::Agc)),
|
||||
(Some("agc"), Some(TrackingClientMarker::Agc)),
|
||||
];
|
||||
|
||||
for (value, expected) in cases {
|
||||
let mut headers = HeaderMap::new();
|
||||
if let Some(value) = value {
|
||||
headers.insert(
|
||||
"X-GENARRATIVE-CLIENT",
|
||||
HeaderValue::from_str(value).expect("marker fixture should be valid"),
|
||||
);
|
||||
}
|
||||
assert_eq!(resolve_tracking_client_marker(&headers), expected);
|
||||
}
|
||||
|
||||
let mut invalid_headers = HeaderMap::new();
|
||||
invalid_headers.insert(
|
||||
"x-genarrative-client",
|
||||
HeaderValue::from_bytes(&[0xff]).expect("invalid text fixture should be accepted"),
|
||||
);
|
||||
assert_eq!(resolve_tracking_client_marker(&invalid_headers), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn route_normalization_preserves_static_segments_and_replaces_ids() {
|
||||
|
||||
Reference in New Issue
Block a user