api/v1/users/model.js

/* eslint-disable camelcase */
import assign from "lodash/assign"
import find from "lodash/find"
import moment from "moment"

import client from "configure/client"
import membershipStatuses from "constants/membershipStatuses"
import membershipIds from "constants/membershipIds"
import PropType from "mixins/PropType"

import BaseModel from "../../BaseModel"
import WordpressModel from "../wordpress/model"

export const name = "users"

/**
 * test
 *
 * @class UserModel
 * @param {string} name | table name that will be used in all your queries
 * @extends {BaseModel}
 */
class UserModel extends BaseModel {
    constructor(props) {
        super(name, UserModel.propTypes)

        if (!props) return

        this.props = { ...props }
        this.error = this.validate(this.props)
        this.initialized = true
    }

    /**
     * reference to the wordpress member
     * @type int
     * @memberof UserModel
     */
    get memberId() {
        return this.props.memberId
    }

    set memberId(value) {
        this.props.memberId = value
    }

    /**
     * reference to the wordpress membership
     * @type int
     * @memberof UserModel
     */
    get membershipId() {
        return this.props.membershipId
    }

    set membershipId(value) {
        this.props.membershipId = value
    }

    /**
     * User's first name
     * @type string
     * @memberof UserModel
     */
    get firstName() {
        return this.props.firstName
    }

    set firstName(value) {
        this.props.firstName = value
    }

    /**
     * User's last name
     * @type string
     * @memberof UserModel
     */
    get lastName() {
        return this.props.lastName
    }

    set lastName(value) {
        this.props.lastName = value
    }

    /**
     * User's email address
     * @type string|required
     * @memberof UserModel
     */
    get emailAddress() {
        return this.props.emailAddress
    }

    set emailAddress(value) {
        this.props.emailAddress = value
    }

    /**
     * User's username referring to WP
     * @type string|required
     * @memberof UserModel
     */
    get username() {
        return this.props.username
    }

    set username(value) {
        this.props.username = value
    }

    /**
     * Registration date in wordpress
     * @type date
     * @memberof UserModel
     */
    get registeredAt() {
        return this.props.registeredAt
    }

    set registeredAt(value) {
        this.props.registeredAt = value
    }

    /**
     * Set and validates user properties
     * @param {object} props | Properties of the UserModel object
     */
    set(props) {
        this.props = assign({}, this.props, props)

        this.error = this.validate(this.props)
        this.initialized = true
    }

    /**
     * Selects the instances that match the given parameters
     * @param {object} params | Parameters to filter the search
     */
    find(params) {
        return this.select(params)
    }

    /**
     * Insert an instance with the validated properties
     */
    create() {
        return this.insert(this.props)
    }

    /**
     * Update an instance with the validated properties
     */
    update() {
        const { id } = this.props

        return this.updateById(id, this.props)
    }

    /**
     * Checks if an instance exists based on the parameters passed
     * @param {object} params | parameters for the search of the instance
     */
    async exists(params) {
        const result = await this.find(params).catch((error) => {
            throw error
        })

        return !!result?.rows?.[0]
    }

    /**
     * Validates the membership of a user
     * @param {string} authorization | Bearer token found in the request header
     */
    async validateMembership() {
        if (!this.initialized) throw new Error("ERROR: Initialize user before validating it's membership")
        if (this.error) return this.error

        const admin = new WordpressModel()
        const canLoginAsAdmin = await admin.login()

        if (!canLoginAsAdmin) {
            this.error = admin.error
            return false
        }

        try {
            const { emailAddress } = this.props

            const result = await client.request({
                method: "GET",
                url: `/mp/v1/members?search=${emailAddress}&per_page=1`,
                headers: {
                    Authorization: `Bearer ${admin.token}`,
                    "MEMBERPRESS-API-KEY": "xpdB3eRkAS",
                },
            })

            const members = result?.data || []
            const member = find(members, { email: emailAddress })

            if (!member) {
                this.error = "memeber_not_found"
                return false
            }

            const activeMemberships = member?.active_memberships || []
            let limitedAccess = false
            let hasValidMembership = false
            let activeMembership = null

            for (let idx = 0; idx < activeMemberships.length; idx += 1) {
                const membershipId = activeMemberships[idx]?.id

                if (membershipId === membershipIds.employee) {
                    hasValidMembership = true
                    activeMembership = activeMemberships[idx]
                    break
                }

                const membership = find(
                    member?.recent_subscriptions,
                    { membership: String(membershipId) },
                )
                const latestTransaction = find(
                    member?.recent_transactions,
                    { membership: String(membershipId) },
                )

                if (membership?.status === membershipStatuses.active) {
                    if (membershipId === membershipIds.limitedAccess) {
                        limitedAccess = true
                    } else if (
                        membershipId !== membershipIds.onlineCoaching
                        && membershipId !== membershipIds.coachingNEW
                    ) {
                        hasValidMembership = true
                        activeMembership = membership || activeMemberships[idx]
                        break
                    }
                } else if (!membership || membership?.status === membershipStatuses.cancelled) {
                    const today = moment()
                    const expirationDate = moment(latestTransaction?.expires_at)

                    if (today.isSameOrBefore(expirationDate)) {
                        hasValidMembership = true
                        activeMembership = membership || activeMemberships[idx]
                    }
                }
            }

            this.set({
                memberId: member?.id,
                firstName: member?.first_name,
                lastName: member?.last_name,
                registeredAt: new Date(member?.registered_at),
            })

            if (!hasValidMembership) {
                this.error = "membership_not_found"

                await this.update().catch((error) => {
                    this.error = error
                })

                return false
            }

            this.set({ membershipId: Number(activeMembership?.id) })

            await this.update().catch((error) => {
                this.error = error
            })

            return {
                ...member,
                hasValidMembership,
                activeMembership,
                limitedAccess,
            }
        } catch (error) {
            return false
        }
    }
}

/**
 * Defines the default properties types of the UserModel class
 * @name propTypes
 * @type object
 * @constant
 * @default
 * @memberof UserModel
 */
UserModel.propTypes = {
    memberId: PropType.number,
    membershipId: PropType.number,
    firstName: PropType.string,
    lastName: PropType.string,
    emailAddress: PropType.string().isRequired(),
    registeredAt: PropType.string,
    username: PropType.string().isRequired(),
}

export default UserModel