scim_server/resource/
builder.rs1use crate::error::{ValidationError, ValidationResult};
7use crate::resource::resource::Resource;
8use crate::resource::value_objects::{
9 Address, EmailAddress, ExternalId, GroupMembers, Meta, MultiValuedAddresses, MultiValuedEmails,
10 MultiValuedPhoneNumbers, Name, PhoneNumber, ResourceId, SchemaUri, UserName,
11};
12use serde_json::{Map, Value};
13
14#[derive(Debug, Clone)]
36pub struct ResourceBuilder {
37 resource_type: String,
38 id: Option<ResourceId>,
39 schemas: Vec<SchemaUri>,
40 external_id: Option<ExternalId>,
41 user_name: Option<UserName>,
42 meta: Option<Meta>,
43 name: Option<Name>,
44 addresses: Option<MultiValuedAddresses>,
45 phone_numbers: Option<MultiValuedPhoneNumbers>,
46 emails: Option<MultiValuedEmails>,
47 members: Option<GroupMembers>,
48 attributes: Map<String, Value>,
49}
50
51impl ResourceBuilder {
52 pub fn new(resource_type: String) -> Self {
54 let mut schemas = Vec::new();
55
56 if resource_type == "User" {
58 if let Ok(schema) =
59 SchemaUri::new("urn:ietf:params:scim:schemas:core:2.0:User".to_string())
60 {
61 schemas.push(schema);
62 }
63 } else if resource_type == "Group" {
64 if let Ok(schema) =
65 SchemaUri::new("urn:ietf:params:scim:schemas:core:2.0:Group".to_string())
66 {
67 schemas.push(schema);
68 }
69 }
70
71 Self {
72 resource_type,
73 id: None,
74 schemas,
75 external_id: None,
76 user_name: None,
77 meta: None,
78 name: None,
79 addresses: None,
80 phone_numbers: None,
81 emails: None,
82 members: None,
83 attributes: Map::new(),
84 }
85 }
86
87 pub fn with_id(mut self, id: ResourceId) -> Self {
89 self.id = Some(id);
90 self
91 }
92
93 pub fn with_external_id(mut self, external_id: ExternalId) -> Self {
95 self.external_id = Some(external_id);
96 self
97 }
98
99 pub fn with_username(mut self, username: UserName) -> Self {
101 self.user_name = Some(username);
102 self
103 }
104
105 pub fn with_meta(mut self, meta: Meta) -> Self {
107 self.meta = Some(meta);
108 self
109 }
110
111 pub fn with_name(mut self, name: Name) -> Self {
113 self.name = Some(name);
114 self
115 }
116
117 pub fn with_addresses(mut self, addresses: MultiValuedAddresses) -> Self {
119 self.addresses = Some(addresses);
120 self
121 }
122
123 pub fn with_phone_numbers(mut self, phone_numbers: MultiValuedPhoneNumbers) -> Self {
125 self.phone_numbers = Some(phone_numbers);
126 self
127 }
128
129 pub fn with_emails(mut self, emails: MultiValuedEmails) -> Self {
131 self.emails = Some(emails);
132 self
133 }
134
135 pub fn with_members(mut self, members: GroupMembers) -> Self {
137 self.members = Some(members);
138 self
139 }
140
141 pub fn add_address(mut self, address: Address) -> Self {
143 match self.addresses {
144 Some(existing) => {
145 let new_addresses = existing.with_value(address);
146 self.addresses = Some(new_addresses);
147 }
148 None => {
149 let new_addresses = MultiValuedAddresses::single(address);
150 self.addresses = Some(new_addresses);
151 }
152 }
153 self
154 }
155
156 pub fn add_phone_number(mut self, phone_number: PhoneNumber) -> Self {
158 match self.phone_numbers {
159 Some(existing) => {
160 let new_phones = existing.with_value(phone_number);
161 self.phone_numbers = Some(new_phones);
162 }
163 None => {
164 let new_phones = MultiValuedPhoneNumbers::single(phone_number);
165 self.phone_numbers = Some(new_phones);
166 }
167 }
168 self
169 }
170
171 pub fn add_email(mut self, email: EmailAddress) -> Self {
173 match self.emails {
174 Some(existing) => {
175 let new_emails = existing.with_value(email);
176 self.emails = Some(new_emails);
177 }
178 None => {
179 let new_emails = MultiValuedEmails::single(email);
180 self.emails = Some(new_emails);
181 }
182 }
183 self
184 }
185
186 pub fn add_schema(mut self, schema: SchemaUri) -> Self {
188 self.schemas.push(schema);
189 self
190 }
191
192 pub fn with_schemas(mut self, schemas: Vec<SchemaUri>) -> Self {
194 self.schemas = schemas;
195 self
196 }
197
198 pub fn with_attribute<S: Into<String>>(mut self, name: S, value: Value) -> Self {
200 self.attributes.insert(name.into(), value);
201 self
202 }
203
204 pub fn with_attributes(mut self, attributes: Map<String, Value>) -> Self {
206 for (key, value) in attributes {
207 self.attributes.insert(key, value);
208 }
209 self
210 }
211
212 pub fn build(self) -> ValidationResult<Resource> {
214 if self.schemas.is_empty() {
216 return Err(ValidationError::custom("At least one schema is required"));
217 }
218
219 Ok(Resource {
220 resource_type: self.resource_type,
221 id: self.id,
222 schemas: self.schemas,
223 external_id: self.external_id,
224 user_name: self.user_name,
225 meta: self.meta,
226 name: self.name,
227 addresses: self.addresses,
228 phone_numbers: self.phone_numbers,
229 emails: self.emails,
230 members: self.members,
231 attributes: self.attributes,
232 })
233 }
234
235 pub fn build_with_meta(mut self, base_url: &str) -> ValidationResult<Resource> {
237 if self.meta.is_none() {
239 let meta = Meta::new_for_creation(self.resource_type.clone())?;
240 let meta_with_location = if let Some(ref id) = self.id {
241 let location = Meta::generate_location(base_url, &self.resource_type, id.as_str());
242 meta.with_location(location)?
243 } else {
244 meta
245 };
246 self.meta = Some(meta_with_location);
247 }
248
249 self.build()
250 }
251}