Skip to content

Commit

Permalink
feature: /dataset/visiblity route to the server to set visibility with
Browse files Browse the repository at this point in the history
an api key
  • Loading branch information
cdxker authored and skeptrunedev committed Oct 24, 2024
1 parent 8457683 commit ed2c4c0
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 18 deletions.
5 changes: 3 additions & 2 deletions server/migrations/2024-10-23-220021_public-page-config/up.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
-- Your SQL goes here
CREATE TABLE IF NOT EXISTS public_page_configuration (
id UUID PRIMARY KEY,
dataset_id UUID NOT NULL REFERENCES datasets(id) ON DELETE CASCADE,
dataset_id UUID NOT NULL UNIQUE REFERENCES datasets(id) ON DELETE CASCADE,
is_public boolean NOT NULL default false,
api_key Text NOT NULL,
created_at TIMESTAMP NOT NULL
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL
);

18 changes: 18 additions & 0 deletions server/src/data/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6834,4 +6834,22 @@ pub struct PublicPageConfiguration {
pub is_public: bool,
pub api_key: String,
pub created_at: chrono::NaiveDateTime,
pub updated_at: chrono::NaiveDateTime,
}

impl PublicPageConfiguration {
pub fn from_details(
dataset_id: uuid::Uuid,
is_public: bool,
api_key: String
) -> Self {
PublicPageConfiguration {
id: uuid::Uuid::new_v4(),
dataset_id,
is_public,
api_key,
created_at: chrono::Utc::now().naive_local(),
updated_at: chrono::Utc::now().naive_local(),
}
}
}
1 change: 1 addition & 0 deletions server/src/data/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ diesel::table! {
is_public -> Bool,
api_key -> Text,
created_at -> Timestamp,
updated_at -> Timestamp,
}
}

Expand Down
78 changes: 69 additions & 9 deletions server/src/handlers/page_handler.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
use crate::data::models::Pool;
use serde::Deserialize;
use crate::{
data::models::{DatasetAndOrgWithSubAndPlan, Pool},
errors::ServiceError, operators::user_operator::set_user_api_key_query,
};
use actix_web::{web, HttpResponse};
use minijinja::context;
use serde::Deserialize;

use crate::{data::models::Templates, operators::page_operator::get_page_by_dataset_id};
use crate::{
data::models::Templates,
operators::page_operator::{get_page_by_dataset_id, upsert_page_visiblity},
};

use super::{auth_handler::LoggedUser, user_handler::SetUserApiKeyRequest};

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicPageParams {
dataset_id: uuid::Uuid,
dataset_id: Option<uuid::Uuid>,
}

