const Schmervice = require('@hapipal/schmervice') module.exports = class MembershipService extends Schmervice.Service { constructor(...args) { super(...args) } /** * Internal method to get list of grouping_ids for this user * @param {number} profileId * @returns {Array} List of all grouping_ids for user */ async _getGroupingIdsForProfileId(profileId, type) { const { Membership } = this.server.models() /** Grab every Membership associated with this id */ let allMemberships if(type) { allMemberships = await Membership.query() .where({ profile_id: profileId }) .where({ membership_type: type }) .where({ is_active: true }) } else { allMemberships = await Membership.query() .where({ profile_id: profileId }) .where({ is_active: true }) } /** Copy a list of the just the Groupings */ const groupingIdsToGrab = allMemberships.map( membership => membership.grouping_id, ) /** Uncomment to dedupe the list just in case */ return [...new Set(groupingIdsToGrab)] } /** * Internal method to create a new grouping * @param {object} groupingToTry from payload data * @param {*} txn * @returns {Grouping} created db record */ async _createGrouping(groupingToTry, txn) { const { Grouping } = this.server.models() const groupingInfo = { grouping_name: groupingToTry.grouping_name, grouping_type: groupingToTry.grouping_type, } return await Grouping.query(txn).insert(groupingInfo) } /** * Tries to grab a grouping_id from payload data * or returns grouping_id from a newly created record * @param {object} groupingToTry from payload data * @returns {number} grouping_id from payload or created record */ async findOrCreateGroupingFromPayload(groupingToTry) { let idToReturn = groupingToTry.grouping_id if (!idToReturn) { /** ?: For some reason this returns the key id */ const grouping = await this._createGrouping(groupingToTry) idToReturn = grouping.id } return idToReturn } /** * Get a list of groupings for user * @param {number} profileId * @returns {Array} */ async findGroupingsByProfileId(profileId, type) { const { Grouping } = this.server.models() const dedupedGroupings = await this._getGroupingIdsForProfileId(profileId, type) /** Grab just the Groupings this id has a Membership for */ return await Grouping.query() .whereIn('grouping_id', dedupedGroupings) } async _groupingIdsInCommon(profileId, targetId) { const dedupedUserGroupingIds = await this._getGroupingIdsForProfileId( profileId, ) const dedupedTargetGroupingIds = await this._getGroupingIdsForProfileId( targetId, ) /** Return true if both people have a group in common */ return dedupedUserGroupingIds.filter(groupingId => dedupedTargetGroupingIds.includes(groupingId), ) } async _patchMembership(memberships, profileId, patch) { const { Membership } = this.server.models() /** Set membership as active only if the user initiates it */ for (let membershipInfo of memberships) { await Membership.query() .where('membership_id', membershipInfo.membership_id) .where('user_id', profileId) .patch(patch) } } /** * Check for grouping membership then add membership record and set to active * or create a new grouping and add membership record for user and membership record for target * @param {number} profileId * @param {number} targetId * @param {object} groupingToWrite * @param {string} role * @returns */ async joinGrouping(profileId, targetId, groupingToWrite, role, txn) { const { Membership } = this.server.models() /** If both people have groups in common */ const matchingGroupingIds = await this._groupingIdsInCommon( profileId, targetId, ) if (matchingGroupingIds.length) { /** Grab all memberships associated with groupingIds */ const memberships = await Membership.query().whereIn( 'grouping_id', matchingGroupingIds, ) /** Set membership as active only if the user initiates it */ await this._patchMembership(memberships, profileId, { is_active: true, }) /** Make a new query to get updated information */ return await Membership.query().whereIn( 'grouping_id', matchingGroupingIds, ) } else { /** * If both have NO grouping in common, create a membership * for both and add to a new grouping but * set membership as inactive for target * */ /** Check if the grouping exists and if NOT create it */ const groupingId = await this.findOrCreateGroupingFromPayload(groupingToWrite) const membershipDetailsInCommon = { grouping_id: groupingId, membership_type: role, can_edit: false, } const userMembership = await Membership.query(txn).insert({ profile_id: profileId, ...membershipDetailsInCommon, is_active: true, }) const targetMembership = await Membership.query(txn).insert({ profile_id: targetId, ...membershipDetailsInCommon, is_active: false, }) return [userMembership, targetMembership] } } /** * Remove membership record based on grouping_id * @param {number} profileId * @param {number} groupingId * @returns */ async leaveGrouping(profileId, groupingId) { const { Membership } = this.server.models() const dedupedGroupings = await this._getGroupingIdsForProfileId(profileId) /** Do NOTHING if NOT in Grouping */ if (!dedupedGroupings.includes(groupingId)) return return await Membership.query() .delete() .where('grouping_id', groupingId) } }