Implement editing of attributes.
This commit is contained in:
parent
c0a48245b1
commit
4bff55cc6b
17
src/db.rs
17
src/db.rs
|
@ -13,16 +13,15 @@ pub(crate) trait Dao {
|
||||||
|
|
||||||
async fn load_user(&self, for_username: String) -> QueryResult<Option<User>>;
|
async fn load_user(&self, for_username: String) -> QueryResult<Option<User>>;
|
||||||
|
|
||||||
//async fn insert_user<'a>(&self, new_user: &'a NewUser<'a>) -> QueryResult<User>;
|
|
||||||
|
|
||||||
async fn insert_user(&self, new_user: NewUser) -> QueryResult<User>;
|
async fn insert_user(&self, new_user: NewUser) -> QueryResult<User>;
|
||||||
|
|
||||||
async fn load_character_list(&self, for_user_id: i32) -> QueryResult<Vec<StrippedCharacter>>;
|
async fn load_character_list(&self, for_user_id: i32) -> QueryResult<Vec<StrippedCharacter>>;
|
||||||
|
|
||||||
async fn load_character(&self, character_id: i32) -> QueryResult<Option<Character>>;
|
async fn load_character(&self, character_id: i32) -> QueryResult<Option<Character>>;
|
||||||
|
|
||||||
//async fn insert_character<'a>(&self, new_character: NewCharacter<'a>) -> QueryResult<()>;
|
|
||||||
async fn insert_character(&self, new_character: NewCharacter) -> QueryResult<()>;
|
async fn insert_character(&self, new_character: NewCharacter) -> QueryResult<()>;
|
||||||
|
|
||||||
|
async fn update_character_sheet(&self, character: Character) -> QueryResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type StrippedCharacterColumns = (
|
type StrippedCharacterColumns = (
|
||||||
|
@ -107,4 +106,16 @@ impl Dao for TenebrousDbConn {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_character_sheet(&self, character: Character) -> QueryResult<()> {
|
||||||
|
use crate::schema::characters::dsl::*;
|
||||||
|
self.run(move |conn| {
|
||||||
|
diesel::update(&character)
|
||||||
|
.set(data.eq(&character.data))
|
||||||
|
.execute(conn)
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@ pub enum Error {
|
||||||
#[error("you do not have permission to access this")]
|
#[error("you do not have permission to access this")]
|
||||||
NoPermission,
|
NoPermission,
|
||||||
|
|
||||||
|
#[error("invalid input")]
|
||||||
|
InvalidInput,
|
||||||
|
|
||||||
#[error("query error: {0}")]
|
#[error("query error: {0}")]
|
||||||
QueryError(#[from] diesel::result::Error),
|
QueryError(#[from] diesel::result::Error),
|
||||||
|
|
||||||
|
@ -35,6 +38,7 @@ impl Error {
|
||||||
use Error::*;
|
use Error::*;
|
||||||
match self {
|
match self {
|
||||||
QueryError(_) => true,
|
QueryError(_) => true,
|
||||||
|
IoError(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ impl CharacterDataType {
|
||||||
|
|
||||||
/// An entry that appears in a user's character list. Properties are
|
/// An entry that appears in a user's character list. Properties are
|
||||||
/// in order of table columns.
|
/// in order of table columns.
|
||||||
#[derive(Serialize, Debug, Queryable)]
|
#[derive(Serialize, Debug, Queryable, Identifiable, AsChangeset)]
|
||||||
pub struct Character {
|
pub struct Character {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub user_id: i32,
|
pub user_id: i32,
|
||||||
|
@ -129,8 +129,22 @@ impl Character {
|
||||||
// fields.
|
// fields.
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the existing character with new serialized protobuf
|
||||||
|
/// data. Consumes the data.
|
||||||
|
pub fn update_data<T>(&mut self, data: T) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
T: prost::Message + std::default::Default,
|
||||||
|
{
|
||||||
|
let mut buf = BytesMut::with_capacity(std::mem::size_of_val(&data));
|
||||||
|
data.encode(&mut buf)?;
|
||||||
|
self.data = buf.to_vec();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same as regular character type, but without the actual protobuf
|
||||||
|
/// data loaded into memory.
|
||||||
#[derive(Serialize, Debug, Queryable)]
|
#[derive(Serialize, Debug, Queryable)]
|
||||||
pub struct StrippedCharacter {
|
pub struct StrippedCharacter {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
|
|
@ -41,15 +41,48 @@ mod cofd {
|
||||||
"lol"
|
"lol"
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/cofd/<owner>/<character_id>/attribute/<attribute>", data = "<info>")]
|
#[patch("/cofd/<owner>/<character_id>/attributes", data = "<attr_update>")]
|
||||||
pub(super) fn update_attribute<'a>(
|
pub(super) async fn update_attribute<'a>(
|
||||||
owner: String,
|
owner: String,
|
||||||
character_id: i32,
|
character_id: i32,
|
||||||
attribute: String,
|
attr_update: Proto<Attribute>,
|
||||||
info: Proto<Attribute>,
|
conn: TenebrousDbConn,
|
||||||
) -> &'a str {
|
logged_in_user: Option<&User>,
|
||||||
println!("incoming request is {:#?}", info);
|
) -> Result<&'a str, Error> {
|
||||||
"lol"
|
let logged_in_user = logged_in_user.ok_or(Error::NotLoggedIn)?;
|
||||||
|
let owner = conn.load_user(owner).await?.ok_or(Error::NotFound)?;
|
||||||
|
let mut character: Character = conn
|
||||||
|
.load_character(character_id)
|
||||||
|
.await?
|
||||||
|
.ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
|
if logged_in_user != &owner {
|
||||||
|
return Err(Error::NoPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sheet: CofdSheet = character.try_deserialize()?;
|
||||||
|
|
||||||
|
match attr_update.name.to_lowercase().as_ref() {
|
||||||
|
"strength" => Ok(sheet.strength += attr_update.value),
|
||||||
|
"dexterity" => Ok(sheet.dexterity += attr_update.value),
|
||||||
|
"stamina" => Ok(sheet.stamina += attr_update.value),
|
||||||
|
"intelligence" => Ok(sheet.intelligence += attr_update.value),
|
||||||
|
"wits" => Ok(sheet.wits += attr_update.value),
|
||||||
|
"resolve" => Ok(sheet.resolve += attr_update.value),
|
||||||
|
"presence" => Ok(sheet.presence += attr_update.value),
|
||||||
|
"manipulation" => Ok(sheet.manipulation += attr_update.value),
|
||||||
|
"composure" => Ok(sheet.composure += attr_update.value),
|
||||||
|
_ => Err(Error::InvalidInput),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"updated {} attribute {} to {}",
|
||||||
|
character.character_name, attr_update.name, attr_update.value
|
||||||
|
);
|
||||||
|
|
||||||
|
character.update_data(sheet)?;
|
||||||
|
conn.update_character_sheet(character).await?;
|
||||||
|
Ok("lol")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/cofd/<owner>/<character_id>/skills", data = "<info>")]
|
#[post("/cofd/<owner>/<character_id>/skills", data = "<info>")]
|
||||||
|
@ -57,6 +90,7 @@ mod cofd {
|
||||||
owner: String,
|
owner: String,
|
||||||
character_id: i32,
|
character_id: i32,
|
||||||
info: Proto<Skills>,
|
info: Proto<Skills>,
|
||||||
|
conn: TenebrousDbConn,
|
||||||
) -> &'a str {
|
) -> &'a str {
|
||||||
"lol"
|
"lol"
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ pub(crate) fn routes() -> Vec<rocket::Route> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct ViewCharacterTemplate<'a> {
|
struct ViewCharacterContext<'a> {
|
||||||
|
pub id: i32,
|
||||||
pub name: &'a str,
|
pub name: &'a str,
|
||||||
pub username: &'a str,
|
pub username: &'a str,
|
||||||
pub data_type: &'a CharacterDataType,
|
pub data_type: &'a CharacterDataType,
|
||||||
|
@ -28,7 +29,8 @@ struct ViewCharacterTemplate<'a> {
|
||||||
fn view_character_template(user: &User, character: Character) -> Result<Template, Error> {
|
fn view_character_template(user: &User, character: Character) -> Result<Template, Error> {
|
||||||
let character = character.uprade()?;
|
let character = character.uprade()?;
|
||||||
|
|
||||||
let context = ViewCharacterTemplate {
|
let context = ViewCharacterContext {
|
||||||
|
id: character.id,
|
||||||
name: &character.character_name,
|
name: &character.character_name,
|
||||||
username: &user.username,
|
username: &user.username,
|
||||||
data_type: &character.data_type,
|
data_type: &character.data_type,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
function makeAPI(root) {
|
function makeAPI(root) {
|
||||||
const Attribute = root.lookupType("models.proto.cofd.api.Attribute");
|
const Attribute = root.lookupType("models.proto.cofd.api.Attribute");
|
||||||
|
|
||||||
const attributeResource = (username, characterID, attribute) =>
|
const attributesResource = (username, characterID) =>
|
||||||
'/api/cofd/' + username + '/' + characterID + '/attribute/' + attribute;
|
'/api/cofd/' + username + '/' + characterID + '/attributes';
|
||||||
|
|
||||||
async function updateAttribute(params) {
|
async function updateAttribute(params) {
|
||||||
const { username, characterID, attribute, newValue } = params;
|
const { username, characterID, attribute, newValue } = params;
|
||||||
|
@ -12,10 +12,10 @@ function makeAPI(root) {
|
||||||
value: parseInt(newValue)
|
value: parseInt(newValue)
|
||||||
});
|
});
|
||||||
|
|
||||||
const resource = attributeResource(username, characterID, attribute);
|
const resource = attributesResource(username, characterID);
|
||||||
|
|
||||||
let resp = await fetch(resource, {
|
let resp = await fetch(resource, {
|
||||||
method: 'POST',
|
method: 'PATCH',
|
||||||
body: Attribute.encode(req).finish()
|
body: Attribute.encode(req).finish()
|
||||||
}).then(async resp => {
|
}).then(async resp => {
|
||||||
console.log("resp is", await resp.text());
|
console.log("resp is", await resp.text());
|
||||||
|
|
|
@ -8,4 +8,8 @@
|
||||||
<p>System: {{data_type}}</h3>
|
<p>System: {{data_type}}</h3>
|
||||||
<p>Strength: {{sheet.strength}}</p>
|
<p>Strength: {{sheet.strength}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a href="/characters/{{username}}/{{id}}/edit">Edit Character</a>
|
||||||
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
Loading…
Reference in New Issue