Browse Source

runtime: Add the ‘apply_on_opening’ extrinsic.

Shamil Gadelshin 4 years ago
parent
commit
fc3470da59

+ 1 - 0
Cargo.lock

@@ -3729,6 +3729,7 @@ dependencies = [
  "frame-system",
  "pallet-balances",
  "pallet-common",
+ "pallet-membership",
  "pallet-timestamp",
  "parity-scale-codec",
  "serde",

+ 3 - 0
runtime-modules/team/Cargo.toml

@@ -13,6 +13,8 @@ system = { package = 'frame-system', default-features = false, git = 'https://gi
 sp-arithmetic = { package = 'sp-arithmetic', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
 sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
 common = { package = 'pallet-common', default-features = false, path = '../common'}
+membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
+
 
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
@@ -31,4 +33,5 @@ std = [
 	'sp-arithmetic/std',
 	'sp-std/std',
 	'common/std',
+	'membership/std',
 ]

+ 2 - 1
runtime-modules/team/src/checks.rs

@@ -5,7 +5,7 @@ use frame_support::dispatch::{DispatchError, DispatchResult};
 use frame_support::StorageValue;
 use system::ensure_root;
 
-// Opening check: verifies opening description length.
+// Check opening: verifies opening description length.
 pub(crate) fn ensure_opening_description_is_valid<T: Trait<I>, I: Instance>(
     text: &[u8],
 ) -> DispatchResult {
@@ -18,6 +18,7 @@ pub(crate) fn ensure_opening_description_is_valid<T: Trait<I>, I: Instance>(
         .map_err(|e| DispatchError::Other(e))
 }
 
+// Check opening: verifies origin and opening type compatibility.
 pub(crate) fn ensure_origin_for_opening_type<T: Trait<I>, I: Instance>(
     origin: T::Origin,
     opening_type: JobOpeningType,

+ 3 - 0
runtime-modules/team/src/errors.rs

@@ -14,5 +14,8 @@ decl_error! {
 
         /// Opening description too long.
         OpeningDescriptionTooLong,
+
+		/// Opening does not exist.
+        OpeningDoesNotExist,
     }
 }

+ 155 - 5
runtime-modules/team/src/lib.rs

@@ -1,3 +1,13 @@
+//! # Working team pallet
+//! Working team pallet for the Joystream platform.
+//! Contains abstract working team workflow.
+//!
+//! Exact working group (eg.: forum working group) should create an instance of the Working group module.
+//!
+//! ## Supported extrinsics
+//!
+//! - [add_opening](./struct.Module.html#method.add_opening) - Add an opening for a regular worker/lead role.
+
 // Ensure we're `no_std` when compiling for Wasm.
 #![cfg_attr(not(feature = "std"), no_std)]
 
@@ -11,25 +21,32 @@ mod tests;
 mod types;
 
 use codec::Codec;
-use frame_support::dispatch::DispatchResult;
 //use frame_support::storage::IterableStorageMap;
 //use frame_support::traits::{Currency, ExistenceRequirement, Get, Imbalance, WithdrawReasons};
 use frame_support::traits::Get;
-use frame_support::{decl_event, decl_module, decl_storage, Parameter, StorageValue}; // ensure, print,
+use frame_support::{decl_event, decl_module, decl_storage, Parameter, StorageValue, ensure}; // print,
 use sp_arithmetic::traits::{BaseArithmetic, One};
 use sp_runtime::traits::{Hash, MaybeSerialize, Member};
 // use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
 // use sp_std::vec;
 // use sp_std::vec::Vec;
+use system::ensure_signed;
 // use system::{ensure_root, ensure_signed};
 
 use common::constraints::InputValidationLengthConstraint;
 
 pub use errors::Error;
-pub use types::{JobOpening, JobOpeningType};
+pub use types::{JobApplication, JobOpening, JobOpeningType};
+
+// Type simplification
+type JobApplicationRecord<T, I> =
+    JobApplication<<T as system::Trait>::AccountId, <T as Trait<I>>::OpeningId, MemberId<T>>;
+
+/// Member identifier in membership::member module.
+pub type MemberId<T> = <T as membership::Trait>::MemberId;
 
 /// The _Team_ main _Trait_
-pub trait Trait<I: Instance>: system::Trait {
+pub trait Trait<I: Instance>: system::Trait + membership::Trait {
     /// OpeningId type
     type OpeningId: Parameter
         + Member
@@ -40,6 +57,16 @@ pub trait Trait<I: Instance>: system::Trait {
         + MaybeSerialize
         + PartialEq;
 
+    /// ApplicationId type
+    type ApplicationId: Parameter
+        + Member
+        + BaseArithmetic
+        + Codec
+        + Default
+        + Copy
+        + MaybeSerialize
+        + PartialEq;
+
     /// _Administration_ event type.
     type Event: From<Event<Self, I>> + Into<<Self as system::Trait>::Event>;
 
@@ -52,11 +79,18 @@ decl_event!(
     pub enum Event<T, I>
     where
        <T as Trait<I>>::OpeningId,
+       <T as Trait<I>>::ApplicationId,
     {
         /// Emits on adding new job opening.
         /// Params:
         /// - Opening id
         OpeningAdded(OpeningId),
+
+        /// Emits on adding the application for the worker opening.
+        /// Params:
+        /// - Opening id
+        /// - Application id
+        AppliedOnOpening(OpeningId, ApplicationId),
     }
 );
 
@@ -75,6 +109,17 @@ decl_storage! {
         /// Opening human readable text length limits.
         pub OpeningDescriptionTextLimit get(fn opening_description_text_limit):
             InputValidationLengthConstraint;
+
+                /// Maps identifier to worker application on opening.
+        pub ApplicationById get(fn application_by_id) : map hasher(blake2_128_concat)
+            T::ApplicationId => JobApplicationRecord<T, I>;
+
+        /// Next identifier value for new worker application.
+        pub NextApplicationId get(fn next_application_id) : T::ApplicationId;
+
+        /// Job application description text length limits.
+        pub ApplicationDescriptionTextLimit get(fn application_description_text_limit):
+            InputValidationLengthConstraint;
     }
     add_extra_genesis {
 //        config(phantom): sp_std::marker::PhantomData<I>;
@@ -99,7 +144,7 @@ decl_module! {
 
         // ****************** Hiring flow **********************
 
-        /// Add a job opening for a worker role.
+        /// Add a job opening for a regular worker/lead role.
         /// Require signed leader origin or the root (to add opening for the leader position).
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn add_opening(
@@ -139,6 +184,99 @@ decl_module! {
 
             Self::deposit_event(RawEvent::OpeningAdded(new_opening_id));
         }
+
+                /// Apply on a worker opening.
+        #[weight = 10_000_000] // TODO: adjust weight
+        pub fn apply_on_opening(
+            origin,
+            member_id: T::MemberId,
+            opening_id: T::OpeningId,
+            role_account_id: T::AccountId,
+//            opt_role_stake_balance: Option<BalanceOf<T>>,
+//            opt_application_stake_balance: Option<BalanceOf<T>>,
+            human_readable_text: Vec<u8>
+        ) {
+            // Ensure origin which will server as the source account for staked funds is signed
+            let source_account = ensure_signed(origin)?;
+
+            // // Ensure the source_account is either the controller or root account of member with given id
+            // ensure!(
+            //     membership::Module::<T>::ensure_member_controller_account(&source_account, &member_id).is_ok() ||
+            //     membership::Module::<T>::ensure_member_root_account(&source_account, &member_id).is_ok(),
+            //     Error::<T, I>::OriginIsNeitherMemberControllerOrRoot
+            // );
+
+            // Ensure job opening exists.
+            let opening = Self::ensure_opening_exists(&opening_id)?;
+
+            // // Ensure that there is sufficient balance to cover stake proposed
+            // Self::ensure_can_make_stake_imbalance(
+            //     vec![&opt_role_stake_balance, &opt_application_stake_balance],
+            //     &source_account
+            // )
+            // .map_err(|_| Error::<T, I>::InsufficientBalanceToApply)?;
+
+            // Ensure application text is valid
+            //Self::ensure_application_text_is_valid(&human_readable_text)?;
+
+            // // Ensure application can actually be added
+            // ensure_on_wrapped_error!(
+            //     hiring::Module::<T>::ensure_can_add_application(
+            //         opening.hiring_opening_id,
+            //         opt_role_stake_balance,
+            //         opt_application_stake_balance)
+            // )?;
+
+            // // Ensure member does not have an active application to this opening
+            // Self::ensure_member_has_no_active_application_on_opening(
+            //     opening.applications,
+            //     member_id
+            // )?;
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            // // Make imbalances for staking
+            // let opt_role_stake_imbalance = Self::make_stake_opt_imbalance(&opt_role_stake_balance, &source_account);
+            // let opt_application_stake_imbalance = Self::make_stake_opt_imbalance(&opt_application_stake_balance, &source_account);
+
+            // // Call hiring module to add application
+            // let add_application = ensure_on_wrapped_error!(
+            //         hiring::Module::<T>::add_application(
+            //         opening.hiring_opening_id,
+            //         opt_role_stake_imbalance,
+            //         opt_application_stake_imbalance,
+            //         human_readable_text
+            //     )
+            // )?;
+
+//            let hiring_application_id = add_application.application_id_added;
+
+            // Save member id to refund the stakes. This piece of date should outlive the 'worker'.
+//            <MemberIdByHiringApplicationId<T, I>>::insert(hiring_application_id, member_id);
+
+            // Get id of new worker/lead application
+            let new_application_id = NextApplicationId::<T, I>::get();
+
+            // Make worker/lead application
+            let application = JobApplication::new(&role_account_id, &opening_id, &member_id);
+
+            // Store application
+            ApplicationById::<T, I>::insert(new_application_id, application);
+
+            // Update next application identifier value
+            NextApplicationId::<T, I>::mutate(|id| *id += <T::ApplicationId as One>::one());
+
+            // Add application to set of application in worker opening
+            // OpeningById::<T, I>::mutate(opening_id, |opening| {
+            //     opening.applications.insert(new_application_id);
+            // });
+
+            // Trigger event
+            Self::deposit_event(RawEvent::AppliedOnOpening(opening_id, new_application_id));
+        }
+
     }
 }
 
@@ -155,4 +293,16 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
     fn current_block() -> T::BlockNumber {
         <system::Module<T>>::block_number()
     }
+
+
+    fn ensure_opening_exists(opening_id: &T::OpeningId) -> Result<JobOpening<T::BlockNumber>, Error<T, I>> {
+        ensure!(
+            OpeningById::<T, I>::contains_key(opening_id),
+            Error::<T, I>::OpeningDoesNotExist
+        );
+
+        let opening = OpeningById::<T, I>::get(opening_id);
+
+        Ok(opening)
+    }
 }

+ 27 - 0
runtime-modules/team/src/types.rs

@@ -45,3 +45,30 @@ impl Default for JobOpeningType {
         Self::Regular
     }
 }
+
+/// An application for the regular worker/lead role opening.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+pub struct JobApplication<AccountId, OpeningId, MemberId> {
+    /// Account used to authenticate in this role.
+    pub role_account_id: AccountId,
+
+    /// Opening on which this application applies.
+    pub opening_id: OpeningId,
+
+    /// Member applying.
+    pub member_id: MemberId,
+}
+
+impl<AccountId: Clone, OpeningId: Clone, MemberId: Clone>
+    JobApplication<AccountId, OpeningId, MemberId>
+{
+    /// Creates a new job application using parameters.
+    pub fn new(role_account_id: &AccountId, opening_id: &OpeningId, member_id: &MemberId) -> Self {
+        JobApplication {
+            role_account_id: role_account_id.clone(),
+            opening_id: opening_id.clone(),
+            member_id: member_id.clone(),
+        }
+    }
+}

+ 1 - 1
runtime-modules/working-group/src/lib.rs

@@ -1,5 +1,5 @@
 //! # Working group module
-//! Working group module for the Joystream platform. Version 1.
+//! Working group module for the Joystream platform.
 //! Contains abstract working group workflow.
 //!
 //! ## Overview