Skip to content
This repository has been archived by the owner on Oct 18, 2022. It is now read-only.

exp: lesson #43

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions .github/workflows/pull_request_review.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
name: Pull request review
on: [pull_request]
on: [ pull_request ]

jobs:
clippy_check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: Install clippy
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
toolchain: stable
override: true
components: clippy

Expand All @@ -23,13 +23,12 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
toolchain: stable
override: true
- name: Test
uses: actions-rs/cargo@v1
with:
command: test
toolchain: stable
command: test
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target
**/*.pem
**/*.pem
.idea
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/database/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ mongodb = "2.2.2"

tokio = "*"
serde = "1.0.137"
rocket = "0.5.0-rc.2"
10 changes: 7 additions & 3 deletions src/database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ pub use mongodb::bson::doc;
pub use mongodb::error::Error;
pub use mongodb::Collection;
use mongodb::{options::ClientOptions, Client};
use model::lesson::lesson_data::Lesson;
use crate::model::auth::user::User;

pub struct Database {
pub client: Option<Client>,
pub user: Option<Collection<model::auth::user::User>>,
pub user: Option<Collection<User>>,
pub lesson: Option<Collection<Lesson>>,
}

/// Init mongodb
// Init mongodb
pub async fn init(mongodb_url: String) -> mongodb::error::Result<Database> {
let mut client_options = ClientOptions::parse(mongodb_url).await?;

Expand All @@ -30,6 +33,7 @@ pub async fn init(mongodb_url: String) -> mongodb::error::Result<Database> {

Ok(Database {
client: Some(client),
user: Some(db.collection("user")),
user: Some(db.collection::<User>("user")),
lesson: Some(db.collection::<Lesson>("lesson")),
})
}
21 changes: 21 additions & 0 deletions src/database/src/model/lesson/lesson_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use mongodb::bson::DateTime;
use mongodb::bson::oid::ObjectId;
use crate::model::lesson::lesson_permission::LessonPermission;
use crate::model::lesson::lesson_state::LessonState;
use serde::Deserialize;
use serde::Serialize;

/// https://hackmd.io/@lipoic/S1k6xgio5
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Lesson {
pub _id: ObjectId,
pub name: String,
pub description: Option<String>,
pub created_at: DateTime,
pub create_by: ObjectId,
pub speakers: Vec<ObjectId>,
pub state: LessonState,
pub permission: LessonPermission,

pub classroom_id: Option<ObjectId>,
}
20 changes: 20 additions & 0 deletions src/database/src/model/lesson/lesson_permission.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use mongodb::bson::oid::ObjectId;
use serde::Deserialize;
use serde::Serialize;
use rocket::form::FromFormField;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct LessonPermission {
pub permission_type: LessonPermissionType,
pub allows: Option<Vec<ObjectId>>
}

#[derive(Debug, Serialize, Deserialize, Clone, FromFormField)]
pub enum LessonPermissionType {
/// All users can access this lesson.
All,
/// Only users in the classroom can access this lesson.
Classroom,
/// Only users in the allows list can access this lesson.
Select,
}
9 changes: 9 additions & 0 deletions src/database/src/model/lesson/lesson_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use serde::Deserialize;
use serde::Serialize;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum LessonState {
Draft,
Open,
Closed,
}
3 changes: 3 additions & 0 deletions src/database/src/model/lesson/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod lesson_data;
pub mod lesson_state;
pub mod lesson_permission;
1 change: 1 addition & 0 deletions src/database/src/model/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod auth;
pub mod lesson;
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rocket::serde::json::Json;
use rocket::State;
use util::oauth::OAuthData;

use crate::data::auth_data::{AuthUrl, Token};
use crate::apis::user::user_data::{AuthUrl, Token};
use crate::data::code::Code;
use crate::data::response::Response;
use crate::Config;
Expand Down Expand Up @@ -159,7 +159,7 @@ async fn facebook_oauth_code(

#[doc(hidden)]
pub fn stage() -> AdHoc {
AdHoc::on_ignite("load authentication stage", |rocket| async {
AdHoc::on_ignite("load authentication api stage", |rocket| async {
rocket.mount(
"/authentication",
routes![
Expand Down
2 changes: 1 addition & 1 deletion src/router/src/apis/authentication/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pub mod api;
pub mod authentication_api;
pub mod data;
pub mod util;
4 changes: 2 additions & 2 deletions src/router/src/apis/authentication/util.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
use database::{
Database,
doc,
model::auth::user::{ConnectAccount, User, UserMode},
mongodb::bson,
Database,
};
use rocket::{response::status::BadRequest, serde::json::Json, State};
use util::{jwt::create_jwt_token, oauth::OAuthData, util::create_exp};

use crate::data::{
auth_data::{Claims, Token},
code::Code,
response::Response,
};

use super::data::{CreateUserInfo, RequestIp};
use database::mongodb::options::FindOneAndUpdateOptions;
use database::{Collection, Error};
use crate::apis::user::user_data::{Claims, Token};

pub async fn connect_account(
oauth: OAuthData,
Expand Down
143 changes: 143 additions & 0 deletions src/router/src/apis/lesson/lesson_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use rocket::fairing::AdHoc;
use rocket::form::Form;
use rocket::serde::json::Json;
use rocket::State;
use database::{Database, doc};
use database::model::lesson::lesson_data::Lesson;
use database::model::lesson::lesson_permission::{LessonPermission, LessonPermissionType};
use database::model::lesson::lesson_state::LessonState;
use database::mongodb::bson::DateTime;
use database::mongodb::bson::oid::ObjectId;
use util::util::string_vec_to_oid;
use crate::apis::lesson::lesson_api_data::LessonApiData;
use crate::apis::user::user_data::{AuthError, LoginUserData};
use crate::data::code::Code;
use crate::data::response::Response;

/// # Create a lesson
/// ## Request
/// - Path `/lesson`
/// - Method `POST`
/// - FromData [LessonApiData]
/// - [X] Authorization
/// ## Response
/// - Code
/// - [Code::AuthError]
/// - [Code::Ok]
/// - Content
/// - [Lesson]
#[post("/", data = "<lesson_data>")]
async fn create_lesson(
login_user_data: Result<LoginUserData, AuthError>,
db: &State<Database>,
lesson_data: Form<LessonApiData>,
) -> Result<Json<Response<Lesson>>, AuthError> {
// Check the user is logged in.
let login_user_data = match login_user_data {
Ok(login_user_data) => login_user_data,
Err(err) => return Err(err),
};

let classroom_id: Option<ObjectId>;
if let Some(_classroom_id) = lesson_data.classroom_id.clone() {
classroom_id = Some(ObjectId::parse_str(&_classroom_id).unwrap());
} else {
classroom_id = None;
}

let permission_allows: Option<Vec<ObjectId>>;
if let Some(_permission_allows) = lesson_data.permission.allows.clone() {
permission_allows = Some(string_vec_to_oid(_permission_allows))
} else {
permission_allows = None;
}


let lesson = Lesson {
_id: ObjectId::new(),
name: lesson_data.name.clone(),
description: lesson_data.description.clone(),
created_at: DateTime::now(),
create_by: login_user_data.id.parse().unwrap(),
speakers: string_vec_to_oid(lesson_data.speakers.clone()),
state: LessonState::Draft,
permission: LessonPermission {
permission_type: lesson_data.permission.permission_type.clone(),
allows: permission_allows,
},
classroom_id,
};

db.lesson.as_ref().unwrap().insert_one(lesson.clone(), None).await.unwrap();

Ok(Response::new(Code::Ok, Some(lesson.clone())))
}

/// # Get lesson info
/// ## Request
/// - Path `/lesson`
/// - Method `GET`
/// - FromData [LessonApiData]
/// - [X] Authorization
/// ## Response
/// - Code
/// - [Code::Ok]
/// - [Code::Forbidden]
/// - [Code::NotFound]
/// - Content
/// - [Lesson]
#[get("/<id>")]
async fn get_lesson(id: String, login_user_data: Result<LoginUserData, AuthError>, db: &State<Database>) -> Json<Response<Lesson>> {
let lesson = db.lesson.as_ref().unwrap().find_one(doc! {
"_id": ObjectId::parse_str(&id).unwrap()
}, None).await.unwrap();

if let Some(lesson) = lesson {
let permission = &lesson.permission;
let permission_type = &permission.permission_type;
let mut can_access = false;

match permission_type {
LessonPermissionType::All => {
can_access = true;
}
LessonPermissionType::Classroom => {
// TODO: Check if the user is in the classroom.
can_access = true;
}
LessonPermissionType::Select => {
let allow_users = &permission.allows;

if login_user_data.is_ok() && allow_users.is_some() {
let user_id = &login_user_data.unwrap().id.parse().unwrap();
can_access = allow_users.as_ref().unwrap().contains(user_id);
}
}
}

if can_access {
Response::new(Code::Ok, Some(lesson))
} else {
Response::new(Code::Forbidden, None)
}
} else {
Response::new(Code::NotFound, None)
}
}

/// # Edit lesson info
#[patch("/")]
async fn edit_lesson() -> Json<Response<String>> {
// TODO: Edit lesson info.
Response::new(Code::Ok, Some(String::from("TODO")))
}

#[doc(hidden)]
pub fn stage() -> AdHoc {
AdHoc::on_ignite("load lesson api stage", |rocket| async {
rocket.mount(
"/lesson",
routes![create_lesson, get_lesson, edit_lesson],
)
})
}
18 changes: 18 additions & 0 deletions src/router/src/apis/lesson/lesson_api_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use database::model::lesson::lesson_permission::LessonPermissionType;

#[derive(FromForm)]
pub struct LessonApiData {
pub name: String,
pub description: Option<String>,
pub speakers: Vec<String>,
pub permission: LessonPermission,

pub classroom_id: Option<String>,
}


#[derive(FromForm, Clone)]
pub struct LessonPermission {
pub permission_type: LessonPermissionType,
pub allows: Option<Vec<String>>,
}
2 changes: 2 additions & 0 deletions src/router/src/apis/lesson/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod lesson_api;
mod lesson_api_data;
6 changes: 4 additions & 2 deletions src/router/src/apis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
mod authentication;
mod user;
mod verify_email;
mod lesson;

use rocket::fairing::AdHoc;

#[doc(hidden)]
pub fn stage() -> AdHoc {
AdHoc::on_ignite("load api stage", |rocket| async {
rocket
.attach(authentication::api::stage())
.attach(authentication::authentication_api::stage())
.attach(verify_email::stage())
.attach(user::api::stage())
.attach(user::user_api::stage())
.attach(lesson::lesson_api::stage())
})
}
3 changes: 2 additions & 1 deletion src/router/src/apis/user/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod api;
pub mod user_api;
pub mod user_data;
Loading