Skip to content

Commit

Permalink
Merge pull request #19 from gamemstr/17-session-endpoints
Browse files Browse the repository at this point in the history
17 session endpoints
  • Loading branch information
jameswolff96 authored Feb 8, 2024
2 parents 6ed9265 + e96c48f commit acb768a
Show file tree
Hide file tree
Showing 14 changed files with 284 additions and 8 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ jobs:

steps:
- uses: actions/checkout@v3
- name: Install latest nightly
- name: Install latest rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
toolchain: stable
override: true
profile: minimal
- name: Build
Expand All @@ -44,10 +44,10 @@ jobs:
steps:
- name: Checkout latest commit
uses: actions/checkout@v3
- name: Install latest rust nightly
- name: Install latest rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
toolchain: stable
override: true
profile: minimal
- name: Diesel Migrations
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0"
custom_derive = "0.1.7"
diesel = { version = "2.0.3", features = ["postgres", "postgres_backend", "r2d2", "serde_json"] }
dotenvy = "0.15.6"
gamemstr-common = { git = "https://github.com/gamemstr/gamemstr-common.git", rev="d455255"}
gamemstr-common = { git = "https://github.com/gamemstr/gamemstr-common.git", rev="c5a5615d3e980d810d92d60d85905335f8269fb3"}
r2d2 = "0.8.10"
r2d2-diesel = "1.0.0"
rocket = { version = "0.5.0-rc.3", features = ["json"] }
Expand Down
1 change: 1 addition & 0 deletions migrations/2024-02-08-142012_sessions/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- This file should undo anything in `up.sql`
10 changes: 10 additions & 0 deletions migrations/2024-02-08-142012_sessions/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Your SQL goes here
CREATE TABLE sessions (
id VARCHAR PRIMARY KEY,
name VARCHAR NOT NULL,
description VARCHAR NOT NULL,
campaign_id VARCHAR NOT NULL,
notes JSONB NOT NULL,
plan JSONB NOT NULL,
recap JSONB NOT NULL
);
72 changes: 72 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![cfg_attr(debug_assertions, allow(unused_imports))]
pub mod models;
pub mod schema;
mod services;
Expand Down Expand Up @@ -32,6 +33,11 @@ fn rocket() -> _ {
.mount("/", routes![services::worlds::locations::delete_location])
.mount("/", routes![services::worlds::locations::create_location])
.mount("/", routes![services::worlds::locations::update_location])
.mount("/", routes![services::worlds::campaigns::sessions::list_sessions])
.mount("/", routes![services::worlds::campaigns::sessions::get_session])
.mount("/", routes![services::worlds::campaigns::sessions::delete_session])
.mount("/", routes![services::worlds::campaigns::sessions::create_session])
.mount("/", routes![services::worlds::campaigns::sessions::update_session])
.attach(Template::fairing())
}

Expand Down Expand Up @@ -395,4 +401,70 @@ mod tests {
assert_eq!(response.status(), Status::Ok);
client.delete("/worlds/2587598027928569265").dispatch();
}

