0
2
server: A login with no Bluesky profile makes a bare grain profile
Only what this pull adds. It is built on grain runs on a Rust server modelled on knotless, without hatk.
crates/grain-serve/src/login/profile.rs+11−5
| @@ -8,7 +8,10 @@ | ||
//! profile its `displayName`, `description` and `avatar` (the blob as the | ||
//! Bluesky profile names it), with the `createdAt` of the grain profile | ||
//! there is, or now. One there is is put over, swapped on its CID; else | ||
//! one is made at `self`. Both are read from the account's PDS, which has | ||
//! one is made at `self`. An account with no Bluesky profile and no grain | ||
//! profile gets a bare one, its `createdAt` alone, so every account that | ||
//! logs in has the profile that makes grain track it; one with an unnamed | ||
//! grain profile and no Bluesky profile is left as it is. Both are read from the account's PDS, which has | ||
//! them before the index may; the write goes through the web session | ||
//! ([`crate::write`]), so it is in the index once it is made. | ||
| ||
| @@ -28,7 +31,8 @@ const SELF: &str = "self"; | ||
pub enum Seeded { | ||
/// The grain profile has a name already. | ||
Named, | ||
/// There is no Bluesky profile to make one from. | ||
/// There is no Bluesky profile to name it from, and a grain profile | ||
/// there is was left as it is. | ||
NoBluesky, | ||
/// A grain profile was made, at this URI. | ||
Made(String), | ||
| @@ -58,10 +62,12 @@ pub async fn seeded(app: &App, did: &str) -> anyhow::Result<Seeded> { | ||
if grain.as_ref().is_some_and(|(_, g)| named(g)) { | ||
return Ok(Seeded::Named); | ||
} | ||
let Some((_, bsky)) = crate::sync::get_record(&pds, did, BSKY, SELF).await? else { | ||
return Ok(Seeded::NoBluesky); | ||
let bsky = crate::sync::get_record(&pds, did, BSKY, SELF).await?; | ||
let record = match (&bsky, &grain) { | ||
(None, Some(_)) => return Ok(Seeded::NoBluesky), | ||
(None, None) => from_bluesky(&json!({}), None), | ||
(Some((_, bsky)), _) => from_bluesky(bsky, grain.as_ref().map(|(_, g)| g)), | ||
}; | ||
let record = from_bluesky(&bsky, grain.as_ref().map(|(_, g)| g)); | ||
match grain { | ||
Some((cid, _)) => { | ||
let swaps = [Some(cid), None]; | ||
crates/grain-serve/tests/login.rs+18−2
| @@ -760,7 +760,7 @@ async fn a_login_seeds_a_grain_profile_from_bluesky() { | ||
assert!(at.get("description").is_none(), "{at}"); | ||
| ||
// A grain profile with a name is left as it is; with no Bluesky | ||
// profile, nothing is made. | ||
// profile and no grain profile, a bare one is made, and then left. | ||
let c = net::account().await; | ||
let named = json!({ "$type": "social.grain.actor.profile", "displayName": "Carol" }); | ||
made(&c, "social.grain.actor.profile", named).await; | ||
| @@ -776,8 +776,24 @@ async fn a_login_seeds_a_grain_profile_from_bluesky() { | ||
site.log_in(&mut jar, &d).await; | ||
assert_eq!(seeded(&site.app, &c.did).await.unwrap(), Seeded::Named); | ||
assert_eq!(grain_profile(&c).await.unwrap()["displayName"], "Carol"); | ||
let bare = format!("at://{}/social.grain.actor.profile/self", d.did); | ||
for _ in 0..200 { | ||
let held = site | ||
.index | ||
.get::<grain_serve::index::records::Profile>(&bare); | ||
if held.unwrap().is_some() { | ||
break; | ||
} | ||
tokio::time::sleep(std::time::Duration::from_millis(50)).await; | ||
} | ||
assert!(site.index.tracks(&d.did)); | ||
let at = grain_profile(&d).await.unwrap(); | ||
assert_eq!(at.as_object().unwrap().len(), 2, "{at}"); | ||
assert!(grain::lexicon::valid_datetime( | ||
at["createdAt"].as_str().unwrap() | ||
)); | ||
assert_eq!(seeded(&site.app, &d.did).await.unwrap(), Seeded::NoBluesky); | ||
assert!(grain_profile(&d).await.is_none()); | ||
assert_eq!(grain_profile(&d).await.unwrap(), at); | ||
| ||
// An account with no web session is not seeded, and says why. | ||
let none = seeded(&site.app, "did:plc:nobody").await.unwrap_err(); | ||