Skip to content

Commit

Permalink
feat(be/user): filter user badge
Browse files Browse the repository at this point in the history
  • Loading branch information
RickyLB authored Jul 18, 2023
2 parents 03faaba + 0a38408 commit 7cde8ab
Show file tree
Hide file tree
Showing 17 changed files with 1,159 additions and 1,020 deletions.
17 changes: 9 additions & 8 deletions backend/api/fixtures/1_user.sql
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
insert into "user" (id, created_at)
values ('1f241e1b-b537-493f-a230-075cb16315be', '2020-08-08T00:11:21Z'::timestamptz), -- user 0: complete admin, used for general API testing
values ('1f241e1b-b537-493f-a230-075cb16315be', '2020-08-09T00:11:21Z'::timestamptz), -- user 0: complete admin, used for general API testing
('7b96a41c-e406-11eb-8176-efd86dd7f444', '2020-08-08T00:11:21Z'::timestamptz), -- user 1: incomplete user, needs profile to complete registration
('a641fd6e-e41b-11eb-8176-57df101c2201', now()); -- user 2: registering user, needs email verification
('a641fd6e-e41b-11eb-8176-57df101c2201', '2020-08-07T00:11:21Z'::timestamptz); -- user 2: registering user, needs email verification

insert into user_email (user_id, email, created_at)
values ('1f241e1b-b537-493f-a230-075cb16315be', '[email protected]', '2020-08-08T00:11:21Z'::timestamptz),
('7b96a41c-e406-11eb-8176-efd86dd7f444', '[email protected]', '2020-08-08T00:11:21Z'::timestamptz);
values ('1f241e1b-b537-493f-a230-075cb16315be', '[email protected]', '2020-08-09T00:11:21Z'::timestamptz),
('7b96a41c-e406-11eb-8176-efd86dd7f444', '[email protected]', '2020-08-08T00:11:21Z'::timestamptz),
('a641fd6e-e41b-11eb-8176-57df101c2201', '[email protected]', '2020-08-07T00:11:21Z'::timestamptz);

-- password is 'password1'
insert into "user_auth_basic" (user_id, email, password) values ('1f241e1b-b537-493f-a230-075cb16315be', '[email protected]', '$argon2id$v=19$m=8192,t=16,p=1$3f60oO10WmwVJ9MIFf1f6w$CcjLqbHaDP7cJXAut6S9cmgGg6NL2Jsg++aIpdvmaBg'), -- 0
Expand All @@ -19,13 +20,13 @@ insert into "session" (user_id, token, scope_mask) values ('1f241e1b-b537-493f-a
('a641fd6e-e41b-11eb-8176-57df101c2201', 'L6gfXvgZeUBt8pdmLBnsGPEWUe3qGCK2_DF', 4); -- user 2

insert into "user_profile" (user_id, username, created_at, given_name, family_name, languages_spoken,
opt_into_edu_resources, over_18, timezone, organization, languages_spoken_public, organization_public)
opt_into_edu_resources, over_18, timezone, organization, languages_spoken_public, organization_public, badge)
values ('1f241e1b-b537-493f-a230-075cb16315be', 'test',
'2020-08-08T00:11:21Z'::timestamptz, 'Bobby', 'Tables', '{en_US}', true, true, 'US/Pacific', 'test org', false, true),
'2020-08-08T00:11:21Z'::timestamptz, 'Bobby', 'Tables', '{en_US}', true, true, 'US/Pacific', 'test org', false, true, 1),
('7b96a41c-e406-11eb-8176-efd86dd7f444', 'test1',
'2020-08-08T00:11:21Z'::timestamptz, 'Post', 'Gres', '{en_US}', true, true, 'US/Pacific', 'test1 org', true, true),
'2020-08-08T00:11:21Z'::timestamptz, 'Post', 'Gres', '{en_US}', true, true, 'US/Pacific', 'test1 org', true, true, 0),
('a641fd6e-e41b-11eb-8176-57df101c2201', 'test2',
'2020-08-08T00:11:21Z'::timestamptz, 'Scrappy', 'Doo', '{en_US}', true, true, 'US/Pacific', 'test2 org', false, true);
'2020-08-08T00:11:21Z'::timestamptz, 'Scrappy', 'Doo', '{en_US}', true, true, 'US/Pacific', 'test2 org', false, true, 10);

