1use crate::{
2 auth_n::oidc::utilities::set_user_password, fhir_client::ServerCTX, services::ServerState,
3};
4use haste_fhir_client::FHIRClient;
5use haste_fhir_model::r4::generated::{
6 resources::{Project, Resource, ResourceType, User},
7 terminology::{IssueType, SupportedFhirVersion},
8 types::FHIRString,
9};
10use haste_fhir_operation_error::OperationOutcomeError;
11use haste_fhir_search::SearchEngine;
12use haste_fhir_terminology::FHIRTerminology;
13use haste_jwt::{ProjectId, TenantId, claims::SubscriptionTier};
14use haste_repository::{
15 Repository,
16 admin::TenantModelAdmin,
17 types::{
18 tenant::{CreateTenant, Tenant},
19 user::CreateUser,
20 },
21 utilities::generate_id,
22};
23use std::sync::Arc;
24
25pub async fn create_user<
26 Repo: Repository + Send + Sync + 'static,
27 Search: SearchEngine + Send + Sync + 'static,
28 Terminology: FHIRTerminology + Send + Sync + 'static,
29>(
30 services: &ServerState<Repo, Search, Terminology>,
31 tenant: &TenantId,
32 user_resource: User,
33 password: Option<&str>,
34) -> Result<User, OperationOutcomeError> {
35 let ctx = Arc::new(ServerCTX::system(
36 tenant.clone(),
37 ProjectId::System,
38 services.fhir_client.clone(),
39 services.rate_limit.clone(),
40 ));
41
42 let user = services
43 .fhir_client
44 .create(ctx, ResourceType::User, Resource::User(user_resource))
45 .await?;
46
47 let user = match user {
48 Resource::User(user) => user,
49 _ => panic!("Created resource is not a User"),
50 };
51
52 let user_id = user.id.clone().unwrap();
53
54 if let Some(password) = password {
55 set_user_password(
56 &*services.repo,
57 tenant,
58 &user
59 .email
60 .as_ref()
61 .and_then(|e| e.value.as_ref())
62 .map(|s| s.to_string())
63 .unwrap_or_default(),
64 &user_id,
65 password,
66 )
67 .await?;
68 }
69
70 Ok(user)
71}
72
73pub struct CreateTenantOutput {
74 pub tenant: Tenant,
75 pub owner: haste_repository::types::user::User,
76}
77
78pub async fn create_tenant<
79 Repo: Repository + Send + Sync + 'static,
80 Search: SearchEngine + Send + Sync + 'static,
81 Terminology: FHIRTerminology + Send + Sync + 'static,
82>(
83 services: &ServerState<Repo, Search, Terminology>,
84 tenant_id: Option<String>,
85 _name: &str,
86 subscription_tier: &SubscriptionTier,
87 owner: haste_fhir_model::r4::generated::resources::User,
88 owner_password: Option<&str>,
89) -> Result<CreateTenantOutput, OperationOutcomeError> {
90 let services = services.transaction().await?;
91
92 let new_tenant = TenantModelAdmin::create(
93 &*services.repo,
94 &TenantId::System,
95 CreateTenant {
96 id: Some(TenantId::new(tenant_id.unwrap_or(generate_id(Some(16))))),
97 subscription_tier: Some(subscription_tier.clone().into()),
98 },
99 )
100 .await?;
101
102 services
103 .fhir_client
104 .create(
105 Arc::new(ServerCTX::system(
106 new_tenant.id.clone(),
107 ProjectId::System,
108 services.fhir_client.clone(),
109 services.rate_limit.clone(),
110 )),
111 ResourceType::Project,
112 Resource::Project(Project {
113 id: Some(ProjectId::System.to_string()),
114 name: Box::new(FHIRString {
115 value: Some(ProjectId::System.to_string()),
116 ..Default::default()
117 }),
118 fhirVersion: SupportedFhirVersion::r4(),
119 ..Default::default()
120 }),
121 )
122 .await?;
123
124 let user = create_user(&services, &new_tenant.id, owner, owner_password).await?;
125
126 let Some(user_id) = user.id else {
127 return Err(OperationOutcomeError::fatal(
128 IssueType::invalid(),
129 "The user ID is required to complete the tenant creation process.".to_string(),
130 ));
131 };
132
133 let Some(user) = TenantModelAdmin::<CreateUser, _, _, _, _>::read(
134 services.repo.as_ref(),
135 &new_tenant.id,
136 &user_id,
137 )
138 .await?
139 else {
140 return Err(OperationOutcomeError::fatal(
141 IssueType::invalid(),
142 "The user does not exist after creation.".to_string(),
143 ));
144 };
145
146 services.commit().await?;
147
148 Ok(CreateTenantOutput {
149 tenant: new_tenant,
150 owner: user,
151 })
152}