create relation

master
Ondřej Hruška 4 years ago
parent 35272674f6
commit ce0b55e64e
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 39
      yopa-web/resources/templates/relation_create.html.tera
  2. 3
      yopa-web/src/main.rs
  3. 104
      yopa-web/src/routes.rs
  4. 12
      yopa/src/lib.rs

@ -0,0 +1,39 @@
{% extends "_layout" %}
{% block title -%}
Define relation
{%- endblock %}
{% block nav -%}
<a href="/">Home</a>
{%- endblock %}
{% block content -%}
<h1>Define new relation from "{{object.name}}"</h1>
<form action="/model/relation/create" method="POST">
<input type="hidden" name="object" value="{{object.id}}">
<label for="name">Name:</label>
<input type="text" id="name" name="name"><br>
<label for="optional">Optional:</label>
<input type="checkbox" name="optional" id="optional" value="1">
<br>
<label for="multiple">Multiple:</label>
<input type="checkbox" name="multiple" id="multiple" value="1"><br>
<label for="related">Related object:</label>
<select name="related" id="related">
{% for m in models %}
<option value="{{ m.id }}">{{ m.name }}</option>
{% endfor %}
</select><br>
<input type="submit" value="Save">
</form>
{%- endblock %}

@ -97,6 +97,8 @@ async fn main() -> std::io::Result<()> {
.service(routes::index) .service(routes::index)
.service(routes::object_model_create_form) .service(routes::object_model_create_form)
.service(routes::object_model_create) .service(routes::object_model_create)
.service(routes::relation_model_create_form)
.service(routes::relation_model_create)
.service(routes::object_model_delete) .service(routes::object_model_delete)
.service(routes::relation_model_delete) .service(routes::relation_model_delete)
.service(routes::property_model_delete) .service(routes::property_model_delete)
@ -107,6 +109,7 @@ async fn main() -> std::io::Result<()> {
.run().await .run().await
} }
fn init_yopa() -> YopaStoreWrapper { fn init_yopa() -> YopaStoreWrapper {
let mut store = Storage::new(); let mut store = Storage::new();

@ -1,12 +1,15 @@
use actix_web::{web, HttpRequest, Responder, HttpResponse}; use actix_web::{web, HttpRequest, Responder, HttpResponse};
use crate::TERA; use crate::TERA;
use crate::tera_ext::TeraExt; use crate::tera_ext::TeraExt;
use yopa::{Storage, StorageError}; use yopa::{Storage, StorageError, ID};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use yopa::model::{PropertyModel, RelationModel, ObjectModel}; use yopa::model::{PropertyModel, RelationModel, ObjectModel};
use std::ops::DerefMut; use std::ops::DerefMut;
use actix_session::Session; use actix_session::Session;
use crate::session_ext::SessionExt; use crate::session_ext::SessionExt;
use std::str::FromStr;
use std::fmt::{Debug, Display};
use actix_web::http::header::IntoHeaderValue;
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
struct ObjectModelDisplay<'a> { struct ObjectModelDisplay<'a> {
@ -23,12 +26,39 @@ struct RelationModelDisplay<'a> {
properties: Vec<&'a PropertyModel>, properties: Vec<&'a PropertyModel>,
} }
fn redirect(path : impl AsRef<str>) -> actix_web::Result<impl Responder> { fn redirect(path : impl IntoHeaderValue) -> actix_web::Result<HttpResponse> {
Ok(HttpResponse::SeeOther() Ok(HttpResponse::SeeOther()
.header("location", path.as_ref()) // back - to where? .header("location", path) // back - to where?
.finish()) .finish())
} }
trait ParseOrBadReq {
fn parse_or_bad_request<T, E>(&self) -> actix_web::Result<T>
where T: FromStr<Err=E>,
E: Display + Debug + 'static;
}
impl ParseOrBadReq for &str {
fn parse_or_bad_request<T, E>(&self) -> actix_web::Result<T>
where T: FromStr<Err=E>,
E: Display + Debug + 'static
{
self.parse::<T>()
.map_err(|e| actix_web::error::ErrorBadRequest(e))
}
}
impl ParseOrBadReq for String {
fn parse_or_bad_request<T, E>(&self) -> actix_web::Result<T>
where T: FromStr<Err=E>,
E: Display + Debug + 'static
{
self.as_str()
.parse_or_bad_request()
}
}
#[get("/")] #[get("/")]
pub(crate) async fn index(session : Session, store : crate::YopaStoreWrapper) -> actix_web::Result<impl Responder> { pub(crate) async fn index(session : Session, store : crate::YopaStoreWrapper) -> actix_web::Result<impl Responder> {
@ -115,6 +145,70 @@ pub(crate) async fn object_model_create(
} }
} }
#[get("/model/relation/create/{object_id}")]
pub(crate) async fn relation_model_create_form(
object_id : web::Path<String>,
store : crate::YopaStoreWrapper,
session : Session
) -> actix_web::Result<impl Responder> {
let mut context = tera::Context::new();
session.render_flash(&mut context);
let rg = store.read().await;
debug!("ID = {}", object_id);
let object = rg.get_object_model(object_id.parse_or_bad_request()?)
.ok_or_else(|| actix_web::error::ErrorNotFound("No such source object"))?;
let mut models: Vec<_> = rg.get_object_models().collect();
models.sort_by_key(|m| &m.name);
context.insert("models", &models);
context.insert("object", &object);
TERA.build_response("relation_create", &context)
}
#[derive(Deserialize)]
pub(crate) struct RelationModelCreate {
pub object : ID,
pub name : String,
pub optional : Option<i32>,
pub multiple : Option<i32>,
pub related : ID,
}
#[post("/model/relation/create")]
pub(crate) async fn relation_model_create(
form : web::Form<RelationModelCreate>,
store : crate::YopaStoreWrapper,
session : Session
) -> actix_web::Result<impl Responder> {
let mut wg = store.write().await;
let form = form.into_inner();
match wg.define_relation(RelationModel {
id: Default::default(),
object: form.object,
name: form.name.clone(),
optional: form.optional.unwrap_or_default() != 0,
multiple: form.multiple.unwrap_or_default() != 0,
related: form.related
}) {
Ok(_id) => {
debug!("Relation created, redirecting to root");
session.flash_success(format!("Relation model \"{}\" created.", form.name));
redirect("/")
}
Err(e) => {
warn!("Error creating relation model: {:?}", e);
session.flash_error(e.to_string());
redirect(format!("/model/relation/create/{}", form.object))
}
}
}
#[get("/model/object/delete/{id}")] #[get("/model/object/delete/{id}")]
pub(crate) async fn object_model_delete( pub(crate) async fn object_model_delete(
id : web::Path<String>, id : web::Path<String>,
@ -143,7 +237,7 @@ pub(crate) async fn relation_model_delete(
session : Session session : Session
) -> actix_web::Result<impl Responder> { ) -> actix_web::Result<impl Responder> {
let mut wg = store.write().await; let mut wg = store.write().await;
match wg.undefine_relation(id.parse().map_err(|e| actix_web::error::ErrorBadRequest(e))?) { match wg.undefine_relation(id.parse_or_bad_request()?) {
Ok(rm) => { Ok(rm) => {
debug!("Relation deleted, redirecting to root"); debug!("Relation deleted, redirecting to root");
session.flash_success(format!("Relation model \"{}\" deleted.", rm.name)); session.flash_success(format!("Relation model \"{}\" deleted.", rm.name));
@ -164,7 +258,7 @@ pub(crate) async fn property_model_delete(
session : Session session : Session
) -> actix_web::Result<impl Responder> { ) -> actix_web::Result<impl Responder> {
let mut wg = store.write().await; let mut wg = store.write().await;
match wg.undefine_property(id.parse().map_err(|e| actix_web::error::ErrorBadRequest(e))?) { match wg.undefine_property(id.parse_or_bad_request()?) {
Ok(rm) => { Ok(rm) => {
debug!("Property deleted, redirecting to root"); debug!("Property deleted, redirecting to root");
session.flash_success(format!("Property \"{}\" deleted.", rm.name)); session.flash_success(format!("Property \"{}\" deleted.", rm.name));

@ -339,6 +339,18 @@ impl Storage {
self.obj_models.values() self.obj_models.values()
} }
pub fn get_object_model(&self, id : ID) -> Option<&ObjectModel> {
self.obj_models.get(&id)
}
pub fn get_relation_model(&self, id : ID) -> Option<&RelationModel> {
self.rel_models.get(&id)
}
pub fn get_prop_model(&self, id : ID) -> Option<&PropertyModel> {
self.prop_models.get(&id)
}
pub fn get_grouped_prop_models(&self) -> HashMap<ID, Vec<&PropertyModel>> { pub fn get_grouped_prop_models(&self) -> HashMap<ID, Vec<&PropertyModel>> {
self.prop_models.values() self.prop_models.values()
.into_group_map_by(|model| model.object) .into_group_map_by(|model| model.object)

Loading…
Cancel
Save