-- 1 is "Admin"
insert into "user_scope" (user_id, scope)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- set no badge users

update user_profile
set badge = 10
where badge is null;

alter table user_profile
alter column badge set default 10,
alter column badge set not null;
1,908 changes: 955 additions & 953 deletions backend/api/sqlx-data.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions backend/api/src/db/jig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use shared::domain::{
user::{UserId, UserScope},
};
use sqlx::{types::Json, PgConnection, PgPool};
use std::{collections::HashMap, ops::Deref};
use std::collections::HashMap;
use tracing::{instrument, Instrument};
use uuid::Uuid;

Expand Down Expand Up @@ -1429,7 +1429,7 @@ select exists (select 1 from "user" where id = $1) as "check_to!"
let mut ids = Vec::new();

for id in jig_ids {
let id = id.deref().0;
let id = id.0;

ids.push(id)
}
Expand Down
43 changes: 27 additions & 16 deletions backend/api/src/db/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ select
language_app,
language_emails,
bio,
badge as "badge?: UserBadge",
(select case when badge <> 10 then badge else null end) as "badge?: UserBadge",
location_public,
languages_spoken_public,
persona_public,
Expand Down Expand Up @@ -137,24 +137,27 @@ where id = $1"#,
}))
}

#[instrument(skip(db))]
pub async fn browse(
db: &sqlx::Pool<sqlx::Postgres>,
author_id: Option<UserId>,
page: i32,
page_limit: u32,
badge: Vec<UserBadge>,
) -> sqlx::Result<Vec<UserResponse>> {
let mut txn = db.begin().await?;

let badges: Vec<i16> = badge.iter().map(|x| *x as i16).collect();

let users = sqlx::query!(
//language=SQL
r#"
with cte as (
select (array_agg("user".id))[1]
from "user"
left join user_profile on "user".id = user_profile.user_id
left join user_email using(user_id)
where ("user".id = $1 or $1 is null)
select (array_agg(user_profile.user_id))[1]
from user_profile
inner join "user" on "user".id = user_profile.user_id
inner join user_email on user_profile.user_id = user_email.user_id
where (user_profile.user_id = $1 or $1 is null)
and (user_profile.badge = any($4) or $4 = array[]::smallint[])
group by "user".created_at
order by "user".created_at desc
),
Expand All @@ -169,19 +172,20 @@ select cte1.id as "id!: UserId",
user_email.email::text as "email!",
language_emails,
user_email.created_at as "created_at!",
badge as "badge?: UserBadge",
(select case when badge <> 10 then badge else null end) as "badge?: UserBadge",
organization,
location
from cte1
inner join user_profile on cte1.id = user_profile.user_id
inner join user_email using(user_id)
inner join user_email on cte1.id = user_email.user_id
order by ord asc
limit $3
offset $2
"#,
author_id.map(|x| x.0),
(page * page_limit as i32) as i32,
page_limit as i32,
&badges[..]
)
.fetch_all(&mut txn)
.instrument(tracing::info_span!("query user_profile"))
Expand Down Expand Up @@ -227,7 +231,7 @@ select "user".id as "id!: UserId",
user_email.email::text as "email!",
language_emails,
user_email.created_at as "created_at!",
badge as "badge?: UserBadge",
(select case when badge <> 10 then badge else null end) as "badge?: UserBadge",
organization,
location
from "user"
Expand Down Expand Up @@ -1098,23 +1102,30 @@ where index > $2 and user_id = $1

// `None` here means do not filter.
#[instrument(skip(db))]
pub async fn filtered_count(db: &PgPool, user_id: Option<UserId>) -> sqlx::Result<u64> {
pub async fn filtered_count(
db: &PgPool,
user_id: Option<UserId>,
badge: Vec<UserBadge>,
) -> sqlx::Result<u64> {
let badges: Vec<i16> = badge.iter().map(|x| *x as i16).collect();

let users = sqlx::query!(
//language=SQL
r#"
with cte as (
select (array_agg(user_profile.user_id))[1]
from user_profile
left join "user" on "user".id = user_profile.user_id
left join user_email using(user_id)
inner join "user" on "user".id = user_profile.user_id
inner join user_email on user_email.user_id = user_profile.user_id
where ("user".id = $1 or $1 is null)
group by family_name
order by family_name desc
and (user_profile.badge = any($2) or $2 = array[]::smallint[])
group by "user".created_at
order by "user".created_at desc
)
select count(*) as "count!" from unnest(array(select cte.array_agg from cte)) with ordinality t(id, ord)
"#,
user_id.map(|it| it.0),

&badges[..]
)
.fetch_one(db)
.await?;
Expand Down
10 changes: 5 additions & 5 deletions backend/api/src/db/user/public_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub async fn get(
given_name as "given_name!",
family_name as "family_name!",
profile_image_id as "profile_image?: ImageId",
badge as "badge?: UserBadge",
(select case when badge <> 10 then badge else null end) as "badge?: UserBadge",
(select languages_spoken from user_profile where user_profile.user_id = "user".id and languages_spoken_public is true) as "languages_spoken?: Vec<String>",
(select organization from user_profile where user_profile.user_id = "user".id and organization_public is true) as "organization?",
(select persona from user_profile where user_profile.user_id = "user".id and persona_public is true) as "persona?: Vec<String>",
Expand Down Expand Up @@ -121,7 +121,7 @@ pub async fn browse_users(
given_name as "given_name!",
family_name as "family_name!",
profile_image_id as "profile_image?: ImageId",
badge as "badge?: UserBadge",
(select case when badge <> 10 then badge else null end) as "badge?: UserBadge",
(select languages_spoken from user_profile where user_profile.user_id = "user".id and languages_spoken_public is true) as "languages_spoken?: Vec<String>",
(select organization from user_profile where user_profile.user_id = "user".id and organization_public is true) as "organization?",
(select persona from user_profile where user_profile.user_id = "user".id and persona_public is true) as "persona?: Vec<String>",
Expand Down Expand Up @@ -363,7 +363,7 @@ pub async fn get_by_ids(
given_name as "given_name!",
family_name as "family_name!",
profile_image_id as "profile_image?: ImageId",
badge as "badge?: UserBadge",
(select case when badge <> 10 then badge else null end) as "badge?: UserBadge",
(select languages_spoken from user_profile where user_profile.user_id = "user".id and languages_spoken_public is true) as "languages_spoken?: Vec<String>",
(select organization from user_profile where user_profile.user_id = "user".id and organization_public is true) as "organization?",
(select persona from user_profile where user_profile.user_id = "user".id and persona_public is true) as "persona?: Vec<String>",
Expand Down Expand Up @@ -522,7 +522,7 @@ pub async fn browse_followers(
given_name as "given_name!",
family_name as "family_name!",
profile_image_id as "profile_image?: ImageId",
badge as "badge?: UserBadge",
(select case when badge <> 10 then badge else null end) as "badge?: UserBadge",
(select languages_spoken from user_profile where user_profile.user_id = "user".id and languages_spoken_public is true) as "languages_spoken?: Vec<String>",
(select organization from user_profile where user_profile.user_id = "user".id and organization_public is true) as "organization?",
(select persona from user_profile where user_profile.user_id = "user".id and persona_public is true) as "persona?: Vec<String>",
Expand Down Expand Up @@ -615,7 +615,7 @@ pub async fn browse_following(
given_name as "given_name!",
family_name as "family_name!",
profile_image_id as "profile_image?: ImageId",
badge as "badge?: UserBadge",
(select case when badge <> 10 then badge else null end) as "badge?: UserBadge",
(select languages_spoken from user_profile where user_profile.user_id = "user".id and languages_spoken_public is true) as "languages_spoken?: Vec<String>",
(select organization from user_profile where user_profile.user_id = "user".id and organization_public is true) as "organization?",
(select persona from user_profile where user_profile.user_id = "user".id and persona_public is true) as "persona?: Vec<String>",
Expand Down
4 changes: 3 additions & 1 deletion backend/api/src/http/endpoints/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1046,9 +1046,11 @@ async fn browse(
query.user_id,
query.page.unwrap_or(0) as i32,
page_limit,
query.badge.to_owned(),
);

let total_count_future = db::user::filtered_count(db.as_ref(), query.user_id);
let total_count_future =
db::user::filtered_count(db.as_ref(), query.user_id, query.badge.to_owned());

let (users, total_count) = try_join!(browse_future, total_count_future,)?;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
source: tests/integration/user.rs
expression: body
---
{
"users": [
{
"id": "a641fd6e-e41b-11eb-8176-57df101c2201",
"username": "test2",
"givenName": "Scrappy",
"familyName": "Doo",
"email": "[email protected]",
"country": null,
"state": null,
"city": null,
"organization": "test2 org",
"createdAt": "2020-08-07",
"language": "en",
"badge": null
}
],
"pages": 1,
"totalUserCount": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
source: tests/integration/user.rs
expression: body
---
{
"users": [
{
"id": "1f241e1b-b537-493f-a230-075cb16315be",
"username": "test",
"givenName": "Bobby",
"familyName": "Tables",
"email": "[email protected]",
"country": null,
"state": null,
"city": null,
"organization": "test org",
"createdAt": "2020-08-09",
"language": "en",
"badge": "jiTeam"
}
],
"pages": 1,
"totalUserCount": 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ expression: body
"over_18": true,
"timezone": "US/Pacific",
"bio": "",
"badge": null,
"badge": "jiTeam",
"location_public": true,
"organization_public": true,
"persona_public": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ expression: body
"over_18": true,
"timezone": "US/Pacific",
"bio": "a test user",
"badge": null,
"badge": "jiTeam",
"location_public": true,
"organization_public": true,
"persona_public": true,
Expand Down
42 changes: 40 additions & 2 deletions backend/api/tests/integration/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use crate::{
use http::StatusCode;
use macros::test_service;

use shared::domain::{meta::AffiliationId, user::PatchProfileRequest};
use shared::domain::{
meta::AffiliationId,
user::{PatchProfileRequest, UserBadge},
};
use sqlx::postgres::{PgConnectOptions, PgPoolOptions};

mod color;
Expand Down Expand Up @@ -90,7 +93,42 @@ async fn patch_profile(port: u16) -> anyhow::Result<()> {
Ok(())
}

//
#[test_service(setup = "setup_service", fixtures("Fixture::User", "Fixture::Image"))]
async fn browse_user_badges(port: u16) -> anyhow::Result<()> {
let name = "browse_user_badges";

let client = reqwest::Client::new();

let resp = client
.get(&format!("http://0.0.0.0:{}/v1/user/browse", port))
.query(&[("badge", "noBadge")])
.login()
.send()
.await?
.error_for_status()?;

let body: serde_json::Value = resp.json().await?;

insta::assert_json_snapshot!(
format!("{}-1", name),
body, { ".updated_at" => "[timestamptz]" });

let resp = client
.get(&format!("http://0.0.0.0:{}/v1/user/browse", port))
.query(&[("badge", "jiTeam")])
.login()
.send()
.await?
.error_for_status()?;

let body: serde_json::Value = resp.json().await?;

insta::assert_json_snapshot!(
format!("{}-2", name),
body, { ".updated_at" => "[timestamptz]" });

Ok(())
}

// Ignored tests aren't captured. Will resolve later
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ expression: body
"familyName": "Tables",
"profileImage": null,
"following": false,
"badge": null,
"badge": "jiTeam",
"organization": "test org",
"circles": [],
"totalAssetCount": 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ expression: body
"familyName": "Gres",
"profileImage": null,
"following": true,
"badge": null,
"badge": "masterTeacher",
"languagesSpoken": [
"en_US"
],
Expand Down
Loading

0 comments on commit 7cde8ab

Please sign in to comment.