Skip to content

Commit ed2c4c0

Browse files
cdxkerskeptrunedev
authored andcommitted
feature: /dataset/visiblity route to the server to set visibility with
an api key
1 parent 8457683 commit ed2c4c0

File tree

7 files changed

+135
-18
lines changed

7 files changed

+135
-18
lines changed
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
-- Your SQL goes here
22
CREATE TABLE IF NOT EXISTS public_page_configuration (
33
id UUID PRIMARY KEY,
4-
dataset_id UUID NOT NULL REFERENCES datasets(id) ON DELETE CASCADE,
4+
dataset_id UUID NOT NULL UNIQUE REFERENCES datasets(id) ON DELETE CASCADE,
55
is_public boolean NOT NULL default false,
66
api_key Text NOT NULL,
7-
created_at TIMESTAMP NOT NULL
7+
created_at TIMESTAMP NOT NULL,
8+
updated_at TIMESTAMP NOT NULL
89
);
910

server/src/data/models.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6834,4 +6834,22 @@ pub struct PublicPageConfiguration {
68346834
pub is_public: bool,
68356835
pub api_key: String,
68366836
pub created_at: chrono::NaiveDateTime,
6837+
pub updated_at: chrono::NaiveDateTime,
6838+
}
6839+
6840+
impl PublicPageConfiguration {
6841+
pub fn from_details(
6842+
dataset_id: uuid::Uuid,
6843+
is_public: bool,
6844+
api_key: String
6845+
) -> Self {
6846+
PublicPageConfiguration {
6847+
id: uuid::Uuid::new_v4(),
6848+
dataset_id,
6849+
is_public,
6850+
api_key,
6851+
created_at: chrono::Utc::now().naive_local(),
6852+
updated_at: chrono::Utc::now().naive_local(),
6853+
}
6854+
}
68376855
}

server/src/data/schema.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ diesel::table! {
195195
is_public -> Bool,
196196
api_key -> Text,
197197
created_at -> Timestamp,
198+
updated_at -> Timestamp,
198199
}
199200
}
200201

server/src/handlers/page_handler.rs

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
1-
use crate::data::models::Pool;
2-
use serde::Deserialize;
1+
use crate::{
2+
data::models::{DatasetAndOrgWithSubAndPlan, Pool},
3+
errors::ServiceError, operators::user_operator::set_user_api_key_query,
4+
};
35
use actix_web::{web, HttpResponse};
46
use minijinja::context;
7+
use serde::Deserialize;
58

6-
use crate::{data::models::Templates, operators::page_operator::get_page_by_dataset_id};
9+
use crate::{
10+
data::models::Templates,
11+
operators::page_operator::{get_page_by_dataset_id, upsert_page_visiblity},
12+
};
713

14+
use super::{auth_handler::LoggedUser, user_handler::SetUserApiKeyRequest};
815

916
#[derive(Deserialize)]
1017
#[serde(rename_all = "camelCase")]
1118
pub struct PublicPageParams {
12-
dataset_id: uuid::Uuid,
19+
dataset_id: Option<uuid::Uuid>,
1320
}
1421

1522
#[utoipa::path(
1623
get,
1724
path = "/public_page",
1825
context_path = "/api",
19-
tag = "Organization",
26+
tag = "Public",
2027
responses(
21-
(status = 200, description = "Organization with the id that was requested", body = OrganizationWithSubAndPlan),
28+
(status = 200, description = "Public Page associated to the dataset", body = OrganizationWithSubAndPlan),
2229
(status = 400, description = "Service error relating to finding the organization by id", body = ErrorResponseBody),
2330
(status = 404, description = "Organization not found", body = ErrorResponseBody)
2431
),
@@ -30,9 +37,10 @@ pub async fn public_page(
3037
page_params: web::Query<PublicPageParams>,
3138
pool: web::Data<Pool>,
3239
templates: Templates<'_>,
33-
) -> Result<HttpResponse, actix_web::Error> {
34-
35-
let dataset_id = page_params.dataset_id;
40+
) -> Result<HttpResponse, ServiceError> {
41+
let Some(dataset_id) = page_params.dataset_id else {
42+
return Ok(HttpResponse::NotFound().finish());
43+
};
3644

3745
let page = get_page_by_dataset_id(dataset_id, pool).await?;
3846

@@ -54,3 +62,55 @@ pub async fn public_page(
5462
Ok(HttpResponse::Forbidden().finish())
5563
}
5664
}
65+
66+
#[derive(Deserialize)]
67+
#[serde(rename_all = "camelCase")]
68+
pub struct SetDatasetVisibilityPayload {
69+
pub is_public: bool,
70+
pub api_key_params: SetUserApiKeyRequest,
71+
}
72+
73+
#[utoipa::path(
74+
put,
75+
path = "/dataset/visibility",
76+
context_path = "/api",
77+
tag = "Public",
78+
responses(
79+
(status = 200, description = "Public Page associated to the dataset", body = OrganizationWithSubAndPlan),
80+
(status = 400, description = "Service error relating to finding the organization by id", body = ErrorResponseBody),
81+
(status = 404, description = "Organization not found", body = ErrorResponseBody)
82+
),
83+
params(
84+
("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."),
85+
("datasetId" = Option<uuid::Uuid>, Path, description = "The id of the organization you want to fetch."),
86+
),
87+
security(
88+
("ApiKey" = ["admin"]),
89+
)
90+
)]
91+
pub async fn set_dataset_visiblity(
92+
page_params: web::Json<SetDatasetVisibilityPayload>,
93+
user: LoggedUser,
94+
pool: web::Data<Pool>,
95+
dataset_org_plan_sub: DatasetAndOrgWithSubAndPlan,
96+
) -> Result<HttpResponse, ServiceError> {
97+
let role = page_params.api_key_params.role;
98+
99+
let new_api_key = set_user_api_key_query(
100+
user.id,
101+
page_params.api_key_params.name.clone(),
102+
role.into(),
103+
page_params.api_key_params.dataset_ids.clone(),
104+
page_params.api_key_params.organization_ids.clone(),
105+
page_params.api_key_params.scopes.clone(),
106+
pool.clone()
107+
)
108+
.await
109+
.map_err(|_err| ServiceError::BadRequest("Failed to set new API key for user".into()))?;
110+
111+
let dataset_id = dataset_org_plan_sub.dataset.id;
112+
113+
let page = upsert_page_visiblity(dataset_id, page_params.is_public, new_api_key, pool).await?;
114+
115+
Ok(HttpResponse::Ok().json(page))
116+
}

