-
Notifications
You must be signed in to change notification settings - Fork 115
For #7, implement simple Data Object Type registry #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a4c152f
8668f1b
8d66c12
ff33136
ba3413d
913538b
6c14ac7
c928b88
854378a
0286059
61dfc3c
a695dd2
701e2b0
32b659a
cb73509
f744032
b3e5ff2
64b79d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,154 @@ | ||||||
| #![cfg_attr(not(feature = "std"), no_std)] | ||||||
|
|
||||||
| use rstd::prelude::*; | ||||||
| use parity_codec::Codec; | ||||||
| use parity_codec_derive::{Encode, Decode}; | ||||||
| use srml_support::{StorageMap, StorageValue, decl_module, decl_storage, decl_event, ensure, Parameter}; | ||||||
| use runtime_primitives::traits::{SimpleArithmetic, As, Member, MaybeSerializeDebug, MaybeDebug}; | ||||||
| use system::{self, ensure_root}; | ||||||
| use crate::traits; | ||||||
|
|
||||||
| pub trait Trait: system::Trait + MaybeDebug | ||||||
| { | ||||||
| type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>; | ||||||
|
|
||||||
| type DataObjectTypeID: Parameter + Member + SimpleArithmetic + Codec + Default + Copy | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By following Substrate and our naming convention for IDs, it should be:
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that's a naming convention that I've seen a lot in Java, and it's pretty stupid. ID is not a word, it's an abbreviation, so CamelCasing it makes no sense at all. Still, I will change it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is a quote from ID article on Wiki:
I believe that in our cases ( |
||||||
| + As<usize> + As<u64> + MaybeSerializeDebug + PartialEq; | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
| static MSG_REQUIRE_NEW_DO_TYPE: &str = "New Data Object Type required; the provided one seems to be in use already!"; | ||||||
| static MSG_DO_TYPE_NOT_FOUND: &str = "Data Object Type with the given ID not found!"; | ||||||
| static MSG_REQUIRE_DO_TYPE_ID: &str = "Can only update Data Object Types that are already registered (with an ID)!"; | ||||||
|
|
||||||
| const DEFAULT_FIRST_DATA_OBJECT_TYPE_ID: u64 = 1; | ||||||
|
|
||||||
| #[derive(Clone, Encode, Decode, PartialEq)] | ||||||
| #[cfg_attr(feature = "std", derive(Debug))] | ||||||
| pub struct DataObjectType<T: Trait> | ||||||
| { | ||||||
| // If the OT is registered, an ID must exist, otherwise it's a new OT. | ||||||
| pub id: Option<T::DataObjectTypeID>, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest changing And in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, why? I suppose it may keep transaction sizes down. I suppose that could be worth it, but is that the rationale?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tx size is certainly a benefit, but I think the rationale behind alex's suggestion is so that you wouldn't have to change the function signature in the future if you introduce new fields to theDataObjectType type. (I don't want to put words in his mouth but that is how I understood it when he recommended the same for the members module update_profile() method)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually didn't look at the function signature (i assumed there was one parameter per field).. so my point doesn't actually make sense.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because object's |
||||||
| pub description: Vec<u8>, | ||||||
| pub active: bool, | ||||||
|
|
||||||
| // TODO in future releases | ||||||
| // - maximum size | ||||||
| // - replication factor | ||||||
| // - storage tranches (empty is ok) | ||||||
| } | ||||||
|
|
||||||
| decl_storage! { | ||||||
| trait Store for Module<T: Trait> as DataObjectTypeRegistry | ||||||
| { | ||||||
| // Start at this value | ||||||
| pub FirstDataObjectTypeID get(first_data_object_type_id) config(first_data_object_type_id): T::DataObjectTypeID = T::DataObjectTypeID::sa(DEFAULT_FIRST_DATA_OBJECT_TYPE_ID); | ||||||
|
|
||||||
| // Increment | ||||||
| pub NextDataObjectTypeID get(next_data_object_type_id) build(|config: &GenesisConfig<T>| config.first_data_object_type_id): T::DataObjectTypeID = T::DataObjectTypeID::sa(DEFAULT_FIRST_DATA_OBJECT_TYPE_ID); | ||||||
|
|
||||||
| // Mapping of Data object types | ||||||
| pub DataObjectTypeMap get(data_object_type): map T::DataObjectTypeID => Option<DataObjectType<T>>; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| decl_event! { | ||||||
| pub enum Event<T> where | ||||||
| <T as Trait>::DataObjectTypeID | ||||||
| { | ||||||
| DataObjectTypeAdded(DataObjectTypeID), | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense, will change. |
||||||
| DataObjectTypeUpdated(DataObjectTypeID), | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
|
|
||||||
| impl<T: Trait> traits::IsActiveDataObjectType<T> for Module<T> | ||||||
| { | ||||||
| fn is_active_data_object_type(which: &T::DataObjectTypeID) -> bool | ||||||
| { | ||||||
| match Self::ensure_data_object_type(*which) | ||||||
| { | ||||||
| Ok(do_type) => do_type.active, | ||||||
| Err(_err) => false | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
| decl_module! { | ||||||
| pub struct Module<T: Trait> for enum Call where origin: T::Origin | ||||||
| { | ||||||
| fn deposit_event<T>() = default; | ||||||
|
|
||||||
| pub fn register_data_object_type(origin, data_object_type: DataObjectType<T>) | ||||||
| { | ||||||
| ensure_root(origin)?; | ||||||
| ensure!(data_object_type.id.is_none(), MSG_REQUIRE_NEW_DO_TYPE); | ||||||
|
|
||||||
| let new_do_type_id = Self::next_data_object_type_id(); | ||||||
| let do_type: DataObjectType<T> = DataObjectType { | ||||||
| id: Some(new_do_type_id), | ||||||
| description: data_object_type.description.clone(), | ||||||
| active: data_object_type.active, | ||||||
| }; | ||||||
|
|
||||||
| <DataObjectTypeMap<T>>::insert(new_do_type_id, do_type); | ||||||
| <NextDataObjectTypeID<T>>::mutate(|n| { *n += T::DataObjectTypeID::sa(1); }); | ||||||
|
|
||||||
| Self::deposit_event(RawEvent::DataObjectTypeAdded(new_do_type_id)); | ||||||
| } | ||||||
|
|
||||||
| pub fn update_data_object_type(origin, data_object_type: DataObjectType<T>) | ||||||
| { | ||||||
| ensure_root(origin)?; | ||||||
| ensure!(data_object_type.id.is_some(), MSG_REQUIRE_DO_TYPE_ID); | ||||||
|
|
||||||
| let id = data_object_type.id.unwrap(); | ||||||
| let mut do_type = Self::ensure_data_object_type(id)?; | ||||||
|
|
||||||
| do_type.description = data_object_type.description.clone(); | ||||||
| do_type.active = data_object_type.active; | ||||||
|
|
||||||
| <DataObjectTypeMap<T>>::insert(id, do_type); | ||||||
|
|
||||||
| Self::deposit_event(RawEvent::DataObjectTypeUpdated(id)); | ||||||
| } | ||||||
|
|
||||||
| // Activate and deactivate functions as separate functions, because | ||||||
| // toggling DO types is likely a more common operation than updating | ||||||
| // other aspects. | ||||||
| pub fn activate_data_object_type(origin, id: T::DataObjectTypeID) | ||||||
| { | ||||||
| ensure_root(origin)?; | ||||||
| let mut do_type = Self::ensure_data_object_type(id)?; | ||||||
|
|
||||||
| do_type.active = true; | ||||||
|
|
||||||
| <DataObjectTypeMap<T>>::insert(id, do_type); | ||||||
|
|
||||||
| Self::deposit_event(RawEvent::DataObjectTypeUpdated(id)); | ||||||
| } | ||||||
|
|
||||||
| pub fn deactivate_data_object_type(origin, id: T::DataObjectTypeID) | ||||||
| { | ||||||
| ensure_root(origin)?; | ||||||
| let mut do_type = Self::ensure_data_object_type(id)?; | ||||||
|
|
||||||
| do_type.active = false; | ||||||
|
|
||||||
| <DataObjectTypeMap<T>>::insert(id, do_type); | ||||||
|
|
||||||
| Self::deposit_event(RawEvent::DataObjectTypeUpdated(id)); | ||||||
| } | ||||||
|
|
||||||
| } | ||||||
| } | ||||||
|
|
||||||
| impl <T: Trait> Module<T> | ||||||
| { | ||||||
| fn ensure_data_object_type(id: T::DataObjectTypeID) -> Result<DataObjectType<T>, &'static str> | ||||||
| { | ||||||
| return Self::data_object_type(&id).ok_or(MSG_DO_TYPE_NOT_FOUND); | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| #![cfg(test)] | ||
|
|
||
| use rstd::prelude::*; | ||
| pub use super::{data_object_type_registry}; | ||
| pub use system; | ||
|
|
||
| pub use primitives::{H256, Blake2Hasher}; | ||
| pub use runtime_primitives::{ | ||
| BuildStorage, | ||
| traits::{BlakeTwo256, OnFinalise, IdentityLookup}, | ||
| testing::{Digest, DigestItem, Header, UintAuthorityId} | ||
| }; | ||
|
|
||
| use srml_support::{impl_outer_origin, impl_outer_event}; | ||
|
|
||
| impl_outer_origin! { | ||
| pub enum Origin for Test {} | ||
| } | ||
|
|
||
| impl_outer_event! { | ||
| pub enum MetaEvent for Test | ||
| { | ||
| data_object_type_registry<T>, | ||
| } | ||
| } | ||
|
|
||
| // For testing the module, we construct most of a mock runtime. This means | ||
| // first constructing a configuration type (`Test`) which `impl`s each of the | ||
| // configuration traits of modules we want to use. | ||
| #[derive(Clone, Eq, PartialEq, Debug)] | ||
| pub struct Test; | ||
| impl system::Trait for Test | ||
| { | ||
| type Origin = Origin; | ||
| type Index = u64; | ||
| type BlockNumber = u64; | ||
| type Hash = H256; | ||
| type Hashing = BlakeTwo256; | ||
| type Digest = Digest; | ||
| type AccountId = u64; | ||
| type Header = Header; | ||
| type Event = MetaEvent; | ||
| type Log = DigestItem; | ||
| type Lookup = IdentityLookup<u64>; | ||
| } | ||
| impl data_object_type_registry::Trait for Test | ||
| { | ||
| type Event = MetaEvent; | ||
| type DataObjectTypeID = u64; | ||
| } | ||
|
|
||
| pub struct ExtBuilder | ||
| { | ||
| first_data_object_type_id: u64, | ||
| } | ||
|
|
||
| impl Default for ExtBuilder | ||
| { | ||
| fn default() -> Self | ||
| { | ||
| Self { | ||
| first_data_object_type_id: 1, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl ExtBuilder | ||
| { | ||
| pub fn first_data_object_type_id(mut self, first_data_object_type_id: u64) -> Self | ||
| { | ||
| self.first_data_object_type_id = first_data_object_type_id; | ||
| self | ||
| } | ||
| pub fn build(self) -> runtime_io::TestExternalities<Blake2Hasher> | ||
| { | ||
| let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0; | ||
|
|
||
| t.extend(data_object_type_registry::GenesisConfig::<Test>{ | ||
| first_data_object_type_id: self.first_data_object_type_id, | ||
| }.build_storage().unwrap().0); | ||
|
|
||
| t.into() | ||
| } | ||
| } | ||
|
|
||
|
|
||
| pub type System = system::Module<Test>; | ||
| pub type TestDataObjectTypeRegistry = data_object_type_registry::Module<Test>; | ||
| pub type TestDataObjectType = data_object_type_registry::DataObjectType<Test>; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| #![cfg_attr(not(feature = "std"), no_std)] | ||
|
|
||
| pub mod data_object_type_registry; | ||
|
|
||
| mod mock; | ||
| mod tests; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Rust there is a convention to put opening
{on the same line with a preceding code:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with changing that, but you're likely going to see this a lot from me :)