Skip to content

Commit 5073931

Browse files
Merge pull request #5 from mocks-rs/0.3.5
0.3.5
2 parents 545f072 + e3d9529 commit 5073931

File tree

13 files changed

+144
-67
lines changed

13 files changed

+144
-67
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mocks"
3-
version = "0.3.4"
3+
version = "0.3.5"
44
edition = "2021"
55
authors = ["codemountains <codemountains@gmail.com>"]
66
description = "Get a mock REST APIs with zero coding within seconds."

docs/DOCUMENT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# mocks docs
22

3-
## Build
3+
## Build for Homebrew
44

55
```shell
66
cargo build --release

src/server/handler/get.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@ pub async fn get_all(
1414
.lock()
1515
.map_err(|e| MocksError::Exception(e.to_string()))?;
1616

17-
let value = state
18-
.storage
19-
.get_all(&resource)
20-
.ok_or(MocksError::ResourceNotFound)?;
17+
let value = state.storage.get_all(&resource)?;
2118
let response = json!({
2219
resource: value
2320
});
@@ -33,9 +30,6 @@ pub async fn get_one(
3330
.lock()
3431
.map_err(|e| MocksError::Exception(e.to_string()))?;
3532

36-
let value = state
37-
.storage
38-
.get_one(&resource, &id)
39-
.ok_or(MocksError::ObjectNotFound)?;
33+
let value = state.storage.get_one(&resource, &id)?;
4034
Ok((StatusCode::OK, Json(value)))
4135
}

src/storage.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ impl Storage {
4242

4343
/// **GET**
4444
/// Retrieve all items for a given resource
45-
pub fn get_all(&self, resource_key: &str) -> Option<Value> {
46-
select_all(&self.data, resource_key).ok()
45+
pub fn get_all(&self, resource_key: &str) -> Result<Value, MocksError> {
46+
self.fetch(|data| select_all(data, resource_key))
4747
}
4848

4949
/// **GET**
5050
/// Retrieve a specific item from a resource
51-
pub fn get_one(&self, resource_key: &str, item_key: &str) -> Option<Value> {
52-
select_one(&self.data, resource_key, item_key).ok()
51+
pub fn get_one(&self, resource_key: &str, item_key: &str) -> Result<Value, MocksError> {
52+
self.fetch(|data| select_one(data, resource_key, item_key))
5353
}
5454

5555
/// **POST**
@@ -98,6 +98,18 @@ impl Storage {
9898
self.operate(|data| remove(data, resource_key, item_key))
9999
}
100100

101+
/// Fetches data from the storage using the provided operation
102+
///
103+
/// This method abstracts the common pattern of performing a fetch operation,
104+
/// and returning the result.
105+
fn fetch<F>(&self, operation: F) -> Result<Value, MocksError>
106+
where
107+
F: FnOnce(&StorageData) -> Result<Value, MocksError>,
108+
{
109+
let result = operation(&self.data)?;
110+
Ok(result)
111+
}
112+
101113
/// Perform an operation on the storage data and write changes if successful
102114
///
103115
/// This method abstracts the common pattern of performing an operation,

src/storage/operation.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
use crate::error::MocksError;
2+
use crate::storage::{Input, StorageData};
3+
use serde_json::Value;
4+
15
pub mod insert;
26
pub mod remove;
37
pub mod replace;
@@ -6,3 +10,36 @@ pub mod select_all;
610
pub mod select_one;
711
pub mod update;
812
pub mod update_one;
13+
14+
pub fn extract_id_in_input(input: &Input) -> Result<String, MocksError> {
15+
input
16+
.get("id")
17+
.and_then(|v| match v {
18+
Value::Number(id) => Some(id.to_string()),
19+
Value::String(id) => Some(id.to_string()),
20+
_ => None,
21+
})
22+
.ok_or(MocksError::InvalidRequest)
23+
}
24+
25+
pub fn check_duplicate_id(
26+
data: &StorageData,
27+
resource_key: &str,
28+
id: &str,
29+
) -> Result<(), MocksError> {
30+
if select_one::select_one(data, resource_key, id).is_ok() {
31+
Err(MocksError::DuplicateId)
32+
} else {
33+
Ok(())
34+
}
35+
}
36+
37+
pub fn extract_array_resource(
38+
data: &StorageData,
39+
resource_key: &str,
40+
) -> Result<Vec<Value>, MocksError> {
41+
data.get(resource_key)
42+
.and_then(Value::as_array)
43+
.ok_or(MocksError::ResourceNotFound)
44+
.cloned()
45+
}

src/storage/operation/insert.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::error::MocksError;
2-
use crate::storage::operation::select_one::select_one;
2+
use crate::storage::operation::{check_duplicate_id, extract_id_in_input};
33
use crate::storage::{Input, StorageData};
44
use serde_json::Value;
55

@@ -9,18 +9,16 @@ pub fn insert(
99
input: &Input,
1010
) -> Result<Value, MocksError> {
1111
// Validation to check duplicate IDs
12-
let id = input
13-
.get("id")
14-
.and_then(|v| match v {
15-
Value::Number(id) => Some(id.to_string()),
16-
Value::String(id) => Some(id.to_string()),
17-
_ => None,
18-
})
19-
.ok_or(MocksError::InvalidRequest)?;
20-
if select_one(data, resource_key, &id).is_ok() {
21-
return Err(MocksError::DuplicateId);
22-
}
12+
let id = extract_id_in_input(input)?;
13+
check_duplicate_id(data, resource_key, &id)?;
14+
insert_input(data, resource_key, input)
15+
}
2316

17+
fn insert_input(
18+
data: &mut StorageData,
19+
resource_key: &str,
20+
input: &Input,
21+
) -> Result<Value, MocksError> {
2422
data.get_mut(resource_key)
2523
.and_then(Value::as_array_mut)
2624
.map(|values| {

src/storage/operation/remove.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::error::MocksError;
2+
use crate::storage::operation::extract_array_resource;
23
use crate::storage::operation::select_one::select_one;
34
use crate::storage::StorageData;
45
use serde_json::Value;
@@ -8,31 +9,30 @@ pub fn remove(
89
resource_key: &str,
910
search_key: &str,
1011
) -> Result<Value, MocksError> {
11-
let values = data
12-
.get(resource_key)
13-
.and_then(Value::as_array)
14-
.ok_or(MocksError::ResourceNotFound)?;
12+
let values = extract_array_resource(data, resource_key)?;
1513

1614
// Get the target to be removed
17-
let removed = select_one(data, resource_key, search_key)?;
15+
let remove_one = select_one(data, resource_key, search_key)?;
16+
let removed_resource = remove_target(values, search_key);
17+
data[resource_key] = Value::Array(removed_resource);
18+
Ok(remove_one)
19+
}
1820

19-
let filtered: Vec<Value> = values
21+
fn remove_target(values: Vec<Value>, key: &str) -> Vec<Value> {
22+
values
2023
.iter()
2124
.filter(|&value| {
2225
value
2326
.get("id")
2427
.and_then(|id| match id {
25-
Value::Number(n) => Some(n.to_string() != search_key),
26-
Value::String(s) => Some(s != search_key),
28+
Value::Number(n) => Some(n.to_string() != key),
29+
Value::String(s) => Some(s != key),
2730
_ => None,
2831
})
2932
.unwrap_or(true)
3033
})
3134
.cloned()
32-
.collect();
33-
34-
data[resource_key] = Value::Array(filtered);
35-
Ok(removed)
35+
.collect()
3636
}
3737

3838
#[cfg(test)]

src/storage/operation/replace.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::error::MocksError;
2+
use crate::storage::operation::extract_array_resource;
23
use crate::storage::operation::select_one::select_one;
34
use crate::storage::{Input, StorageData};
45
use serde_json::Value;
@@ -9,30 +10,29 @@ pub fn replace(
910
search_key: &str,
1011
input: &Input,
1112
) -> Result<Value, MocksError> {
12-
let values = data
13-
.get(resource_key)
14-
.and_then(Value::as_array)
15-
.ok_or(MocksError::ResourceNotFound)?;
13+
let values = extract_array_resource(data, resource_key)?;
1614

1715
// Validation to confirm the existence
1816
select_one(data, resource_key, search_key)?;
17+
let replaced_resource = replace_target_with_input(values, search_key, input);
18+
data[resource_key] = Value::Array(replaced_resource);
19+
Ok(input.clone())
20+
}
1921

20-
let replaced: Vec<Value> = values
22+
fn replace_target_with_input(values: Vec<Value>, key: &str, input: &Input) -> Vec<Value> {
23+
values
2124
.iter()
2225
.map(|value| {
2326
let id = value.get("id");
24-
if matches!(id, Some(Value::Number(n)) if n.to_string() == search_key)
25-
|| matches!(id, Some(Value::String(s)) if s == search_key)
27+
if matches!(id, Some(Value::Number(n)) if n.to_string() == key)
28+
|| matches!(id, Some(Value::String(s)) if s == key)
2629
{
2730
input.clone()
2831
} else {
2932
value.clone()
3033
}
3134
})
32-
.collect();
33-
34-
data[resource_key] = Value::Array(replaced);
35-
Ok(input.clone())
35+
.collect()
3636
}
3737

3838
#[cfg(test)]

src/storage/operation/replace_one.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
use crate::error::MocksError;
2+
use crate::storage::operation::extract_id_in_input;
23
use crate::storage::{Input, StorageData};
3-
use serde_json::Value;
4+
use serde_json::{Map, Value};
45

56
pub fn replace_one(
67
data: &mut StorageData,
78
resource_key: &str,
89
input: &Input,
910
) -> Result<Value, MocksError> {
11+
extract_id_in_input(input)?;
1012
let valid_input = input.as_object().ok_or(MocksError::InvalidRequest)?;
13+
replace_target_with_map_input(data, resource_key, valid_input.clone())
14+
}
1115

16+
fn replace_target_with_map_input(
17+
data: &mut StorageData,
18+
resource_key: &str,
19+
map_input: Map<String, Value>,
20+
) -> Result<Value, MocksError> {
1221
data.get_mut(resource_key)
1322
.and_then(Value::as_object_mut)
1423
.map(|v| {
15-
*v = valid_input.clone();
16-
input.clone()
24+
*v = map_input.clone();
25+
Value::Object(map_input)
1726
})
1827
.ok_or(MocksError::ObjectNotFound)
1928
}

src/storage/operation/update.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,18 @@ pub fn update(
1212
.get_mut(resource_key)
1313
.and_then(Value::as_array_mut)
1414
.ok_or(MocksError::ResourceNotFound)?;
15+
match update_resource_with_input(values, search_key, input) {
16+
Some(value) => Ok(value),
17+
None => Err(MocksError::ObjectNotFound),
18+
}
19+
}
1520

16-
let updated = values.iter_mut().find_map(|value| {
21+
fn update_resource_with_input(
22+
values: &mut [Value],
23+
search_key: &str,
24+
input: &Input,
25+
) -> Option<Value> {
26+
values.iter_mut().find_map(|value| {
1727
let obj = value.as_object_mut()?;
1828
let id = obj.get("id")?;
1929

@@ -31,12 +41,7 @@ pub fn update(
3141
} else {
3242
None
3343
}
34-
});
35-
36-
match updated {
37-
Some(value) => Ok(value),
38-
None => Err(MocksError::ObjectNotFound),
39-
}
44+
})
4045
}
4146

4247
#[cfg(test)]

src/storage/operation/update_one.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ pub fn update_one(
1010
let resource = data
1111
.get_mut(resource_key)
1212
.ok_or(MocksError::ObjectNotFound)?;
13+
update_target_with_input(resource, input)
14+
}
1315

14-
match resource {
16+
fn update_target_with_input(value: &mut Value, input: &Input) -> Result<Value, MocksError> {
17+
match value {
1518
Value::Object(map) => {
1619
if let Value::Object(input_map) = input {
1720
map.extend(input_map.iter().map(|(k, v)| (k.clone(), v.clone())));
@@ -20,7 +23,7 @@ pub fn update_one(
2023
return Err(MocksError::Exception(EXCEPTION_ERROR_MESSAGE.to_string()));
2124
}
2225

23-
Ok(resource.clone())
26+
Ok(value.clone())
2427
}
2528
_ => Err(MocksError::ObjectNotFound),
2629
}

storage.json

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
11
{
22
"posts": [
3-
{ "id": "01J7BAKH37HPG116ZRRFKHBDGB", "title": "first post", "views": 100 },
4-
{ "id": "01J7BAKH37GE8B688PT4RC7TP4", "title": "second post", "views": 10 }
3+
{
4+
"id": "01J7BAKH37HPG116ZRRFKHBDGB",
5+
"title": "first post",
6+
"views": 100
7+
},
8+
{
9+
"id": "01J7BAKH37GE8B688PT4RC7TP4",
10+
"title": "second post",
11+
"views": 10
12+
}
513
],
614
"comments": [
7-
{ "id": 1, "text": "a comment", "post_id": "01J7BAKH37HPG116ZRRFKHBDGB" },
8-
{ "id": 2, "text": "another comment", "post_id": "01J7BAKH37HPG116ZRRFKHBDGB" }
15+
{
16+
"id": 1,
17+
"text": "a comment",
18+
"post_id": "01J7BAKH37HPG116ZRRFKHBDGB"
19+
},
20+
{
21+
"id": 2,
22+
"text": "another comment",
23+
"post_id": "01J7BAKH37HPG116ZRRFKHBDGB"
24+
}
925
],
10-
"profile": { "id": "01J7BAQE1GMD78FN3J0FJCNS8T", "name": "mocks" },
26+
"profile": {
27+
"id": "01J7BAQE1GMD78FN3J0FJCNS8T",
28+
"name": "mocks"
29+
},
1130
"friends": []
1231
}

0 commit comments

Comments
 (0)