#[test]
#[should_panic]
pub fn test_sessions_api() {
let client = Client::tracked(crate::rocket()).expect("valid rocket instance");
let world = gamemstr_common::world::World {
id: "2587598027928569265".to_string(),
name: "Test World".to_string(),
description: "Test Description".to_string(),
};
client
.post("/worlds/add")
.header(ContentType::JSON)
.body(serde_json::to_string(&world).unwrap())
.dispatch();
// TODO: Add campaign
let response = client
.get("/worlds/2587598027928569265/campaigns/2587598027928569265/sessions")
.dispatch();
assert_eq!(response.status(), Status::Ok);
assert!(response.into_string().unwrap().contains("Sessions"));
let session = gamemstr_common::world::campaign::session::Session {
id: "2587598027928569265".to_string(),
name: "Test World".to_string(),
description: "Test Description".to_string(),
campaign_id: "2587598027928569265".to_string(),
notes: vec![],
plan: gamemstr_common::world::campaign::session::Plan::default(),
recap: gamemstr_common::world::campaign::session::Recap::default(),
};
client
.post("/worlds/2587598027928569265/campaigns/2587598027928569265/sessions/add")
.header(ContentType::JSON)
.body(serde_json::to_string(&session).unwrap())
.dispatch();
let response = client
.get("/worlds/2587598027928569265/campaigns/2587598027928569265/sessions/2587598027928569265")
.dispatch();
assert_eq!(response.status(), Status::Ok);
assert_eq!(
response.into_string(),
Some(serde_json::to_string(&session).unwrap())
);
let new_session = gamemstr_common::world::campaign::session::Session {
name: "Updated Session".to_string(),
..session
};
let response = client
.post("/worlds/2587598027928569265/campaigns/2587598027928569265/sessions/2587598027928569265")
.header(ContentType::JSON)
.body(serde_json::to_string(&new_session).unwrap())
.dispatch();
assert_eq!(response.status(), Status::Accepted);
assert_eq!(
serde_json::from_str::<gamemstr_common::world::campaign::session::Session>(
&response.into_string().unwrap()
)
.unwrap()
.name,
"Updated Session"
);
let response = client
.delete("/worlds/2587598027928569265/campaigns/2587598027928569265/sessions/2587598027928569265")
.dispatch();
assert_eq!(response.status(), Status::Ok);
}
}
1 change: 1 addition & 0 deletions src/models/worlds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};

use crate::schema::worlds;

pub mod campaigns;
pub mod locations;

#[derive(Debug, Serialize, Deserialize, Queryable, Insertable, AsChangeset)]
Expand Down
1 change: 1 addition & 0 deletions src/models/worlds/campaigns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod sessions;
44 changes: 44 additions & 0 deletions src/models/worlds/campaigns/sessions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use diesel::prelude::*;
use serde::{Deserialize, Serialize};

use crate::schema::sessions;

#[derive(Debug, Serialize, Deserialize, Queryable, Insertable, AsChangeset)]
#[diesel(table_name = sessions)]
pub struct Session {
pub id: String,
pub name: String,
pub campaign_id: String,
pub description: String,
pub notes: serde_json::Value,
pub plan: serde_json::Value,
pub recap: serde_json::Value,
}

impl super::super::super::Model for Session {
type Entity = gamemstr_common::world::campaign::session::Session;

fn new(entity: Self::Entity) -> Self {
Self {
id: entity.id,
name: entity.name,
campaign_id: entity.campaign_id,
description: entity.description,
notes: serde_json::to_value(entity.notes).unwrap(),
plan: serde_json::to_value(entity.plan).unwrap(),
recap: serde_json::to_value(entity.recap).unwrap(),
}
}

fn to_entity(&self) -> Self::Entity {
Self::Entity {
id: self.id.clone(),
name: self.name.clone(),
campaign_id: self.campaign_id.clone(),
description: self.description.clone(),
notes: serde_json::from_value(self.notes.clone()).unwrap(),
plan: serde_json::from_value(self.plan.clone()).unwrap(),
recap: serde_json::from_value(self.recap.clone()).unwrap(),
}
}
}
21 changes: 20 additions & 1 deletion src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ diesel::table! {
}
}

diesel::table! {
sessions (id) {
id -> Varchar,
name -> Varchar,
description -> Varchar,
campaign_id -> Varchar,
notes -> Jsonb,
plan -> Jsonb,
recap -> Jsonb,
}
}

