Build L7 allow-rules from observed (method, path) samples. Groups paths by HTTP method and generalises path patterns where possible: - `/v1/models/abc123` → `/v1/models/**` (ID-like trailing segments) - `/api/v2/users/42` → `/api/v2/users/*` (numeric trailing segment) Falls back to the exact observed path when no pattern applies.
(samples: &HashMap<(String, String), u32>)
| 335 | /// |
| 336 | /// Falls back to the exact observed path when no pattern applies. |
| 337 | fn build_l7_rules(samples: &HashMap<(String, String), u32>) -> Vec<L7Rule> { |
| 338 | // Deduplicate after generalisation. |
| 339 | let mut seen: std::collections::HashSet<(String, String)> = std::collections::HashSet::new(); |
| 340 | let mut rules = Vec::new(); |
| 341 | |
| 342 | for (method, path) in samples.keys() { |
| 343 | let generalised = generalise_path(path); |
| 344 | let key = (method.clone(), generalised.clone()); |
| 345 | if !seen.insert(key) { |
| 346 | continue; |
| 347 | } |
| 348 | |
| 349 | rules.push(L7Rule { |
| 350 | allow: Some(L7Allow { |
| 351 | method: method.clone(), |
| 352 | path: generalised, |
| 353 | command: String::new(), |
| 354 | query: HashMap::new(), |
| 355 | operation_type: String::new(), |
| 356 | operation_name: String::new(), |
| 357 | fields: Vec::new(), |
| 358 | params: HashMap::new(), |
| 359 | }), |
| 360 | }); |
| 361 | } |
| 362 | |
| 363 | // Sort for deterministic output. |
| 364 | rules.sort_by(|a, b| { |
| 365 | let a = a.allow.as_ref().unwrap(); |
| 366 | let b = b.allow.as_ref().unwrap(); |
| 367 | (&a.method, &a.path).cmp(&(&b.method, &b.path)) |
| 368 | }); |
| 369 | |
| 370 | rules |
| 371 | } |
| 372 | |
| 373 | /// Generalise a URL path for policy rules. |
| 374 | /// |
no test coverage detected