TryGhost/Ghost

View on GitHub
ghost/email-service/lib/EmailController.js

Summary

Maintainability
A
35 mins
Test Coverage
const errors = require('@tryghost/errors');
const tpl = require('@tryghost/tpl');

const messages = {
    postNotFound: 'Post not found.',
    noEmailsProvided: 'No emails provided.',
    emailNotFound: 'Email not found.',
    tooManyEmailsProvided: 'Too many emails provided. Maximum of 1 test email can be sent at once.'
};

class EmailController {
    service;
    models;

    /**
     *
     * @param {EmailService} service
     * @param {{models: {Post: any, Newsletter: any, Email: any}}} dependencies
     */
    constructor(service, {models}) {
        this.service = service;
        this.models = models;
    }

    async _getFrameData(frame) {
        // Bit absurd situation in email-previews endpoints that one endpoint is using options and other one is using data.
        // So we need to handle both cases.
        let post;
        if (frame.options.id) {
            post = await this.models.Post.findOne({...frame.options, status: 'all'}, {withRelated: ['posts_meta', 'authors']});
        } else {
            post = await this.models.Post.findOne({...frame.data, status: 'all'}, {...frame.options, withRelated: ['posts_meta', 'authors']});
        }

        if (!post) {
            throw new errors.NotFoundError({
                message: tpl(messages.postNotFound)
            });
        }

        let newsletter;
        const slug = frame?.options?.newsletter ?? frame?.data?.newsletter ?? null;
        if (slug) {
            newsletter = await this.models.Newsletter.findOne({slug}, {require: true});
        } else {
            newsletter = (await post.getLazyRelation('newsletter')) ?? (await this.models.Newsletter.getDefaultNewsletter());
        }
        return {
            post,
            newsletter,
            segment: frame.options.memberSegment ?? frame.data.memberSegment ?? null
        };
    }

    async previewEmail(frame) {
        const {post, newsletter, segment} = await this._getFrameData(frame);
        return await this.service.previewEmail(post, newsletter, segment);
    }

    async sendTestEmail(frame) {
        const {post, newsletter, segment} = await this._getFrameData(frame);

        const emails = frame.data.emails ?? [];

        if (emails.length === 0) {
            throw new errors.ValidationError({
                message: tpl(messages.noEmailsProvided)
            });
        }

        // test emails are limited to 1
        if (emails.length > 1) {
            throw new errors.ValidationError({
                message: tpl(messages.tooManyEmailsProvided)
            });
        }

        await this.service.sendTestEmail(post, newsletter, segment, emails);
    }

    async retryFailedEmail(frame) {
        const email = await this.models.Email.findOne(frame.data, {require: false});

        if (!email) {
            throw new errors.NotFoundError({
                message: tpl(messages.emailNotFound)
            });
        }

        return await this.service.retryEmail(email);
    }
}

module.exports = EmailController;