First pass on shop/item handling
+ The `add_item` command adds items to a location of type `Shop` + `selling` allows items to be queried + Items are not sorted yet + The user will be able to apply optional sorting params + Allowed the `insert` function of the DB to be used to update a model already in the DB + Clippy + Fmtmain
parent
22ffe75422
commit
8e8798f509
|
@ -0,0 +1,67 @@
|
||||||
|
use crate::commands::{Command, RequestType};
|
||||||
|
use crate::context::Context;
|
||||||
|
use crate::Result;
|
||||||
|
use geoffrey_db::helper::load_location;
|
||||||
|
use geoffrey_models::models::item::ItemListing;
|
||||||
|
use geoffrey_models::models::locations::{Location, LocationDataDb, LocationDb, LocationType};
|
||||||
|
use geoffrey_models::models::parameters::add_item_params::AddItemParams;
|
||||||
|
use geoffrey_models::models::player::Player;
|
||||||
|
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
|
||||||
|
use geoffrey_models::models::CommandLevel;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub struct AddItem {}
|
||||||
|
|
||||||
|
impl Command for AddItem {
|
||||||
|
type Req = AddItemParams;
|
||||||
|
type Resp = Location;
|
||||||
|
|
||||||
|
fn command_name() -> String {
|
||||||
|
"add_item".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_type() -> RequestType {
|
||||||
|
RequestType::POST
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_level() -> CommandLevel {
|
||||||
|
CommandLevel::REGISTERED
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_command(ctx: Arc<Context>, req: Self::Req, user: Option<Player>) -> Result<Self::Resp> {
|
||||||
|
let user = user.unwrap();
|
||||||
|
|
||||||
|
let shops: Vec<LocationDb> = ctx
|
||||||
|
.db
|
||||||
|
.filter(|_, loc: &LocationDb| {
|
||||||
|
let loc_type: LocationType = loc.loc_data.clone().into();
|
||||||
|
|
||||||
|
loc_type == LocationType::Shop
|
||||||
|
&& loc.owners().contains(&user.id.unwrap())
|
||||||
|
&& loc.name.to_lowercase().contains(&req.shop.to_lowercase())
|
||||||
|
})?
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if shops.is_empty() {
|
||||||
|
Err(GeoffreyAPIError::EntryNotFound)
|
||||||
|
} else if shops.len() > 1 {
|
||||||
|
Err(GeoffreyAPIError::MultipleLocationsMatch)
|
||||||
|
} else {
|
||||||
|
let mut shop = shops[0].clone();
|
||||||
|
|
||||||
|
if let LocationDataDb::Shop(shop_data) = &mut shop.loc_data {
|
||||||
|
shop_data.item_listings.insert(ItemListing::new(
|
||||||
|
&req.item_name,
|
||||||
|
req.price,
|
||||||
|
req.quantity,
|
||||||
|
));
|
||||||
|
|
||||||
|
let shop = ctx.db.insert(shop)?;
|
||||||
|
|
||||||
|
load_location(&ctx.db, &shop).map_err(|err| err.into())
|
||||||
|
} else {
|
||||||
|
Err(GeoffreyAPIError::EntryNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
|
use crate::commands::add_item::AddItem;
|
||||||
use crate::commands::add_location::AddLocation;
|
use crate::commands::add_location::AddLocation;
|
||||||
use crate::commands::find::FindCommand;
|
use crate::commands::find::FindCommand;
|
||||||
use crate::commands::register::Register;
|
use crate::commands::register::Register;
|
||||||
|
use crate::commands::selling::Selling;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::helper::{get_player_from_req, get_token_from_req};
|
use crate::helper::{get_player_from_req, get_token_from_req};
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
@ -17,10 +19,12 @@ use std::sync::Arc;
|
||||||
use warp::filters::BoxedFilter;
|
use warp::filters::BoxedFilter;
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
|
|
||||||
|
pub mod add_item;
|
||||||
pub mod add_location;
|
pub mod add_location;
|
||||||
pub mod add_token;
|
pub mod add_token;
|
||||||
pub mod find;
|
pub mod find;
|
||||||
pub mod register;
|
pub mod register;
|
||||||
|
pub mod selling;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
|
@ -109,6 +113,8 @@ pub fn command_filter(
|
||||||
warp::path("command").and(
|
warp::path("command").and(
|
||||||
create_command_filter::<FindCommand>(ctx.clone())
|
create_command_filter::<FindCommand>(ctx.clone())
|
||||||
.or(create_command_filter::<AddLocation>(ctx.clone()))
|
.or(create_command_filter::<AddLocation>(ctx.clone()))
|
||||||
.or(create_command_filter::<Register>(ctx)),
|
.or(create_command_filter::<Register>(ctx.clone()))
|
||||||
|
.or(create_command_filter::<Selling>(ctx.clone()))
|
||||||
|
.or(create_command_filter::<AddItem>(ctx)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
use crate::commands::{Command, RequestType};
|
||||||
|
use crate::context::Context;
|
||||||
|
use crate::Result;
|
||||||
|
use geoffrey_models::models::locations::{LocationDataDb, LocationDb};
|
||||||
|
use geoffrey_models::models::parameters::selling_params::SellingParams;
|
||||||
|
use geoffrey_models::models::player::Player;
|
||||||
|
use geoffrey_models::models::response::selling_listing::SellingListing;
|
||||||
|
use geoffrey_models::models::CommandLevel;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub struct Selling {}
|
||||||
|
|
||||||
|
impl Command for Selling {
|
||||||
|
type Req = SellingParams;
|
||||||
|
type Resp = Vec<SellingListing>;
|
||||||
|
|
||||||
|
fn command_name() -> String {
|
||||||
|
"selling".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_type() -> RequestType {
|
||||||
|
RequestType::GET
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_level() -> CommandLevel {
|
||||||
|
CommandLevel::ALL
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_command(ctx: Arc<Context>, req: Self::Req, _: Option<Player>) -> Result<Self::Resp> {
|
||||||
|
let shops: Vec<SellingListing> = ctx
|
||||||
|
.db
|
||||||
|
.filter(|_, loc: &LocationDb| {
|
||||||
|
if let LocationDataDb::Shop(shop_data) = loc.loc_data.clone() {
|
||||||
|
shop_data.item_listings.iter().any(|item| {
|
||||||
|
item.item
|
||||||
|
.name
|
||||||
|
.to_lowercase()
|
||||||
|
.contains(&req.query.to_lowercase())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})?
|
||||||
|
.filter_map(|shop: LocationDb| {
|
||||||
|
if let LocationDataDb::Shop(shop_data) = shop.clone().loc_data {
|
||||||
|
let listings: Vec<SellingListing> = shop_data
|
||||||
|
.item_listings
|
||||||
|
.iter()
|
||||||
|
.filter_map(|item| {
|
||||||
|
if item.item.name.to_lowercase().contains(&req.query) {
|
||||||
|
Some(SellingListing {
|
||||||
|
listing: item.clone(),
|
||||||
|
shop_name: shop.name.clone(),
|
||||||
|
shop_loc: shop.position,
|
||||||
|
portal: shop.tunnel.clone(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Some(listings)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(shops)
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,9 @@ impl Database {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let match_count = self.filter(|_, o: &T| !o.check_unique(&model))?.count();
|
let match_count = self
|
||||||
|
.filter(|o_id, o: &T| o_id != id && !o.check_unique(&model))?
|
||||||
|
.count();
|
||||||
|
|
||||||
if match_count > 0 {
|
if match_count > 0 {
|
||||||
log::debug!("{} is not unique: {:?}", T::tree(), model);
|
log::debug!("{} is not unique: {:?}", T::tree(), model);
|
||||||
|
|
|
@ -23,14 +23,14 @@ pub struct ItemListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ItemListing {
|
impl ItemListing {
|
||||||
fn new(item: &str, price: u32, count_per_price: u32, restocked_time: DateTime<Utc>) -> Self {
|
pub fn new(item: &str, price: u32, count_per_price: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
item: Item {
|
item: Item {
|
||||||
name: item.to_string(),
|
name: item.to_string(),
|
||||||
},
|
},
|
||||||
price,
|
price,
|
||||||
count_per_price,
|
count_per_price,
|
||||||
restocked_time,
|
restocked_time: Utc::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub mod market;
|
||||||
pub mod shop;
|
pub mod shop;
|
||||||
pub mod town;
|
pub mod town;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, PartialEq)]
|
||||||
pub enum LocationType {
|
pub enum LocationType {
|
||||||
Base,
|
Base,
|
||||||
Shop,
|
Shop,
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
use crate::models::parameters::CommandRequest;
|
||||||
|
use crate::models::player::UserID;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct AddItemParams {
|
||||||
|
pub token: String,
|
||||||
|
pub user_id: UserID,
|
||||||
|
pub item_name: String,
|
||||||
|
pub price: u32,
|
||||||
|
pub quantity: u32,
|
||||||
|
pub shop: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandRequest for AddItemParams {
|
||||||
|
fn token(&self) -> String {
|
||||||
|
self.token.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn user_id(&self) -> Option<UserID> {
|
||||||
|
Some(self.user_id.clone())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
|
pub mod add_item_params;
|
||||||
pub mod add_location_params;
|
pub mod add_location_params;
|
||||||
pub mod add_token_params;
|
pub mod add_token_params;
|
||||||
pub mod find_params;
|
pub mod find_params;
|
||||||
pub mod register_params;
|
pub mod register_params;
|
||||||
|
pub mod selling_params;
|
||||||
|
|
||||||
use crate::models::player::{Player, UserID};
|
use crate::models::player::{Player, UserID};
|
||||||
use crate::models::token::{Permissions, Token};
|
use crate::models::token::{Permissions, Token};
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
use crate::models::parameters::CommandRequest;
|
||||||
|
use crate::models::player::UserID;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub enum ItemSort {
|
||||||
|
Price,
|
||||||
|
Restock,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub enum Order {
|
||||||
|
High,
|
||||||
|
Low,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct SellingParams {
|
||||||
|
pub token: String,
|
||||||
|
pub query: String,
|
||||||
|
pub sort: Option<ItemSort>,
|
||||||
|
pub order: Option<Order>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandRequest for SellingParams {
|
||||||
|
fn token(&self) -> String {
|
||||||
|
self.token.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn user_id(&self) -> Option<UserID> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ pub enum GeoffreyAPIError {
|
||||||
EntryNotUnique,
|
EntryNotUnique,
|
||||||
DatabaseError(String),
|
DatabaseError(String),
|
||||||
TokenNotAuthorized,
|
TokenNotAuthorized,
|
||||||
|
MultipleLocationsMatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for GeoffreyAPIError {
|
impl Display for GeoffreyAPIError {
|
||||||
|
@ -26,6 +27,9 @@ impl Display for GeoffreyAPIError {
|
||||||
GeoffreyAPIError::TokenNotAuthorized => {
|
GeoffreyAPIError::TokenNotAuthorized => {
|
||||||
"Token supplied in request is not authorized".to_string()
|
"Token supplied in request is not authorized".to_string()
|
||||||
}
|
}
|
||||||
|
GeoffreyAPIError::MultipleLocationsMatch => {
|
||||||
|
"The location query returned multiple locations.".to_string()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
write!(f, "{}", string)
|
write!(f, "{}", string)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::models::response::api_error::GeoffreyAPIError;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod api_error;
|
pub mod api_error;
|
||||||
|
pub mod selling_listing;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum APIResponse<T> {
|
pub enum APIResponse<T> {
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
use crate::models::item::ItemListing;
|
||||||
|
use crate::models::{Position, Tunnel};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct SellingListing {
|
||||||
|
pub shop_name: String,
|
||||||
|
pub shop_loc: Position,
|
||||||
|
pub portal: Option<Tunnel>,
|
||||||
|
pub listing: ItemListing,
|
||||||
|
}
|
Loading…
Reference in New Issue