From cfccad725a6b8c58b98b4c35677565dc5f0a2d90 Mon Sep 17 00:00:00 2001 From: Andrew Danger Lyon Date: Fri, 4 Sep 2020 00:46:31 -0700 Subject: [PATCH 1/3] first implementation of the voting user. has two modes of operation: systemic (super user/no member) and company (regular user/super member). also in this edition: how i learned to export the Agent trait and AgentID enum without exposing a bunch of internal model junk and polluting the model namespaces with a random `lib` module. --- src/lib.rs | 1 + src/models/lib/mod.rs | 2 +- src/models/mod.rs | 2 + src/voter.rs | 167 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 src/voter.rs diff --git a/src/lib.rs b/src/lib.rs index 97dbe3e..1816460 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,4 +43,5 @@ pub mod access; pub mod models; pub mod costs; pub mod transactions; +pub mod voter; diff --git a/src/models/lib/mod.rs b/src/models/lib/mod.rs index 2446bb3..6112830 100644 --- a/src/models/lib/mod.rs +++ b/src/models/lib/mod.rs @@ -52,6 +52,6 @@ macro_rules! load_models { } #[macro_use] -pub mod basis_model; +pub(crate) mod basis_model; pub mod agent; diff --git a/src/models/mod.rs b/src/models/mod.rs index 2c760c6..ee2263b 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -36,6 +36,8 @@ use std::convert::TryFrom; #[macro_use] pub(crate) mod lib; +pub use lib::agent::{Agent, AgentID}; + // load all of our pub mod ; ... lines load_models!{ pub mod } diff --git a/src/voter.rs b/src/voter.rs new file mode 100644 index 0000000..465d11e --- /dev/null +++ b/src/voter.rs @@ -0,0 +1,167 @@ +//! The voter module assists in creating user objects that effectively have +//! network super powers. The idea is that this user can be wielded by whatever +//! system implements the core to represent votes either systemicall or within +//! specific companies. +//! +//! The idea here is to provide an interface for democracy without the core +//! needing to know the implementation details. +//! +//! ```rust +//! use basis_core::{ +//! access::Role, +//! models::{ +//! Agent, +//! company::{CompanyID, Permission as CompanyPermission}, +//! }, +//! voter::Voter, +//! }; +//! use chrono::Utc; +//! +//! let systemic_voter = Voter::systemic(&Utc::now()).unwrap(); +//! assert_eq!(systemic_voter.user().roles(), &vec![Role::SuperAdmin]); +//! assert_eq!(systemic_voter.member(), &None); +//! +//! let company_id = CompanyID::new("hairy larry's scrumptious dairies"); +//! let company_voter = Voter::company(&company_id, &Utc::now()).unwrap(); +//! assert_eq!(company_voter.user().roles(), &vec![Role::User]); +//! assert_eq!(company_voter.member().as_ref().unwrap().inner().subject(), &company_voter.user().agent_id()); +//! assert_eq!(company_voter.member().as_ref().unwrap().inner().object(), &company_id.clone().into()); +//! assert_eq!(company_voter.member().as_ref().unwrap().permissions(), &vec![CompanyPermission::All]); +//! ``` + +use chrono::{DateTime, Utc}; +use crate::{ + access::Role, + error::{Error, Result}, + models::{ + company::{CompanyID, Permission as CompanyPermission}, + lib::agent::{Agent, AgentID}, + member::*, + user::{User, UserID}, + }, +}; +use getset::Getters; +use vf_rs::vf; + +/// An object that holds information about a voting user as well as any extra +/// information we might need. For instance, we might only need a user object if +/// voting on something systemic, but if a vote occurs within a specific company +/// then we need both a user and a member object created for us. +#[derive(Clone, Debug, PartialEq, Getters)] +#[getset(get = "pub")] +pub struct Voter { + /// Holds our voting user + user: User, + /// Holds our voting member, if we have one + member: Option, +} + +impl Voter { + /// Utility function to make a new user with a given role. + fn make_voter(role: Role, now: &DateTime) -> Result { + let id = UserID::create(); + User::builder() + .id(id.clone()) + .roles(vec![role]) + .email(format!("vote-{}@basisproject.net", id.as_str())) + .name(format!("Voter {}", id.as_str())) + .active(true) + .created(now.clone()) + .updated(now.clone()) + .build() + .map_err(|e| Error::BuilderFailed(e)) + } + + /// Create a new systemic voting user. + /// + /// This can be used for systemic changes that don't need a company or a + /// member object. For instance, this might be used to adjust costs of + /// various tracked resources or manage occupation data. This user is given + /// super admin abilities. + /// + /// If you want to vote to run a transaction for a specific company, see + /// the `Voter::company()` method. + pub fn systemic(now: &DateTime) -> Result { + let user = Self::make_voter(Role::SuperAdmin, now)?; + Ok(Self { + user, + member: None, + }) + } + + /// Create a new voting company member. + /// + /// This is specifically for voting to run a transaction internal to a + /// company. This member is given company-wide admin abilities. + pub fn company(company_id: &CompanyID, now: &DateTime) -> Result { + let user = Self::make_voter(Role::User, now)?; + let id = MemberID::create(); + let company_agent_id: AgentID = company_id.clone().into(); + let member = Member::builder() + .id(id) + .inner( + vf::AgentRelationship::builder() + .subject(user.agent_id()) + .object(company_agent_id) + .relationship(()) + .build() + .map_err(|e| Error::BuilderFailed(e))? + ) + .class(MemberClass::User(MemberUser::new())) + .permissions(vec![CompanyPermission::All]) + .agreement(None) + .active(true) + .created(now.clone()) + .updated(now.clone()) + .build() + .map_err(|e| Error::BuilderFailed(e))?; + Ok(Self { + user, + member: Some(member), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + util, + }; + + #[test] + fn systemic() { + let now = util::time::now(); + let voter = Voter::systemic(&now).unwrap(); + assert_eq!(voter.user().roles(), &vec![Role::SuperAdmin]); + assert_eq!(voter.user().active(), &true); + assert_eq!(voter.user().created(), &now); + assert_eq!(voter.user().updated(), &now); + assert_eq!(voter.member(), &None); + } + + #[test] + fn company() { + let now = util::time::now(); + let company_id = CompanyID::new("hairy larry's scrumptious dairies"); + let voter = Voter::company(&company_id, &now).unwrap(); + let user = voter.user().clone(); + assert_eq!(user.roles(), &vec![Role::User]); + assert_eq!(user.active(), &true); + assert_eq!(user.created(), &now); + assert_eq!(user.updated(), &now); + + let member = voter.member().clone().unwrap(); + assert_eq!(member.inner().subject(), &user.agent_id()); + assert_eq!(member.inner().object(), &company_id.clone().into()); + match member.class() { + MemberClass::User(_) => {} + _ => panic!("voter::tests::company() -- bad class"), + } + assert_eq!(member.permissions(), &vec![CompanyPermission::All]); + assert_eq!(member.active(), &true); + assert_eq!(member.created(), &now); + assert_eq!(member.updated(), &now); + } +} + From 39cc3bad72c909527a2f4ff6ac10152dfbea4ef4 Mon Sep 17 00:00:00 2001 From: Andrew Danger Lyon Date: Fri, 4 Sep 2020 00:49:07 -0700 Subject: [PATCH 2/3] anal typos --- src/voter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/voter.rs b/src/voter.rs index 465d11e..0348b3d 100644 --- a/src/voter.rs +++ b/src/voter.rs @@ -1,6 +1,6 @@ -//! The voter module assists in creating user objects that effectively have -//! network super powers. The idea is that this user can be wielded by whatever -//! system implements the core to represent votes either systemicall or within +//! The voter module allows creating user objects that effectively have network +//! super powers. The idea is that this user can be wielded by whatever system +//! implements the core to represent votes either systemically or within //! specific companies. //! //! The idea here is to provide an interface for democracy without the core From 9a4e382517bb72bcf7a8380a8bbb841a7ad70129 Mon Sep 17 00:00:00 2001 From: Andrew Danger Lyon Date: Fri, 4 Sep 2020 19:18:39 -0700 Subject: [PATCH 3/3] moving vote system to new toplevel `system` module, which will eventually house the consumer purchase module and other types of systemic actors --- src/lib.rs | 2 +- src/system/mod.rs | 6 ++++++ src/{voter.rs => system/vote.rs} | 20 ++++++++++---------- 3 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 src/system/mod.rs rename src/{voter.rs => system/vote.rs} (91%) diff --git a/src/lib.rs b/src/lib.rs index 1816460..7c7ed57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,5 +43,5 @@ pub mod access; pub mod models; pub mod costs; pub mod transactions; -pub mod voter; +pub mod system; diff --git a/src/system/mod.rs b/src/system/mod.rs new file mode 100644 index 0000000..ed9cec0 --- /dev/null +++ b/src/system/mod.rs @@ -0,0 +1,6 @@ +//! A module containing objects, methods, or processes that can act on behalf of +//! the system itself. For instance, a voting user/member that acts on behalf of +//! the system or a company, or a user that masks/anonymizes consumer purchases. + +pub mod vote; + diff --git a/src/voter.rs b/src/system/vote.rs similarity index 91% rename from src/voter.rs rename to src/system/vote.rs index 0348b3d..438dc1c 100644 --- a/src/voter.rs +++ b/src/system/vote.rs @@ -1,4 +1,4 @@ -//! The voter module allows creating user objects that effectively have network +//! The vote module allows creating user objects that effectively have network //! super powers. The idea is that this user can be wielded by whatever system //! implements the core to represent votes either systemically or within //! specific companies. @@ -13,16 +13,16 @@ //! Agent, //! company::{CompanyID, Permission as CompanyPermission}, //! }, -//! voter::Voter, +//! system::vote::Vote, //! }; //! use chrono::Utc; //! -//! let systemic_voter = Voter::systemic(&Utc::now()).unwrap(); +//! let systemic_voter = Vote::systemic(&Utc::now()).unwrap(); //! assert_eq!(systemic_voter.user().roles(), &vec![Role::SuperAdmin]); //! assert_eq!(systemic_voter.member(), &None); //! //! let company_id = CompanyID::new("hairy larry's scrumptious dairies"); -//! let company_voter = Voter::company(&company_id, &Utc::now()).unwrap(); +//! let company_voter = Vote::company(&company_id, &Utc::now()).unwrap(); //! assert_eq!(company_voter.user().roles(), &vec![Role::User]); //! assert_eq!(company_voter.member().as_ref().unwrap().inner().subject(), &company_voter.user().agent_id()); //! assert_eq!(company_voter.member().as_ref().unwrap().inner().object(), &company_id.clone().into()); @@ -49,14 +49,14 @@ use vf_rs::vf; /// then we need both a user and a member object created for us. #[derive(Clone, Debug, PartialEq, Getters)] #[getset(get = "pub")] -pub struct Voter { +pub struct Vote { /// Holds our voting user user: User, /// Holds our voting member, if we have one member: Option, } -impl Voter { +impl Vote { /// Utility function to make a new user with a given role. fn make_voter(role: Role, now: &DateTime) -> Result { let id = UserID::create(); @@ -64,7 +64,7 @@ impl Voter { .id(id.clone()) .roles(vec![role]) .email(format!("vote-{}@basisproject.net", id.as_str())) - .name(format!("Voter {}", id.as_str())) + .name(format!("Vote {}", id.as_str())) .active(true) .created(now.clone()) .updated(now.clone()) @@ -80,7 +80,7 @@ impl Voter { /// super admin abilities. /// /// If you want to vote to run a transaction for a specific company, see - /// the `Voter::company()` method. + /// the `Vote::company()` method. pub fn systemic(now: &DateTime) -> Result { let user = Self::make_voter(Role::SuperAdmin, now)?; Ok(Self { @@ -132,7 +132,7 @@ mod tests { #[test] fn systemic() { let now = util::time::now(); - let voter = Voter::systemic(&now).unwrap(); + let voter = Vote::systemic(&now).unwrap(); assert_eq!(voter.user().roles(), &vec![Role::SuperAdmin]); assert_eq!(voter.user().active(), &true); assert_eq!(voter.user().created(), &now); @@ -144,7 +144,7 @@ mod tests { fn company() { let now = util::time::now(); let company_id = CompanyID::new("hairy larry's scrumptious dairies"); - let voter = Voter::company(&company_id, &now).unwrap(); + let voter = Vote::company(&company_id, &now).unwrap(); let user = voter.user().clone(); assert_eq!(user.roles(), &vec![Role::User]); assert_eq!(user.active(), &true);