Skip to main content

linkrs_server/session/
request_context.rs

1//! Request Context Module - manages context information for query requests
2
3use linkrs_core::ErrorCode;
4use linkrs_core::Value;
5use parking_lot::RwLock;
6use std::collections::HashMap;
7use std::sync::Arc;
8use std::time::Instant;
9
10use super::session_manager::SessionInfo;
11
12/// Request Parameters
13#[derive(Debug, Clone)]
14pub struct RequestParams {
15    pub query: String,
16    pub parameters: HashMap<String, Value>,
17}
18
19impl RequestParams {
20    pub fn new(query: String) -> Self {
21        Self {
22            query,
23            parameters: HashMap::new(),
24        }
25    }
26
27    pub fn with_parameters(mut self, params: HashMap<String, Value>) -> Self {
28        self.parameters = params;
29        self
30    }
31}
32
33/// response object
34#[derive(Debug, Clone)]
35pub struct Response {
36    pub success: bool,
37    pub error_code: ErrorCode,
38    pub data: Option<Value>,
39    pub error_message: Option<String>,
40    pub execution_time_ms: u64,
41    pub affected_rows: u64,
42    pub warnings: Vec<String>,
43}
44
45impl Response {
46    pub fn new(success: bool) -> Self {
47        Self {
48            success,
49            error_code: if success {
50                ErrorCode::Success
51            } else {
52                ErrorCode::Unknown
53            },
54            data: None,
55            error_message: None,
56            execution_time_ms: 0,
57            affected_rows: 0,
58            warnings: Vec::new(),
59        }
60    }
61
62    pub fn with_data(mut self, data: Value) -> Self {
63        self.data = Some(data);
64        self
65    }
66
67    pub fn with_error(mut self, error: String) -> Self {
68        self.error_message = Some(error);
69        self.success = false;
70        self.error_code = ErrorCode::ExecutionError;
71        self
72    }
73
74    pub fn with_error_code(mut self, code: ErrorCode) -> Self {
75        self.error_code = code;
76        if code != ErrorCode::Success {
77            self.success = false;
78        }
79        self
80    }
81
82    pub fn with_execution_time(mut self, time_ms: u64) -> Self {
83        self.execution_time_ms = time_ms;
84        self
85    }
86
87    pub fn with_affected_rows(mut self, rows: u64) -> Self {
88        self.affected_rows = rows;
89        self
90    }
91
92    pub fn with_warning(mut self, warning: String) -> Self {
93        self.warnings.push(warning);
94        self
95    }
96
97    pub fn is_success(&self) -> bool {
98        self.success
99    }
100
101    pub fn get_data(&self) -> Option<&Value> {
102        self.data.as_ref()
103    }
104
105    pub fn get_error(&self) -> Option<&String> {
106        self.error_message.as_ref()
107    }
108}
109
110/// request context
111///
112/// Manage the complete lifecycle of a query request, including:
113/// 1. Session information management
114/// 2. Request parameter management
115/// 3. Response object management
116#[derive(Debug)]
117pub struct RequestContext {
118    // session information
119    session_info: Option<SessionInfo>,
120
121    // Request Parameters
122    request_params: Arc<RequestParams>,
123
124    // Response Objects - Using RwLock to Support Internal Mutability
125    response: Arc<RwLock<Response>>,
126
127    // Inquiry start time
128    query_start_time: Instant,
129}
130
131impl RequestContext {
132    /// Creating a new request context
133    pub fn new(session_info: Option<SessionInfo>, request_params: RequestParams) -> Self {
134        Self {
135            session_info,
136            request_params: Arc::new(request_params),
137            response: Arc::new(RwLock::new(Response::new(true))),
138            query_start_time: Instant::now(),
139        }
140    }
141
142    /// Creating a request context with session information
143    pub fn with_session(
144        query: String,
145        session_id: &str,
146        user_name: &str,
147        client_ip: &str,
148        client_port: u16,
149    ) -> Result<Self, String> {
150        let session_info =
151            SessionInfo::from_params(session_id, user_name, None, client_ip, client_port)?;
152        let request_params = RequestParams::new(query);
153        Ok(Self::new(Some(session_info), request_params))
154    }
155
156    /// Creating a request context with parameters
157    pub fn with_parameters(
158        query: String,
159        parameters: HashMap<String, Value>,
160        session_id: &str,
161        user_name: &str,
162        client_ip: &str,
163        client_port: u16,
164    ) -> Result<Self, String> {
165        let session_info =
166            SessionInfo::from_params(session_id, user_name, None, client_ip, client_port)?;
167        let request_params = RequestParams::new(query).with_parameters(parameters);
168        Ok(Self::new(Some(session_info), request_params))
169    }
170
171    /// Get session information
172    pub fn session_info(&self) -> Option<&SessionInfo> {
173        self.session_info.as_ref()
174    }
175
176    /// Get request parameters
177    pub fn request_params(&self) -> &RequestParams {
178        &self.request_params
179    }
180
181    /// Get query string
182    pub fn query(&self) -> &str {
183        &self.request_params.query
184    }
185
186    /// Getting Parameters
187    pub fn parameters(&self) -> &HashMap<String, Value> {
188        &self.request_params.parameters
189    }
190
191    /// Get Response
192    pub fn response(&self) -> Response {
193        self.response.read().clone()
194    }
195
196    /// Setting the response
197    pub fn set_response(&self, response: Response) {
198        let mut guard = self.response.write();
199        *guard = response;
200    }
201
202    /// Get Session ID
203    pub fn session_id(&self) -> Option<i64> {
204        self.session_info.as_ref().map(|s| s.session_id)
205    }
206
207    /// Get User Name
208    pub fn user_name(&self) -> Option<&str> {
209        self.session_info.as_ref().map(|s| s.user_name.as_str())
210    }
211
212    /// Get the name of the graph space
213    pub fn space_name(&self) -> Option<&str> {
214        self.session_info
215            .as_ref()
216            .and_then(|s| s.space_name.as_deref())
217    }
218
219    /// Setting the name of the map space
220    pub fn set_space_name(&mut self, space_name: String) {
221        if let Some(ref mut session) = self.session_info {
222            session.space_name = Some(space_name);
223        }
224    }
225
226    /// Setting Response Errors
227    pub fn set_response_error(&self, error: String) {
228        let mut guard = self.response.write();
229        guard.success = false;
230        guard.error_code = ErrorCode::ExecutionError;
231        guard.error_message = Some(error);
232    }
233
234    /// Setting Response Errors with Error Codes
235    pub fn set_response_error_with_code(&self, error: String, code: ErrorCode) {
236        let mut guard = self.response.write();
237        guard.success = false;
238        guard.error_code = code;
239        guard.error_message = Some(error);
240    }
241
242    /// Adding a Warning Message
243    pub fn add_warning(&self, warning: String) {
244        let mut guard = self.response.write();
245        guard.warnings.push(warning);
246    }
247
248    /// Setting the execution time
249    pub fn set_execution_time(&self) {
250        let elapsed = self.query_start_time.elapsed().as_millis() as u64;
251        let mut guard = self.response.write();
252        guard.execution_time_ms = elapsed;
253    }
254
255    /// Get execution time in milliseconds
256    pub fn elapsed_ms(&self) -> u64 {
257        self.query_start_time.elapsed().as_millis() as u64
258    }
259
260    /// Getting Parameters
261    pub fn get_parameter(&self, param: &str) -> Option<Value> {
262        self.request_params.parameters.get(param).cloned()
263    }
264
265    /// Update session last access time
266    pub fn touch_session(&mut self) {
267        if let Some(ref mut session) = self.session_info {
268            session.touch();
269        }
270    }
271}
272
273impl Default for RequestContext {
274    fn default() -> Self {
275        Self {
276            session_info: None,
277            request_params: Arc::new(RequestParams::new(String::new())),
278            response: Arc::new(RwLock::new(Response::new(true))),
279            query_start_time: Instant::now(),
280        }
281    }
282}
283
284impl Clone for RequestContext {
285    fn clone(&self) -> Self {
286        Self {
287            session_info: self.session_info.clone(),
288            request_params: self.request_params.clone(),
289            response: self.response.clone(),
290            query_start_time: self.query_start_time,
291        }
292    }
293}
294
295/// Creating a QueryRequestContext from a ClientSession
296///
297/// This conversion function ensures that the session information from the api layer is correctly passed to the query layer
298pub fn build_query_request_context(
299    session: &super::ClientSession,
300    query: String,
301    parameters: std::collections::HashMap<String, linkrs_core::Value>,
302) -> crate::query::QueryRequestContext {
303    use crate::query::QueryRequestContext;
304
305    QueryRequestContext {
306        session_id: Some(session.id()),
307        isolation_level: None,
308        user_name: Some(session.user()),
309        space_name: session.space_name(),
310        query,
311        parameters,
312        session_variables: session.variables_snapshot(),
313        query_id: None,
314        parsed_statement: None,
315        transaction_id: session.current_transaction(),
316        auto_commit: session.is_auto_commit(),
317        read_only: false,
318        operation_context: None,
319        operation_storage: None,
320        ryw_config: None,
321    }
322}