Skip to content

Commit

Permalink
feat(api-internal): implement applicationsList
Browse files Browse the repository at this point in the history
  • Loading branch information
sergeysova committed Jun 28, 2022
1 parent a4bdfac commit f09624f
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 5 deletions.
81 changes: 81 additions & 0 deletions api-internal/src/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,21 @@ pub mod api {
self.api = self.api.bind("/application.get", Method::POST, handler);
self
}

pub fn bind_applications_list<F, T, R>(mut self, handler: F) -> Self
where
F: Handler<T, R>,
T: FromRequest + 'static,
R: Future<
Output = Result<
super::paths::applications_list::Response,
super::paths::applications_list::Error,
>,
> + 'static,
{
self.api = self.api.bind("/applications.list", Method::POST, handler);
self
}
}
}

Expand Down Expand Up @@ -509,6 +524,12 @@ pub mod components {
#[from]
pub error: ApplicationGetError,
}

#[derive(Debug, Serialize)]
pub struct ApplicationsListSuccess {
pub installed: Vec<super::schemas::Application>,
pub available: Vec<super::schemas::Application>,
}
}

pub mod request_bodies {
Expand Down Expand Up @@ -1222,4 +1243,64 @@ pub mod paths {
}
}
}

pub mod applications_list {
use actix_swagger::ContentType;
use actix_web::http::StatusCode;
use actix_web::{HttpRequest, HttpResponse, Responder, ResponseError};
use serde::Serialize;

use super::responses;

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum Response {
Ok(responses::ApplicationsListSuccess),
}

#[derive(Debug, Serialize, thiserror::Error)]
#[serde(untagged)]
pub enum Error {
#[error(transparent)]
InternalServerError(
#[from]
#[serde(skip)]
eyre::Report,
),
}

impl Responder for Response {
fn respond_to(self, _: &HttpRequest) -> HttpResponse {
match self {
Response::Ok(r) => HttpResponse::build(StatusCode::OK).json(r),
}
}
}

impl ResponseError for Error {
fn status_code(&self) -> StatusCode {
match self {
Error::InternalServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}

fn error_response(&self) -> HttpResponse {
let content_type = match self {
Self::InternalServerError(_) => Some(ContentType::Json),
};

let mut res = &mut HttpResponse::build(self.status_code());
if let Some(content_type) = content_type {
res = res.content_type(content_type.to_string());

match content_type {
ContentType::Json => res.body(serde_json::to_string(self).unwrap()),
ContentType::FormData => res.body(serde_plain::to_string(self).unwrap()),
}
} else {
HttpResponse::build(self.status_code()).finish()
}
}
}
}
}
3 changes: 2 additions & 1 deletion api-internal/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ async fn main() -> eyre::Result<()> {
.bind_session_delete(routes::session::delete::route)
.bind_session_get(routes::session::get::route)
.bind_account_edit(account::edit::route)
.bind_application_get(routes::application::get::route),
.bind_application_get(routes::application::get::route)
.bind_applications_list(routes::application::list::route),
)
});

Expand Down
45 changes: 45 additions & 0 deletions api-internal/src/routes/application/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use actix_web::web::Data;

use accesso_core::app::application::{Application as _, ApplicationsListError};
use accesso_core::models;

use crate::generated::{
components::{responses::ApplicationsListSuccess, schemas},
paths::applications_list::{Error, Response},
};
use crate::session::Session;

pub async fn route(app: Data<accesso_app::App>, session: Session) -> Result<Response, Error> {
let applications_list = app
.applications_list(session.user.id)
.await
.map_err(map_applications_list_error)?;

Ok(Response::Ok(ApplicationsListSuccess {
available: applications_list
.available
.iter()
.map(map_application)
.collect(),
installed: applications_list
.installed
.iter()
.map(map_application)
.collect(),
}))
}

fn map_application(application: &models::Application) -> schemas::Application {
schemas::Application {
id: application.id,
title: application.title.clone(),
allowed_registrations: application.allowed_registrations,
avatar: None,
}
}

fn map_applications_list_error(error: ApplicationsListError) -> Error {
match error {
ApplicationsListError::Unexpected(report) => Error::InternalServerError(report.into()),
}
}
1 change: 1 addition & 0 deletions api-internal/src/routes/application/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod get;
pub mod list;
34 changes: 30 additions & 4 deletions app/src/application.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use crate::{App, Service};
use accesso_core::app::application::{Application, ApplicationGetError};
use accesso_core::contracts::Repository;
use accesso_core::models;
use async_trait::async_trait;
use uuid::Uuid;

use accesso_core::app::application::{
Application, ApplicationGetError, ApplicationsList, ApplicationsListError,
};
use accesso_core::contracts::Repository;
use accesso_core::models;

