-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
added schema and crl apis for organisation #322
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
#![deny(unused_crate_dependencies)] | ||
mod app_state; | ||
mod auth; | ||
mod organisation; | ||
|
||
use idgenerator::{IdGeneratorOptions, IdInstance}; | ||
use std::{collections::HashSet, io::Result, time::Duration}; | ||
|
||
use actix_files::Files; | ||
|
@@ -44,6 +46,15 @@ async fn main() -> Result<()> { | |
let service_prefix: String = | ||
get_from_env_unsafe("SERVICE_PREFIX").expect("SERVICE_PREFIX is not set"); | ||
|
||
let worker_id: u32 = get_from_env_unsafe("WORKER_ID").expect("WORKER_ID is not set"); | ||
|
||
let options = IdGeneratorOptions::new() | ||
.worker_id(worker_id) | ||
.worker_id_bit_len(8) | ||
.seq_bit_len(12); | ||
|
||
IdInstance::init(options).expect("Failed to initialize ID generator"); | ||
|
||
/* | ||
Reading from a env returns a String at best we cannot obtain a &'static str from it, | ||
which seems logical as it not known at compiletime, and there is no straightforward way to do this. | ||
|
@@ -181,6 +192,11 @@ async fn main() -> Result<()> { | |
AppExecutionScopeMiddlewareFactory::new(AppScope::EXPERIMENTATION), | ||
), | ||
) | ||
.service( | ||
scope("/organisation") | ||
.wrap(AppExecutionScopeMiddlewareFactory::new(AppScope::SUPERPOSITION)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this has to be cleaned up later |
||
.service(organisation::endpoints()), | ||
) | ||
Comment on lines
+195
to
+199
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. by the way, this url would fail right ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we need to add this in the tenant exclusion list as well |
||
/***************************** UI Routes ******************************/ | ||
.route("/fxn/{tail:.*}", leptos_actix::handle_server_fns()) | ||
// serve JS/WASM/CSS from `pkg` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
mod handlers; | ||
pub mod types; | ||
pub use handlers::endpoints; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
use actix_web::{ | ||
get, post, | ||
web::{self, Json, Query}, | ||
HttpResponse, Scope, | ||
}; | ||
use chrono::Utc; | ||
use diesel::prelude::*; | ||
use idgenerator::IdInstance; | ||
use service_utils::service::types::DbConnection; | ||
use superposition_types::database::{ | ||
models::organisation::{OrgStatus, Organisation}, | ||
schema::organisations::dsl::organisations, | ||
}; | ||
use superposition_types::{ | ||
custom_query::PaginationParams, result as superposition, PaginatedResponse, User, | ||
}; | ||
|
||
use super::types::{CreateOrganisationRequest, CreateOrganisationResponse}; | ||
|
||
pub fn endpoints() -> Scope { | ||
Scope::new("") | ||
.service(create_organisation) | ||
.service(list_organisations) | ||
.service(get_organisation) | ||
} | ||
|
||
#[post("")] | ||
pub async fn create_organisation( | ||
req: web::Json<CreateOrganisationRequest>, | ||
db_conn: DbConnection, | ||
user: User, | ||
) -> superposition::Result<HttpResponse> { | ||
let DbConnection(mut conn) = db_conn; | ||
|
||
// Generating a numeric ID from IdInstance and prefixing it with `orgid` | ||
let numeric_id = IdInstance::next_id(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we planned to keep the org id of fixed length 25 right ?
or are we keeping the id value dynamic and not fixed length, because I was of the impression that we planned to keep the id fixed length |
||
let org_id = format!("orgid{}", numeric_id); | ||
let now = Utc::now().naive_utc(); | ||
|
||
let new_org = Organisation { | ||
id: org_id.clone(), | ||
name: req.name.clone(), | ||
country_code: req.country_code.clone(), | ||
contact_email: req.contact_email.clone(), | ||
contact_phone: req.contact_phone.clone(), | ||
created_by: user.get_username(), | ||
admin_email: req.admin_email.clone(), | ||
status: OrgStatus::PendingKyb, | ||
sector: req.sector.clone(), | ||
created_at: now, | ||
updated_at: now, | ||
updated_by: user.get_username(), | ||
}; | ||
|
||
diesel::insert_into(organisations) | ||
.values(&new_org) | ||
.execute(&mut conn) | ||
.map_err(|e| { | ||
log::error!("Failed to insert new organisation: {:?}", e); | ||
superposition::AppError::UnexpectedError(anyhow::anyhow!( | ||
"Failed to create organisation" | ||
)) | ||
})?; | ||
|
||
let mut http_resp = HttpResponse::Created(); | ||
Ok(http_resp.json(CreateOrganisationResponse { org_id })) | ||
} | ||
|
||
#[get("/{org_id}")] | ||
pub async fn get_organisation( | ||
org_id: web::Path<String>, | ||
db_conn: DbConnection, | ||
) -> superposition::Result<HttpResponse> { | ||
let DbConnection(mut conn) = db_conn; | ||
|
||
let org = organisations | ||
.find(org_id.as_str()) | ||
.first::<Organisation>(&mut conn) | ||
.map_err(|e| { | ||
log::error!("Failed to fetch organisation {}: {:?}", org_id, e); | ||
match e { | ||
diesel::result::Error::NotFound => superposition::AppError::NotFound( | ||
format!("Organisation {} not found", org_id), | ||
), | ||
_ => superposition::AppError::UnexpectedError(anyhow::anyhow!( | ||
"Failed to fetch organisation" | ||
)), | ||
} | ||
})?; | ||
|
||
Ok(HttpResponse::Ok().json(org)) | ||
} | ||
|
||
#[get("/list")] | ||
pub async fn list_organisations( | ||
db_conn: DbConnection, | ||
filters: Query<PaginationParams>, | ||
) -> superposition::Result<Json<PaginatedResponse<Organisation>>> { | ||
use superposition_types::database::schema::organisations::dsl::*; | ||
let DbConnection(mut conn) = db_conn; | ||
log::info!("list_organisations"); | ||
let result = | ||
conn.transaction::<_, superposition::AppError, _>(|transaction_conn| { | ||
// If all parameter is true, return all organisations | ||
if let Some(true) = filters.all { | ||
let result: Vec<Organisation> = organisations | ||
.order(created_at.desc()) | ||
.get_results(transaction_conn)?; | ||
log::info!("organisations: {organisations:?}"); | ||
return Ok(PaginatedResponse { | ||
total_pages: 1, | ||
total_items: result.len() as i64, | ||
data: result, | ||
}); | ||
} | ||
|
||
// Get total count of organisations | ||
let total_items: i64 = organisations.count().get_result(transaction_conn)?; | ||
|
||
// Set up pagination | ||
let limit = filters.count.unwrap_or(10); | ||
let mut builder = organisations | ||
.into_boxed() | ||
.order(created_at.desc()) | ||
.limit(limit); | ||
|
||
// Apply offset if page is specified | ||
if let Some(page) = filters.page { | ||
let offset = (page - 1) * limit; | ||
builder = builder.offset(offset); | ||
} | ||
|
||
// Get paginated results | ||
let data: Vec<Organisation> = builder.load(transaction_conn)?; | ||
|
||
let total_pages = (total_items as f64 / limit as f64).ceil() as i64; | ||
|
||
Ok(PaginatedResponse { | ||
total_pages, | ||
total_items, | ||
data: data, | ||
}) | ||
})?; | ||
|
||
Ok(Json(result)) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
use serde::{Deserialize, Serialize}; | ||
|
||
// Request payload for creating an organisation | ||
#[derive(Deserialize)] | ||
pub struct CreateOrganisationRequest { | ||
pub country_code: Option<String>, | ||
pub contact_email: Option<String>, | ||
pub contact_phone: Option<String>, | ||
pub admin_email: String, | ||
pub name: String, | ||
pub sector: Option<String>, | ||
} | ||
|
||
// Response type to include `org_id` | ||
#[derive(Serialize)] | ||
pub struct CreateOrganisationResponse { | ||
pub org_id: String, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
-- down.sql | ||
DROP INDEX IF EXISTS superposition.idx_organisation_admin_email; | ||
DROP INDEX IF EXISTS superposition.idx_organisation_created_at; | ||
DROP INDEX IF EXISTS superposition.idx_organisation_status; | ||
DROP INDEX IF EXISTS superposition.idx_organisation_contact_email; | ||
DROP TABLE IF EXISTS superposition.organisation; | ||
DROP TYPE IF EXISTS superposition.org_status; | ||
DROP SCHEMA IF EXISTS superposition; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
-- up.sql | ||
CREATE SCHEMA IF NOT EXISTS superposition; | ||
|
||
CREATE TYPE superposition.org_status AS ENUM ('ACTIVE', 'INACTIVE', 'PENDING_KYB'); | ||
|
||
CREATE TABLE IF NOT EXISTS superposition.organisations ( | ||
id VARCHAR(30) PRIMARY KEY NOT NULL, | ||
name TEXT NOT NULL, | ||
country_code VARCHAR(10), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't country code supposed to be 2/3 characters, depending on the format? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have created a ticket for country code validation to be picked up later. |
||
contact_email VARCHAR(255), | ||
contact_phone VARCHAR(15), | ||
created_by VARCHAR(255) NOT NULL, | ||
admin_email VARCHAR(255) NOT NULL, | ||
status superposition.org_status NOT NULL DEFAULT 'ACTIVE', | ||
sector VARCHAR(100), | ||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, | ||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, | ||
updated_by VARCHAR(255) NOT NULL | ||
); | ||
|
||
-- Indexes for optimizing queries | ||
CREATE INDEX IF NOT EXISTS idx_organisation_contact_email ON superposition.organisations (contact_email); | ||
CREATE INDEX IF NOT EXISTS idx_organisation_status ON superposition.organisations (status); | ||
CREATE INDEX IF NOT EXISTS idx_organisation_created_at ON superposition.organisations (created_at); | ||
CREATE INDEX IF NOT EXISTS idx_organisation_admin_email ON superposition.organisations (admin_email); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
pub mod cac; | ||
#[cfg(feature = "experimentation")] | ||
pub mod experimentation; | ||
pub mod organisation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bit confused, should
superposition
be a part of this env ?