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)]
41pub struct ResourceBuilder {
42 resource_type: String,
43 id: Option<ResourceId>,
44 schemas: Vec<SchemaUri>,
45 external_id: Option<ExternalId>,
46 user_name: Option<UserName>,
47 meta: Option<Meta>,
48 name: Option<Name>,
49 addresses: Option<MultiValuedAddresses>,
50 phone_numbers: Option<MultiValuedPhoneNumbers>,
51 emails: Option<MultiValuedEmails>,
52 members: Option<GroupMembers>,
53 attributes: Map<String, Value>,
54}
55
56impl ResourceBuilder {
57 pub fn new(resource_type: String) -> Self {
59 let mut schemas = Vec::new();
60
61 if resource_type == "User" {
63 if let Ok(schema) =
64 SchemaUri::new("urn:ietf:params:scim:schemas:core:2.0:User".to_string())
65 {
66 schemas.push(schema);
67 }
68 } else if resource_type == "Group" {
69 if let Ok(schema) =
70 SchemaUri::new("urn:ietf:params:scim:schemas:core:2.0:Group".to_string())
71 {
72 schemas.push(schema);
73 }
74 }
75
76 Self {
77 resource_type,
78 id: None,
79 schemas,
80 external_id: None,
81 user_name: None,
82 meta: None,
83 name: None,
84 addresses: None,
85 phone_numbers: None,
86 emails: None,
87 members: None,
88 attributes: Map::new(),
89 }
90 }
91
92 pub fn with_id(mut self, id: ResourceId) -> Self {
94 self.id = Some(id);
95 self
96 }
97
98 pub fn with_external_id(mut self, external_id: ExternalId) -> Self {
100 self.external_id = Some(external_id);
101 self
102 }
103
104 pub fn with_username(mut self, username: UserName) -> Self {
106 self.user_name = Some(username);
107 self
108 }
109
110 pub fn with_meta(mut self, meta: Meta) -> Self {
112 self.meta = Some(meta);
113 self
114 }
115
116 pub fn with_name(mut self, name: Name) -> Self {
118 self.name = Some(name);
119 self
120 }
121
122 pub fn with_addresses(mut self, addresses: MultiValuedAddresses) -> Self {
124 self.addresses = Some(addresses);
125 self
126 }
127
128 pub fn with_phone_numbers(mut self, phone_numbers: MultiValuedPhoneNumbers) -> Self {
130 self.phone_numbers = Some(phone_numbers);
131 self
132 }
133
134 pub fn with_emails(mut self, emails: MultiValuedEmails) -> Self {
136 self.emails = Some(emails);
137 self
138 }
139
140 pub fn with_members(mut self, members: GroupMembers) -> Self {
142 self.members = Some(members);
143 self
144 }
145
146 pub fn add_address(mut self, address: Address) -> Self {
148 match self.addresses {
149 Some(existing) => {
150 let new_addresses = existing.with_value(address);
151 self.addresses = Some(new_addresses);
152 }
153 None => {
154 let new_addresses = MultiValuedAddresses::single(address);
155 self.addresses = Some(new_addresses);
156 }
157 }
158 self
159 }
160
161 pub fn add_phone_number(mut self, phone_number: PhoneNumber) -> Self {
163 match self.phone_numbers {
164 Some(existing) => {
165 let new_phones = existing.with_value(phone_number);
166 self.phone_numbers = Some(new_phones);
167 }
168 None => {
169 let new_phones = MultiValuedPhoneNumbers::single(phone_number);
170 self.phone_numbers = Some(new_phones);
171 }
172 }
173 self
174 }
175
176 pub fn add_email(mut self, email: EmailAddress) -> Self {
178 match self.emails {
179 Some(existing) => {
180 let new_emails = existing.with_value(email);
181 self.emails = Some(new_emails);
182 }
183 None => {
184 let new_emails = MultiValuedEmails::single(email);
185 self.emails = Some(new_emails);
186 }
187 }
188 self
189 }
190
191 pub fn add_schema(mut self, schema: SchemaUri) -> Self {
193 self.schemas.push(schema);
194 self
195 }
196
197 pub fn with_schemas(mut self, schemas: Vec<SchemaUri>) -> Self {
199 self.schemas = schemas;
200 self
201 }
202
203 pub fn with_attribute<S: Into<String>>(mut self, name: S, value: Value) -> Self {
205 self.attributes.insert(name.into(), value);
206 self
207 }
208
209 pub fn with_attributes(mut self, attributes: Map<String, Value>) -> Self {
211 for (key, value) in attributes {
212 self.attributes.insert(key, value);
213 }
214 self
215 }
216
217 pub fn build(self) -> ValidationResult<Resource> {
219 if self.schemas.is_empty() {
221 return Err(ValidationError::custom("At least one schema is required"));
222 }
223
224 Ok(Resource {
225 resource_type: self.resource_type,
226 id: self.id,
227 schemas: self.schemas,
228 external_id: self.external_id,
229 user_name: self.user_name,
230 meta: self.meta,
231 name: self.name,
232 addresses: self.addresses,
233 phone_numbers: self.phone_numbers,
234 emails: self.emails,
235 members: self.members,
236 attributes: self.attributes,
237 })
238 }
239
240 pub fn build_with_meta(mut self, base_url: &str) -> ValidationResult<Resource> {
242 if self.meta.is_none() {
244 let meta = Meta::new_for_creation(self.resource_type.clone())?;
245 let meta_with_location = if let Some(ref id) = self.id {
246 let location = Meta::generate_location(base_url, &self.resource_type, id.as_str());
247 meta.with_location(location)?
248 } else {
249 meta
250 };
251 self.meta = Some(meta_with_location);
252 }
253
254 self.build()
255 }
256}