Skip to content

Commit

Permalink
Merge pull request #4 from ismaelxyz/main
Browse files Browse the repository at this point in the history
Resuscitating the project
  • Loading branch information
TaeyoonKwon authored Jul 15, 2024
2 parents d1612ba + c8140fe commit afd5a7a
Show file tree
Hide file tree
Showing 14 changed files with 191 additions and 186 deletions.
3 changes: 2 additions & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
# Below users will be requested for
# review when someone opens a pull request.

@TaeyoonKwon
@TaeyoonKwon
@ismaelxyz
38 changes: 29 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,23 +1,43 @@
[package]
name = "rust-rocket-sample"
description = "Rocket.rs CRUD REST API example"
version = "1.3.0"
version = "1.3.1"
edition = "2021"
authors = ["TaeyoonKwon <[email protected]>"]
authors = ["TaeyoonKwon <[email protected]>", "Ismael Belisario <[email protected]>"]
readme = "README.md"
repository = "https://github.com/TaeyoonKwon/rust-rocket-sample"
repository = "https://github.com/ismaelxyz/rust-rocket-sample"
license = "MIT"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
schemars = "0.8"
okapi = { version = "0.6.0-alpha-1" }
rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger", "secrets"] }
okapi = "0.7.0"
dotenv = "0.15.0"
mongodb = "2.1.0"
futures = "0.3"
chrono = "0.4"

[dependencies.rocket]
version = "0.5.0-rc.4"
default-features = false
features = ["json"]

[dependencies.serde]
version = "1.0"
features = ["derive"]

[dependencies.rocket_okapi]
version = "0.8.0"
features = ["swagger", "secrets"]

[dependencies.mongodb]
version = "2.8.2"
features = ["bson-chrono-0_4"]

[dependencies.bson]
version = "2.10.0"
features = ["chrono-0_4"]

[dependencies.chrono]
version = "0.4"
features = ["serde"]
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
MIT License

Copyright (c) 2022 Taeyoon Kwon
Copyright (c) 2024 Ismael Belisario

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
3 changes: 3 additions & 0 deletions env_template
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MONGO_URI=mongodb://localhost:27017
MONGO_DB_NAME=customersdb
API_KEY=1234567890
73 changes: 33 additions & 40 deletions src/db/customer.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
use crate::models::customer::Customer;
use crate::models::customer::CustomerDocument;
use crate::models::customer::CustomerInput;

// use chrono::prelude::*;
use crate::models::customer::{Customer, CustomerDocument, CustomerInput};
use chrono::Utc;
use futures::stream::TryStreamExt;
use mongodb::bson::oid::ObjectId;
use mongodb::bson::{doc, DateTime, Document};
use mongodb::options::FindOneAndUpdateOptions;
use mongodb::options::FindOptions;
use mongodb::options::ReturnDocument;
use mongodb::Database;
use mongodb::{
bson::{doc, oid::ObjectId, DateTime, Document},
options::{FindOneAndUpdateOptions, FindOptions, ReturnDocument},
Database,
};
use rocket::serde::json::Json;