use crate::{App, Service};

#[async_trait]
impl Application for App {
async fn application_get(
Expand All @@ -20,4 +24,26 @@ impl Application for App {
None => Err(ApplicationGetError::ApplicationNotFound),
}
}

async fn applications_list(
&self,
user_id: Uuid,
) -> Result<ApplicationsList, ApplicationsListError> {
let db = self.get::<Service<dyn Repository>>()?;

let available_future = db.applications_allowed_to_register();
let installed_future = db.applications_user_registered_in(user_id);

let mut available = available_future.await?;
let installed = installed_future.await?;

for application in &installed {
available.retain(|found| found.id != application.id);
}

Ok(ApplicationsList {
available,
installed,
})
}
}
22 changes: 22 additions & 0 deletions core/src/app/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ use uuid::Uuid;
#[async_trait]
pub trait Application {
async fn application_get(&self, id: Uuid) -> Result<models::Application, ApplicationGetError>;
async fn applications_list(
&self,
user_id: Uuid,
) -> Result<ApplicationsList, ApplicationsListError>;
}

#[derive(Debug)]
pub struct ApplicationsList {
pub available: Vec<models::Application>,
pub installed: Vec<models::Application>,
}

#[derive(Debug, thiserror::Error)]
Expand All @@ -21,3 +31,15 @@ impl From<UnexpectedDatabaseError> for ApplicationGetError {
ApplicationGetError::Unexpected(e.into())
}
}

#[derive(Debug, thiserror::Error)]
pub enum ApplicationsListError {
#[error(transparent)]
Unexpected(#[from] eyre::Report),
}

impl From<UnexpectedDatabaseError> for ApplicationsListError {
fn from(e: UnexpectedDatabaseError) -> Self {
ApplicationsListError::Unexpected(e.into())
}
}
24 changes: 24 additions & 0 deletions core/src/contracts/repo/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ pub trait ApplicationRepo {

async fn application_list(&self) -> Result<Vec<Application>, UnexpectedDatabaseError>;

async fn applications_allowed_to_register(
&self,
) -> Result<Vec<Application>, UnexpectedDatabaseError>;

async fn applications_user_registered_in(
&self,
user_id: Uuid,
) -> Result<Vec<Application>, UnexpectedDatabaseError>;

async fn application_create(
&self,
application: ApplicationForm,
Expand All @@ -72,6 +81,21 @@ impl ApplicationRepo for crate::contracts::MockDb {
self.application.application_list().await
}

async fn applications_allowed_to_register(
&self,
) -> Result<Vec<Application>, UnexpectedDatabaseError> {
self.application.applications_allowed_to_register().await
}

async fn applications_user_registered_in(
&self,
user_id: Uuid,
) -> Result<Vec<Application>, UnexpectedDatabaseError> {
self.application
.applications_user_registered_in(user_id)
.await
}

async fn application_create(
&self,
application: ApplicationForm,
Expand Down
43 changes: 43 additions & 0 deletions db/src/repos/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use accesso_core::contracts::{
ApplicationCreateError, ApplicationForm, ApplicationRepo, UnexpectedDatabaseError,
};
use accesso_core::models;
use accesso_core::models::Application;
use uuid::Uuid;

use crate::entities;
use crate::Database;
Expand Down Expand Up @@ -49,6 +51,47 @@ impl ApplicationRepo for Database {
.collect())
}

async fn applications_allowed_to_register(
&self,
) -> Result<Vec<Application>, UnexpectedDatabaseError> {
Ok(sqlx::query_as!(
entities::Client,
// language=PostgreSQL
r#"
SELECT id, is_dev, redirect_uri, secret_key, title, allowed_registrations
FROM clients
WHERE allowed_registrations = true AND is_dev = false
"#
)
.fetch_all(&self.pool)
.await?
.into_iter()
.map(|client| client.into())
.collect())
}

async fn applications_user_registered_in(
&self,
user_id: Uuid,
) -> Result<Vec<Application>, UnexpectedDatabaseError> {
Ok(sqlx::query_as!(
entities::Client,
// language=PostgreSQL
r#"
SELECT clients.id, is_dev, redirect_uri, secret_key, title, allowed_registrations
FROM clients
LEFT JOIN user_registrations ON clients.id = user_registrations.client_id
WHERE user_registrations.user_id = $1
"#,
user_id,
)
.fetch_all(&self.pool)
.await?
.into_iter()
.map(|client| client.into())
.collect())
}

async fn application_create(
&self,
application: ApplicationForm,
Expand Down

0 comments on commit f09624f

Please sign in to comment.