diesel::table! {
spells (id) {
id -> Varchar,
Expand All @@ -82,4 +94,11 @@ diesel::table! {
}
}

diesel::allow_tables_to_appear_in_same_query!(creatures, items, locations, spells, worlds,);
diesel::allow_tables_to_appear_in_same_query!(
creatures,
items,
locations,
sessions,
spells,
worlds,
);
1 change: 1 addition & 0 deletions src/services/worlds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use rocket::{
};
use rocket_dyn_templates::{context, Template};

pub mod campaigns;
pub mod locations;

#[get("/worlds")]
Expand Down
1 change: 1 addition & 0 deletions src/services/worlds/campaigns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod sessions;
101 changes: 101 additions & 0 deletions src/services/worlds/campaigns/sessions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use super::super::super::Result;
use crate::{
models::{self, Model},
schema,
};
use diesel::prelude::*;
use gamemstr_common::world::campaign::session::Session;
use rocket::{
delete, get, post,
response::status::{Accepted, Created, NotFound},
serde::json::Json,
Either,
};
use rocket_dyn_templates::{context, Template};

#[get("/worlds/<_world_id>/campaigns/<campaign_id>/sessions")]
pub fn list_sessions(_world_id: String, campaign_id: String) -> Template {
let connection = &mut crate::services::establish_connection_pg();
let sessions = schema::sessions::dsl::sessions
.filter(schema::sessions::dsl::campaign_id.eq(campaign_id))
.load::<models::worlds::campaigns::sessions::Session>(connection)
.expect("Error loading sessions")
.iter()
.map(|session| session.to_entity())
.collect::<Vec<_>>();
Template::render(
"sessions",
context! {
sessions: &sessions,
count: sessions.len()
},
)
}

#[get("/worlds/<_world_id>/campaigns/<_campaign_id>/sessions/<id>")]
pub fn get_session(_world_id: String, _campaign_id: String, id: String) -> Result<Json<Session>> {
let connection = &mut crate::services::establish_connection_pg();
let session = schema::sessions::dsl::sessions
.find(id)
.first::<models::worlds::campaigns::sessions::Session>(connection)
.expect("Error loading session")
.to_entity();
Ok(Json(session))
}

#[delete("/worlds/<_world_id>/campaigns/<_campaign_id>/sessions/<id>")]
pub fn delete_session(_world_id: String, _campaign_id: String, id: String) -> Result<Json<Session>> {
let connection = &mut crate::services::establish_connection_pg();
let session = schema::sessions::dsl::sessions
.find(&id)
.first::<models::worlds::campaigns::sessions::Session>(connection)
.expect("Error loading session")
.to_entity();
diesel::delete(schema::sessions::dsl::sessions.find(&id))
.execute(connection)
.expect("Error deleting session");
Ok(Json(session))
}

#[post(
"/worlds/<_world_id>/campaigns/<_campaign_id>/sessions/add",
rank = 1,
format = "json",
data = "<session>"
)]
pub fn create_session(
_world_id: String,
_campaign_id: String,
session: Json<Session>,
) -> Result<Created<Json<Session>>> {
let connection = &mut crate::services::establish_connection_pg();
let new_session = models::worlds::campaigns::sessions::Session::new(session.clone().0);
diesel::insert_into(schema::sessions::dsl::sessions)
.values(&new_session)
.execute(connection)
.expect("Error creating session");
Ok(Created::new("/").body(session))
}

#[post(
"/worlds/<_world_id>/campaigns/<_campaign_id>/sessions/<id>",
rank = 2,
format = "json",
data = "<session>"
)]
pub fn update_session(
_world_id: String,
_campaign_id: String,
id: String,
session: Json<Session>,
) -> Either<Result<Accepted<Json<Session>>>, Result<NotFound<String>>> {
let connection = &mut crate::services::establish_connection_pg();
let new_session = models::worlds::campaigns::sessions::Session::new(session.clone().0);
let result = diesel::update(schema::sessions::dsl::sessions.find(&id))
.set(&new_session)
.execute(connection);
match result {
Ok(_) => Either::Left(Ok(Accepted(Some(session)))),
Err(_) => Either::Right(Ok(NotFound(format!("Session not found with id {}", id)))),
}
}
25 changes: 25 additions & 0 deletions templates/sessions.html.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Sessions</title>
</head>
<body>
<section id = "hello">
<h1>Sessions</h1>
{{#each sessions as |session|}}
<ul>
<li>ID: {{session.id}}</li>
<li>Name: {{session.name}}</li>
<li>Description: {{session.description}}</li>
<li>Campaign ID: {{session.campaign_id}}</li>
<li>Notes: {{session.notes}}</li>
<li>Plan: {{session.plan}}</li>
<li>Recap: {{session.recap}}</li>
</ul>
{{/each}}
</section>
</body>
</html>

0 comments on commit acb768a

Please sign in to comment.