1use std::sync::{
2 atomic::{AtomicU64, Ordering},
3 Arc,
4};
5
6use soma_application::{ExecutionContext, SomaApplication};
7use soma_domain::{AuthorizationMode, Principal, RequestId, ScopeSet, Surface};
8
9#[cfg(test)]
10#[path = "state_tests.rs"]
11mod tests;
12
13#[derive(Clone)]
14pub struct ApiState {
15 application: Arc<SomaApplication>,
16 authorization_mode: AuthorizationMode,
17 server_name: Arc<str>,
18}
19
20impl ApiState {
21 pub fn new(
22 application: Arc<SomaApplication>,
23 authorization_mode: AuthorizationMode,
24 server_name: impl Into<Arc<str>>,
25 ) -> Self {
26 Self {
27 application,
28 authorization_mode,
29 server_name: server_name.into(),
30 }
31 }
32
33 pub fn application(&self) -> &SomaApplication {
34 self.application.as_ref()
35 }
36
37 pub fn server_name(&self) -> &str {
38 &self.server_name
39 }
40
41 pub fn execution_context(&self, subject: Option<&str>, scopes: &[String]) -> ExecutionContext {
42 ExecutionContext {
43 principal: subject
44 .map(|subject| Principal::new(subject, ScopeSet::new(scopes.iter().cloned()))),
45 authorization_mode: self.authorization_mode,
46 surface: Surface::Rest,
47 trace: None,
48 destructive_confirmation: Default::default(),
49 response_limit: None,
50 request_id: next_request_id(),
51 }
52 }
53}
54
55fn next_request_id() -> RequestId {
56 static REQUEST_SEQUENCE: AtomicU64 = AtomicU64::new(1);
57 let sequence = REQUEST_SEQUENCE.fetch_add(1, Ordering::Relaxed);
58 RequestId::new(format!("rest-{}-{sequence}", std::process::id()))
59 .expect("generated REST request ids are valid")
60}