server/src/handlers/user_handler.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,15 @@ pub async fn update_user(
107107
#[derive(Debug, Serialize, Deserialize, ToSchema)]
108108
pub struct SetUserApiKeyRequest {
109109
/// The name which will be assigned to the new api key.
110-
name: String,
110+
pub name: String,
111111
/// The role which will be assigned to the new api key. Either 0 (read), 1 (read and write at the level of the currently auth'ed user). The auth'ed user must have a role greater than or equal to the role being assigned which means they must be an admin (1) or owner (2) of the organization to assign write permissions with a role of 1.
112-
role: i32,
112+
pub role: i32,
113113
/// The dataset ids which the api key will have access to. If not provided or empty, the api key will have access to all datasets the auth'ed user has access to. If both dataset_ids and organization_ids are provided, the api key will have access to the intersection of the datasets and organizations.
114-
dataset_ids: Option<Vec<uuid::Uuid>>,
114+
pub dataset_ids: Option<Vec<uuid::Uuid>>,
115115
/// The organization ids which the api key will have access to. If not provided or empty, the api key will have access to all organizations the auth'ed user has access to.
116-
organization_ids: Option<Vec<uuid::Uuid>>,
116+
pub organization_ids: Option<Vec<uuid::Uuid>>,
117117
/// The routes which the api key will have access to. If not provided or empty, the api key will have access to all routes the auth'ed user has access to. Specify the routes as a list of strings. For example, ["GET /api/dataset", "POST /api/dataset"].
118-
scopes: Option<Vec<String>>,
118+
pub scopes: Option<Vec<String>>,
119119
}
120120

121121
#[derive(Serialize, Deserialize, ToSchema)]

server/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,12 @@ pub fn main() -> std::io::Result<()> {
791791
)
792792
.route(web::put().to(handlers::dataset_handler::update_dataset))
793793
)
794+
.service(
795+
web::resource("/visibility")
796+
.route(
797+
web::put().to(handlers::page_handler::set_dataset_visiblity)
798+
)
799+
)
794800
.service(
795801
web::resource("/organization/{organization_id}").route(
796802
web::get().to(

server/src/operators/page_operator.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use crate::data::models::Pool;
22
use crate::data::models::PublicPageConfiguration;
3+
use crate::data::schema::public_page_configuration;
34
use crate::errors::ServiceError;
45
use actix_web::web;
56
use diesel::prelude::*;
7+
use diesel::upsert::excluded;
68
use diesel::QueryDsl;
79
use diesel_async::RunQueryDsl;
810

@@ -23,6 +25,35 @@ pub async fn get_page_by_dataset_id(
2325
.load::<PublicPageConfiguration>(&mut conn)
2426
.await
2527
.map_err(|e| ServiceError::InternalServerError(e.to_string()))?
26-
.pop()
27-
)
28+
.pop())
29+
}
30+
31+
32+
pub async fn upsert_page_visiblity(
33+
dataset_id: uuid::Uuid,
34+
is_public: bool,
35+
api_key: String,
36+
pool: web::Data<Pool>,
37+
) -> Result<PublicPageConfiguration, ServiceError> {
38+
use crate::data::schema::public_page_configuration::dsl as public_page_configuration_table;
39+
40+
let page = PublicPageConfiguration::from_details(dataset_id, is_public, api_key);
41+
42+
let mut conn = pool
43+
.get()
44+
.await
45+
.map_err(|e| ServiceError::InternalServerError(e.to_string()))?;
46+
47+
diesel::insert_into(public_page_configuration_table::public_page_configuration)
48+
.values(&page)
49+
.on_conflict(public_page_configuration::dataset_id)
50+
.do_update()
51+
.set(
52+
public_page_configuration::is_public.eq(excluded(public_page_configuration::is_public)),
53+
)
54+
.execute(&mut conn)
55+
.await
56+
.map_err(|err| ServiceError::BadRequest(err.to_string()))?;
57+
58+
Ok(page)
2859
}

0 commit comments

Comments
 (0)