mirror of https://github.com/realaravinth/gitpad
feat: DB: visibility filtered gist retrieval
SUMMARY GistDatabase::get_user_gists retrieved all gists that were created by the user. GistDatabase::get_user_public_gists and GistDatabase::get_user_public_unlisted_gists produce visibility-filtered results DESCRIPTION Gist visibility levels are ordered in the following order: --------------------------------- | Public < Unlisted < Private | --------------------------------- A user with permissions to access a visibility-level will also have access to the levels lower levels to it. Accessibility filters use this mechanism to filter gists: a higher visibility-level request will also return gists with lower visibility-levels. GistDatabase::get_user_public_unlisted_gists: Return all gists that belong to a user with public(GistVisibility::Public) and unlisted(GistVisibility::UnliUnlisted) visibility levels. Gists with private(GistVisibility::Private) visibility levels are ignored. GistDatabase::get_user_public_gists: Return all gists that belong to a user with public(GistVisibility::Public) only is returned. Private and Unlisted resources are ignored. GistDatabase::get_user_gists: Returns all gists belonging to a usermaster
parent
7cdfcc47bb
commit
06830bfd2c
|
@ -236,6 +236,13 @@ pub trait GistDatabase: std::marker::Send + std::marker::Sync + CloneGistDatabas
|
||||||
/// Retrieve gists belonging to user
|
/// Retrieve gists belonging to user
|
||||||
async fn get_user_gists(&self, owner: &str) -> DBResult<Vec<Gist>>;
|
async fn get_user_gists(&self, owner: &str) -> DBResult<Vec<Gist>>;
|
||||||
|
|
||||||
|
/// Retrieve gists belonging to user that are [GistVisibility::Public]
|
||||||
|
async fn get_user_public_gists(&self, owner: &str) -> DBResult<Vec<Gist>>;
|
||||||
|
|
||||||
|
/// Retrieve gists belonging to user that are [GistVisibility::Public] and
|
||||||
|
/// [GistVisibility::UnliUnlisted]
|
||||||
|
async fn get_user_public_unlisted_gists(&self, owner: &str) -> DBResult<Vec<Gist>>;
|
||||||
|
|
||||||
/// Delete gist
|
/// Delete gist
|
||||||
async fn delete_gist(&self, owner: &str, public_id: &str) -> DBResult<()>;
|
async fn delete_gist(&self, owner: &str, public_id: &str) -> DBResult<()>;
|
||||||
|
|
||||||
|
@ -323,6 +330,14 @@ impl GistDatabase for Box<dyn GistDatabase> {
|
||||||
(**self).get_user_gists(owner).await
|
(**self).get_user_gists(owner).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_user_public_gists(&self, owner: &str) -> DBResult<Vec<Gist>> {
|
||||||
|
(**self).get_user_public_gists(owner).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_user_public_unlisted_gists(&self, owner: &str) -> DBResult<Vec<Gist>> {
|
||||||
|
(**self).get_user_public_unlisted_gists(owner).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn delete_gist(&self, owner: &str, public_id: &str) -> DBResult<()> {
|
async fn delete_gist(&self, owner: &str, public_id: &str) -> DBResult<()> {
|
||||||
(**self).delete_gist(owner, public_id).await
|
(**self).delete_gist(owner, public_id).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,6 +142,49 @@ pub async fn gists_work<T: GistDatabase>(
|
||||||
DBError::CommentNotFound
|
DBError::CommentNotFound
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// visibility filters
|
||||||
|
let create_unlisted_gist = CreateGist {
|
||||||
|
owner: username.into(),
|
||||||
|
description: Some("foo"),
|
||||||
|
public_id: &format!("{}unlisted", public_id),
|
||||||
|
visibility: &GistVisibility::Unlisted,
|
||||||
|
};
|
||||||
|
db.new_gist(&create_unlisted_gist).await.unwrap();
|
||||||
|
let create_private_gist = CreateGist {
|
||||||
|
owner: username.into(),
|
||||||
|
description: Some("foo"),
|
||||||
|
public_id: &format!("{}private", public_id),
|
||||||
|
visibility: &GistVisibility::Private,
|
||||||
|
};
|
||||||
|
db.new_gist(&create_private_gist).await.unwrap();
|
||||||
|
|
||||||
|
let public_gists = db.get_user_public_gists(username).await.unwrap();
|
||||||
|
assert_eq!(public_gists.len(), 1);
|
||||||
|
assert_gists(&create_gist, &public_gists[0]);
|
||||||
|
|
||||||
|
let public_unlisted_gists = db.get_user_public_unlisted_gists(username).await.unwrap();
|
||||||
|
assert_eq!(public_unlisted_gists.len(), 2);
|
||||||
|
for gist in public_unlisted_gists {
|
||||||
|
assert_ne!(gist.visibility, GistVisibility::Private);
|
||||||
|
if gist.visibility == GistVisibility::Public {
|
||||||
|
assert_gists(&create_gist, &gist);
|
||||||
|
} else {
|
||||||
|
assert_gists(&create_unlisted_gist, &gist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let all_gists = db.get_user_gists(username).await.unwrap();
|
||||||
|
assert_eq!(all_gists.len(), 3);
|
||||||
|
for gist in all_gists {
|
||||||
|
if gist.visibility == GistVisibility::Public {
|
||||||
|
assert_gists(&create_gist, &gist);
|
||||||
|
} else if gist.visibility == GistVisibility::Unlisted {
|
||||||
|
assert_gists(&create_unlisted_gist, &gist);
|
||||||
|
} else {
|
||||||
|
assert_gists(&create_private_gist, &gist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// delete gist
|
// delete gist
|
||||||
db.delete_gist(username, &create_gist.public_id)
|
db.delete_gist(username, &create_gist.public_id)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -72,6 +72,57 @@
|
||||||
"nullable": []
|
"nullable": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"36dec233992fb484e0ee86dac49197ddcea4e2ea47bb98a3aee89f857e13cc80": {
|
||||||
|
"query": "SELECT\n owner,\n visibility,\n created,\n updated,\n public_id,\n description\n FROM\n gists_gists_view\n WHERE \n owner = $1\n AND\n visibility = $2\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "owner",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "visibility",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "created",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "updated",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "public_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "description",
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"38409cc4e1e1edf0a5f15160d54a59b741fc668795bcdc11570e3963661c77e0": {
|
"38409cc4e1e1edf0a5f15160d54a59b741fc668795bcdc11570e3963661c77e0": {
|
||||||
"query": "SELECT\n owner,\n visibility,\n created,\n updated,\n public_id,\n description\n FROM\n gists_gists_view\n WHERE public_id = $1\n ",
|
"query": "SELECT\n owner,\n visibility,\n created,\n updated,\n public_id,\n description\n FROM\n gists_gists_view\n WHERE public_id = $1\n ",
|
||||||
"describe": {
|
"describe": {
|
||||||
|
@ -465,6 +516,57 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"f8f0c9da439206cfc4df5f916d9c4cf731c19cbf6c005a5e7f56dac5d3b90b8e": {
|
||||||
|
"query": "SELECT\n owner,\n visibility,\n created,\n updated,\n public_id,\n description\n FROM\n gists_gists_view\n WHERE \n owner = $1\n AND\n visibility <> $2\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "owner",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "visibility",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "created",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "updated",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "public_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "description",
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"fbeb7f634647d0082b9a083590b027ba668e59f2173e0699b78c2261ace2638f": {
|
"fbeb7f634647d0082b9a083590b027ba668e59f2173e0699b78c2261ace2638f": {
|
||||||
"query": "INSERT INTO gists_gists \n (owner_id , public_id, visibility, created, updated)\n VALUES (\n (SELECT ID FROM gists_users WHERE username = $1),\n $2, (SELECT ID FROM gists_visibility WHERE name = $3), $4, $5\n )",
|
"query": "INSERT INTO gists_gists \n (owner_id , public_id, visibility, created, updated)\n VALUES (\n (SELECT ID FROM gists_users WHERE username = $1),\n $2, (SELECT ID FROM gists_visibility WHERE name = $3), $4, $5\n )",
|
||||||
"describe": {
|
"describe": {
|
||||||
|
|
|
@ -398,6 +398,78 @@ impl GistDatabase for Database {
|
||||||
Ok(gists)
|
Ok(gists)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve gists belonging to user from database
|
||||||
|
async fn get_user_public_gists(&self, owner: &str) -> DBResult<Vec<Gist>> {
|
||||||
|
const PUBLIC: &str = GistVisibility::Public.to_str();
|
||||||
|
let mut res = sqlx::query_as!(
|
||||||
|
InnerGist,
|
||||||
|
"SELECT
|
||||||
|
owner,
|
||||||
|
visibility,
|
||||||
|
created,
|
||||||
|
updated,
|
||||||
|
public_id,
|
||||||
|
description
|
||||||
|
FROM
|
||||||
|
gists_gists_view
|
||||||
|
WHERE
|
||||||
|
owner = $1
|
||||||
|
AND
|
||||||
|
visibility = $2
|
||||||
|
",
|
||||||
|
owner,
|
||||||
|
PUBLIC
|
||||||
|
)
|
||||||
|
.fetch_all(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| match e {
|
||||||
|
Error::RowNotFound => DBError::GistNotFound,
|
||||||
|
e => DBError::DBError(Box::new(e)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut gists = Vec::with_capacity(res.len());
|
||||||
|
for r in res.drain(..) {
|
||||||
|
gists.push(r.to_gist()?);
|
||||||
|
}
|
||||||
|
Ok(gists)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve gists belonging to user from database
|
||||||
|
async fn get_user_public_unlisted_gists(&self, owner: &str) -> DBResult<Vec<Gist>> {
|
||||||
|
const PRIVATE: &str = GistVisibility::Private.to_str();
|
||||||
|
let mut res = sqlx::query_as!(
|
||||||
|
InnerGist,
|
||||||
|
"SELECT
|
||||||
|
owner,
|
||||||
|
visibility,
|
||||||
|
created,
|
||||||
|
updated,
|
||||||
|
public_id,
|
||||||
|
description
|
||||||
|
FROM
|
||||||
|
gists_gists_view
|
||||||
|
WHERE
|
||||||
|
owner = $1
|
||||||
|
AND
|
||||||
|
visibility <> $2
|
||||||
|
",
|
||||||
|
owner,
|
||||||
|
PRIVATE
|
||||||
|
)
|
||||||
|
.fetch_all(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| match e {
|
||||||
|
Error::RowNotFound => DBError::GistNotFound,
|
||||||
|
e => DBError::DBError(Box::new(e)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut gists = Vec::with_capacity(res.len());
|
||||||
|
for r in res.drain(..) {
|
||||||
|
gists.push(r.to_gist()?);
|
||||||
|
}
|
||||||
|
Ok(gists)
|
||||||
|
}
|
||||||
|
|
||||||
async fn delete_gist(&self, owner: &str, public_id: &str) -> DBResult<()> {
|
async fn delete_gist(&self, owner: &str, public_id: &str) -> DBResult<()> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"DELETE FROM gists_gists
|
"DELETE FROM gists_gists
|
||||||
|
|
|
@ -72,6 +72,54 @@
|
||||||
"nullable": []
|
"nullable": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"36dec233992fb484e0ee86dac49197ddcea4e2ea47bb98a3aee89f857e13cc80": {
|
||||||
|
"query": "SELECT\n owner,\n visibility,\n created,\n updated,\n public_id,\n description\n FROM\n gists_gists_view\n WHERE \n owner = $1\n AND\n visibility = $2\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "owner",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "visibility",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "created",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "updated",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "public_id",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"ordinal": 5,
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 2
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"38409cc4e1e1edf0a5f15160d54a59b741fc668795bcdc11570e3963661c77e0": {
|
"38409cc4e1e1edf0a5f15160d54a59b741fc668795bcdc11570e3963661c77e0": {
|
||||||
"query": "SELECT\n owner,\n visibility,\n created,\n updated,\n public_id,\n description\n FROM\n gists_gists_view\n WHERE public_id = $1\n ",
|
"query": "SELECT\n owner,\n visibility,\n created,\n updated,\n public_id,\n description\n FROM\n gists_gists_view\n WHERE public_id = $1\n ",
|
||||||
"describe": {
|
"describe": {
|
||||||
|
@ -422,6 +470,54 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"f8f0c9da439206cfc4df5f916d9c4cf731c19cbf6c005a5e7f56dac5d3b90b8e": {
|
||||||
|
"query": "SELECT\n owner,\n visibility,\n created,\n updated,\n public_id,\n description\n FROM\n gists_gists_view\n WHERE \n owner = $1\n AND\n visibility <> $2\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "owner",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "visibility",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "created",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "updated",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Int64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "public_id",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"ordinal": 5,
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 2
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"fbeb7f634647d0082b9a083590b027ba668e59f2173e0699b78c2261ace2638f": {
|
"fbeb7f634647d0082b9a083590b027ba668e59f2173e0699b78c2261ace2638f": {
|
||||||
"query": "INSERT INTO gists_gists \n (owner_id , public_id, visibility, created, updated)\n VALUES (\n (SELECT ID FROM gists_users WHERE username = $1),\n $2, (SELECT ID FROM gists_visibility WHERE name = $3), $4, $5\n )",
|
"query": "INSERT INTO gists_gists \n (owner_id , public_id, visibility, created, updated)\n VALUES (\n (SELECT ID FROM gists_users WHERE username = $1),\n $2, (SELECT ID FROM gists_visibility WHERE name = $3), $4, $5\n )",
|
||||||
"describe": {
|
"describe": {
|
||||||
|
|
|
@ -361,6 +361,78 @@ impl GistDatabase for Database {
|
||||||
Ok(gists)
|
Ok(gists)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve gists belonging to user from database
|
||||||
|
async fn get_user_public_gists(&self, owner: &str) -> DBResult<Vec<Gist>> {
|
||||||
|
const PUBLIC: &str = GistVisibility::Public.to_str();
|
||||||
|
let mut res = sqlx::query_as!(
|
||||||
|
InnerGist,
|
||||||
|
"SELECT
|
||||||
|
owner,
|
||||||
|
visibility,
|
||||||
|
created,
|
||||||
|
updated,
|
||||||
|
public_id,
|
||||||
|
description
|
||||||
|
FROM
|
||||||
|
gists_gists_view
|
||||||
|
WHERE
|
||||||
|
owner = $1
|
||||||
|
AND
|
||||||
|
visibility = $2
|
||||||
|
",
|
||||||
|
owner,
|
||||||
|
PUBLIC
|
||||||
|
)
|
||||||
|
.fetch_all(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| match e {
|
||||||
|
Error::RowNotFound => DBError::GistNotFound,
|
||||||
|
e => DBError::DBError(Box::new(e)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut gists = Vec::with_capacity(res.len());
|
||||||
|
for r in res.drain(..) {
|
||||||
|
gists.push(r.to_gist()?);
|
||||||
|
}
|
||||||
|
Ok(gists)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve gists belonging to user from database
|
||||||
|
async fn get_user_public_unlisted_gists(&self, owner: &str) -> DBResult<Vec<Gist>> {
|
||||||
|
const PRIVATE: &str = GistVisibility::Private.to_str();
|
||||||
|
let mut res = sqlx::query_as!(
|
||||||
|
InnerGist,
|
||||||
|
"SELECT
|
||||||
|
owner,
|
||||||
|
visibility,
|
||||||
|
created,
|
||||||
|
updated,
|
||||||
|
public_id,
|
||||||
|
description
|
||||||
|
FROM
|
||||||
|
gists_gists_view
|
||||||
|
WHERE
|
||||||
|
owner = $1
|
||||||
|
AND
|
||||||
|
visibility <> $2
|
||||||
|
",
|
||||||
|
owner,
|
||||||
|
PRIVATE
|
||||||
|
)
|
||||||
|
.fetch_all(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| match e {
|
||||||
|
Error::RowNotFound => DBError::GistNotFound,
|
||||||
|
e => DBError::DBError(Box::new(e)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut gists = Vec::with_capacity(res.len());
|
||||||
|
for r in res.drain(..) {
|
||||||
|
gists.push(r.to_gist()?);
|
||||||
|
}
|
||||||
|
Ok(gists)
|
||||||
|
}
|
||||||
|
|
||||||
async fn delete_gist(&self, owner: &str, public_id: &str) -> DBResult<()> {
|
async fn delete_gist(&self, owner: &str, public_id: &str) -> DBResult<()> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"DELETE FROM gists_gists
|
"DELETE FROM gists_gists
|
||||||
|
|
Loading…
Reference in New Issue