Merge pull request #214 from buster-so/evals

Evals -> Staging
This commit is contained in:
dal 2025-04-22 08:45:49 -07:00 committed by GitHub
commit a957aa351a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 105 additions and 42 deletions

View File

@ -30,7 +30,7 @@ pub async fn invite_user_handler(
// Fetch inviter details
let inviter = users::table
.find(inviting_user.id)
.filter(users::id.eq(inviting_user.id)) // Find the specific user
.select(User::as_select())
.first::<User>(&mut conn)
.await
@ -45,6 +45,7 @@ pub async fn invite_user_handler(
// Fetch organization details
let organization = organizations::table
.filter(organizations::id.eq(organization_id)) // Find the specific organization
.first::<Organization>(&mut conn)
.await
.context("Failed to find organization")?;
@ -53,6 +54,7 @@ pub async fn invite_user_handler(
let inviter_id = inviting_user.id;
let now = Utc::now();
let mut successful_emails: Vec<String> = Vec::new();
let assigned_role = UserOrganizationRole::RestrictedQuerier; // Define role once
for email in emails {
// Use a separate connection for each user to avoid holding one connection for the whole loop
@ -64,45 +66,78 @@ pub async fn invite_user_handler(
}
};
// 1. Generate ID and construct attributes first
let new_user_id = Uuid::new_v4();
let user_email = email.clone();
let assigned_role = UserOrganizationRole::RestrictedQuerier;
let user_attributes = json!({
"user_id": new_user_id.to_string(),
"user_email": user_email,
"organization_id": organization_id.to_string(),
"organization_role": format!("{:?}", assigned_role)
});
// 2. Create User struct instance
let user_to_insert = User {
id: new_user_id,
email: email.clone(),
name: None,
config: json!({}),
created_at: now,
updated_at: now,
attributes: user_attributes,
avatar_url: None,
};
// 3. Insert user
let user_insert_result = diesel::insert_into(users::table)
.values(&user_to_insert)
.execute(&mut user_conn)
let email_for_lookup = email.clone(); // Clone email for lookup/use
let existing_user_result = users::table
.filter(users::email.eq(&email_for_lookup))
.select(User::as_select())
.first::<User>(&mut user_conn)
.await;
if let Err(e) = user_insert_result {
tracing::error!(error = %e, email = %email, "Failed to insert user record");
continue; // Skip this user if insertion fails
}
let user_id: Uuid;
let user_email_for_attributes: String;
match existing_user_result {
Ok(user) => {
// User exists, use their ID
user_id = user.id;
user_email_for_attributes = user.email; // Use existing user's email
tracing::info!(email = %user_email_for_attributes, user_id = %user_id, "User already exists, using existing ID for organization association.");
}
Err(diesel::NotFound) => {
// User does not exist, create them
let new_user_id = Uuid::new_v4();
user_email_for_attributes = email.clone(); // Use the email from the loop input
// Attributes for the new user
let user_attributes = json!({
"user_id": new_user_id.to_string(),
"user_email": user_email_for_attributes,
"organization_id": organization_id.to_string(),
"organization_role": format!("{:?}", assigned_role) // Use pre-defined role
});
let user_to_insert = User {
id: new_user_id,
email: email.clone(), // Use email from loop input
name: None,
config: json!({}),
created_at: now,
updated_at: now,
attributes: user_attributes,
avatar_url: None,
};
// Insert the new user
match diesel::insert_into(users::table)
.values(&user_to_insert)
.execute(&mut user_conn)
.await
{
Ok(_) => {
user_id = new_user_id; // Assign the new ID
tracing::info!(email = %email, user_id = %user_id, "Successfully inserted new user.");
}
Err(e) => {
// Handle unexpected insertion errors (e.g., DB connection issue mid-operation)
tracing::error!(error = %e, email = %email, "Failed to insert non-existent user record");
continue; // Skip this user if insertion fails unexpectedly
}
}
}
Err(e) => {
// Other database error during lookup
tracing::error!(error = %e, email = %email, "Failed to query for existing user");
continue; // Skip this user
}
};
// At this point, user_id is set (either existing or newly created)
// Attempt to add the user to the organization, ignoring conflicts
// 4. Create UserToOrganization struct instance
let user_org_to_insert = UserToOrganization {
user_id: new_user_id,
user_id, // Use the determined user_id
organization_id,
role: assigned_role,
role: assigned_role, // Use pre-defined role
sharing_setting: SharingSetting::None,
edit_sql: false,
upload_csv: false,
@ -117,21 +152,32 @@ pub async fn invite_user_handler(
status: UserOrganizationStatus::Active,
};
// 5. Insert user organization mapping
// Insert user organization mapping with ON CONFLICT DO NOTHING
let user_org_insert_result = diesel::insert_into(users_to_organizations::table)
.values(&user_org_to_insert)
.on_conflict((
users_to_organizations::user_id,
users_to_organizations::organization_id,
))
.do_nothing() // If user is already in the org, do nothing
.execute(&mut user_conn)
.await;
match user_org_insert_result {
Ok(_) => {
// Only add email if both inserts were successful
successful_emails.push(email.clone());
Ok(rows_affected) => {
// User was either newly inserted or already existed in the org.
// The association is now confirmed.
if rows_affected > 0 {
tracing::info!(email = %email, user_id = %user_id, organization_id = %organization_id, "Successfully associated user with organization.");
} else {
tracing::info!(email = %email, user_id = %user_id, organization_id = %organization_id, "User association with organization already existed.");
}
successful_emails.push(email); // Use the original email from the loop input
}
Err(e) => {
tracing::error!(error = %e, email = %email, user_id = %new_user_id, "Failed to insert user_to_organization record");
// Consider rolling back the user insert here if desired, although it's complex without a transaction per user.
// For now, we just log and don't add the email to the success list.
// Handle unexpected insertion errors for the join table
tracing::error!(error = %e, email = %email, user_id = %user_id, "Failed unexpectedly while inserting or ignoring user_to_organization record");
// Don't add to successful_emails if the DB operation failed unexpectedly
}
};
}

View File

@ -50,6 +50,14 @@ SELECT alter_fk_on_update('collections', 'created_by', 'users', 'id', 'CASCADE')
SELECT alter_fk_on_update('collections', 'updated_by', 'users', 'id', 'CASCADE');
SELECT alter_fk_on_update('collections_to_assets', 'created_by', 'users', 'id', 'CASCADE');
SELECT alter_fk_on_update('collections_to_assets', 'updated_by', 'users', 'id', 'CASCADE');
-- Clean up orphan dashboard_files before altering the FK
DELETE FROM dashboard_files
WHERE created_by IS NOT NULL
AND NOT EXISTS (
SELECT 1 FROM users WHERE users.id = dashboard_files.created_by
);
SELECT alter_fk_on_update('dashboard_files', 'created_by', 'users', 'id', 'CASCADE');
SELECT alter_fk_on_update('dashboard_files', 'publicly_enabled_by', 'users', 'id', 'CASCADE');
SELECT alter_fk_on_update('dashboards', 'created_by', 'users', 'id', 'CASCADE');

View File

@ -24,6 +24,9 @@ const options: Partial<PostHogConfig> = {
person_profiles: 'always',
session_recording: {
recordBody: true
},
loaded: (x) => {
console.log('Posthog loaded', x);
}
};
@ -34,6 +37,12 @@ const PosthogWrapper: React.FC<PropsWithChildren<{}>> = ({ children }) => {
const team = userTeams[0];
useEffect(() => {
console.log('user', user);
console.log('team', team);
console.log(POSTHOG_KEY);
console.log(isServer);
console.log(posthog);
if (POSTHOG_KEY && !isServer && user && posthog && team) {
posthog.init(POSTHOG_KEY, options);