Skip to content

Commit b0e068a

Browse files
committed
Add groups list endpoint
1 parent 123c88a commit b0e068a

9 files changed

Lines changed: 173 additions & 4 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DROP VIEW groups_list;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CREATE VIEW groups_list AS SELECT groups.name, groups.typ, groups.trust, count(memberships.user_uuid) as members_count FROM groups JOIN memberships ON groups.group_id = memberships.group_id GROUP BY groups.group_id;

openapi.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,65 @@ paths:
3535
application/json:
3636
schema:
3737
$ref: "#/components/schemas/GenericError"
38+
get:
39+
summary: paginated groups
40+
description: |
41+
get paginated list of a groups
42+
parameters:
43+
- in: query
44+
name: f
45+
description: filter results based on name prefix
46+
required: false
47+
schema:
48+
type: string
49+
- in: query
50+
name: by
51+
description: order by
52+
required: false
53+
schema:
54+
type: string
55+
enum: ["MemberCount", "NameAsc", "NameDesc"]
56+
- in: query
57+
name: "n"
58+
description: next page id (offset)
59+
required: false
60+
schema:
61+
type: integer
62+
default: 0
63+
- in: query
64+
name: s
65+
description: size of the result
66+
required: false
67+
schema:
68+
type: integer
69+
default: 20
70+
responses:
71+
"200":
72+
description: a page of members
73+
content:
74+
application/json:
75+
schema:
76+
type: object
77+
properties:
78+
members:
79+
type: array
80+
items:
81+
$ref: "#/components/schemas/DisplayMember"
82+
next:
83+
type: integer
84+
example: 20
85+
"400":
86+
description: bad request
87+
content:
88+
application/json:
89+
schema:
90+
$ref: "#/components/schemas/GenericError"
91+
"403":
92+
description: operation forbidden
93+
content:
94+
application/json:
95+
schema:
96+
$ref: "#/components/schemas/GenericError"
3897
"/groups/api/v1/groups/{groupName}":
3998
get:
4099
summary: group information

src/api/groups.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::api::models::GroupInfo;
44
use crate::db::operations;
55
use crate::db::operations::models::GroupUpdate;
66
use crate::db::operations::models::NewGroup;
7+
use crate::db::operations::models::SortGroupsBy;
78
use crate::db::types::*;
89
use crate::db::Pool;
910
use crate::utils::valid_group_name;
@@ -16,11 +17,34 @@ use actix_web::Responder;
1617
use cis_client::CisClient;
1718
use dino_park_gate::scope::ScopeAndUser;
1819
use log::info;
20+
use serde_derive::Deserialize;
1921
use std::sync::Arc;
2022

23+
#[derive(Deserialize)]
24+
struct ListGroupsQuery {
25+
f: Option<String>,
26+
#[serde(default)]
27+
n: i64,
28+
#[serde(default = "default_groups_list_size")]
29+
s: i64,
30+
#[serde(default)]
31+
by: SortGroupsBy,
32+
}
33+
34+
fn default_groups_list_size() -> i64 {
35+
20
36+
}
37+
38+
#[guard(Authenticated)]
2139
async fn get_group(pool: web::Data<Pool>, group_name: web::Path<String>) -> impl Responder {
22-
operations::groups::get_group(&pool, &group_name)
23-
.map(|group| HttpResponse::Ok().json(group))
40+
operations::groups::get_group(&pool, &group_name).map(|group| HttpResponse::Ok().json(group))
41+
}
42+
43+
#[guard(Authenticated)]
44+
async fn list_groups(pool: web::Data<Pool>, query: web::Query<ListGroupsQuery>) -> impl Responder {
45+
let query = query.into_inner();
46+
operations::groups::list_groups(&pool, query.f, query.by, query.s, query.n)
47+
.map(|groups| HttpResponse::Ok().json(groups))
2448
.map_err(ApiError::GenericBadRequest)
2549
}
2650

