mirror of
				https://github.com/nitnelave/lldap.git
				synced 2023-04-12 14:25:13 +00:00 
			
		
		
		
	server: Add support for users' avatars in GrahpQL
This commit is contained in:
		
							parent
							
								
									f29c88842a
								
							
						
					
					
						commit
						61eb71ebd3
					
				
							
								
								
									
										11
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -2115,6 +2115,7 @@ dependencies = [
 | 
				
			|||||||
 "futures-util",
 | 
					 "futures-util",
 | 
				
			||||||
 "hmac 0.10.1",
 | 
					 "hmac 0.10.1",
 | 
				
			||||||
 "http",
 | 
					 "http",
 | 
				
			||||||
 | 
					 "image",
 | 
				
			||||||
 "itertools",
 | 
					 "itertools",
 | 
				
			||||||
 "juniper",
 | 
					 "juniper",
 | 
				
			||||||
 "juniper_actix",
 | 
					 "juniper_actix",
 | 
				
			||||||
@ -2133,6 +2134,7 @@ dependencies = [
 | 
				
			|||||||
 "sea-query-binder",
 | 
					 "sea-query-binder",
 | 
				
			||||||
 "secstr",
 | 
					 "secstr",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 | 
					 "serde_bytes",
 | 
				
			||||||
 "serde_json",
 | 
					 "serde_json",
 | 
				
			||||||
 "sha2",
 | 
					 "sha2",
 | 
				
			||||||
 "sqlx",
 | 
					 "sqlx",
 | 
				
			||||||
@ -3317,6 +3319,15 @@ dependencies = [
 | 
				
			|||||||
 "serde_derive",
 | 
					 "serde_derive",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "serde_bytes"
 | 
				
			||||||
 | 
					version = "0.11.7"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "serde_derive"
 | 
					name = "serde_derive"
 | 
				
			||||||
version = "1.0.137"
 | 
					version = "1.0.137"
 | 
				
			||||||
 | 
				
			|||||||
@ -90,6 +90,7 @@ impl CommonComponent<CreateUserForm> for CreateUserForm {
 | 
				
			|||||||
                        displayName: to_option(model.display_name),
 | 
					                        displayName: to_option(model.display_name),
 | 
				
			||||||
                        firstName: to_option(model.first_name),
 | 
					                        firstName: to_option(model.first_name),
 | 
				
			||||||
                        lastName: to_option(model.last_name),
 | 
					                        lastName: to_option(model.last_name),
 | 
				
			||||||
 | 
					                        avatar: None,
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                self.common.call_graphql::<CreateUser, _>(
 | 
					                self.common.call_graphql::<CreateUser, _>(
 | 
				
			||||||
 | 
				
			|||||||
@ -234,6 +234,7 @@ impl UserDetailsForm {
 | 
				
			|||||||
            displayName: None,
 | 
					            displayName: None,
 | 
				
			||||||
            firstName: None,
 | 
					            firstName: None,
 | 
				
			||||||
            lastName: None,
 | 
					            lastName: None,
 | 
				
			||||||
 | 
					            avatar: None,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let default_user_input = user_input.clone();
 | 
					        let default_user_input = user_input.clone();
 | 
				
			||||||
        let model = self.form.model();
 | 
					        let model = self.form.model();
 | 
				
			||||||
 | 
				
			|||||||
@ -85,6 +85,7 @@ impl User {
 | 
				
			|||||||
                display_name,
 | 
					                display_name,
 | 
				
			||||||
                first_name,
 | 
					                first_name,
 | 
				
			||||||
                last_name,
 | 
					                last_name,
 | 
				
			||||||
 | 
					                avatar: None,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            password,
 | 
					            password,
 | 
				
			||||||
            dn,
 | 
					            dn,
 | 
				
			||||||
 | 
				
			|||||||
@ -60,6 +60,7 @@ input CreateUserInput {
 | 
				
			|||||||
  displayName: String
 | 
					  displayName: String
 | 
				
			||||||
  firstName: String
 | 
					  firstName: String
 | 
				
			||||||
  lastName: String
 | 
					  lastName: String
 | 
				
			||||||
 | 
					  avatar: String
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type User {
 | 
					type User {
 | 
				
			||||||
@ -68,6 +69,7 @@ type User {
 | 
				
			|||||||
  displayName: String!
 | 
					  displayName: String!
 | 
				
			||||||
  firstName: String!
 | 
					  firstName: String!
 | 
				
			||||||
  lastName: String!
 | 
					  lastName: String!
 | 
				
			||||||
 | 
					  avatar: String!
 | 
				
			||||||
  creationDate: DateTimeUtc!
 | 
					  creationDate: DateTimeUtc!
 | 
				
			||||||
  uuid: String!
 | 
					  uuid: String!
 | 
				
			||||||
  "The groups to which this user belongs."
 | 
					  "The groups to which this user belongs."
 | 
				
			||||||
@ -85,6 +87,7 @@ input UpdateUserInput {
 | 
				
			|||||||
  displayName: String
 | 
					  displayName: String
 | 
				
			||||||
  firstName: String
 | 
					  firstName: String
 | 
				
			||||||
  lastName: String
 | 
					  lastName: String
 | 
				
			||||||
 | 
					  avatar: String
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
schema {
 | 
					schema {
 | 
				
			||||||
 | 
				
			|||||||
@ -45,6 +45,7 @@ tracing-actix-web = "0.4.0-beta.7"
 | 
				
			|||||||
tracing-attributes = "^0.1.21"
 | 
					tracing-attributes = "^0.1.21"
 | 
				
			||||||
tracing-log = "*"
 | 
					tracing-log = "*"
 | 
				
			||||||
rustls-pemfile = "1.0.0"
 | 
					rustls-pemfile = "1.0.0"
 | 
				
			||||||
 | 
					serde_bytes = "0.11.7"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies.chrono]
 | 
					[dependencies.chrono]
 | 
				
			||||||
features = ["serde"]
 | 
					features = ["serde"]
 | 
				
			||||||
@ -117,5 +118,10 @@ version = "^0.1.4"
 | 
				
			|||||||
features = ["default", "rustls"]
 | 
					features = ["default", "rustls"]
 | 
				
			||||||
version = "=3.0.0-beta.5"
 | 
					version = "=3.0.0-beta.5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies.image]
 | 
				
			||||||
 | 
					features = ["jpeg"]
 | 
				
			||||||
 | 
					default-features = false
 | 
				
			||||||
 | 
					version = "0.24"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					[dev-dependencies]
 | 
				
			||||||
mockall = "0.9.1"
 | 
					mockall = "0.9.1"
 | 
				
			||||||
 | 
				
			|||||||
@ -82,6 +82,69 @@ impl From<String> for UserId {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(PartialEq, Eq, Clone, Debug, Default, Serialize, Deserialize, sqlx::Type)]
 | 
				
			||||||
 | 
					#[sqlx(transparent)]
 | 
				
			||||||
 | 
					pub struct JpegPhoto(#[serde(with = "serde_bytes")] Vec<u8>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<JpegPhoto> for sea_query::Value {
 | 
				
			||||||
 | 
					    fn from(photo: JpegPhoto) -> Self {
 | 
				
			||||||
 | 
					        photo.0.into()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<&JpegPhoto> for sea_query::Value {
 | 
				
			||||||
 | 
					    fn from(photo: &JpegPhoto) -> Self {
 | 
				
			||||||
 | 
					        photo.0.as_slice().into()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl TryFrom<Vec<u8>> for JpegPhoto {
 | 
				
			||||||
 | 
					    type Error = anyhow::Error;
 | 
				
			||||||
 | 
					    fn try_from(bytes: Vec<u8>) -> anyhow::Result<Self> {
 | 
				
			||||||
 | 
					        // Confirm that it's a valid Jpeg, then store only the bytes.
 | 
				
			||||||
 | 
					        image::io::Reader::with_format(
 | 
				
			||||||
 | 
					            std::io::Cursor::new(bytes.as_slice()),
 | 
				
			||||||
 | 
					            image::ImageFormat::Jpeg,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .decode()?;
 | 
				
			||||||
 | 
					        Ok(JpegPhoto(bytes))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl TryFrom<String> for JpegPhoto {
 | 
				
			||||||
 | 
					    type Error = anyhow::Error;
 | 
				
			||||||
 | 
					    fn try_from(string: String) -> anyhow::Result<Self> {
 | 
				
			||||||
 | 
					        // The String format is in base64.
 | 
				
			||||||
 | 
					        Self::try_from(base64::decode(string.as_str())?)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<&JpegPhoto> for String {
 | 
				
			||||||
 | 
					    fn from(val: &JpegPhoto) -> Self {
 | 
				
			||||||
 | 
					        base64::encode(&val.0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl JpegPhoto {
 | 
				
			||||||
 | 
					    pub fn for_tests() -> Self {
 | 
				
			||||||
 | 
					        use image::{ImageOutputFormat, Rgb, RgbImage};
 | 
				
			||||||
 | 
					        let img = RgbImage::from_fn(32, 32, |x, y| {
 | 
				
			||||||
 | 
					            if (x + y) % 2 == 0 {
 | 
				
			||||||
 | 
					                Rgb([0, 0, 0])
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                Rgb([255, 255, 255])
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        let mut bytes: Vec<u8> = Vec::new();
 | 
				
			||||||
 | 
					        img.write_to(
 | 
				
			||||||
 | 
					            &mut std::io::Cursor::new(&mut bytes),
 | 
				
			||||||
 | 
					            ImageOutputFormat::Jpeg(0),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					        Self(bytes)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
 | 
					#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
 | 
				
			||||||
pub struct User {
 | 
					pub struct User {
 | 
				
			||||||
    pub user_id: UserId,
 | 
					    pub user_id: UserId,
 | 
				
			||||||
@ -89,7 +152,7 @@ pub struct User {
 | 
				
			|||||||
    pub display_name: String,
 | 
					    pub display_name: String,
 | 
				
			||||||
    pub first_name: String,
 | 
					    pub first_name: String,
 | 
				
			||||||
    pub last_name: String,
 | 
					    pub last_name: String,
 | 
				
			||||||
    // pub avatar: ?,
 | 
					    pub avatar: JpegPhoto,
 | 
				
			||||||
    pub creation_date: chrono::DateTime<chrono::Utc>,
 | 
					    pub creation_date: chrono::DateTime<chrono::Utc>,
 | 
				
			||||||
    pub uuid: Uuid,
 | 
					    pub uuid: Uuid,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -105,6 +168,7 @@ impl Default for User {
 | 
				
			|||||||
            display_name: String::new(),
 | 
					            display_name: String::new(),
 | 
				
			||||||
            first_name: String::new(),
 | 
					            first_name: String::new(),
 | 
				
			||||||
            last_name: String::new(),
 | 
					            last_name: String::new(),
 | 
				
			||||||
 | 
					            avatar: JpegPhoto::default(),
 | 
				
			||||||
            creation_date: epoch,
 | 
					            creation_date: epoch,
 | 
				
			||||||
            uuid: Uuid::from_name_and_date("", &epoch),
 | 
					            uuid: Uuid::from_name_and_date("", &epoch),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -159,6 +223,7 @@ pub struct CreateUserRequest {
 | 
				
			|||||||
    pub display_name: Option<String>,
 | 
					    pub display_name: Option<String>,
 | 
				
			||||||
    pub first_name: Option<String>,
 | 
					    pub first_name: Option<String>,
 | 
				
			||||||
    pub last_name: Option<String>,
 | 
					    pub last_name: Option<String>,
 | 
				
			||||||
 | 
					    pub avatar: Option<JpegPhoto>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Default)]
 | 
					#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Default)]
 | 
				
			||||||
@ -169,6 +234,7 @@ pub struct UpdateUserRequest {
 | 
				
			|||||||
    pub display_name: Option<String>,
 | 
					    pub display_name: Option<String>,
 | 
				
			||||||
    pub first_name: Option<String>,
 | 
					    pub first_name: Option<String>,
 | 
				
			||||||
    pub last_name: Option<String>,
 | 
					    pub last_name: Option<String>,
 | 
				
			||||||
 | 
					    pub avatar: Option<JpegPhoto>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
 | 
					#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
 | 
				
			||||||
@ -263,4 +329,11 @@ mod tests {
 | 
				
			|||||||
            Uuid::from_name_and_date(user_id, &date2)
 | 
					            Uuid::from_name_and_date(user_id, &date2)
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_jpeg_try_from_bytes() {
 | 
				
			||||||
 | 
					        let base64_raw = "/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////2wBDAf//////////////////////////////////////////////////////////////////////////////////////wAARCADqATkDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECA//EACQQAQEBAAIBBAMBAQEBAAAAAAABESExQQISUXFhgZGxocHw/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/xAAWEQEBAQAAAAAAAAAAAAAAAAAAEQH/2gAMAwEAAhEDEQA/AMriLyCKgg1gQwCgs4FTMOdutepjQak+FzMSVqgxZdRdPPIIvH5WzzGdBriphtTeAXg2ZjKA1pqKDUGZca3foBek8gFv8Ie3fKdA1qb8s7hoL6eLVt51FsAnql3Ut1M7AWbflLMDkEMX/F6/YjK/pADFQAUNA6alYagKk72m/j9p4Bq2fDDSYKLNXPNLoHE/NT6RYC31cJxZ3yWVM+aBYi/S2ZgiAsnYJx5D21vPmqrm3PTfpQQwyAC8JZvSKDni41ZrMuUVVl+Uz9w9v/1QWrZsZ5nFPHYH+JZyureQSF5M+fJ0CAfwRAVRBQA1DAWVUayoJUWoDpsxntPsueBV4+VxhdyAtv8AjOLGpIDMLbeGvbF4iozJfr/WukAVABAXAQXEAAASzVAZdO2WNordm+emFl7XcQSNZiFtv0C9w90nhJf4mA1u+GcJFwIyAqL/AOovwgGNfSRqdIrNa29M0gKCAojU9PAMjWXpckEJFNFEAAXEUBABYz6rZ0ureQc9vyt9XxDF2QAXtABcQAs0AZywkvluJbyipifas52DcyxjlZweAO0xri/hc+wZOEKIu6nSyeToVZyWXwvCg53gW81QQ7aTNAn5dGZJPs1UXURQAUEMCXQLZE93PRZ5hPTgNMrbIzKCm52LZwCs+2M8w2g3sjPuZAXb4IsMAUACzVUGM4/K+md6vEXUUyM5PDR0IxYe6ramih0VNBrS4xoqN8Q1BFQk3yqyAsioioAAKgDSJL4/jQIn5igLrPqtOuf6oOaxbMoAltUAhhIoJiiggrPu+AaOIxtAX3JbaAIaLwi4t9X4T3fg2AFtqcrUUarP20zUDAmqoE0WRBZPNVUVEAAAAVAC8kvih2DSKxOdBqs7Z0l0gI0mKAC4AuHE7ZtBriM+744QAAAAABAFsveIttBICyaikvy1+r/Cen5rWQHIBQa4rIDRqSl5qDWqziqgAAAATA7BpGdqXb2C2+J/UgAtRQBSQtkBWb6vhLbQAAAAAEBRAAAAAUbm+GZNdPxAP+ql2Tjwx7/wIgZ8iKvBk+CJoCXii9gaqZ/qqihAAAEVABGkBFUwBftNkZ3QW34QAAABFAQAVAAAAAARVkl8gs/43sk1jL45LvHArepk+E9XTG35oLqsmIKmLAEygKg0y1AFQBUXwgAAAoBC34S3UAAABAVAAAAAABAUQAVABdRQa1PcYyit2z58M8C4ouM2NXpOEGeWtNZUatiAIoAKIoCoAoG4C9MW6dgIoAIAAAAAAACKWAgL0CAAAALiANCKioNLgM1CrLihmTafkt1EF3SZ5ZVUW4mnIKvAi5fhEURVDWVQBRAAAAAAAAQFRVyAyulgAqCKlF8IqLsEgC9mGoC+IusqCrv5ZEUVOk1RuJfwSLOOkGFi4XPCoYYrNiKauosBGi9ICstM1UAAAAAAFQ0VcTBAXUGgIqGoKhKAzRRUQUAwxoSrGRpkQA/qiosOL9oJptMRRVZa0VUqSiChE6BqMgCwqKqIogAIAqKCKgKoogg0lBFuIKgAAAKNRlf2gqsftsEtZWoAAqAACKoMqAAeSoqp39kL2AqLOlE8rEBFQARYALhigrNC9gGmooLp4TweEQFFBFAECgIoAu0ifIAqAAA//9k=";
 | 
				
			||||||
 | 
					        let base64_jpeg = base64::decode(base64_raw).unwrap();
 | 
				
			||||||
 | 
					        JpegPhoto::try_from(base64_jpeg).unwrap();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -367,6 +367,7 @@ impl BackendHandler for SqlBackendHandler {
 | 
				
			|||||||
            Users::DisplayName,
 | 
					            Users::DisplayName,
 | 
				
			||||||
            Users::FirstName,
 | 
					            Users::FirstName,
 | 
				
			||||||
            Users::LastName,
 | 
					            Users::LastName,
 | 
				
			||||||
 | 
					            Users::Avatar,
 | 
				
			||||||
            Users::CreationDate,
 | 
					            Users::CreationDate,
 | 
				
			||||||
            Users::Uuid,
 | 
					            Users::Uuid,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
@ -378,6 +379,7 @@ impl BackendHandler for SqlBackendHandler {
 | 
				
			|||||||
            request.display_name.unwrap_or_default().into(),
 | 
					            request.display_name.unwrap_or_default().into(),
 | 
				
			||||||
            request.first_name.unwrap_or_default().into(),
 | 
					            request.first_name.unwrap_or_default().into(),
 | 
				
			||||||
            request.last_name.unwrap_or_default().into(),
 | 
					            request.last_name.unwrap_or_default().into(),
 | 
				
			||||||
 | 
					            request.avatar.unwrap_or_default().into(),
 | 
				
			||||||
            now.naive_utc().into(),
 | 
					            now.naive_utc().into(),
 | 
				
			||||||
            uuid.into(),
 | 
					            uuid.into(),
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
@ -409,6 +411,9 @@ impl BackendHandler for SqlBackendHandler {
 | 
				
			|||||||
        if let Some(last_name) = request.last_name {
 | 
					        if let Some(last_name) = request.last_name {
 | 
				
			||||||
            values.push((Users::LastName, last_name.into()));
 | 
					            values.push((Users::LastName, last_name.into()));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if let Some(avatar) = request.avatar {
 | 
				
			||||||
 | 
					            values.push((Users::Avatar, avatar.into()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if values.is_empty() {
 | 
					        if values.is_empty() {
 | 
				
			||||||
            return Ok(());
 | 
					            return Ok(());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,8 @@
 | 
				
			|||||||
use crate::domain::handler::{
 | 
					use crate::domain::handler::{
 | 
				
			||||||
    BackendHandler, CreateUserRequest, GroupId, UpdateGroupRequest, UpdateUserRequest, UserId,
 | 
					    BackendHandler, CreateUserRequest, GroupId, JpegPhoto, UpdateGroupRequest, UpdateUserRequest,
 | 
				
			||||||
 | 
					    UserId,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					use anyhow::Context as AnyhowContext;
 | 
				
			||||||
use juniper::{graphql_object, FieldResult, GraphQLInputObject, GraphQLObject};
 | 
					use juniper::{graphql_object, FieldResult, GraphQLInputObject, GraphQLObject};
 | 
				
			||||||
use tracing::{debug, debug_span, Instrument};
 | 
					use tracing::{debug, debug_span, Instrument};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -28,6 +30,8 @@ pub struct CreateUserInput {
 | 
				
			|||||||
    display_name: Option<String>,
 | 
					    display_name: Option<String>,
 | 
				
			||||||
    first_name: Option<String>,
 | 
					    first_name: Option<String>,
 | 
				
			||||||
    last_name: Option<String>,
 | 
					    last_name: Option<String>,
 | 
				
			||||||
 | 
					    // Base64 encoded JpegPhoto.
 | 
				
			||||||
 | 
					    avatar: Option<String>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
 | 
					#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
 | 
				
			||||||
@ -38,6 +42,8 @@ pub struct UpdateUserInput {
 | 
				
			|||||||
    display_name: Option<String>,
 | 
					    display_name: Option<String>,
 | 
				
			||||||
    first_name: Option<String>,
 | 
					    first_name: Option<String>,
 | 
				
			||||||
    last_name: Option<String>,
 | 
					    last_name: Option<String>,
 | 
				
			||||||
 | 
					    // Base64 encoded JpegPhoto.
 | 
				
			||||||
 | 
					    avatar: Option<String>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
 | 
					#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
 | 
				
			||||||
@ -73,6 +79,14 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
 | 
				
			|||||||
            return Err("Unauthorized user creation".into());
 | 
					            return Err("Unauthorized user creation".into());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let user_id = UserId::new(&user.id);
 | 
					        let user_id = UserId::new(&user.id);
 | 
				
			||||||
 | 
					        let avatar = user
 | 
				
			||||||
 | 
					            .avatar
 | 
				
			||||||
 | 
					            .map(base64::decode)
 | 
				
			||||||
 | 
					            .transpose()
 | 
				
			||||||
 | 
					            .context("Invalid base64 image")?
 | 
				
			||||||
 | 
					            .map(JpegPhoto::try_from)
 | 
				
			||||||
 | 
					            .transpose()
 | 
				
			||||||
 | 
					            .context("Provided image is not a valid JPEG")?;
 | 
				
			||||||
        context
 | 
					        context
 | 
				
			||||||
            .handler
 | 
					            .handler
 | 
				
			||||||
            .create_user(CreateUserRequest {
 | 
					            .create_user(CreateUserRequest {
 | 
				
			||||||
@ -81,6 +95,7 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
 | 
				
			|||||||
                display_name: user.display_name,
 | 
					                display_name: user.display_name,
 | 
				
			||||||
                first_name: user.first_name,
 | 
					                first_name: user.first_name,
 | 
				
			||||||
                last_name: user.last_name,
 | 
					                last_name: user.last_name,
 | 
				
			||||||
 | 
					                avatar,
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .instrument(span.clone())
 | 
					            .instrument(span.clone())
 | 
				
			||||||
            .await?;
 | 
					            .await?;
 | 
				
			||||||
@ -126,6 +141,14 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
 | 
				
			|||||||
            span.in_scope(|| debug!("Unauthorized"));
 | 
					            span.in_scope(|| debug!("Unauthorized"));
 | 
				
			||||||
            return Err("Unauthorized user update".into());
 | 
					            return Err("Unauthorized user update".into());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        let avatar = user
 | 
				
			||||||
 | 
					            .avatar
 | 
				
			||||||
 | 
					            .map(base64::decode)
 | 
				
			||||||
 | 
					            .transpose()
 | 
				
			||||||
 | 
					            .context("Invalid base64 image")?
 | 
				
			||||||
 | 
					            .map(JpegPhoto::try_from)
 | 
				
			||||||
 | 
					            .transpose()
 | 
				
			||||||
 | 
					            .context("Provided image is not a valid JPEG")?;
 | 
				
			||||||
        context
 | 
					        context
 | 
				
			||||||
            .handler
 | 
					            .handler
 | 
				
			||||||
            .update_user(UpdateUserRequest {
 | 
					            .update_user(UpdateUserRequest {
 | 
				
			||||||
@ -134,6 +157,7 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
 | 
				
			|||||||
                display_name: user.display_name,
 | 
					                display_name: user.display_name,
 | 
				
			||||||
                first_name: user.first_name,
 | 
					                first_name: user.first_name,
 | 
				
			||||||
                last_name: user.last_name,
 | 
					                last_name: user.last_name,
 | 
				
			||||||
 | 
					                avatar,
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .instrument(span)
 | 
					            .instrument(span)
 | 
				
			||||||
            .await?;
 | 
					            .await?;
 | 
				
			||||||
 | 
				
			|||||||
@ -217,6 +217,10 @@ impl<Handler: BackendHandler + Sync> User<Handler> {
 | 
				
			|||||||
        &self.user.last_name
 | 
					        &self.user.last_name
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn avatar(&self) -> String {
 | 
				
			||||||
 | 
					        (&self.user.avatar).into()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn creation_date(&self) -> chrono::DateTime<chrono::Utc> {
 | 
					    fn creation_date(&self) -> chrono::DateTime<chrono::Utc> {
 | 
				
			||||||
        self.user.creation_date
 | 
					        self.user.creation_date
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1459,6 +1459,7 @@ mod tests {
 | 
				
			|||||||
                        display_name: "Jimminy Cricket".to_string(),
 | 
					                        display_name: "Jimminy Cricket".to_string(),
 | 
				
			||||||
                        first_name: "Jim".to_string(),
 | 
					                        first_name: "Jim".to_string(),
 | 
				
			||||||
                        last_name: "Cricket".to_string(),
 | 
					                        last_name: "Cricket".to_string(),
 | 
				
			||||||
 | 
					                        avatar: JpegPhoto::for_tests(),
 | 
				
			||||||
                        uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
 | 
					                        uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
 | 
				
			||||||
                        creation_date: Utc.ymd(2014, 7, 8).and_hms(9, 10, 11),
 | 
					                        creation_date: Utc.ymd(2014, 7, 8).and_hms(9, 10, 11),
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user