1use serde::{Deserialize, Serialize};
10
11pub use roder_api::hosted_hooks::{
12 HookDelivery, HookDeliveryStatus, HookId, HookRetryPolicy, HookScope, HostedHookDefinition,
13};
14pub use roder_api::identity::{
15 HostedRequestContext, HostedRole, HostedScope, PrincipalContext, ServiceAccountId,
16 TenantContext, TenantId,
17};
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21#[serde(rename_all = "camelCase")]
22pub struct HostedWhoamiResult {
23 pub context: HostedRequestContext,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(rename_all = "camelCase")]
28pub struct HostedTenantsListResult {
29 pub tenants: Vec<TenantContext>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(rename_all = "camelCase")]
35pub struct HostedTenantReadParams {
36 #[serde(default, skip_serializing_if = "Option::is_none")]
39 pub tenant_id: Option<TenantId>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43#[serde(rename_all = "camelCase")]
44pub struct HostedTenantReadResult {
45 pub tenant: TenantContext,
46 pub member_count: u64,
47 pub service_account_count: u64,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct HostedServiceAccountSummary {
53 pub service_account_id: ServiceAccountId,
54 pub display_name: String,
55 pub scopes: Vec<HostedScope>,
56 pub credential_id: String,
58 pub revoked: bool,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(rename_all = "camelCase")]
63pub struct HostedServiceAccountsListResult {
64 pub service_accounts: Vec<HostedServiceAccountSummary>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68#[serde(rename_all = "camelCase")]
69pub struct HostedServiceAccountCreateParams {
70 pub display_name: String,
71 pub scopes: Vec<HostedScope>,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
77#[serde(rename_all = "camelCase")]
78pub struct HostedServiceAccountCreateResult {
79 pub service_account: HostedServiceAccountSummary,
80 pub api_key_once: String,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
84#[serde(rename_all = "camelCase")]
85pub struct HostedServiceAccountRevokeParams {
86 pub service_account_id: ServiceAccountId,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
90#[serde(rename_all = "camelCase")]
91pub struct HostedServiceAccountRevokeResult {
92 pub service_account_id: ServiceAccountId,
93 pub revoked: bool,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
97#[serde(rename_all = "camelCase")]
98pub struct HostedHooksListResult {
99 pub hooks: Vec<HostedHookDefinition>,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
103#[serde(rename_all = "camelCase")]
104pub struct HostedHookCreateParams {
105 pub scope: HookScope,
106 pub event_kinds: Vec<String>,
107 pub url: String,
108 #[serde(default, skip_serializing_if = "Option::is_none")]
109 pub signing_secret_ref: Option<String>,
110 #[serde(default)]
111 pub retry: Option<HookRetryPolicy>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
115#[serde(rename_all = "camelCase")]
116pub struct HostedHookUpdateParams {
117 pub hook_id: HookId,
118 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub enabled: Option<bool>,
120 #[serde(default, skip_serializing_if = "Option::is_none")]
121 pub event_kinds: Option<Vec<String>>,
122 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub url: Option<String>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
127#[serde(rename_all = "camelCase")]
128pub struct HostedHookDeleteParams {
129 pub hook_id: HookId,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
133#[serde(rename_all = "camelCase")]
134pub struct HostedHookResult {
135 pub hook: HostedHookDefinition,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
139#[serde(rename_all = "camelCase")]
140pub struct HostedAuditListParams {
141 #[serde(default, skip_serializing_if = "Option::is_none")]
142 pub since_ms: Option<i64>,
143 #[serde(default, skip_serializing_if = "Option::is_none")]
144 pub limit: Option<u64>,
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
150#[serde(rename_all = "camelCase")]
151pub struct HostedAuditRecord {
152 pub action: String,
153 pub outcome: String,
154 pub tenant_id: TenantId,
155 pub principal: PrincipalContext,
156 pub timestamp_ms: i64,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
160#[serde(rename_all = "camelCase")]
161pub struct HostedAuditListResult {
162 pub records: Vec<HostedAuditRecord>,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
166#[serde(rename_all = "camelCase")]
167pub struct HostedUsageReadParams {
168 #[serde(default, skip_serializing_if = "Option::is_none")]
169 pub since_ms: Option<i64>,
170 #[serde(default, skip_serializing_if = "Option::is_none")]
171 pub until_ms: Option<i64>,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
175#[serde(rename_all = "camelCase")]
176pub struct HostedUsageReadResult {
177 pub tenant_id: TenantId,
178 pub turn_count: u64,
179 pub tool_call_count: u64,
180 pub total_tokens: u64,
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186 use time::OffsetDateTime;
187
188 #[test]
189 fn hosted_dtos_use_camel_case_and_round_trip() {
190 let whoami = HostedWhoamiResult {
191 context: HostedRequestContext {
192 tenant: TenantContext {
193 tenant_id: "tenant-a".to_string(),
194 display_name: None,
195 },
196 principal: PrincipalContext::User {
197 user_id: "user-1".to_string(),
198 display_name: Some("Dev".to_string()),
199 },
200 role: HostedRole::TenantAdmin,
201 scopes: vec![HostedScope::Admin],
202 credential_id: Some("key-9".to_string()),
203 authenticated_at: OffsetDateTime::UNIX_EPOCH,
204 },
205 };
206 let json = serde_json::to_value(&whoami).unwrap();
207 assert_eq!(json["context"]["tenant"]["tenantId"], "tenant-a");
208 assert_eq!(json["context"]["role"], "tenant_admin");
209
210 let params: HostedTenantReadParams = serde_json::from_value(serde_json::json!({})).unwrap();
213 assert!(params.tenant_id.is_none());
214
215 let create: HostedHookCreateParams = serde_json::from_value(serde_json::json!({
216 "scope": "tenant",
217 "eventKinds": ["turn."],
218 "url": "https://hooks.example.test/x"
219 }))
220 .unwrap();
221 assert_eq!(create.scope, HookScope::Tenant);
222 assert!(create.retry.is_none());
223 }
224}