1use devboy_core::{
2 Error, KnowledgeBaseProvider, MeetingNotesProvider, MessengerProvider, Provider, Result,
3 ToolEnricher,
4};
5
6use crate::context::{
7 ClickUpScope, ConfluenceAuthConfig, ConfluenceScope, GitHubScope, GitLabScope, JiraScope,
8 ProviderConfig, ProviderMetadata, ProxyConfig, SlackScope,
9};
10
11pub fn create_provider(
21 config: &ProviderConfig,
22 proxy: Option<&ProxyConfig>,
23) -> Result<Box<dyn Provider>> {
24 match config {
25 ProviderConfig::GitLab {
26 base_url,
27 access_token,
28 scope,
29 ..
30 } => match scope {
31 GitLabScope::Project { id } => {
32 let client = if let Some(proxy) = proxy {
33 devboy_gitlab::GitLabClient::with_base_url(
34 &proxy.url,
35 id,
36 access_token.clone(),
37 )
38 .with_proxy(proxy.headers.clone())
39 } else {
40 devboy_gitlab::GitLabClient::with_base_url(
41 base_url,
42 id,
43 access_token.clone(),
44 )
45 };
46 Ok(Box::new(client))
47 }
48 GitLabScope::Group { id } => Err(Error::ProviderUnsupported {
49 provider: "gitlab".into(),
50 operation: format!("group scope (group_id: {id}) not yet implemented"),
51 }),
52 GitLabScope::Global => Err(Error::ProviderUnsupported {
53 provider: "gitlab".into(),
54 operation: "global scope not yet implemented".into(),
55 }),
56 },
57
58 ProviderConfig::GitHub {
59 base_url,
60 access_token,
61 scope,
62 ..
63 } => match scope {
64 GitHubScope::Repository { owner, repo } => {
65 Ok(Box::new(devboy_github::GitHubClient::with_base_url(
66 base_url,
67 owner,
68 repo,
69 access_token.clone(),
70 )))
71 }
72 GitHubScope::Organization { name } => Err(Error::ProviderUnsupported {
73 provider: "github".into(),
74 operation: format!("organization scope (org: {name}) not yet implemented"),
75 }),
76 GitHubScope::Global => Err(Error::ProviderUnsupported {
77 provider: "github".into(),
78 operation: "global scope not yet implemented".into(),
79 }),
80 },
81
82 ProviderConfig::ClickUp {
83 access_token,
84 scope,
85 ..
86 } => match scope {
87 ClickUpScope::List { id, team_id } => {
88 let mut client = devboy_clickup::ClickUpClient::new(id, access_token.clone());
89 if let Some(tid) = team_id {
90 client = client.with_team_id(tid);
91 }
92 Ok(Box::new(client))
93 }
94 },
95
96 ProviderConfig::Jira {
97 base_url,
98 access_token,
99 email,
100 scope,
101 flavor,
102 ..
103 } => match scope {
104 JiraScope::Project { key } => {
105 let mut client = if let Some(proxy) = proxy {
106 devboy_jira::JiraClient::new(
107 &proxy.url,
108 key,
109 email,
110 access_token.clone(),
111 )
112 .with_proxy(proxy.headers.clone())
113 .with_instance_url(base_url)
114 } else {
115 devboy_jira::JiraClient::new(base_url, key, email, access_token.clone())
116 };
117 if let Some(f) = flavor {
118 client = client.with_flavor(*f);
119 }
120 Ok(Box::new(client))
121 }
122 JiraScope::MultiProject { keys } => Err(Error::ProviderUnsupported {
123 provider: "jira".into(),
124 operation: format!(
125 "multi-project scope ({}) not yet implemented",
126 keys.join(", ")
127 ),
128 }),
129 },
130
131 ProviderConfig::Confluence { .. } => Err(Error::ProviderUnsupported {
132 provider: "confluence".into(),
133 operation: "Confluence is a KnowledgeBaseProvider, not a Provider. Use create_knowledge_base_provider() instead.".into(),
134 }),
135
136 ProviderConfig::Fireflies { .. } => Err(Error::ProviderUnsupported {
137 provider: "fireflies".into(),
138 operation: "Fireflies is a MeetingNotesProvider, not a Provider. Use create_meeting_notes_provider() instead.".into(),
139 }),
140
141 ProviderConfig::Slack { .. } => Err(Error::ProviderUnsupported {
142 provider: "slack".into(),
143 operation: "Slack is a MessengerProvider, not a Provider. Use create_messenger_provider() instead.".into(),
144 }),
145
146 ProviderConfig::Custom { name, .. } => Err(Error::ProviderNotFound(format!(
147 "custom provider '{name}' not yet supported"
148 ))),
149 }
150}
151
152pub fn create_knowledge_base_provider(
153 config: &ProviderConfig,
154 proxy: Option<&ProxyConfig>,
155) -> Result<Box<dyn KnowledgeBaseProvider>> {
156 match config {
157 ProviderConfig::Confluence {
158 base_url,
159 auth,
160 api_version,
161 scope: ConfluenceScope::Space { .. },
162 ..
163 } => {
164 let client = if let Some(proxy) = proxy {
165 devboy_confluence::ConfluenceClient::new(
166 &proxy.url,
167 devboy_confluence::ConfluenceAuth::None,
168 )
169 .with_api_version(api_version.as_deref())
170 .with_proxy(proxy.headers.clone())
171 } else {
172 devboy_confluence::ConfluenceClient::new(base_url, confluence_auth(auth))
173 .with_api_version(api_version.as_deref())
174 };
175 Ok(Box::new(client))
176 }
177 other => Err(Error::ProviderUnsupported {
178 provider: other.provider_name().into(),
179 operation: "not a knowledge base provider".into(),
180 }),
181 }
182}
183
184pub fn create_knowledge_base_enricher(config: &ProviderConfig) -> Option<Box<dyn ToolEnricher>> {
190 match config {
191 ProviderConfig::Confluence { .. } => {
192 Some(Box::new(devboy_confluence::ConfluenceSchemaEnricher::new()))
193 }
194 _ => None,
195 }
196}
197
198pub fn create_meeting_notes_provider(
203 config: &ProviderConfig,
204) -> Result<Box<dyn MeetingNotesProvider>> {
205 match config {
206 ProviderConfig::Fireflies { api_key, .. } => Ok(Box::new(
207 devboy_fireflies::FirefliesClient::new(api_key.clone()),
208 )),
209 other => Err(Error::ProviderUnsupported {
210 provider: other.provider_name().into(),
211 operation: "not a meeting notes provider".into(),
212 }),
213 }
214}
215
216pub fn create_messenger_provider(config: &ProviderConfig) -> Result<Box<dyn MessengerProvider>> {
217 match config {
218 ProviderConfig::Slack {
219 base_url,
220 access_token,
221 scope: SlackScope::Workspace { .. },
222 required_scopes,
223 ..
224 } => Ok(Box::new(
225 devboy_slack::SlackClient::new(access_token.clone())
226 .with_base_url(base_url)
227 .with_required_scopes(required_scopes.clone()),
228 )),
229 other => Err(Error::ProviderUnsupported {
230 provider: other.provider_name().into(),
231 operation: "not a messenger provider".into(),
232 }),
233 }
234}
235
236pub fn create_enricher(
243 config: &ProviderConfig,
244 metadata: Option<&ProviderMetadata>,
245) -> Option<Box<dyn ToolEnricher>> {
246 match config {
247 ProviderConfig::GitLab { .. } => Some(Box::new(devboy_gitlab::GitLabSchemaEnricher)),
248 ProviderConfig::GitHub { .. } => Some(Box::new(devboy_github::GitHubSchemaEnricher)),
249 ProviderConfig::ClickUp { .. } => {
250 let meta = metadata?;
251 let clickup_meta: devboy_clickup::ClickUpMetadata =
252 serde_json::from_value(meta.data.clone()).ok()?;
253 Some(Box::new(devboy_clickup::ClickUpSchemaEnricher::new(
254 clickup_meta,
255 )))
256 }
257 ProviderConfig::Jira { .. } => {
258 let meta = metadata?;
259 let jira_meta: devboy_jira::JiraMetadata =
260 serde_json::from_value(meta.data.clone()).ok()?;
261 Some(Box::new(devboy_jira::JiraSchemaEnricher::new(jira_meta)))
262 }
263 ProviderConfig::Confluence { .. } => None,
264 ProviderConfig::Fireflies { .. } => {
265 Some(Box::new(devboy_fireflies::FirefliesSchemaEnricher))
266 }
267 ProviderConfig::Slack { .. } => None,
268 ProviderConfig::Custom { .. } => None,
269 }
270}
271
272fn confluence_auth(auth: &ConfluenceAuthConfig) -> devboy_confluence::ConfluenceAuth {
273 match auth {
274 ConfluenceAuthConfig::BearerToken { token } => {
275 devboy_confluence::ConfluenceAuth::BearerToken(token.clone())
276 }
277 ConfluenceAuthConfig::Basic { username, password } => {
278 devboy_confluence::ConfluenceAuth::Basic {
279 username: username.clone(),
280 password: password.clone(),
281 }
282 }
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289 use crate::context::*;
290 use devboy_core::{IssueProvider, KnowledgeBaseProvider};
291 use httpmock::Method::GET;
292 use httpmock::MockServer;
293 use std::collections::HashMap;
294
295 #[test]
296 fn test_create_gitlab_project_provider() {
297 let config = ProviderConfig::GitLab {
298 base_url: "https://gitlab.com".into(),
299 access_token: "test-token".into(),
300 scope: GitLabScope::Project { id: "12345".into() },
301 extra: HashMap::new(),
302 };
303 let provider = create_provider(&config, None);
304 assert!(provider.is_ok());
305 assert_eq!(
306 IssueProvider::provider_name(provider.unwrap().as_ref()),
307 "gitlab"
308 );
309 }
310
311 #[test]
312 fn test_create_github_repo_provider() {
313 let config = ProviderConfig::GitHub {
314 base_url: "https://api.github.com".into(),
315 access_token: "ghp_test".into(),
316 scope: GitHubScope::Repository {
317 owner: "meteora-pro".into(),
318 repo: "devboy-tools".into(),
319 },
320 extra: HashMap::new(),
321 };
322 let provider = create_provider(&config, None);
323 assert!(provider.is_ok());
324 assert_eq!(
325 IssueProvider::provider_name(provider.unwrap().as_ref()),
326 "github"
327 );
328 }
329
330 #[test]
331 fn test_create_clickup_provider() {
332 let config = ProviderConfig::ClickUp {
333 access_token: "pk_test".into(),
334 scope: ClickUpScope::List {
335 id: "list123".into(),
336 team_id: Some("team456".into()),
337 },
338 extra: HashMap::new(),
339 };
340 let provider = create_provider(&config, None);
341 assert!(provider.is_ok());
342 assert_eq!(
343 IssueProvider::provider_name(provider.unwrap().as_ref()),
344 "clickup"
345 );
346 }
347
348 #[test]
349 fn test_create_jira_provider() {
350 let config = ProviderConfig::Jira {
351 base_url: "https://myorg.atlassian.net".into(),
352 access_token: "jira-token".into(),
353 email: "user@example.com".into(),
354 scope: JiraScope::Project { key: "PROJ".into() },
355 flavor: None,
356 extra: HashMap::new(),
357 };
358 let provider = create_provider(&config, None);
359 assert!(provider.is_ok());
360 assert_eq!(
361 IssueProvider::provider_name(provider.unwrap().as_ref()),
362 "jira"
363 );
364 }
365
366 #[test]
367 fn test_create_confluence_knowledge_base_provider() {
368 let config = ProviderConfig::Confluence {
369 base_url: "https://wiki.example.com".into(),
370 auth: ConfluenceAuthConfig::BearerToken {
371 token: "test-token".into(),
372 },
373 scope: ConfluenceScope::Space {
374 key: Some("ENG".into()),
375 },
376 api_version: Some("v1".into()),
377 extra: HashMap::new(),
378 };
379 let provider = create_knowledge_base_provider(&config, None);
380 assert!(provider.is_ok());
381 assert_eq!(
382 KnowledgeBaseProvider::provider_name(provider.unwrap().as_ref()),
383 "confluence"
384 );
385 }
386
387 #[test]
388 fn test_create_confluence_knowledge_base_enricher() {
389 let config = ProviderConfig::Confluence {
390 base_url: "https://wiki.example.com".into(),
391 auth: ConfluenceAuthConfig::BearerToken {
392 token: "test-token".into(),
393 },
394 scope: ConfluenceScope::Space {
395 key: Some("ENG".into()),
396 },
397 api_version: Some("v1".into()),
398 extra: HashMap::new(),
399 };
400 let enricher = create_knowledge_base_enricher(&config);
401 assert!(enricher.is_some());
402 assert_eq!(
403 enricher.unwrap().supported_categories(),
404 &[devboy_core::ToolCategory::KnowledgeBase]
405 );
406 }
407
408 #[test]
409 fn test_confluence_is_not_regular_provider() {
410 let config = ProviderConfig::Confluence {
411 base_url: "https://wiki.example.com".into(),
412 auth: ConfluenceAuthConfig::BearerToken {
413 token: "test-token".into(),
414 },
415 scope: ConfluenceScope::Space { key: None },
416 api_version: None,
417 extra: HashMap::new(),
418 };
419
420 let result = create_provider(&config, None);
421 assert!(matches!(
422 result,
423 Err(Error::ProviderUnsupported { provider, .. }) if provider == "confluence"
424 ));
425 }
426
427 #[tokio::test]
428 async fn test_create_confluence_knowledge_base_provider_honors_api_version() {
429 let server = MockServer::start();
430 let mock = server.mock(|when, then| {
431 when.method(GET)
432 .path("/api/v2/space")
433 .query_param("limit", "100")
434 .query_param("type", "global,personal");
435 then.status(200)
436 .header("content-type", "application/json")
437 .body(r#"{"results":[],"start":0,"limit":100,"size":0,"_links":{}}"#);
438 });
439
440 let config = ProviderConfig::Confluence {
441 base_url: server.base_url(),
442 auth: ConfluenceAuthConfig::BearerToken {
443 token: "test-token".into(),
444 },
445 scope: ConfluenceScope::Space { key: None },
446 api_version: Some("v2".into()),
447 extra: HashMap::new(),
448 };
449
450 let provider = create_knowledge_base_provider(&config, None).unwrap();
451 let _ = provider.get_spaces().await.unwrap();
452
453 mock.assert();
454 }
455
456 #[test]
457 fn test_create_custom_provider_unsupported() {
458 let config = ProviderConfig::Custom {
459 name: "my-plugin".into(),
460 config: HashMap::new(),
461 };
462 let result = create_provider(&config, None);
463 assert!(result.is_err());
464 }
465
466 #[test]
467 fn test_gitlab_group_scope_unsupported() {
468 let config = ProviderConfig::GitLab {
469 base_url: "https://gitlab.com".into(),
470 access_token: "token".into(),
471 scope: GitLabScope::Group {
472 id: "group1".into(),
473 },
474 extra: HashMap::new(),
475 };
476 let result = create_provider(&config, None);
477 assert!(result.is_err());
478 }
479
480 #[test]
481 fn test_create_enricher_gitlab_static() {
482 let config = ProviderConfig::GitLab {
483 base_url: "https://gitlab.com".into(),
484 access_token: "token".into(),
485 scope: GitLabScope::Project { id: "123".into() },
486 extra: HashMap::new(),
487 };
488 let enricher = create_enricher(&config, None);
489 assert!(enricher.is_some());
490 }
491
492 #[test]
493 fn test_create_enricher_github_static() {
494 let config = ProviderConfig::GitHub {
495 base_url: "https://api.github.com".into(),
496 access_token: "token".into(),
497 scope: GitHubScope::Repository {
498 owner: "test".into(),
499 repo: "test".into(),
500 },
501 extra: HashMap::new(),
502 };
503 let enricher = create_enricher(&config, None);
504 assert!(enricher.is_some());
505 }
506
507 #[test]
508 fn test_create_enricher_clickup_needs_metadata() {
509 let config = ProviderConfig::ClickUp {
510 access_token: "token".into(),
511 scope: ClickUpScope::List {
512 id: "list1".into(),
513 team_id: None,
514 },
515 extra: HashMap::new(),
516 };
517 assert!(create_enricher(&config, None).is_none());
519
520 let meta = ProviderMetadata::new(serde_json::json!({
522 "statuses": [{ "name": "To Do" }],
523 "custom_fields": []
524 }));
525 assert!(create_enricher(&config, Some(&meta)).is_some());
526 }
527
528 #[test]
529 fn test_create_enricher_jira_needs_metadata() {
530 let config = ProviderConfig::Jira {
531 base_url: "https://test.atlassian.net".into(),
532 access_token: "token".into(),
533 email: "test@test.com".into(),
534 scope: JiraScope::Project { key: "PROJ".into() },
535 flavor: None,
536 extra: HashMap::new(),
537 };
538 assert!(create_enricher(&config, None).is_none());
540
541 let meta = ProviderMetadata::new(serde_json::json!({
543 "flavor": "cloud",
544 "projects": {
545 "PROJ": {
546 "issue_types": [],
547 "priorities": [],
548 "components": [],
549 "link_types": [],
550 "custom_fields": []
551 }
552 }
553 }));
554 assert!(create_enricher(&config, Some(&meta)).is_some());
555 }
556
557 #[test]
560 fn test_create_gitlab_provider_with_proxy() {
561 let config = ProviderConfig::GitLab {
562 base_url: "https://gitlab.internal.com".into(),
563 access_token: "test-token".into(),
564 scope: GitLabScope::Project { id: "99".into() },
565 extra: HashMap::new(),
566 };
567 let mut headers = HashMap::new();
568 headers.insert("X-Proxy-Auth".into(), "proxy-secret".into());
569 let proxy = ProxyConfig {
570 url: "https://proxy.example.com/gitlab".into(),
571 headers,
572 };
573 let provider = create_provider(&config, Some(&proxy));
574 assert!(provider.is_ok());
575 assert_eq!(
576 IssueProvider::provider_name(provider.unwrap().as_ref()),
577 "gitlab"
578 );
579 }
580
581 #[test]
582 fn test_create_jira_provider_with_proxy() {
583 let config = ProviderConfig::Jira {
584 base_url: "https://jira.mycompany.com".into(),
585 access_token: "jira-token".into(),
586 email: "dev@mycompany.com".into(),
587 scope: JiraScope::Project { key: "DEV".into() },
588 flavor: None,
589 extra: HashMap::new(),
590 };
591 let mut headers = HashMap::new();
592 headers.insert("X-Proxy-Auth".into(), "secret".into());
593 headers.insert("X-Route".into(), "jira-dc".into());
594 let proxy = ProxyConfig {
595 url: "https://proxy.internal/jira".into(),
596 headers,
597 };
598 let provider = create_provider(&config, Some(&proxy));
599 assert!(provider.is_ok());
600 assert_eq!(
601 IssueProvider::provider_name(provider.unwrap().as_ref()),
602 "jira"
603 );
604 }
605
606 #[test]
607 fn test_create_jira_provider_with_flavor_cloud() {
608 let config = ProviderConfig::Jira {
609 base_url: "https://myorg.atlassian.net".into(),
610 access_token: "tok".into(),
611 email: "a@b.com".into(),
612 scope: JiraScope::Project { key: "CLD".into() },
613 flavor: Some(devboy_jira::JiraFlavor::Cloud),
614 extra: HashMap::new(),
615 };
616 let provider = create_provider(&config, None);
617 assert!(provider.is_ok());
618 }
619
620 #[test]
621 fn test_create_jira_provider_with_flavor_self_hosted() {
622 let config = ProviderConfig::Jira {
623 base_url: "https://jira.local".into(),
624 access_token: "tok".into(),
625 email: "a@b.com".into(),
626 scope: JiraScope::Project { key: "SH".into() },
627 flavor: Some(devboy_jira::JiraFlavor::SelfHosted),
628 extra: HashMap::new(),
629 };
630 let provider = create_provider(&config, None);
631 assert!(provider.is_ok());
632 }
633
634 #[test]
635 fn test_create_jira_provider_with_proxy_and_flavor_override() {
636 let config = ProviderConfig::Jira {
637 base_url: "https://jira.dc.mycompany.com".into(),
638 access_token: "tok".into(),
639 email: "a@b.com".into(),
640 scope: JiraScope::Project { key: "DC".into() },
641 flavor: Some(devboy_jira::JiraFlavor::SelfHosted),
642 extra: HashMap::new(),
643 };
644 let proxy = ProxyConfig {
645 url: "https://proxy.internal/jira".into(),
646 headers: HashMap::new(),
647 };
648 let provider = create_provider(&config, Some(&proxy));
649 assert!(provider.is_ok());
650 }
651
652 #[test]
655 fn test_gitlab_global_scope_unsupported() {
656 let config = ProviderConfig::GitLab {
657 base_url: "https://gitlab.com".into(),
658 access_token: "tok".into(),
659 scope: GitLabScope::Global,
660 extra: HashMap::new(),
661 };
662 let result = create_provider(&config, None);
663 match result {
664 Err(e) => assert!(e.to_string().contains("global scope")),
665 Ok(_) => panic!("expected error for global scope"),
666 }
667 }
668
669 #[test]
670 fn test_github_organization_scope_unsupported() {
671 let config = ProviderConfig::GitHub {
672 base_url: "https://api.github.com".into(),
673 access_token: "tok".into(),
674 scope: GitHubScope::Organization {
675 name: "myorg".into(),
676 },
677 extra: HashMap::new(),
678 };
679 let result = create_provider(&config, None);
680 match result {
681 Err(e) => assert!(e.to_string().contains("organization scope")),
682 Ok(_) => panic!("expected error for organization scope"),
683 }
684 }
685
686 #[test]
687 fn test_github_global_scope_unsupported() {
688 let config = ProviderConfig::GitHub {
689 base_url: "https://api.github.com".into(),
690 access_token: "tok".into(),
691 scope: GitHubScope::Global,
692 extra: HashMap::new(),
693 };
694 let result = create_provider(&config, None);
695 assert!(result.is_err());
696 }
697
698 #[test]
699 fn test_jira_multi_project_scope_unsupported() {
700 let config = ProviderConfig::Jira {
701 base_url: "https://test.atlassian.net".into(),
702 access_token: "tok".into(),
703 email: "a@b.com".into(),
704 scope: JiraScope::MultiProject {
705 keys: vec!["A".into(), "B".into()],
706 },
707 flavor: None,
708 extra: HashMap::new(),
709 };
710 let result = create_provider(&config, None);
711 match result {
712 Err(e) => assert!(e.to_string().contains("multi-project")),
713 Ok(_) => panic!("expected error for multi-project scope"),
714 }
715 }
716
717 #[test]
720 fn test_create_clickup_provider_without_team_id() {
721 let config = ProviderConfig::ClickUp {
722 access_token: "pk_test".into(),
723 scope: ClickUpScope::List {
724 id: "list999".into(),
725 team_id: None,
726 },
727 extra: HashMap::new(),
728 };
729 let provider = create_provider(&config, None);
730 assert!(provider.is_ok());
731 assert_eq!(
732 IssueProvider::provider_name(provider.unwrap().as_ref()),
733 "clickup"
734 );
735 }
736
737 #[test]
740 fn test_create_enricher_custom_returns_none() {
741 let config = ProviderConfig::Custom {
742 name: "custom-plugin".into(),
743 config: HashMap::new(),
744 };
745 assert!(create_enricher(&config, None).is_none());
746 }
747
748 #[test]
749 fn test_create_knowledge_base_enricher_non_kb_returns_none() {
750 let config = ProviderConfig::GitHub {
751 base_url: "https://api.github.com".into(),
752 access_token: "tok".into(),
753 scope: GitHubScope::Repository {
754 owner: "test".into(),
755 repo: "test".into(),
756 },
757 extra: HashMap::new(),
758 };
759 assert!(create_knowledge_base_enricher(&config).is_none());
760 }
761
762 #[test]
765 fn test_create_enricher_clickup_invalid_metadata_returns_none() {
766 let config = ProviderConfig::ClickUp {
767 access_token: "token".into(),
768 scope: ClickUpScope::List {
769 id: "list1".into(),
770 team_id: None,
771 },
772 extra: HashMap::new(),
773 };
774 let meta = ProviderMetadata::new(serde_json::json!("invalid_data"));
775 assert!(create_enricher(&config, Some(&meta)).is_none());
776 }
777
778 #[test]
779 fn test_create_enricher_jira_invalid_metadata_returns_none() {
780 let config = ProviderConfig::Jira {
781 base_url: "https://test.atlassian.net".into(),
782 access_token: "token".into(),
783 email: "a@b.com".into(),
784 scope: JiraScope::Project { key: "X".into() },
785 flavor: None,
786 extra: HashMap::new(),
787 };
788 let meta = ProviderMetadata::new(serde_json::json!("not_valid"));
789 assert!(create_enricher(&config, Some(&meta)).is_none());
790 }
791}