Progress on events
This commit is contained in:
parent
01bfe925b6
commit
91ec035439
456
src/api/event.rs
456
src/api/event.rs
|
@ -1,11 +1,10 @@
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, future, hash::Hash};
|
||||||
|
|
||||||
use futures::TryStreamExt;
|
use futures::{StreamExt, TryStreamExt};
|
||||||
use rocket::serde::json::json;
|
use rocket_db_pools::mongodb::bson::to_bson;
|
||||||
use rocket_db_pools::mongodb::{bson::to_bson, options::InsertOneOptions};
|
|
||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
use super::{prelude::*, util::PartyError};
|
use super::{prelude::*, util::PartyError};
|
||||||
|
use paste::paste;
|
||||||
|
|
||||||
api_routes!(
|
api_routes!(
|
||||||
create_event,
|
create_event,
|
||||||
|
@ -22,6 +21,21 @@ pub struct EventOutcome {
|
||||||
points: HashMap<String, i64>,
|
points: HashMap<String, i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EventOutcome {
|
||||||
|
pub async fn apply(&self, db: &Connection<Db>) -> Result<(), PartyError> {
|
||||||
|
for (player, reward) in self.points.iter() {
|
||||||
|
db.users()
|
||||||
|
.update_one(
|
||||||
|
doc! { "id": player },
|
||||||
|
doc! { "$inc": { "score": reward } },
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// # Event
|
/// # Event
|
||||||
///
|
///
|
||||||
/// An event in which participants can win or lose points
|
/// An event in which participants can win or lose points
|
||||||
|
@ -32,53 +46,399 @@ pub struct Event {
|
||||||
#[serde(default = "uuid")]
|
#[serde(default = "uuid")]
|
||||||
id: String,
|
id: String,
|
||||||
/// Has this event concluded?
|
/// Has this event concluded?
|
||||||
|
#[serde(default)]
|
||||||
concluded: bool,
|
concluded: bool,
|
||||||
|
/// Name of the event
|
||||||
|
#[serde(default)]
|
||||||
|
name: String,
|
||||||
|
/// Description of the event
|
||||||
|
#[serde(default)]
|
||||||
|
description: String,
|
||||||
/// Event type
|
/// Event type
|
||||||
event_type: EventType,
|
event_type: EventType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
pub fn outcome(&self) -> EventOutcome {
|
pub fn outcome(&self) -> EventOutcome {
|
||||||
match self.event_type {
|
self.event_type.outcome()
|
||||||
_ => EventOutcome::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn conclude(&self) {}
|
pub fn conclude(&self) {}
|
||||||
|
|
||||||
|
pub fn apply_update(&mut self, update: EventUpdate) -> Result<(), PartyError> {
|
||||||
|
self.event_type.apply_update(update)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uuid() -> String {
|
fn uuid() -> String {
|
||||||
uuid::Uuid::new_v4().to_string()
|
uuid::Uuid::new_v4().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # EventType
|
/// # EventSpec
|
||||||
///
|
///
|
||||||
/// Enumeration of all different types of events
|
/// A specification of an event
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub enum EventType {
|
pub struct EventSpec {
|
||||||
FreeForAllGame {},
|
name: String,
|
||||||
TeamGame {},
|
description: String,
|
||||||
Test {},
|
event_type: EventTypeSpec,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! events {
|
||||||
|
($($module:ident => $name:ident),* $(,)?) => {
|
||||||
|
paste! {
|
||||||
|
/// # EventTypeSpec
|
||||||
|
///
|
||||||
|
/// A specification of an event type
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub enum EventTypeSpec {
|
||||||
|
$($name($module::[<$name Spec>]),)*
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # EventUpdate
|
||||||
|
///
|
||||||
|
/// An update that can be applied to an event
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub enum EventUpdate {
|
||||||
|
$($name($module::[<$name Update>]),)*
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # EventType
|
||||||
|
///
|
||||||
|
/// An enumeration of event types
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub enum EventType {
|
||||||
|
$($name($module::$name),)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventSpec {
|
||||||
|
pub fn create_event(self) -> Result<Event, PartyError> {
|
||||||
|
let event_type = match self.event_type {
|
||||||
|
$(EventTypeSpec::$name(s) => {
|
||||||
|
EventType::$name($module::$name::from_spec(s))
|
||||||
|
})*
|
||||||
|
};
|
||||||
|
|
||||||
|
let event = Event {
|
||||||
|
id: uuid(),
|
||||||
|
name: self.name,
|
||||||
|
description: self.description,
|
||||||
|
concluded: false,
|
||||||
|
event_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventType {
|
||||||
|
pub fn apply_update(&mut self, update: EventUpdate) -> Result<(), PartyError> {
|
||||||
|
match (self, update) {
|
||||||
|
$((EventType::$name(s), EventUpdate::$name(u)) => {
|
||||||
|
s.apply_update(u)
|
||||||
|
})*
|
||||||
|
_ => Err(PartyError::Unknown("invalid update".into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn outcome(&self) -> EventOutcome {
|
||||||
|
match self {
|
||||||
|
$(EventType::$name(s) => {
|
||||||
|
s.outcome()
|
||||||
|
})*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EventTrait {
|
||||||
|
type Spec;
|
||||||
|
type Update;
|
||||||
|
|
||||||
|
fn from_spec(spec: Self::Spec) -> Self;
|
||||||
|
|
||||||
|
fn apply_update(&mut self, _update: Self::Update) -> Result<(), PartyError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outcome(&self) -> EventOutcome {
|
||||||
|
EventOutcome::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Test {
|
||||||
|
num_players: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct TestSpec {
|
||||||
|
num_players: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct TestUpdate {
|
||||||
|
win_game: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventTrait for Test {
|
||||||
|
type Spec = TestSpec;
|
||||||
|
type Update = TestUpdate;
|
||||||
|
|
||||||
|
fn from_spec(spec: TestSpec) -> Self {
|
||||||
|
Test {
|
||||||
|
num_players: spec.num_players,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_update(&mut self, update: TestUpdate) -> Result<(), PartyError> {
|
||||||
|
if update.win_game {
|
||||||
|
self.num_players += 1;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outcome(&self) -> EventOutcome {
|
||||||
|
let mut points = HashMap::new();
|
||||||
|
points.insert("420".into(), self.num_players);
|
||||||
|
EventOutcome { points }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod team_game {
|
||||||
|
use super::{
|
||||||
|
free_for_all_game::{FreeForAllGame, FreeForAllGameSpec, FreeForAllGameUpdate},
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct TeamGame {
|
||||||
|
/// Map of teams with a name as key and an array of players as value
|
||||||
|
teams: HashMap<String, Vec<String>>,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
ffa_game: FreeForAllGame,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct TeamGameSpec {
|
||||||
|
/// Map of teams with a name as key and an array of players as value
|
||||||
|
teams: HashMap<String, Vec<String>>,
|
||||||
|
|
||||||
|
/// Rewards for winning the game (first element for first place, second element for second
|
||||||
|
/// place, etc.)
|
||||||
|
#[schemars(example = "super::free_for_all_game::example_win_rewards")]
|
||||||
|
win_rewards: Vec<i64>,
|
||||||
|
/// Rewards for losing the game (first element for last place, second element for second to
|
||||||
|
/// last place, etc.)
|
||||||
|
#[schemars(example = "super::free_for_all_game::example_lose_rewards")]
|
||||||
|
lose_rewards: Vec<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct TeamGameUpdate {
|
||||||
|
#[serde(flatten)]
|
||||||
|
inner: FreeForAllGameUpdate,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventTrait for TeamGame {
|
||||||
|
type Spec = TeamGameSpec;
|
||||||
|
type Update = TeamGameUpdate;
|
||||||
|
|
||||||
|
fn from_spec(spec: TeamGameSpec) -> Self {
|
||||||
|
let players: Vec<String> = spec
|
||||||
|
.teams
|
||||||
|
.values()
|
||||||
|
.map(|v| v.iter().cloned())
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let ffa_game_spec = FreeForAllGameSpec {
|
||||||
|
players,
|
||||||
|
win_rewards: spec.win_rewards,
|
||||||
|
lose_rewards: spec.lose_rewards,
|
||||||
|
};
|
||||||
|
|
||||||
|
TeamGame {
|
||||||
|
teams: spec.teams,
|
||||||
|
ffa_game: FreeForAllGame::from_spec(ffa_game_spec),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_update(&mut self, update: Self::Update) -> Result<(), PartyError> {
|
||||||
|
self.ffa_game.apply_update(update.inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outcome(&self) -> EventOutcome {
|
||||||
|
let ffa_outcome = self.ffa_game.outcome();
|
||||||
|
|
||||||
|
let mut points = HashMap::new();
|
||||||
|
|
||||||
|
for (team, reward) in ffa_outcome.points.iter() {
|
||||||
|
if let Some(team) = self.teams.get(team) {
|
||||||
|
for player in team {
|
||||||
|
let score = points.get(player).unwrap_or(&0);
|
||||||
|
points.insert(player.clone(), score + reward);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EventOutcome { points }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod free_for_all_game {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub enum FreeForAllGameRanking {
|
||||||
|
/// Ranking of players by user id (first element is first place, second element is second
|
||||||
|
/// place, etc.)
|
||||||
|
Ranking(Vec<String>),
|
||||||
|
/// Score based ranking of players by user id
|
||||||
|
Scores(HashMap<String, i64>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct FreeForAllGame {
|
||||||
|
/// Ranking of players by user id (first element is first place, second element is second
|
||||||
|
/// place, etc.)
|
||||||
|
ranking: Option<FreeForAllGameRanking>,
|
||||||
|
|
||||||
|
/// Specification of the game
|
||||||
|
#[serde(flatten)]
|
||||||
|
spec: FreeForAllGameSpec,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn example_win_rewards() -> Vec<i64> {
|
||||||
|
vec![10, 7, 5, 3, 2, 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn example_lose_rewards() -> Vec<i64> {
|
||||||
|
vec![-3, -2, -1]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct FreeForAllGameSpec {
|
||||||
|
/// Array of user ids that participate in the game
|
||||||
|
pub(crate) players: Vec<String>,
|
||||||
|
|
||||||
|
/// Rewards for winning the game (first element for first place, second element for second
|
||||||
|
/// place, etc.)
|
||||||
|
#[schemars(example = "example_win_rewards")]
|
||||||
|
pub(crate) win_rewards: Vec<i64>,
|
||||||
|
/// Rewards for losing the game (first element for last place, second element for second to
|
||||||
|
/// last place, etc.)
|
||||||
|
#[schemars(example = "example_lose_rewards")]
|
||||||
|
pub(crate) lose_rewards: Vec<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub enum FreeForAllGameUpdate {
|
||||||
|
/// Replace the current ranking with the given ranking
|
||||||
|
Ranking(FreeForAllGameRanking),
|
||||||
|
|
||||||
|
/// If the current ranking is of type `Scores`, apply the given score deltas
|
||||||
|
ScoreDelta(HashMap<String, i64>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventTrait for FreeForAllGame {
|
||||||
|
type Spec = FreeForAllGameSpec;
|
||||||
|
type Update = FreeForAllGameUpdate;
|
||||||
|
|
||||||
|
fn from_spec(spec: Self::Spec) -> Self {
|
||||||
|
FreeForAllGame {
|
||||||
|
ranking: None,
|
||||||
|
spec,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_update(&mut self, update: Self::Update) -> Result<(), PartyError> {
|
||||||
|
match update {
|
||||||
|
FreeForAllGameUpdate::Ranking(r) => self.ranking = Some(r),
|
||||||
|
FreeForAllGameUpdate::ScoreDelta(d) => match &mut self.ranking {
|
||||||
|
Some(FreeForAllGameRanking::Ranking(_)) | None => {
|
||||||
|
return Err(PartyError::Unknown("cannot apply score delta".into()))
|
||||||
|
}
|
||||||
|
Some(FreeForAllGameRanking::Scores(s)) => {
|
||||||
|
for (player, delta) in d.iter() {
|
||||||
|
if let Some(value) = s.get(player) {
|
||||||
|
s.insert(player.clone(), value + delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outcome(&self) -> EventOutcome {
|
||||||
|
let ranking = match &self.ranking {
|
||||||
|
Some(FreeForAllGameRanking::Ranking(r)) => r.clone(),
|
||||||
|
Some(FreeForAllGameRanking::Scores(s)) => {
|
||||||
|
let mut results: Vec<(_, _)> = s.iter().collect();
|
||||||
|
results.sort_by(|a, b| b.1.cmp(a.1));
|
||||||
|
results.into_iter().map(|(k, _)| k.clone()).collect()
|
||||||
|
}
|
||||||
|
None => return EventOutcome::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut points = HashMap::new();
|
||||||
|
|
||||||
|
for (player, reward) in ranking.iter().zip(self.spec.win_rewards.iter()) {
|
||||||
|
let score = points.get(player).unwrap_or(&0);
|
||||||
|
points.insert(player.clone(), score + reward);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (player, reward) in ranking.iter().rev().zip(self.spec.lose_rewards.iter()) {
|
||||||
|
let score = points.get(player).unwrap_or(&0);
|
||||||
|
points.insert(player.clone(), score + reward);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventOutcome { points }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
events!(test => Test, team_game => TeamGame, free_for_all_game => FreeForAllGame);
|
||||||
|
|
||||||
/// # Create event
|
/// # Create event
|
||||||
///
|
///
|
||||||
/// If an `id` is supplied, it will be replaced by a randomly generated
|
/// If an `id` is supplied, it will be replaced by a randomly generated
|
||||||
/// one.
|
/// one.
|
||||||
///
|
///
|
||||||
/// Returns the created event. #[openapi(tag = "Event")]
|
/// Returns the created event.
|
||||||
#[openapi(tag = "Event")]
|
#[openapi(tag = "Event")]
|
||||||
#[post("/", data = "<event>")]
|
#[post("/", data = "<event>")]
|
||||||
pub async fn create_event(
|
pub async fn create_event(
|
||||||
_api_key: ApiKey,
|
_api_key: ApiKey,
|
||||||
db: Connection<Db>,
|
db: Connection<Db>,
|
||||||
mut event: Json<Event>,
|
event: Json<EventSpec>,
|
||||||
) -> Result<Json<Event>, PartyError> {
|
) -> Result<Json<Event>, PartyError> {
|
||||||
event.id = uuid();
|
let event = event.into_inner().create_event()?;
|
||||||
event.concluded = false;
|
db.events().insert_one(&event, None).await?;
|
||||||
db.events().insert_one(&*event, None).await?;
|
Ok(Json(event))
|
||||||
Ok(event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Update event
|
/// # Update event
|
||||||
|
@ -86,21 +446,23 @@ pub async fn create_event(
|
||||||
/// Update the supplied values in the event with the given id. The `id` of an event cannot be
|
/// Update the supplied values in the event with the given id. The `id` of an event cannot be
|
||||||
/// changed and `concluded` will always be false.
|
/// changed and `concluded` will always be false.
|
||||||
#[openapi(tag = "Event")]
|
#[openapi(tag = "Event")]
|
||||||
#[post("/<id>", data = "<event>")]
|
#[post("/<id>", data = "<update>")]
|
||||||
pub async fn update_event(
|
pub async fn update_event(
|
||||||
_api_key: ApiKey,
|
_api_key: ApiKey,
|
||||||
db: Connection<Db>,
|
db: Connection<Db>,
|
||||||
id: String,
|
id: String,
|
||||||
mut event: Json<Value>, // Use serde_json::Value to allow a partial struct
|
update: Json<EventUpdate>,
|
||||||
) -> Result<Status, PartyError> {
|
) -> Result<Status, PartyError> {
|
||||||
if let Some(i) = event.get_mut("id") {
|
let mut event = db
|
||||||
*i = json! { &id };
|
.events()
|
||||||
}
|
.find_one(doc! { "id": &id }, None)
|
||||||
if let Some(i) = event.get_mut("concluded") {
|
.await?
|
||||||
*i = json! { false };
|
.ok_or(PartyError::EventNotFound(id.clone()))?;
|
||||||
}
|
|
||||||
db.users()
|
event.apply_update(update.into_inner())?;
|
||||||
.update_one(doc! { "id": &id }, doc! { "$set": to_bson(&*event)? }, None)
|
|
||||||
|
db.events()
|
||||||
|
.update_one(doc! { "id": &id }, doc! { "$set": to_bson(&event)? }, None)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Status::Ok)
|
Ok(Status::Ok)
|
||||||
|
@ -126,21 +488,32 @@ pub async fn get_event(
|
||||||
|
|
||||||
/// # Get events
|
/// # Get events
|
||||||
///
|
///
|
||||||
/// Returns the event with the given id
|
/// Returns all events
|
||||||
#[openapi(tag = "Event")]
|
#[openapi(tag = "Event")]
|
||||||
#[get("/")]
|
#[get("/?<concluded>")]
|
||||||
pub async fn get_all_events(
|
pub async fn get_all_events(
|
||||||
_api_key: ApiKey,
|
_api_key: ApiKey,
|
||||||
db: Connection<Db>,
|
db: Connection<Db>,
|
||||||
|
concluded: Option<bool>,
|
||||||
) -> Result<Json<Vec<Event>>, PartyError> {
|
) -> Result<Json<Vec<Event>>, PartyError> {
|
||||||
|
let filter = if let Some(concluded) = concluded {
|
||||||
|
doc! { "concluded": concluded }
|
||||||
|
} else {
|
||||||
|
doc! {}
|
||||||
|
};
|
||||||
Ok(Json(
|
Ok(Json(
|
||||||
db.events().find(doc! {}, None).await?.try_collect().await?,
|
db.events()
|
||||||
|
.find(filter, None)
|
||||||
|
.await?
|
||||||
|
.filter(|e| future::ready(e.is_ok()))
|
||||||
|
.try_collect()
|
||||||
|
.await?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Get outcome
|
/// # Get outcome
|
||||||
///
|
///
|
||||||
/// Returns the outcome of a concluded event.
|
/// Returns the outcome of an event.
|
||||||
#[openapi(tag = "Event")]
|
#[openapi(tag = "Event")]
|
||||||
#[get("/<id>/outcome")]
|
#[get("/<id>/outcome")]
|
||||||
pub async fn event_outcome(
|
pub async fn event_outcome(
|
||||||
|
@ -157,6 +530,8 @@ pub async fn event_outcome(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Stop event
|
/// # Stop event
|
||||||
|
///
|
||||||
|
/// This will conclude the event, apply the outcome to the users and return the outcome.
|
||||||
#[openapi(tag = "Event")]
|
#[openapi(tag = "Event")]
|
||||||
#[post("/<id>/stop")]
|
#[post("/<id>/stop")]
|
||||||
pub async fn stop_event(
|
pub async fn stop_event(
|
||||||
|
@ -170,8 +545,15 @@ pub async fn stop_event(
|
||||||
.await?
|
.await?
|
||||||
.ok_or(PartyError::EventNotFound(id.clone()))?;
|
.ok_or(PartyError::EventNotFound(id.clone()))?;
|
||||||
event.conclude();
|
event.conclude();
|
||||||
db.users()
|
db.events()
|
||||||
.update_one(doc! { "id": &id }, doc! { "concluded": true }, None)
|
.update_one(
|
||||||
|
doc! { "id": &id },
|
||||||
|
doc! { "$set": { "concluded": true } },
|
||||||
|
None,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(Json(event.outcome()))
|
let outcome = event.outcome();
|
||||||
|
outcome.apply(&db).await?;
|
||||||
|
|
||||||
|
Ok(Json(outcome))
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ impl OpenApiResponderInner for PartyError {
|
||||||
|
|
||||||
impl<'r, 'o: 'r> Responder<'r, 'o> for PartyError {
|
impl<'r, 'o: 'r> Responder<'r, 'o> for PartyError {
|
||||||
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> {
|
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> {
|
||||||
|
println!("{:#?}", &self);
|
||||||
match self {
|
match self {
|
||||||
Self::UserNotFound(_) | Self::EventNotFound(_) => Status::NotFound,
|
Self::UserNotFound(_) | Self::EventNotFound(_) => Status::NotFound,
|
||||||
_ => Status::InternalServerError,
|
_ => Status::InternalServerError,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
podman run -d --rm --name lan_party_db \
|
#!/bin/sh
|
||||||
|
|
||||||
|
podman run -d --rm --name lan_party_db \
|
||||||
-v lan-party-db:/data/db:z \
|
-v lan-party-db:/data/db:z \
|
||||||
-p 27017:27017 \
|
-p "27017:27017" \
|
||||||
-e MONGO_INITDB_ROOT_USERNAME=root \
|
-e MONGO_INITDB_ROOT_USERNAME=root \
|
||||||
-e MONGO_INITDB_ROOT_PASSWORD=example \
|
-e MONGO_INITDB_ROOT_PASSWORD=example \
|
||||||
docker.io/mongo:latest
|
docker.io/mongo:latest
|
||||||
|
|
Loading…
Reference in New Issue