Skip to content

Commit 3d17412

Browse files
committed
feat: improvements to response payloads
1 parent da43e3e commit 3d17412

File tree

18 files changed

+273
-65
lines changed

18 files changed

+273
-65
lines changed

crates/inferadb-control-api/src/handlers/clients.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ fn client_to_detail(client: Client) -> ClientDetail {
3232
ClientDetail {
3333
id: client.id,
3434
name: client.name,
35+
description: client.description,
36+
vault_id: client.vault_id,
3537
is_active: client.deleted_at.is_none(),
3638
organization_id: client.organization_id,
3739
created_at: client.created_at.to_rfc3339(),
@@ -77,8 +79,14 @@ pub async fn create_client(
7779
let client_id = IdGenerator::next_id();
7880

7981
// Create client entity
80-
let client =
81-
Client::new(client_id, org_ctx.organization_id, payload.name, org_ctx.member.user_id)?;
82+
let client = Client::new(
83+
client_id,
84+
org_ctx.organization_id,
85+
payload.vault_id,
86+
payload.name,
87+
payload.description,
88+
org_ctx.member.user_id,
89+
)?;
8290

8391
// Save to repository
8492
repos.client.create(client.clone()).await?;
@@ -89,7 +97,8 @@ pub async fn create_client(
8997
client: ClientInfo {
9098
id: client.id,
9199
name: client.name.clone(),
92-
description: payload.description.unwrap_or_default(),
100+
description: client.description,
101+
vault_id: client.vault_id,
93102
is_active: client.deleted_at.is_none(),
94103
organization_id: client.organization_id,
95104
created_at: client.created_at.to_rfc3339(),
@@ -186,14 +195,23 @@ pub async fn update_client(
186195
return Err(CoreError::NotFound("Client not found".to_string()).into());
187196
}
188197

189-
// Validate and update name
190-
Client::validate_name(&payload.name)?;
191-
client.name = payload.name.clone();
198+
// Update fields if provided
199+
if let Some(name) = payload.name {
200+
client.set_name(name)?;
201+
}
202+
if let Some(description) = payload.description {
203+
client.set_description(description);
204+
}
205+
if let Some(vault_id) = payload.vault_id {
206+
client.set_vault_id(Some(vault_id));
207+
}
192208

193209
// Save changes
194210
repos.client.update(client.clone()).await?;
195211

196-
Ok(Json(UpdateClientResponse { id: client.id, name: client.name }))
212+
Ok(Json(UpdateClientResponse {
213+
client: client_to_detail(client),
214+
}))
197215
}
198216

199217
/// Delete a client (soft delete)

crates/inferadb-control-api/src/handlers/teams.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,23 @@ fn team_to_response(team: OrganizationTeam) -> TeamResponse {
3030
TeamResponse {
3131
id: team.id,
3232
name: team.name,
33+
description: team.description,
3334
organization_id: team.organization_id,
3435
created_at: team.created_at.to_rfc3339(),
3536
deleted_at: team.deleted_at.map(|dt| dt.to_rfc3339()),
3637
}
3738
}
3839

40+
fn team_to_info(team: OrganizationTeam) -> TeamInfo {
41+
TeamInfo {
42+
id: team.id,
43+
name: team.name,
44+
description: team.description,
45+
organization_id: team.organization_id,
46+
created_at: team.created_at.to_rfc3339(),
47+
}
48+
}
49+
3950
fn team_member_to_response(member: OrganizationTeamMember) -> TeamMemberResponse {
4051
TeamMemberResponse {
4152
id: member.id,
@@ -96,7 +107,7 @@ pub async fn create_team(
96107

97108
// Generate ID and create team
98109
let team_id = IdGenerator::next_id();
99-
let team = OrganizationTeam::new(team_id, org_ctx.organization_id, payload.name)?;
110+
let team = OrganizationTeam::new(team_id, org_ctx.organization_id, payload.name, payload.description)?;
100111

101112
// Store team
102113
repos.org_team.create(team.clone()).await?;
@@ -106,8 +117,8 @@ pub async fn create_team(
106117
Json(CreateTeamResponse {
107118
team: TeamInfo {
108119
id: team.id,
109-
name: team.name,
110-
description: payload.description.unwrap_or_default(),
120+
name: team.name.clone(),
121+
description: team.description,
111122
organization_id: team.organization_id,
112123
created_at: team.created_at.to_rfc3339(),
113124
},
@@ -232,11 +243,16 @@ pub async fn update_team(
232243
.into());
233244
}
234245

235-
// Update team name
236-
team.set_name(payload.name)?;
246+
// Update team fields
247+
if let Some(name) = payload.name {
248+
team.set_name(name)?;
249+
}
250+
if let Some(description) = payload.description {
251+
team.set_description(description);
252+
}
237253
repos.org_team.update(team.clone()).await?;
238254

239-
Ok(Json(UpdateTeamResponse { id: team.id, name: team.name }))
255+
Ok(Json(UpdateTeamResponse { team: team_to_info(team) }))
240256
}
241257

242258
/// Delete a team

crates/inferadb-control-api/src/handlers/vaults.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use inferadb_control_types::{
1111
ListTeamGrantsResponse, ListUserGrantsResponse, ListVaultsResponse, TeamGrantInfo,
1212
TeamGrantResponse, UpdateTeamGrantRequest, UpdateTeamGrantResponse, UpdateUserGrantRequest,
1313
UpdateUserGrantResponse, UpdateVaultRequest, UpdateVaultResponse, UserGrantInfo,
14-
UserGrantResponse, VaultDetail, VaultInfo, VaultResponse,
14+
UserGrantResponse, VaultInfo, VaultResponse,
1515
},
1616
entities::{Vault, VaultRole, VaultTeamGrant, VaultUserGrant},
1717
};
@@ -32,8 +32,9 @@ fn vault_to_response(vault: Vault) -> VaultResponse {
3232
VaultResponse {
3333
id: vault.id,
3434
name: vault.name,
35+
description: vault.description,
3536
organization_id: vault.organization_id,
36-
sync_status: format!("{:?}", vault.sync_status),
37+
sync_status: vault.sync_status,
3738
sync_error: vault.sync_error,
3839
created_at: vault.created_at.to_rfc3339(),
3940
updated_at: vault.updated_at.to_rfc3339(),
@@ -104,7 +105,7 @@ pub async fn create_vault(
104105

105106
// Create vault entity (starts with PENDING sync status)
106107
let mut vault =
107-
Vault::new(vault_id, org_ctx.organization_id, payload.name, org_ctx.member.user_id)?;
108+
Vault::new(vault_id, org_ctx.organization_id, payload.name, payload.description.clone(), org_ctx.member.user_id)?;
108109

109110
// Save to repository
110111
repos.vault.create(vault.clone()).await?;
@@ -141,7 +142,7 @@ pub async fn create_vault(
141142
vault: VaultInfo {
142143
id: vault.id,
143144
name: vault.name,
144-
description: payload.description.unwrap_or_default(),
145+
description: vault.description,
145146
organization_id: vault.organization_id,
146147
sync_status: vault.sync_status,
147148
created_at: vault.created_at.to_rfc3339(),
@@ -300,6 +301,11 @@ pub async fn update_vault(
300301
Vault::validate_name(&payload.name)?;
301302
vault.name = payload.name.clone();
302303

304+
// Update description if provided
305+
if let Some(desc) = payload.description.clone() {
306+
vault.description = desc;
307+
}
308+
303309
// Save changes
304310
repos.vault.update(vault.clone()).await?;
305311

@@ -309,10 +315,13 @@ pub async fn update_vault(
309315
}
310316

311317
Ok(Json(UpdateVaultResponse {
312-
vault: VaultDetail {
318+
vault: VaultInfo {
313319
id: vault.id,
314320
name: vault.name,
315-
description: payload.description.unwrap_or_default(),
321+
description: vault.description,
322+
organization_id: vault.organization_id,
323+
sync_status: vault.sync_status,
324+
created_at: vault.created_at.to_rfc3339(),
316325
},
317326
}))
318327
}

crates/inferadb-control-api/src/middleware/permission.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ mod tests {
245245

246246
// Create a team with a permission
247247
let team_repo = OrganizationTeamRepository::new(memory.clone());
248-
let team = OrganizationTeam::new(1, 1, "Test Team".to_string()).unwrap();
248+
let team = OrganizationTeam::new(1, 1, "Test Team".to_string(), None).unwrap();
249249
team_repo.create(team).await.unwrap();
250250

251251
// Add user to team
@@ -296,7 +296,7 @@ mod tests {
296296

297297
// Create a team with CLIENT_MANAGE permission
298298
let team_repo = OrganizationTeamRepository::new(memory.clone());
299-
let team = OrganizationTeam::new(1, 1, "Test Team".to_string()).unwrap();
299+
let team = OrganizationTeam::new(1, 1, "Test Team".to_string(), None).unwrap();
300300
team_repo.create(team).await.unwrap();
301301

302302
let member_repo = OrganizationTeamMemberRepository::new(memory.clone());

crates/inferadb-control-api/tests/security_authorization_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ async fn test_member_cannot_delete_vault() {
513513

514514
// Create a vault
515515
let vault_repo = VaultRepository::new((*state.storage).clone());
516-
let vault = Vault::new(5000, org.id, "Test Vault".to_string(), 100).unwrap();
516+
let vault = Vault::new(5000, org.id, "Test Vault".to_string(), None, 100).unwrap();
517517
vault_repo.create(vault.clone()).await.unwrap();
518518

519519
// Setup member

crates/inferadb-control-api/tests/security_isolation_tests.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ async fn test_cross_organization_vault_access_denied() {
6363

6464
// Create a vault in Organization B
6565
let vault_repo = VaultRepository::new((*state.storage).clone());
66-
let vault_b = Vault::new(5000, org_b.id, "Vault B".to_string(), 200).unwrap();
66+
let vault_b = Vault::new(5000, org_b.id, "Vault B".to_string(), None, 200).unwrap();
6767
vault_repo.create(vault_b.clone()).await.unwrap();
6868

6969
// User A tries to access Organization B's vault
@@ -100,7 +100,7 @@ async fn test_cross_organization_client_access_denied() {
100100

101101
// Create a client in Organization B
102102
let client_repo = ClientRepository::new((*state.storage).clone());
103-
let client_b = Client::new(6000, org_b.id, "Client B".to_string(), 200).unwrap();
103+
let client_b = Client::new(6000, org_b.id, None, "Client B".to_string(), None, 200).unwrap();
104104
client_repo.create(client_b.clone()).await.unwrap();
105105

106106
// User A tries to access Organization B's client
@@ -135,7 +135,7 @@ async fn test_cross_organization_team_access_denied() {
135135

136136
// Create a team in Organization B
137137
let team_repo = OrganizationTeamRepository::new((*state.storage).clone());
138-
let team_b = OrganizationTeam::new(7000, org_b.id, "Team B".to_string()).unwrap();
138+
let team_b = OrganizationTeam::new(7000, org_b.id, "Team B".to_string(), None).unwrap();
139139
team_repo.create(team_b.clone()).await.unwrap();
140140

141141
// User A tries to access Organization B's team
@@ -170,7 +170,7 @@ async fn test_cannot_modify_other_organization_resources() {
170170

171171
// Create a vault in Organization B
172172
let vault_repo = VaultRepository::new((*state.storage).clone());
173-
let vault_b = Vault::new(5000, org_b.id, "Vault B".to_string(), 200).unwrap();
173+
let vault_b = Vault::new(5000, org_b.id, "Vault B".to_string(), None, 200).unwrap();
174174
vault_repo.create(vault_b.clone()).await.unwrap();
175175

176176
// User A tries to update Organization B's vault
@@ -211,7 +211,7 @@ async fn test_cannot_delete_other_organization_resources() {
211211

212212
// Create a client in Organization B
213213
let client_repo = ClientRepository::new((*state.storage).clone());
214-
let client_b = Client::new(6000, org_b.id, "Client B".to_string(), 200).unwrap();
214+
let client_b = Client::new(6000, org_b.id, None, "Client B".to_string(), None, 200).unwrap();
215215
client_repo.create(client_b.clone()).await.unwrap();
216216

217217
// User A tries to delete Organization B's client
@@ -277,18 +277,18 @@ async fn test_vault_jwt_isolation() {
277277
let (_, _session_a, org_a, _) = setup_user_and_org(&state, 100, 1, 1000, 10000, "userA").await;
278278

279279
let vault_repo = VaultRepository::new((*state.storage).clone());
280-
let vault_a = Vault::new(5000, org_a.id, "Vault A".to_string(), 100).unwrap();
280+
let vault_a = Vault::new(5000, org_a.id, "Vault A".to_string(), None, 100).unwrap();
281281
vault_repo.create(vault_a.clone()).await.unwrap();
282282

283283
// Setup Organization B with vault
284284
let (_, _session_b, org_b, _) = setup_user_and_org(&state, 200, 2, 2000, 20000, "userB").await;
285285

286-
let vault_b = Vault::new(6000, org_b.id, "Vault B".to_string(), 200).unwrap();
286+
let vault_b = Vault::new(6000, org_b.id, "Vault B".to_string(), None, 200).unwrap();
287287
vault_repo.create(vault_b.clone()).await.unwrap();
288288

289289
// Create a client in Organization A
290290
let client_repo = ClientRepository::new((*state.storage).clone());
291-
let client_a = Client::new(7000, org_a.id, "Client A".to_string(), 100).unwrap();
291+
let client_a = Client::new(7000, org_a.id, None, "Client A".to_string(), None, 100).unwrap();
292292
client_repo.create(client_a.clone()).await.unwrap();
293293

294294
// Create a certificate for the client

crates/inferadb-control-config/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,13 @@ pub struct EmailConfig {
192192
/// From display name
193193
#[serde(default = "default_email_name")]
194194
pub name: String,
195+
196+
/// Allow insecure (unencrypted) SMTP connections.
197+
///
198+
/// **WARNING**: Only enable this for local development/testing with tools like Mailpit.
199+
/// Never enable in production as it transmits credentials in plain text.
200+
#[serde(default)]
201+
pub insecure: bool,
195202
}
196203

197204
/// Rate limits configuration
@@ -374,6 +381,7 @@ impl Default for ControlConfig {
374381
password: None,
375382
address: "noreply@inferadb.com".to_string(),
376383
name: default_email_name(),
384+
insecure: false,
377385
},
378386
limits: LimitsConfig {
379387
login_attempts_per_ip_per_hour: default_login_attempts_per_ip_per_hour(),

crates/inferadb-control-core/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub use crypto::{MasterKey, PrivateKeyEncryptor, keypair};
2828
pub use email::{
2929
EmailSender, EmailService, EmailTemplate, InvitationAcceptedEmailTemplate,
3030
InvitationEmailTemplate, MockEmailSender, OrganizationDeletionWarningEmailTemplate,
31-
PasswordResetEmailTemplate, RoleChangeEmailTemplate, SmtpEmailService,
31+
PasswordResetEmailTemplate, RoleChangeEmailTemplate, SmtpConfig, SmtpEmailService,
3232
VerificationEmailTemplate,
3333
};
3434
pub use id::{IdGenerator, WorkerRegistry, acquire_worker_id};

crates/inferadb-control-core/src/repository/client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ mod tests {
245245
}
246246

247247
fn create_test_client(id: i64, org_id: i64, name: &str) -> Result<Client> {
248-
Client::new(id, org_id, name.to_string(), 999)
248+
Client::new(id, org_id, None, name.to_string(), None, 999)
249249
}
250250

251251
#[tokio::test]

crates/inferadb-control-core/src/repository/team.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ mod tests {
690690
let storage = MemoryBackend::new();
691691
let repo = OrganizationTeamRepository::new(storage);
692692

693-
let team = OrganizationTeam::new(1, 100, "Engineering".to_string()).unwrap();
693+
let team = OrganizationTeam::new(1, 100, "Engineering".to_string(), None).unwrap();
694694
repo.create(team.clone()).await.unwrap();
695695

696696
let retrieved = repo.get(1).await.unwrap().unwrap();
@@ -704,10 +704,10 @@ mod tests {
704704
let storage = MemoryBackend::new();
705705
let repo = OrganizationTeamRepository::new(storage);
706706

707-
let team1 = OrganizationTeam::new(1, 100, "Engineering".to_string()).unwrap();
707+
let team1 = OrganizationTeam::new(1, 100, "Engineering".to_string(), None).unwrap();
708708
repo.create(team1).await.unwrap();
709709

710-
let team2 = OrganizationTeam::new(2, 100, "Engineering".to_string()).unwrap();
710+
let team2 = OrganizationTeam::new(2, 100, "Engineering".to_string(), None).unwrap();
711711
let result = repo.create(team2).await;
712712
assert!(result.is_err());
713713
}
@@ -717,9 +717,9 @@ mod tests {
717717
let storage = MemoryBackend::new();
718718
let repo = OrganizationTeamRepository::new(storage);
719719

720-
let team1 = OrganizationTeam::new(1, 100, "Engineering".to_string()).unwrap();
721-
let team2 = OrganizationTeam::new(2, 100, "Sales".to_string()).unwrap();
722-
let team3 = OrganizationTeam::new(3, 200, "Other".to_string()).unwrap();
720+
let team1 = OrganizationTeam::new(1, 100, "Engineering".to_string(), None).unwrap();
721+
let team2 = OrganizationTeam::new(2, 100, "Sales".to_string(), None).unwrap();
722+
let team3 = OrganizationTeam::new(3, 200, "Other".to_string(), None).unwrap();
723723

724724
repo.create(team1).await.unwrap();
725725
repo.create(team2).await.unwrap();
@@ -734,7 +734,7 @@ mod tests {
734734
let storage = MemoryBackend::new();
735735
let repo = OrganizationTeamRepository::new(storage);
736736

737-
let mut team = OrganizationTeam::new(1, 100, "Old Name".to_string()).unwrap();
737+
let mut team = OrganizationTeam::new(1, 100, "Old Name".to_string(), None).unwrap();
738738
repo.create(team.clone()).await.unwrap();
739739

740740
team.set_name("New Name".to_string()).unwrap();
@@ -749,7 +749,7 @@ mod tests {
749749
let storage = MemoryBackend::new();
750750
let repo = OrganizationTeamRepository::new(storage);
751751

752-
let team = OrganizationTeam::new(1, 100, "Engineering".to_string()).unwrap();
752+
let team = OrganizationTeam::new(1, 100, "Engineering".to_string(), None).unwrap();
753753
repo.create(team).await.unwrap();
754754

755755
repo.delete(1).await.unwrap();

0 commit comments

Comments
 (0)