#[utoipa::path(
get,
path = "/public_page",
context_path = "/api",
tag = "Organization",
tag = "Public",
responses(
(status = 200, description = "Organization with the id that was requested", body = OrganizationWithSubAndPlan),
(status = 200, description = "Public Page associated to the dataset", body = OrganizationWithSubAndPlan),
(status = 400, description = "Service error relating to finding the organization by id", body = ErrorResponseBody),
(status = 404, description = "Organization not found", body = ErrorResponseBody)
),
Expand All @@ -30,9 +37,10 @@ pub async fn public_page(
page_params: web::Query<PublicPageParams>,
pool: web::Data<Pool>,
templates: Templates<'_>,
) -> Result<HttpResponse, actix_web::Error> {

let dataset_id = page_params.dataset_id;
) -> Result<HttpResponse, ServiceError> {
let Some(dataset_id) = page_params.dataset_id else {
return Ok(HttpResponse::NotFound().finish());
};

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

Expand All @@ -54,3 +62,55 @@ pub async fn public_page(
Ok(HttpResponse::Forbidden().finish())
}
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetDatasetVisibilityPayload {
pub is_public: bool,
pub api_key_params: SetUserApiKeyRequest,
}

#[utoipa::path(
put,
path = "/dataset/visibility",
context_path = "/api",
tag = "Public",
responses(
(status = 200, description = "Public Page associated to the dataset", body = OrganizationWithSubAndPlan),
(status = 400, description = "Service error relating to finding the organization by id", body = ErrorResponseBody),
(status = 404, description = "Organization not found", body = ErrorResponseBody)
),
params(
("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."),
("datasetId" = Option<uuid::Uuid>, Path, description = "The id of the organization you want to fetch."),
),
security(
("ApiKey" = ["admin"]),
)
)]
pub async fn set_dataset_visiblity(
page_params: web::Json<SetDatasetVisibilityPayload>,
user: LoggedUser,
pool: web::Data<Pool>,
dataset_org_plan_sub: DatasetAndOrgWithSubAndPlan,
) -> Result<HttpResponse, ServiceError> {
let role = page_params.api_key_params.role;

let new_api_key = set_user_api_key_query(
user.id,
page_params.api_key_params.name.clone(),
role.into(),
page_params.api_key_params.dataset_ids.clone(),
page_params.api_key_params.organization_ids.clone(),
page_params.api_key_params.scopes.clone(),
pool.clone()
)
.await
.map_err(|_err| ServiceError::BadRequest("Failed to set new API key for user".into()))?;

let dataset_id = dataset_org_plan_sub.dataset.id;

let page = upsert_page_visiblity(dataset_id, page_params.is_public, new_api_key, pool).await?;

Ok(HttpResponse::Ok().json(page))
}
10 changes: 5 additions & 5 deletions server/src/handlers/user_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,15 @@ pub async fn update_user(
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct SetUserApiKeyRequest {
/// The name which will be assigned to the new api key.
name: String,
pub name: String,
/// 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.
role: i32,
pub role: i32,
/// 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.
dataset_ids: Option<Vec<uuid::Uuid>>,
pub dataset_ids: Option<Vec<uuid::Uuid>>,
/// 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.
organization_ids: Option<Vec<uuid::Uuid>>,
pub organization_ids: Option<Vec<uuid::Uuid>>,
/// 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"].
scopes: Option<Vec<String>>,
pub scopes: Option<Vec<String>>,
}

#[derive(Serialize, Deserialize, ToSchema)]
Expand Down
6 changes: 6 additions & 0 deletions server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,12 @@ pub fn main() -> std::io::Result<()> {
)
.route(web::put().to(handlers::dataset_handler::update_dataset))
)
.service(
web::resource("/visibility")
.route(
web::put().to(handlers::page_handler::set_dataset_visiblity)
)
)
.service(
web::resource("/organization/{organization_id}").route(
web::get().to(
Expand Down
35 changes: 33 additions & 2 deletions server/src/operators/page_operator.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use crate::data::models::Pool;
use crate::data::models::PublicPageConfiguration;
use crate::data::schema::public_page_configuration;
use crate::errors::ServiceError;
use actix_web::web;
use diesel::prelude::*;
use diesel::upsert::excluded;
use diesel::QueryDsl;
use diesel_async::RunQueryDsl;

Expand All @@ -23,6 +25,35 @@ pub async fn get_page_by_dataset_id(
.load::<PublicPageConfiguration>(&mut conn)
.await
.map_err(|e| ServiceError::InternalServerError(e.to_string()))?
.pop()
)
.pop())
}


pub async fn upsert_page_visiblity(
dataset_id: uuid::Uuid,
is_public: bool,
api_key: String,
pool: web::Data<Pool>,
) -> Result<PublicPageConfiguration, ServiceError> {
use crate::data::schema::public_page_configuration::dsl as public_page_configuration_table;

let page = PublicPageConfiguration::from_details(dataset_id, is_public, api_key);

let mut conn = pool
.get()
.await
.map_err(|e| ServiceError::InternalServerError(e.to_string()))?;

diesel::insert_into(public_page_configuration_table::public_page_configuration)
.values(&page)
.on_conflict(public_page_configuration::dataset_id)
.do_update()
.set(
public_page_configuration::is_public.eq(excluded(public_page_configuration::is_public)),
)
.execute(&mut conn)
.await
.map_err(|err| ServiceError::BadRequest(err.to_string()))?;

Ok(page)
}

0 comments on commit ed2c4c0

Please sign in to comment.