const _get = require('lodash.get'); import { ValidationError, ForbiddenError } from '../errors'; import { INSIDE_POLITICS_EMAIL_NEWSLETTER_PRODUCT_CODE, UNHEDGED_EMAIL_NEWSLETTER_PRODUCT_CODE, FT_EDIT_PRODUCT_CODE, PREMIUM_DIGITAL_PRODUCT_CODE, EPAPER_CODE } from '../static-data/product-codes'; import { ProfileAccessSubscriberLicence } from '../types/response'; import { Demographics, HomeAddress, Profiles, } from '../types/user'; import { Access } from '../types/access'; import { Subscriber, Subscription } from '../types/subscription'; import { UserSubscription } from './user-subscription'; import { mapLicenceResponse } from '../mappers/licence'; import { MembershipLicenceResponse } from '../types/licence'; import { Licence } from '../models/licence'; import { OfferNameResolver } from '../offer-names'; /** Class representing a user. */ export class User { details: ProfileAccessSubscriberLicence; profile: Profiles; access: Access; subscription: Subscriber; id: string; email: string; secondaryEmail: string; title: string; firstName: string; lastName: string; nonLogin: boolean; creationDate: string; demographics: Demographics; industry: string; organisationName: string; position: string; responsibility: string; paymentMethod: string; billingCurrencyCode: string; billingCountryCode: string; billingState: string; billingPostcode: string; accountType: string; accountId: string; lastTransactionStatus: string; isSSO: boolean; isB2C: boolean; isB2B: boolean; isStaff: boolean; licences: Array; isRegistered: boolean; isTrialist: boolean; isPremium: boolean; isEpaper: boolean; isFTEdit: boolean; isInsidePoliticsEmailNewsletter: boolean; isUnhedgedEmailNewsletter: boolean; isProfileRestricted: boolean; sessionId: string; isEmailVerified: boolean; isDeferredBilling: boolean; password: string; jobTitle: string; graduationDate: string; externalUserId: string; /** * Create a user * @param userDetailsResponse membership graphql userBySession response. * @param isProfileRestricted is the FTSession_s id old. */ constructor(userDetailsResponse: ProfileAccessSubscriberLicence, isProfileRestricted: boolean) { try { this.details = userDetailsResponse; this.id = userDetailsResponse.id; if (!this.id) { throw new Error('No user id found.'); } this.sessionId = _get(userDetailsResponse, 'session.sessionId'); this.profile = _get(userDetailsResponse, 'profiles'); this.email = _get(userDetailsResponse, 'profiles.basic.email'); this.secondaryEmail = _get(userDetailsResponse, 'profiles.basic.secondaryEmail'); this.firstName = _get(userDetailsResponse, 'profiles.basic.firstName'); this.title = _get(userDetailsResponse, 'profiles.basic.title'); this.lastName = _get(userDetailsResponse, 'profiles.basic.lastName'); this.nonLogin = _get(userDetailsResponse, 'profiles.basic.nonLogin'); this.creationDate = _get(userDetailsResponse, 'profiles.basic.createDate'); this.demographics = _get(userDetailsResponse, 'profiles.demographics'); this.industry = _get(userDetailsResponse, 'profiles.demographics.industry.code'); this.responsibility = _get(userDetailsResponse, 'profiles.demographics.responsibility.code'); this.position = _get(userDetailsResponse, 'profiles.demographics.position.code'); // isEmailVerified is not returned yet by membership, so assigning null as default for now this.isEmailVerified = _get(userDetailsResponse, 'profiles.basic.isEmailVerified', null) ; this.jobTitle = _get(userDetailsResponse, 'profiles.b2b.jobTitle'); this.graduationDate = _get(userDetailsResponse, 'profiles.b2b.graduationDate'); this.organisationName = _get(userDetailsResponse, 'profiles.b2b.organisationName'); this.externalUserId = ''; this.access = userDetailsResponse.access; this.isB2C = _get(userDetailsResponse, 'access.isB2c'); // User on a licence (not including unmasking users) this.isB2B = _get(userDetailsResponse, 'access.isB2b'); this.isStaff = _get(userDetailsResponse, 'access.isStaff'); this.isSSO = _get(userDetailsResponse, 'access.isSso'); this.licences = _get(userDetailsResponse, 'access.licences', []).map((licence: MembershipLicenceResponse) => mapLicenceResponse(licence)); this.subscription = userDetailsResponse.subscriber; this.paymentMethod = _get(userDetailsResponse, 'subscriber.billingAccount.paymentMethod.type'); this.billingCurrencyCode = _get(userDetailsResponse, 'subscriber.billingAccount.currencyCode'); this.isDeferredBilling = _get(userDetailsResponse, 'subscriber.billingAccount.isDeferredBilling'); this.lastTransactionStatus = _get(userDetailsResponse, 'subscriber.billingAccount.paymentMethod.lastTransactionStatus') || ''; this.billingCountryCode = ''; this.billingState = ''; this.billingPostcode = ''; this.accountType = _get(userDetailsResponse, 'subscriber.accountInfo.basicInfo.accountType'); this.accountId = _get(userDetailsResponse, 'subscriber.accountInfo.basicInfo.id'); // User with a FT profile but not currently subscribed this.isRegistered = !_get(userDetailsResponse, 'subscriber.status.currentSubscriber'); this.isTrialist = _get(userDetailsResponse, 'subscriber.status.currentTriallist'); this.isPremium = _get(userDetailsResponse, 'access.productCodes', []).includes(PREMIUM_DIGITAL_PRODUCT_CODE); this.isEpaper = _get(userDetailsResponse, 'access.productCodes', []).includes(EPAPER_CODE); this.isFTEdit = _get(userDetailsResponse, 'access.productCodes', []).includes(FT_EDIT_PRODUCT_CODE); this.isInsidePoliticsEmailNewsletter = _get( userDetailsResponse, 'access.productCodes', [] ).includes(INSIDE_POLITICS_EMAIL_NEWSLETTER_PRODUCT_CODE); this.isUnhedgedEmailNewsletter = _get( userDetailsResponse, 'access.productCodes', [] ).includes(UNHEDGED_EMAIL_NEWSLETTER_PRODUCT_CODE); this.isProfileRestricted = isProfileRestricted; this.password = ''; } catch (error) { throw new ValidationError('Invalid user details by session response', error); } } /** * Get user's primary telephone number * @returns user's primary telephone number */ public get primaryTelephone(): string { this.checkIfProfileIsRestricted(); return _get(this.profile, 'restricted.primaryTelephone'); } /** * Set user's primary telephone number * @param number user's updated primary telephone number * @returns the user's updated primary telephone number */ public set primaryTelephone(number) { this.checkIfProfileIsRestricted(); this.profile.restricted.primaryTelephone = number; } /** * Get user's home address * @returns user's home address - only returns postcode and country code */ public get homeAddress(): HomeAddress { this.checkIfProfileIsRestricted(); return _get(this.profile, 'restricted.homeAddress'); } /** * Get user's three letter ISO 3166-1 country code * @returns three letter ISO 3166-1 country code */ public get country(): string { this.checkIfProfileIsRestricted(); return _get(this.profile, 'restricted.homeAddress.country'); } /** * Set user's three letter ISO 3166-1 country code * @param countryCode user's updated three letter ISO 3166-1 country code * @returns the user's updated three letter ISO 3166-1 country code */ public set country(countryCode) { this.checkIfProfileIsRestricted(); this.profile.restricted.homeAddress.country = countryCode; } /** * Get user's postcode * @returns user's postcode */ public get postcode(): string { this.checkIfProfileIsRestricted(); return _get(this.profile, 'restricted.homeAddress.postcode'); } /** * Set user's postcode * @param code user's updated postcode * @returns user's updated postcode */ public set postcode(code) { this.checkIfProfileIsRestricted(); this.profile.restricted.homeAddress.postcode = code; } /** * Creates a new object to format the user's details. * @returns user's details needed to create a subscription. */ public formatForSubscription() { return { id: this.id, email: this.email, secondaryEmail: this.secondaryEmail, firstName: this.firstName, lastName: this.lastName, primaryTelephone: this.primaryTelephone, homeAddress: _get(this.profile, 'restricted.homeAddress'), demographics: this.demographics }; } /** * Finds out if user is on a licence. * @param licenceId ID of the licence a user is trying to access. * @returns true if user's licence contains the licence they are trying to access. */ // B2C users join individual licences public belongsToLicence(licenceId: string): boolean { return !!this.licences.find(licence => licence.id === licenceId); } /** * Get active subscription for the user. * @returns formatted active subscription details. */ public get activeSubscription(): UserSubscription | null { const subscriptions = _get(this.subscription, 'status.subscriptions', []); const activeSubscription = subscriptions && subscriptions.length >= 1 ? subscriptions.find((subscription: Subscription) => subscription.active) : null; return activeSubscription ? new UserSubscription(activeSubscription) : null; } /** * Attempt to get a friendly offer name for the active subscription. * @returns friendly name. */ public get activeSubscriptionName(): string | undefined { return OfferNameResolver.resolveBySubscription(this.activeSubscription, this.isTrialist); } /** * Gets all subscription for the user - active and inactive. * @returns formatted all subscription details. */ public get allSubscriptions(): Array { const subscriptions = _get(this.subscription, 'status.subscriptions', []); return subscriptions && subscriptions.length ? subscriptions.map((subscription: Subscription) => new UserSubscription(subscription)) : []; } /** * Get renewal date for the user's currently active subscription. * @returns renewal date for the user's currently active subscription. */ public get renewalDate(): string | null { return this.activeSubscription && this.activeSubscription.renewalDate || null; } /** * Checks whether the profile is restricted. * @returns empty * @throws {ForbiddenError} an error if profile is restricted. */ private checkIfProfileIsRestricted() { if (this.isProfileRestricted) { throw new ForbiddenError('Forbidden from accessing restricted user profile'); } else { return; } } }