Skip to content

Commit

Permalink
Refactor profiles to spaces as profiles (#106)
Browse files Browse the repository at this point in the history
* Fix runtime and errors

* Fix integration tests error

* Replace social account with space as profile

* Fix code formatting

* Loose couple profiles with space-ownership

- Profiles and space-ownership aren't more tight coupled
- Rename `ProfileSpaceByAccount` to `ProfileSpaceIdByAccount`
- Add `unset_space_as_profile` extrinsic
- Add `ensure_space_owner` to SpacePermissionsProvider

* Rename extrinsics in profiles pallet

Now two extrinsics to manage profiles are:`set_profile` and `reset_profile`
  • Loading branch information
F3Joule committed Jul 26, 2022
1 parent c542a57 commit 9b37441
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 60 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

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

6 changes: 5 additions & 1 deletion integration-tests/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ impl pallet_posts::Config for TestRuntime {
type IsPostBlocked = MockModeration;
}

impl pallet_profiles::Config for TestRuntime {}
impl pallet_profiles::Config for TestRuntime {
type Event = Event;
type SpacePermissionsProvider = Spaces;
}

impl pallet_reactions::Config for TestRuntime {
type Event = Event;
Expand All @@ -150,6 +153,7 @@ impl pallet_space_follows::Config for TestRuntime {

impl pallet_space_ownership::Config for TestRuntime {
type Event = Event;
type ProfileManager = Profiles;
}

impl pallet_spaces::Config for TestRuntime {
Expand Down
117 changes: 85 additions & 32 deletions pallets/profiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,66 +7,119 @@ pub use pallet::*;
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct SocialAccount {
pub followers_count: u32,
pub following_accounts_count: u16,
pub following_spaces_count: u16,
}
use pallet_permissions::SpacePermissions;
use subsocial_support::{
traits::{ProfileManager, SpacePermissionsProvider},
SpaceId, SpacePermissionsInfo,
};

type SpacePermissionsInfoOf<T> =
SpacePermissionsInfo<<T as frame_system::Config>::AccountId, SpacePermissions>;

/// The pallet's configuration trait.
#[pallet::config]
pub trait Config: frame_system::Config {}
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;

type SpacePermissionsProvider: SpacePermissionsProvider<
Self::AccountId,
SpacePermissionsInfoOf<Self>,
>;
}

#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::generate_store(pub (super) trait Store)]
pub struct Pallet<T>(_);

#[pallet::storage]
#[pallet::getter(fn social_account_by_id)]
pub type SocialAccountById<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, SocialAccount>;
#[pallet::getter(fn profile_space_id_by_account)]
pub type ProfileSpaceIdByAccount<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, SpaceId>;

#[pallet::event]
#[pallet::generate_deposit(pub (super) fn deposit_event)]
pub enum Event<T: Config> {
/// Space was successfully assigned as a profile.
SpaceAsProfileAssigned { account: T::AccountId, space: SpaceId },
}

#[pallet::error]
pub enum Error<T> {
/// Social account was not found by id.
SocialAccountNotFound,
/// There is no space set as profile.
NoSpaceSetAsProfile,
}

impl SocialAccount {
pub fn inc_followers(&mut self) {
self.followers_count = self.followers_count.saturating_add(1);
#[pallet::call]
impl<T: Config> Pallet<T> {
// FIXME: cover with tests
#[pallet::weight(10_000 + T::DbWeight::get().writes(1))]
pub fn set_space_as_profile(origin: OriginFor<T>, space_id: SpaceId) -> DispatchResult {
let sender = ensure_signed(origin)?;

Self::try_set_profile(&sender, space_id)?;

Self::deposit_event(Event::SpaceAsProfileAssigned { account: sender, space: space_id });
Ok(())
}

pub fn dec_followers(&mut self) {
self.followers_count = self.followers_count.saturating_sub(1);
// FIXME: cover with tests
#[pallet::weight(10_000 + T::DbWeight::get().writes(1))]
pub fn unset_space_as_profile(origin: OriginFor<T>) -> DispatchResult {
let sender = ensure_signed(origin)?;

let space_id =
Self::profile_space_id_by_account(&sender).ok_or(Error::<T>::NoSpaceSetAsProfile)?;

Self::try_reset_profile(&sender, space_id)?;

Self::deposit_event(Event::SpaceAsProfileAssigned { account: sender, space: space_id });
Ok(())
}
}

impl<T: Config> Pallet<T> {
// FIXME: cover with tests
pub fn try_set_profile(
account: &T::AccountId,
space_id: SpaceId,
) -> DispatchResult {
T::SpacePermissionsProvider::ensure_space_owner(space_id, account)?;

pub fn inc_following_accounts(&mut self) {
self.following_accounts_count = self.following_accounts_count.saturating_add(1);
<ProfileSpaceIdByAccount<T>>::insert(account, space_id);
Ok(())
}

pub fn dec_following_accounts(&mut self) {
self.following_accounts_count = self.following_accounts_count.saturating_sub(1);
// FIXME: cover with tests
pub fn try_reset_profile(
account: &T::AccountId,
space_id: SpaceId,
) -> DispatchResult {
T::SpacePermissionsProvider::ensure_space_owner(space_id, account)?;

if let Some(profile_space_id) = Self::profile_space_id_by_account(account) {
if profile_space_id == space_id {
<ProfileSpaceIdByAccount<T>>::remove(account);
}
}
Ok(())
}
}

pub fn inc_following_spaces(&mut self) {
self.following_spaces_count = self.following_spaces_count.saturating_add(1);
impl<T: Config> ProfileManager<T::AccountId> for Pallet<T> {
fn profile_space_id(account: &T::AccountId) -> Option<SpaceId> {
Self::profile_space_id_by_account(account)
}

pub fn dec_following_spaces(&mut self) {
self.following_spaces_count = self.following_spaces_count.saturating_sub(1);
fn try_set_profile(account: &T::AccountId, space_id: SpaceId) -> DispatchResult {
Self::try_set_profile(account, space_id)
}
}

impl<T: Config> Pallet<T> {
pub fn get_or_new_social_account(account: T::AccountId) -> SocialAccount {
Self::social_account_by_id(account).unwrap_or(SocialAccount {
followers_count: 0,
following_accounts_count: 0,
following_spaces_count: 0,
})
fn try_reset_profile(account: &T::AccountId, space_id: SpaceId) -> DispatchResult {
Self::try_reset_profile(account, space_id)
}
}
}
2 changes: 1 addition & 1 deletion pallets/roles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub mod pallet {
#[pallet::constant]
type MaxUsersToProcessPerDeleteRole: Get<u16>;

type SpacePermissionsProvider: SpacePermissionsProvider<SpacePermissionsInfoOf<Self>>;
type SpacePermissionsProvider: SpacePermissionsProvider<Self::AccountId, SpacePermissionsInfoOf<Self>>;

type SpaceFollows: SpaceFollowsProvider<AccountId = Self::AccountId>;

Expand Down
12 changes: 11 additions & 1 deletion pallets/roles/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ impl Config for Test {
type IsContentBlocked = ();
}

impl SpacePermissionsProviderT<SpacePermissionsInfo<AccountId, SpacePermissions>> for Test {
impl SpacePermissionsProviderT<AccountId, SpacePermissionsInfo<AccountId, SpacePermissions>> for Test {
// This function should return an error every time Space doesn't exist by SpaceId
// Currently, we have a list of valid space id's to check
fn space_permissions_info(
Expand All @@ -134,6 +134,16 @@ impl SpacePermissionsProviderT<SpacePermissionsInfo<AccountId, SpacePermissions>

Err("SpaceNotFound".into())
}

fn ensure_space_owner(id: SpaceId, account: &AccountId) -> DispatchResult {
if valid_space_ids().contains(&id) {
if *account == ACCOUNT1 {
return Ok(())
}
}

Err("NotSpaceOwner".into())
}
}

impl<T: Config> SpaceFollowsProvider for Pallet<T> {
Expand Down
2 changes: 0 additions & 2 deletions pallets/space-follows/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ std = [
'frame-support/std',
'frame-system/std',
'sp-std/std',
'pallet-profiles/std',
'pallet-spaces/std',
'subsocial-support/std',
]
Expand All @@ -28,7 +27,6 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features =
scale-info = { version = "2.1.2", default-features = false, features = ["derive"] }

# Local depenpdencies
pallet-profiles = { default-features = false, path = '../profiles' }
pallet-spaces = { default-features = false, path = '../spaces' }
subsocial-support = { default-features = false, path = '../support' }

Expand Down
16 changes: 0 additions & 16 deletions pallets/space-follows/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pub use pallet::*;

use frame_support::dispatch::DispatchResult;

use pallet_profiles::{Pallet as Profiles, SocialAccountById};
use pallet_spaces::{BeforeSpaceCreated, Pallet as Spaces, types::Space, SpaceById};

// pub mod rpc;
Expand All @@ -25,7 +24,6 @@ pub mod pallet {
pub trait Config:
frame_system::Config
+ pallet_spaces::Config
+ pallet_profiles::Config
{
/// The overarching event type.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
Expand Down Expand Up @@ -142,13 +140,8 @@ pub mod pallet {

impl<T: Config> Pallet<T> {
fn add_space_follower(follower: T::AccountId, space: &mut Space<T>) -> DispatchResult {
use frame_support::StorageMap;

space.inc_followers();

let mut social_account = Profiles::<T>::get_or_new_social_account(follower.clone());
social_account.inc_following_spaces();

T::BeforeSpaceFollowed::before_space_followed(
follower.clone(),
space,
Expand All @@ -160,23 +153,16 @@ pub mod pallet {
SpacesFollowedByAccount::<T>::mutate(follower.clone(), |space_ids| {
space_ids.push(space_id)
});
SocialAccountById::<T>::insert(follower.clone(), social_account);

Self::deposit_event(Event::SpaceFollowed(follower, space_id));

Ok(())
}

pub fn unfollow_space_by_account(follower: T::AccountId, space_id: SpaceId) -> DispatchResult {
use frame_support::StorageMap;

let space = &mut Spaces::require_space(space_id)?;
space.dec_followers();

let mut social_account = Profiles::<T>::social_account_by_id(follower.clone())
.ok_or(Error::<T>::SocialAccountNotFound)?;
social_account.dec_following_spaces();

T::BeforeSpaceUnfollowed::before_space_unfollowed(follower.clone(), space)?;

SpacesFollowedByAccount::<T>::mutate(follower.clone(), |space_ids| {
Expand All @@ -186,8 +172,6 @@ pub mod pallet {
remove_from_vec(account_ids, follower.clone())
});
SpaceFollowedByAccount::<T>::remove((follower.clone(), space_id));
SocialAccountById::<T>::insert(follower.clone(), social_account);
SpaceById::<T>::insert(space_id, space);

Self::deposit_event(Event::SpaceUnfollowed(follower, space_id));
Ok(())
Expand Down
7 changes: 7 additions & 0 deletions pallets/space-ownership/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

use subsocial_support::traits::ProfileManager;

#[pallet::config]
pub trait Config: frame_system::Config + pallet_spaces::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;

type ProfileManager: ProfileManager<Self::AccountId>;
}

#[pallet::pallet]
Expand Down Expand Up @@ -111,6 +115,9 @@ pub mod pallet {
space.owner = new_owner.clone();
SpaceById::<T>::insert(space_id, space);

// FIXME: cover with tests
let _ = T::ProfileManager::try_reset_profile(&old_owner, space_id);

// Remove space id from the list of spaces by old owner
SpaceIdsByOwner::<T>::mutate(old_owner, |space_ids| {
remove_from_bounded_vec(space_ids, space_id)
Expand Down
8 changes: 7 additions & 1 deletion pallets/spaces/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,14 +391,20 @@ pub mod pallet {
}
}

impl<T: Config> SpacePermissionsProvider<SpacePermissionsInfoOf<T>> for Pallet<T> {
impl<T: Config> SpacePermissionsProvider<T::AccountId, SpacePermissionsInfoOf<T>> for Pallet<T> {
fn space_permissions_info(
id: SpaceId,
) -> Result<SpacePermissionsInfoOf<T>, DispatchError> {
let space = Pallet::<T>::require_space(id)?;

Ok(SpacePermissionsInfo { owner: space.owner, permissions: space.permissions })
}

fn ensure_space_owner(id: SpaceId, account: &T::AccountId) -> DispatchResult {
let space = Pallet::<T>::require_space(id)?;
ensure!(space.is_owner(account), Error::<T>::NotASpaceOwner);
Ok(())
}
}

pub trait BeforeSpaceCreated<T: Config> {
Expand Down
2 changes: 1 addition & 1 deletion pallets/support/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod common;
pub use common::{SpaceFollowsProvider, SpacePermissionsProvider};
pub use common::{SpaceFollowsProvider, SpacePermissionsProvider, ProfileManager};

mod moderation;
pub use moderation::{IsAccountBlocked, IsContentBlocked, IsPostBlocked, IsSpaceBlocked};
20 changes: 18 additions & 2 deletions pallets/support/src/traits/common.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::dispatch::DispatchError;
use frame_support::dispatch::{DispatchResult, DispatchError};

use crate::SpaceId;

pub trait SpacePermissionsProvider<SpacePermissionsInfo> {
pub trait SpacePermissionsProvider<AccountId, SpacePermissionsInfo> {
fn space_permissions_info(id: SpaceId) -> Result<SpacePermissionsInfo, DispatchError>;

fn ensure_space_owner(id: SpaceId, account: &AccountId) -> DispatchResult;
}

pub trait SpaceFollowsProvider {
type AccountId;

fn is_space_follower(account: Self::AccountId, space_id: SpaceId) -> bool;
}

pub trait ProfileManager<AccountId> {
fn profile_space_id(account: &AccountId) -> Option<SpaceId>;

fn try_set_profile(
account: &AccountId,
space_id: SpaceId,
) -> DispatchResult;

fn try_reset_profile(
account: &AccountId,
space_id: SpaceId,
) -> DispatchResult;
}
Loading

0 comments on commit 9b37441

Please sign in to comment.