Skip to content

Commit

Permalink
feat: POST /v1/links support
Browse files Browse the repository at this point in the history
  • Loading branch information
kentSarmiento committed Dec 18, 2023
1 parent 6a4c880 commit 26a5db4
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 30 deletions.
167 changes: 167 additions & 0 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ publish = false

[dependencies]
axum = "0.7.2"
chrono = { version = "0.4.31", default-features = false, features=["clock", "serde"] }
http-body-util = "0.1.0"
#lambda_http = "0.8.3"
lambda_http = { git = "https://github.com/awslabs/aws-lambda-rust-runtime", branch = "hyper1_upgrade" }
lambda_runtime = "0.8.3"
serde = { version = "1.0.193", features = [ "derive" ] }
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
tokio = { version = "1", features = ["macros"] }
tower = "0.4.13"
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
uuid = { version = "1.6.1", features = ["v4", "serde"] }

[dev-dependencies]
mockall = "0.12.0"
Expand Down
25 changes: 17 additions & 8 deletions src/controller/links.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use axum::{
extract::{Path, State},
extract::{self, Path, State},
http::StatusCode,
response::{IntoResponse, Json},
routing, Router,
response::IntoResponse,
routing, Json, Router,
};
use serde_json::json;

use crate::types::state::Router as RouterState;
use crate::types::{request::PostLink as PostLinkRequest, state::Router as RouterState};

const LINKS_ROUTE: &str = "/v1/links";
const LINKS_ID_ROUTE: &str = "/v1/links/:id";
Expand Down Expand Up @@ -35,10 +35,17 @@ async fn list(State(app_state): State<RouterState>) -> impl IntoResponse {
}
}

async fn post(State(app_state): State<RouterState>) -> impl IntoResponse {
async fn post(
State(app_state): State<RouterState>,
Json(payload): extract::Json<PostLinkRequest>,
) -> impl IntoResponse {
let links_service = app_state.get_links_service();
match links_service.post(&app_state).await {
Ok(link) => Json(link).into_response(),

match links_service
.post(&app_state, &payload.to_string().into())
.await
{
Ok(link) => (StatusCode::CREATED, Json(link)).into_response(),
Err(e) => {
tracing::error!("Error: {}", e);
(
Expand Down Expand Up @@ -131,10 +138,12 @@ mod tests {
async fn test_get_links_non_empty() {
let mut mock_links_service = MockService::new();
let mock_links_repo = MockRepository::new();
let item: LinkItem = "http://link".into();

mock_links_service
.expect_list()
.times(1)
.returning(|_| Ok(vec![LinkItem::new("http://link")]));
.returning(move |_| Ok(vec![item.clone()]));

let app_state = RouterState::new(Arc::new(mock_links_service), Arc::new(mock_links_repo));

Expand Down
4 changes: 2 additions & 2 deletions src/repository/links.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ impl Links for Repository {
Ok(vec![])
}

async fn post(&self) -> Result<LinkItem> {
Err("Not implemented".into())
async fn post(&self, item: &LinkItem) -> Result<LinkItem> {
Ok(item.clone())
}

async fn get(&self, _id: &str) -> Result<LinkItem> {
Expand Down
12 changes: 7 additions & 5 deletions src/service/links.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ impl Links for Service {
links_repo.list().await
}

async fn post<'a>(&self, app_state: &'a state::Router) -> Result<LinkItem> {
async fn post<'a>(&self, app_state: &'a state::Router, item: &LinkItem) -> Result<LinkItem> {
let links_repo = app_state.get_links_repo();
links_repo.post().await
links_repo.post(item).await
}

async fn get<'a>(&self, id: &str, app_state: &'a state::Router) -> Result<LinkItem> {
Expand Down Expand Up @@ -69,12 +69,14 @@ mod tests {
async fn test_get_links_non_empty() {
let mock_links_service = MockService::new();
let mut mock_links_repo = MockRepository::new();

let item: LinkItem = "http://link".into();
let expected_items = vec![item.clone()];

mock_links_repo
.expect_list()
.times(1)
.returning(|| Ok(vec![LinkItem::new("http://link")]));

let expected_items = vec![LinkItem::new("http://link")];
.returning(move || Ok(vec![item.clone()]));

let app_state = RouterState::new(Arc::new(mock_links_service), Arc::new(mock_links_repo));

Expand Down
1 change: 1 addition & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod links;
pub mod repository;
pub mod request;
pub mod service;
pub mod state;
13 changes: 8 additions & 5 deletions src/types/links.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use chrono::Utc;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct LinkItem {
id: String,
owner: String,
Expand All @@ -11,11 +13,12 @@ pub struct LinkItem {
updated_at: String,
}

impl LinkItem {
#[allow(dead_code)]
pub fn new(url: &str) -> Self {
impl<T: ToString> From<T> for LinkItem {
fn from(link_url: T) -> Self {
Self {
url: url.to_string(),
url: link_url.to_string(),
id: Uuid::new_v4().to_string(),
created_at: Utc::now().to_rfc3339(),
..Default::default()
}
}
Expand Down
Loading

0 comments on commit 26a5db4

Please sign in to comment.