pub async fn find_customer(
Expand All @@ -28,14 +24,14 @@ pub async fn find_customer(

let mut customers: Vec<Customer> = vec![];
while let Some(result) = cursor.try_next().await? {
let _id = result._id;
let _id = result.id;
let name = result.name;
let created_at = result.createdAt;
let created_at = result.created_at;
// transform ObjectId to String
let customer_json = Customer {
_id: _id.to_string(),
id: _id.to_string(),
name: name.to_string(),
createdAt: created_at.to_string(),
created_at: created_at.to_string(),
};
customers.push(customer_json);
}
Expand All @@ -49,16 +45,15 @@ pub async fn find_customer_by_id(
) -> mongodb::error::Result<Option<Customer>> {
let collection = db.collection::<CustomerDocument>("customer");

let customer_doc = collection.find_one(doc! {"_id":oid }, None).await?;
if customer_doc.is_none() {
let Some(customer_doc) = collection.find_one(doc! {"_id":oid }, None).await? else {
return Ok(None);
}
let unwrapped_doc = customer_doc.unwrap();
};

// transform ObjectId to String
let customer_json = Customer {
_id: unwrapped_doc._id.to_string(),
name: unwrapped_doc.name.to_string(),
createdAt: unwrapped_doc.createdAt.to_string(),
id: customer_doc.id.to_string(),
name: customer_doc.name.to_string(),
created_at: customer_doc.created_at.to_string(),
};

Ok(Some(customer_json))
Expand All @@ -70,7 +65,7 @@ pub async fn insert_customer(
) -> mongodb::error::Result<String> {
let collection = db.collection::<Document>("customer");

let created_at: DateTime = DateTime::now();
let created_at = Utc::now();

let insert_one_result = collection
.insert_one(
Expand All @@ -94,23 +89,22 @@ pub async fn update_customer_by_id(

let created_at: DateTime = DateTime::now();

let customer_doc = collection
let Some(customer_doc) = collection
.find_one_and_update(
doc! {"_id":oid },
doc! {"name": input.name.clone(), "createdAt": created_at},
find_one_and_update_options,
)
.await?;

if customer_doc.is_none() {
.await?
else {
return Ok(None);
}
let unwrapped_doc = customer_doc.unwrap();
};

// transform ObjectId to String
let customer_json = Customer {
_id: unwrapped_doc._id.to_string(),
name: unwrapped_doc.name.to_string(),
createdAt: unwrapped_doc.createdAt.to_string(),
id: customer_doc.id.to_string(),
name: customer_doc.name.to_string(),
created_at: customer_doc.created_at.to_string(),
};

Ok(Some(customer_json))
Expand All @@ -123,19 +117,18 @@ pub async fn delete_customer_by_id(
let collection = db.collection::<CustomerDocument>("customer");

// if you just unwrap,, when there is no document it results in 500 error.
let customer_doc = collection
let Some(customer_doc) = collection
.find_one_and_delete(doc! {"_id":oid }, None)
.await?;
if customer_doc.is_none() {
.await?
else {
return Ok(None);
}
};

let unwrapped_doc = customer_doc.unwrap();
// transform ObjectId to String
let customer_json = Customer {
_id: unwrapped_doc._id.to_string(),
name: unwrapped_doc.name.to_string(),
createdAt: unwrapped_doc.createdAt.to_string(),
id: customer_doc.id.to_string(),
name: customer_doc.name.to_string(),
created_at: customer_doc.created_at.to_string(),
};

Ok(Some(customer_json))
Expand Down
3 changes: 1 addition & 2 deletions src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// use mongodb::bson::{doc, Document};
use mongodb::options::ClientOptions;
use mongodb::{Client, Database};
use mongodb::{options::ClientOptions, Client, Database};
use rocket::fairing::AdHoc;
use std::env;

Expand Down
25 changes: 14 additions & 11 deletions src/errors/response.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use rocket_okapi::gen::OpenApiGenerator;
use rocket_okapi::okapi;
use rocket_okapi::okapi::openapi3::{MediaType, Responses};
use rocket_okapi::response::OpenApiResponderInner;
use rocket_okapi::OpenApiError;
use rocket_okapi::{
gen::OpenApiGenerator,
okapi::{
self,
openapi3::{MediaType, Responses},
},
response::OpenApiResponderInner,
OpenApiError,
};

/// error type
#[derive(Debug, serde::Serialize, schemars::JsonSchema)]
Expand All @@ -24,12 +28,11 @@ pub struct MyError {
impl MyError {
// building a custom error.
pub fn build(code: u16, description: Option<String>) -> MyError {
let reason: String;
match code {
400 => reason = "Bad Request".to_string(),
401 => reason = "Unauthorized".to_string(),
_ => reason = "Error".to_string(),
}
let reason = match code {
400 => "Bad Request".to_string(),
401 => "Unauthorized".to_string(),
_ => "Error".to_string(),
};
MyError {
error: ErrorContent {
code,
Expand Down
4 changes: 2 additions & 2 deletions src/fairings/cors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::Header;
use rocket::{Request, Response};

pub struct CORS;
pub struct Cors;

#[rocket::async_trait]
impl Fairing for CORS {
impl Fairing for Cors {
fn info(&self) -> Info {
Info {
name: "Add CORS headers to responses",
Expand Down
2 changes: 1 addition & 1 deletion src/fairings/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::{ContentType, Method, Status};
use rocket::{Data, Request, Response};

struct Counter {
pub struct Counter {
get: AtomicUsize,
post: AtomicUsize,
}
Expand Down
8 changes: 5 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
extern crate rocket;

use dotenv::dotenv;
use rocket_okapi::openapi_get_routes;
use rocket_okapi::swagger_ui::{make_swagger_ui, SwaggerUIConfig};
use rocket_okapi::{
openapi_get_routes,
swagger_ui::{make_swagger_ui, SwaggerUIConfig},
};

mod db;
mod errors;
Expand All @@ -17,7 +19,7 @@ fn rocket() -> _ {
dotenv().ok();
rocket::build()
.attach(db::init())
.attach(fairings::cors::CORS)
.attach(fairings::cors::Cors)
.mount(
"/",
openapi_get_routes![
Expand Down
19 changes: 12 additions & 7 deletions src/models/customer.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
use chrono::{DateTime, Utc};
use mongodb::bson::oid::ObjectId;
use mongodb::bson::DateTime;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[allow(non_snake_case)]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CustomerDocument {
/// Document Id
pub _id: ObjectId,
#[serde(rename = "_id")]
pub id: ObjectId,
/// customer name
pub name: String,
/// createdAt
pub createdAt: DateTime,
#[serde(
with = "bson::serde_helpers::chrono_datetime_as_bson_datetime",
rename = "createdAt"
)]
pub created_at: DateTime<Utc>,
}

#[allow(non_snake_case)]
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
pub struct Customer {
/// Document Id
pub _id: String,
#[serde(rename = "_id")]
pub id: String,
/// customer name
pub name: String,
/// createdAt
pub createdAt: String,
#[serde(rename = "createdAt")]
pub created_at: String,
}

#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
Expand Down
9 changes: 5 additions & 4 deletions src/request_guards/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::env;

use crate::errors::response::unauthorized_response;

// #[derive(OpenApiFromRequest)]
#[allow(dead_code)]
pub struct ApiKey(String);

#[derive(Debug)]
Expand All @@ -28,13 +28,14 @@ impl<'r> FromRequest<'r> for ApiKey {
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
fn is_valid(key: &str) -> bool {
let api_key = env::var("API_KEY").expect("env.API_KEY is not found.");
return key == api_key;

key == api_key
}

match req.headers().get_one("x-api-key") {
None => Outcome::Failure((Status::Unauthorized, ApiKeyError::Missing)),
None => Outcome::Error((Status::Unauthorized, ApiKeyError::Missing)),
Some(key) if is_valid(key) => Outcome::Success(ApiKey(key.to_owned())),
Some(_) => Outcome::Failure((Status::Unauthorized, ApiKeyError::Invalid)),
Some(_) => Outcome::Error((Status::Unauthorized, ApiKeyError::Invalid)),
}
}
}
Expand Down
Loading

0 comments on commit afd5a7a

Please sign in to comment.