@@ -142,7 +166,11 @@ pub fn groups_app() -> impl HttpServiceFactory {
142166
.max_age(3600)
143167
.finish(),
144168
)
145-
.service(web::resource("").route(web::post().to(add_group)))
169+
.service(
170+
web::resource("")
171+
.route(web::post().to(add_group))
172+
.route(web::get().to(list_groups)),
173+
)
146174
.service(
147175
web::resource("/{group_name}")
148176
.route(web::get().to(get_group))

src/db/internal/group.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@ use crate::db::model::*;
55
use crate::db::operations::models::GroupUpdate;
66
use crate::db::operations::models::GroupWithTermsFlag;
77
use crate::db::operations::models::NewGroup;
8+
use crate::db::operations::models::PaginatedGroupsLists;
9+
use crate::db::operations::models::SortGroupsBy;
810
use crate::db::schema;
911
use crate::db::types::*;
10-
12+
use crate::db::views;
1113
use diesel::dsl::exists;
1214
use diesel::dsl::select;
1315
use diesel::prelude::*;
1416
use failure::Error;
1517
use serde_json::Value;
18+
use std::convert::TryFrom;
1619
use uuid::Uuid;
1720

1821
pub fn get_group_with_terms_flag(
@@ -182,3 +185,27 @@ pub fn groups_for_user(connection: &PgConnection, user_uuid: &Uuid) -> Result<Ve
182185
.get_results::<Group>(connection)
183186
.map_err(Into::into)
184187
}
188+
189+
pub fn list_groups(
190+
connection: &PgConnection,
191+
filter: Option<String>,
192+
sort_by: SortGroupsBy,
193+
limit: i64,
194+
offset: i64,
195+
) -> Result<PaginatedGroupsLists, Error> {
196+
let mut query = views::groups_list::table.into_boxed();
197+
if let Some(filter) = filter {
198+
query = query.filter(views::groups_list::name.ilike(format!("{}%", filter)))
199+
};
200+
query = match sort_by {
201+
SortGroupsBy::MembersCount => query.order(views::groups_list::members_count.desc()),
202+
SortGroupsBy::NameAsc => query.order(views::groups_list::name.asc()),
203+
SortGroupsBy::NameDesc => query.order(views::groups_list::name.desc()),
204+
};
205+
let groups: Vec<GroupsList> = query.offset(offset).limit(limit).get_results(connection)?;
206+
let next = match i64::try_from(groups.len()) {
207+
Ok(x) if x == limit => Some(offset + x),
208+
_ => None,
209+
};
210+
Ok(PaginatedGroupsLists { groups, next })
211+
}

src/db/model.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ pub struct Invitation {
6060
pub added_by: Uuid,
6161
}
6262

63+
#[derive(Queryable, Serialize)]
64+
pub struct GroupsList {
65+
pub name: String,
66+
pub typ: GroupType,
67+
pub trust: TrustType,
68+
pub member_count: i64,
69+
}
70+
6371
#[derive(Insertable)]
6472
#[table_name = "groups"]
6573
pub struct InsertGroup {

src/db/operations/groups.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use crate::db::operations;
66
use crate::db::operations::models::GroupUpdate;
77
use crate::db::operations::models::GroupWithTermsFlag;
88
use crate::db::operations::models::NewGroup;
9+
use crate::db::operations::models::PaginatedGroupsLists;
10+
use crate::db::operations::models::SortGroupsBy;
911
use crate::db::Pool;
1012
use crate::error::PacksError;
1113
use crate::rules::engine::CREATE_GROUP;
@@ -123,3 +125,14 @@ pub fn get_group_with_terms_flag(
123125
let connection = pool.get()?;
124126
internal::group::get_group_with_terms_flag(&connection, group_name)
125127
}
128+
129+
pub fn list_groups(
130+
pool: &Pool,
131+
filter: Option<String>,
132+
sort_by: SortGroupsBy,
133+
limit: i64,
134+
offset: i64,
135+
) -> Result<PaginatedGroupsLists, Error> {
136+
let connection = pool.get()?;
137+
internal::group::list_groups(&connection, filter, sort_by, limit, offset)
138+
}

src/db/operations/models.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
use crate::db::model::Group;
2+
use crate::db::model::GroupsList;
23
use crate::db::types::*;
34
use chrono::NaiveDateTime;
45
use serde_derive::Deserialize;
56
use serde_derive::Serialize;
67
use uuid::Uuid;
78

9+
#[derive(Deserialize)]
10+
pub enum SortGroupsBy {
11+
MembersCount,
12+
NameAsc,
13+
NameDesc,
14+
}
15+
16+
impl Default for SortGroupsBy {
17+
fn default() -> Self {
18+
Self::MembersCount
19+
}
20+
}
21+
822
#[derive(Deserialize)]
923
pub struct GroupUpdate {
1024
pub description: Option<String>,
@@ -215,6 +229,12 @@ pub struct PaginatedDisplayMembersAndHost {
215229
pub next: Option<i64>,
216230
}
217231

232+
#[derive(Serialize)]
233+
pub struct PaginatedGroupsLists {
234+
pub groups: Vec<GroupsList>,
235+
pub next: Option<i64>,
236+
}
237+
218238
#[cfg(test)]
219239
mod test {
220240
use super::*;

src/db/views.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,15 @@ hosts_table!(hosts_ndaed, users_ndaed);
2626
hosts_table!(hosts_vouched, users_vouched);
2727
hosts_table!(hosts_authenticated, users_authenticated);
2828
hosts_table!(hosts_public, users_public);
29+
30+
table! {
31+
use diesel::sql_types::*;
32+
use crate::db::types::*;
33+
34+
groups_list (name) {
35+
name -> VarChar,
36+
typ -> Group_type,
37+
trust -> Trust_type,
38+
members_count -> BigInt,
39+
}
40+
}

0 commit comments

Comments
 (0)