1use crate::account::*;
4use crate::accounting::Address;
5use crate::device::device::DeviceStruct;
6use crate::rest::common::{
7 ApiError, ListRequest,
8};
9use crate::RecordReference;
10use serde::{Deserialize, Serialize};
11
12#[cfg(feature = "openapi")]
13use utoipa::{IntoParams, ToSchema};
14
15#[derive(Debug, Deserialize)]
17#[cfg_attr(feature = "openapi", derive(ToSchema, IntoParams))]
18#[cfg_attr(feature = "openapi", schema(
19 title ="Query parameters containing account ID"
20))]
21pub struct AccountIdQuery {
22 #[cfg_attr(feature = "openapi", schema(example = "507f1f77bcf86cd799439011"))]
24 pub account_id: String,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29#[cfg_attr(feature = "openapi", derive(ToSchema))]
30#[cfg_attr(feature = "openapi", schema(
31 title ="Request payload for creating a new account",
32 example = json!({
33 "name": "Acme Corporation",
34 "mbn": "12345",
35 "domain": "acme.com",
36 "organisation": {
37 "id": "507f1f77bcf86cd799439011",
38 "name": "Acme Org"
39 },
40 "clusterSettings": {
41 "region": "us-east-1",
42 "zone": "a"
43 }
44 })
45))]
46#[serde(rename_all = "camelCase")]
47pub struct CreateAccountRequest {
48 #[cfg_attr(feature = "openapi", schema(example = "Acme Corporation", min_length = 1))]
50 pub name: String,
51
52 #[cfg_attr(feature = "openapi", schema(example = "12345", min_length = 1))]
54 pub mbn: String,
55
56 #[cfg_attr(feature = "openapi", schema(example = "acme.com", min_length = 1))]
58 pub domain: String,
59
60 pub organisation: RecordReference,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub address: Option<Address>,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub teams: Option<TeamsAccount>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub environment: Option<Environment>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub spend_cap: Option<SpendCap>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub concurrency: Option<Concurrency>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub class_of_service: Option<ClassOfService>,
86
87 pub cluster_settings: Cluster,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93#[cfg_attr(feature = "openapi", derive(ToSchema))]
94#[cfg_attr(feature = "openapi", schema(
95 title ="Request payload for updating an account. All fields are optional."
96))]
97#[serde(rename_all = "camelCase")]
98pub struct UpdateAccountRequest {
99 #[serde(skip_serializing_if = "Option::is_none")]
101 #[cfg_attr(feature = "openapi", schema(example = "Acme Corp Updated"))]
102 pub name: Option<String>,
103
104 #[serde(skip_serializing_if = "Option::is_none")]
106 #[cfg_attr(feature = "openapi", schema(example = "54321"))]
107 pub mbn: Option<String>,
108
109 #[serde(skip_serializing_if = "Option::is_none")]
111 #[cfg_attr(feature = "openapi", schema(example = "new-acme.com"))]
112 pub domain: Option<String>,
113
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub organisation: Option<RecordReference>,
117
118 #[serde(skip_serializing_if = "Option::is_none")]
120 pub address: Option<Address>,
121
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub teams: Option<TeamsAccount>,
125
126 #[serde(skip_serializing_if = "Option::is_none")]
128 pub environment: Option<Environment>,
129
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub spend_cap: Option<SpendCap>,
133
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub concurrency: Option<Concurrency>,
137
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub class_of_service: Option<ClassOfService>,
141
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub cluster_settings: Option<Cluster>,
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
149#[cfg_attr(feature = "openapi", derive(ToSchema))]
150#[cfg_attr(feature = "openapi", schema(
151 title ="Query parameters for listing accounts with filtering and pagination"
152))]
153#[serde(rename_all = "camelCase")]
154pub struct AccountListRequest {
155 #[serde(flatten)]
157 pub common: ListRequest,
158
159 #[serde(skip_serializing_if = "Option::is_none")]
161 #[cfg_attr(feature = "openapi", schema(example = "507f1f77bcf86cd799439011"))]
162 pub organisation_id: Option<String>,
163
164 #[serde(skip_serializing_if = "Option::is_none")]
166 #[cfg_attr(feature = "openapi", schema(example = "Acme"))]
167 pub name: Option<String>,
168
169 #[serde(skip_serializing_if = "Option::is_none")]
171 #[cfg_attr(feature = "openapi", schema(example = "12345"))]
172 pub mbn: Option<String>,
173
174 #[serde(skip_serializing_if = "Option::is_none")]
176 #[cfg_attr(feature = "openapi", schema(example = "acme.com"))]
177 pub domain: Option<String>,
178}
179
180#[derive(Debug, Clone)]
182#[cfg_attr(feature = "openapi", derive(ToSchema))]
183pub enum AccountErrorCode {
184 NotFound,
186 DuplicateMbn,
188 DuplicateDomain,
190 InvalidOrganisation,
192 InvalidCluster,
194 MissingRequiredField,
196}
197
198impl AccountErrorCode {
199 pub fn as_str(&self) -> &'static str {
201 match self {
202 Self::NotFound => "ACCOUNT_NOT_FOUND",
203 Self::DuplicateMbn => "DUPLICATE_MBN",
204 Self::DuplicateDomain => "DUPLICATE_DOMAIN",
205 Self::InvalidOrganisation => "INVALID_ORGANISATION",
206 Self::InvalidCluster => "INVALID_CLUSTER",
207 Self::MissingRequiredField => "MISSING_REQUIRED_FIELD",
208 }
209 }
210}
211
212impl CreateAccountRequest {
214 pub fn validate(&self) -> Result<(), ApiError> {
216 if self.name.trim().is_empty() {
217 return Err(ApiError::new(
218 AccountErrorCode::MissingRequiredField.as_str(),
219 "Account name is required",
220 )
221 .with_field("name"));
222 }
223
224 if self.mbn.trim().is_empty() {
225 return Err(ApiError::new(
226 AccountErrorCode::MissingRequiredField.as_str(),
227 "MBN is required",
228 )
229 .with_field("mbn"));
230 }
231
232 if self.domain.trim().is_empty() {
233 return Err(ApiError::new(
234 AccountErrorCode::MissingRequiredField.as_str(),
235 "Domain is required",
236 )
237 .with_field("domain"));
238 }
239
240 Ok(())
241 }
242}
243
244impl UpdateAccountRequest {
245 pub fn is_empty(&self) -> bool {
247 self.name.is_none()
248 && self.mbn.is_none()
249 && self.domain.is_none()
250 && self.organisation.is_none()
251 && self.address.is_none()
252 && self.teams.is_none()
253 && self.environment.is_none()
254 && self.spend_cap.is_none()
255 && self.concurrency.is_none()
256 && self.class_of_service.is_none()
257 && self.cluster_settings.is_none()
258 }
259}
260
261impl Default for AccountListRequest {
262 fn default() -> Self {
263 Self {
264 common: ListRequest {
265 pagination: crate::rest::common::PaginationParams::default(),
266 sort: crate::rest::common::SortParams::default(),
267 search: None,
268 time_range: None,
269 filters: None,
270 },
271 organisation_id: None,
272 name: None,
273 mbn: None,
274 domain: None,
275 }
276 }
277}
278
279impl AccountListRequest {
280 pub fn new() -> Self {
282 Self::default()
283 }
284
285 pub fn with_pagination(mut self, page: u32, page_size: u32) -> Self {
287 self.common.pagination = crate::rest::common::PaginationParams::new(page, page_size);
288 self
289 }
290
291 pub fn with_organisation_id(mut self, organisation_id: String) -> Self {
293 self.organisation_id = Some(organisation_id);
294 self
295 }
296}