static_tr_plugin/domain/
service.rs1use std::collections::{HashMap, HashSet};
4
5use tenant_resolver_sdk::{TenantFilter, TenantId, TenantInfo};
6
7use crate::config::StaticTrPluginConfig;
8
9pub struct Service {
13 pub(super) tenants: HashMap<TenantId, TenantInfo>,
15
16 pub(super) access_rules: HashSet<(TenantId, TenantId)>,
18
19 pub(super) accessible_by: HashMap<TenantId, Vec<TenantId>>,
22}
23
24impl Service {
25 #[must_use]
27 pub fn from_config(cfg: &StaticTrPluginConfig) -> Self {
28 let tenants: HashMap<TenantId, TenantInfo> = cfg
29 .tenants
30 .iter()
31 .map(|t| {
32 (
33 t.id,
34 TenantInfo {
35 id: t.id,
36 name: t.name.clone(),
37 status: t.status,
38 tenant_type: t.tenant_type.clone(),
39 },
40 )
41 })
42 .collect();
43
44 let access_rules: HashSet<(TenantId, TenantId)> = cfg
45 .access_rules
46 .iter()
47 .map(|r| (r.source, r.target))
48 .collect();
49
50 let mut accessible_by: HashMap<TenantId, Vec<TenantId>> = HashMap::new();
52 for (source, target) in &access_rules {
53 accessible_by.entry(*source).or_default().push(*target);
54 }
55
56 Self {
57 tenants,
58 access_rules,
59 accessible_by,
60 }
61 }
62
63 pub(super) fn matches_filter(tenant: &TenantInfo, filter: Option<&TenantFilter>) -> bool {
65 let Some(filter) = filter else {
66 return true;
67 };
68
69 if !filter.id.is_empty() && !filter.id.contains(&tenant.id) {
71 return false;
72 }
73
74 if !filter.status.is_empty() && !filter.status.contains(&tenant.status) {
76 return false;
77 }
78
79 true
80 }
81}
82
83#[cfg(test)]
84#[cfg_attr(coverage_nightly, coverage(off))]
85mod tests {
86 use super::*;
87 use crate::config::{AccessRuleConfig, TenantConfig};
88 use tenant_resolver_sdk::TenantStatus;
89 use uuid::Uuid;
90
91 fn tenant(id: &str, name: &str, status: TenantStatus) -> TenantConfig {
93 TenantConfig {
94 id: Uuid::parse_str(id).unwrap(),
95 name: name.to_owned(),
96 status,
97 tenant_type: None,
98 }
99 }
100
101 fn access_rule(source: &str, target: &str) -> AccessRuleConfig {
103 AccessRuleConfig {
104 source: Uuid::parse_str(source).unwrap(),
105 target: Uuid::parse_str(target).unwrap(),
106 }
107 }
108
109 const TENANT_A: &str = "11111111-1111-1111-1111-111111111111";
111 const TENANT_B: &str = "22222222-2222-2222-2222-222222222222";
112 const TENANT_C: &str = "33333333-3333-3333-3333-333333333333";
113
114 #[test]
117 fn from_config_empty() {
118 let cfg = StaticTrPluginConfig::default();
119 let service = Service::from_config(&cfg);
120
121 assert!(service.tenants.is_empty());
122 assert!(service.access_rules.is_empty());
123 assert!(service.accessible_by.is_empty());
124 }
125
126 #[test]
127 fn from_config_with_tenants_only() {
128 let cfg = StaticTrPluginConfig {
129 tenants: vec![
130 tenant(TENANT_A, "Tenant A", TenantStatus::Active),
131 tenant(TENANT_B, "Tenant B", TenantStatus::Suspended),
132 ],
133 ..Default::default()
134 };
135 let service = Service::from_config(&cfg);
136
137 assert_eq!(service.tenants.len(), 2);
138 assert!(service.access_rules.is_empty());
139 assert!(service.accessible_by.is_empty());
140
141 let a = service
142 .tenants
143 .get(&Uuid::parse_str(TENANT_A).unwrap())
144 .unwrap();
145 assert_eq!(a.name, "Tenant A");
146 assert_eq!(a.status, TenantStatus::Active);
147
148 let b = service
149 .tenants
150 .get(&Uuid::parse_str(TENANT_B).unwrap())
151 .unwrap();
152 assert_eq!(b.name, "Tenant B");
153 assert_eq!(b.status, TenantStatus::Suspended);
154 }
155
156 #[test]
157 fn from_config_with_access_rules() {
158 let cfg = StaticTrPluginConfig {
159 tenants: vec![
160 tenant(TENANT_A, "A", TenantStatus::Active),
161 tenant(TENANT_B, "B", TenantStatus::Active),
162 tenant(TENANT_C, "C", TenantStatus::Active),
163 ],
164 access_rules: vec![
165 access_rule(TENANT_A, TENANT_B), access_rule(TENANT_A, TENANT_C), access_rule(TENANT_B, TENANT_C), ],
169 ..Default::default()
170 };
171 let service = Service::from_config(&cfg);
172
173 assert_eq!(service.access_rules.len(), 3);
174
175 let a_id = Uuid::parse_str(TENANT_A).unwrap();
177 let b_id = Uuid::parse_str(TENANT_B).unwrap();
178 let c_id = Uuid::parse_str(TENANT_C).unwrap();
179
180 let a_accessible = service.accessible_by.get(&a_id).unwrap();
181 assert_eq!(a_accessible.len(), 2);
182 assert!(a_accessible.contains(&b_id));
183 assert!(a_accessible.contains(&c_id));
184
185 let b_accessible = service.accessible_by.get(&b_id).unwrap();
186 assert_eq!(b_accessible.len(), 1);
187 assert!(b_accessible.contains(&c_id));
188
189 assert!(!service.accessible_by.contains_key(&c_id));
191 }
192
193 #[test]
194 fn from_config_with_tenant_type() {
195 let cfg = StaticTrPluginConfig {
196 tenants: vec![TenantConfig {
197 id: Uuid::parse_str(TENANT_A).unwrap(),
198 name: "Enterprise".to_owned(),
199 status: TenantStatus::Active,
200 tenant_type: Some("enterprise".to_owned()),
201 }],
202 ..Default::default()
203 };
204 let service = Service::from_config(&cfg);
205
206 let a = service
207 .tenants
208 .get(&Uuid::parse_str(TENANT_A).unwrap())
209 .unwrap();
210 assert_eq!(a.tenant_type, Some("enterprise".to_owned()));
211 }
212}