From 071f6a7f31fa049b0e93c95ed55e0588924c9734 Mon Sep 17 00:00:00 2001 From: Eric Dobbertin Date: Tue, 21 Apr 2020 07:35:11 -0500 Subject: [PATCH] refactor: move email-smtp plugin to NPM package Signed-off-by: Eric Dobbertin --- jest.config.cjs | 1 + package-lock.json | 54 +++++++++++++++++++ package.json | 1 + plugins.json | 2 +- src/plugins/email-smtp/config.js | 36 ------------- src/plugins/email-smtp/index.js | 27 ---------- .../verifySMTPEmailSettings.test.js.snap | 3 -- src/plugins/email-smtp/mutations/index.js | 5 -- .../mutations/verifySMTPEmailSettings.js | 35 ------------ .../mutations/verifySMTPEmailSettings.test.js | 21 -------- src/plugins/email-smtp/policies.json | 11 ---- .../email-smtp/resolvers/Mutation/index.js | 5 -- .../Mutation/verifySMTPEmailSettings.js | 26 --------- src/plugins/email-smtp/resolvers/index.js | 5 -- src/plugins/email-smtp/schemas/index.js | 5 -- src/plugins/email-smtp/schemas/schema.graphql | 22 -------- src/plugins/email-smtp/startup.js | 11 ---- .../email-smtp/util/getConfigFromMailUrl.js | 42 --------------- src/plugins/email-smtp/util/sendSMTPEmail.js | 25 --------- 19 files changed, 57 insertions(+), 280 deletions(-) delete mode 100644 src/plugins/email-smtp/config.js delete mode 100644 src/plugins/email-smtp/index.js delete mode 100644 src/plugins/email-smtp/mutations/__snapshots__/verifySMTPEmailSettings.test.js.snap delete mode 100644 src/plugins/email-smtp/mutations/index.js delete mode 100644 src/plugins/email-smtp/mutations/verifySMTPEmailSettings.js delete mode 100644 src/plugins/email-smtp/mutations/verifySMTPEmailSettings.test.js delete mode 100644 src/plugins/email-smtp/policies.json delete mode 100644 src/plugins/email-smtp/resolvers/Mutation/index.js delete mode 100644 src/plugins/email-smtp/resolvers/Mutation/verifySMTPEmailSettings.js delete mode 100644 src/plugins/email-smtp/resolvers/index.js delete mode 100644 src/plugins/email-smtp/schemas/index.js delete mode 100644 src/plugins/email-smtp/schemas/schema.graphql delete mode 100644 src/plugins/email-smtp/startup.js delete mode 100644 src/plugins/email-smtp/util/getConfigFromMailUrl.js delete mode 100644 src/plugins/email-smtp/util/sendSMTPEmail.js diff --git a/jest.config.cjs b/jest.config.cjs index b2c45134fcd..9cb182fc41c 100644 --- a/jest.config.cjs +++ b/jest.config.cjs @@ -11,6 +11,7 @@ const externalNodeModules = [ "@reactioncommerce/api-plugin-carts", "@reactioncommerce/api-plugin-catalogs", "@reactioncommerce/api-plugin-email", + "@reactioncommerce/api-plugin-email-smtp", "@reactioncommerce/api-plugin-i18n", "@reactioncommerce/api-plugin-job-queue", "@reactioncommerce/api-plugin-notifications", diff --git a/package-lock.json b/package-lock.json index 46ea3073b91..9413ed21682 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1592,6 +1592,34 @@ "accounting-js": "^1.1.1", "lodash": "^4.17.15", "simpl-schema": "^1.5.9" + }, + "dependencies": { + "mongo-object": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/mongo-object/-/mongo-object-0.1.4.tgz", + "integrity": "sha512-QtYk0gupWEn2+iB+DDRt1L+WbcNYvJRaHdih/dcqthOa1DbnREUGSs2WGcW478GNYpElflo/yybZXu0sTiRXHg==" + }, + "simpl-schema": { + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/simpl-schema/-/simpl-schema-1.5.9.tgz", + "integrity": "sha512-WGJA10wYD1bBzx6UpAdn5aOOKcITTwbKmxEnMqebnlJRNqSVDNkzVEowPQiZdXFMg+rUNO2Nd50cnce7v8fRlA==", + "requires": { + "clone": "^2.1.2", + "extend": "^3.0.2", + "lodash.every": "^4.6.0", + "lodash.find": "^4.6.0", + "lodash.findwhere": "^3.1.0", + "lodash.includes": "^4.3.0", + "lodash.isempty": "^4.4.0", + "lodash.isobject": "^3.0.2", + "lodash.omit": "^4.5.0", + "lodash.pick": "^4.4.0", + "lodash.union": "^4.6.0", + "lodash.uniq": "^4.5.0", + "message-box": "^0.2.2", + "mongo-object": "^0.1.4" + } + } } }, "@reactioncommerce/api-plugin-catalogs": { @@ -1625,6 +1653,32 @@ "@reactioncommerce/logger": "^1.1.3" } }, + "@reactioncommerce/api-plugin-email-smtp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@reactioncommerce/api-plugin-email-smtp/-/api-plugin-email-smtp-1.0.0.tgz", + "integrity": "sha512-EZVGrLQzFs8LUEpxaiAqXH70tKC0hpYJTgCayn0X/SywoBk7t4oL8VSVWanOWzZTYYZ5q/buo+JuDMQgI+fs3w==", + "requires": { + "@reactioncommerce/api-utils": "^1.9.0", + "@reactioncommerce/logger": "^1.1.3", + "@reactioncommerce/nodemailer": "^5.0.5", + "@reactioncommerce/reaction-error": "^1.0.1", + "envalid": "^6.0.1", + "simpl-schema": "^1.5.7" + }, + "dependencies": { + "envalid": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/envalid/-/envalid-6.0.1.tgz", + "integrity": "sha512-o2+xxQhpp7NSjrSeAJautFVYeNb6MUpEceVVuMoSEfgp9oq4XMb9fp3HODvZuh5Sg9zmrG3a5nClVaY1XqOdlg==", + "requires": { + "chalk": "^3.0.0", + "dotenv": "^8.2.0", + "meant": "^1.0.1", + "validator": "^12.0.0" + } + } + } + }, "@reactioncommerce/api-plugin-i18n": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@reactioncommerce/api-plugin-i18n/-/api-plugin-i18n-1.0.0.tgz", diff --git a/package.json b/package.json index e905fd93529..401d196bc5e 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@reactioncommerce/api-plugin-carts": "^1.0.0", "@reactioncommerce/api-plugin-catalogs": "~1.0.0", "@reactioncommerce/api-plugin-email": "~1.0.0", + "@reactioncommerce/api-plugin-email-smtp": "~1.0.0", "@reactioncommerce/api-plugin-i18n": "~1.0.0", "@reactioncommerce/api-plugin-job-queue": "~1.0.0", "@reactioncommerce/api-plugin-notifications": "~1.0.0", diff --git a/plugins.json b/plugins.json index b33c3531557..8832b7226cb 100644 --- a/plugins.json +++ b/plugins.json @@ -21,7 +21,7 @@ "payments": "./src/core-services/payments/index.js", "pricing": "./src/plugins/simple-pricing/index.js", "product": "./src/core-services/product/index.js", - "sMTPEmail": "./src/plugins/email-smtp/index.js", + "sMTPEmail": "@reactioncommerce/api-plugin-email-smtp", "settings": "@reactioncommerce/api-plugin-settings", "shipping": "./src/core-services/shipping/index.js", "shippingRates": "./src/plugins/shipping-rates/index.js", diff --git a/src/plugins/email-smtp/config.js b/src/plugins/email-smtp/config.js deleted file mode 100644 index 9bc1dfd48e6..00000000000 --- a/src/plugins/email-smtp/config.js +++ /dev/null @@ -1,36 +0,0 @@ -import envalid from "envalid"; -import Logger from "@reactioncommerce/logger"; -import getConfigFromMailUrl from "./util/getConfigFromMailUrl.js"; - -const { bool, str } = envalid; - -const config = envalid.cleanEnv(process.env, { - EMAIL_DEBUG: bool({ - default: false - }), - MAIL_URL: str({ - desc: "An SMTP mail url, i.e. smtp://user:pass@example.com:465", - default: "" - }) -}, { - dotEnvPath: null -}); - -export const SMTPConfig = { logger: config.EMAIL_DEBUG }; - -// Parse the MAIL_URL and add the parsed config -if (typeof config.MAIL_URL === "string" && config.MAIL_URL.length) { - Object.assign(SMTPConfig, getConfigFromMailUrl(config.MAIL_URL)); - - const logConfig = { ...SMTPConfig }; - if (SMTPConfig.auth) { - // Hide password from auth logging - logConfig.auth = { - user: SMTPConfig.auth.user, - pass: "*".repeat(SMTPConfig.auth.pass.length) - }; - } - Logger.debug(logConfig, "Parsed SMTP email config"); -} - -export default config; diff --git a/src/plugins/email-smtp/index.js b/src/plugins/email-smtp/index.js deleted file mode 100644 index 88bbccc2fc4..00000000000 --- a/src/plugins/email-smtp/index.js +++ /dev/null @@ -1,27 +0,0 @@ -import mutations from "./mutations/index.js"; -import policies from "./policies.json"; -import resolvers from "./resolvers/index.js"; -import schemas from "./schemas/index.js"; -import startup from "./startup.js"; - -/** - * @summary Import and call this function to add this plugin to your API. - * @param {ReactionAPI} app The ReactionAPI instance - * @returns {undefined} - */ -export default async function register(app) { - await app.registerPlugin({ - label: "SMTP Email", - name: "reaction-email-smtp", - version: app.context.appVersion, - functionsByType: { - startup: [startup] - }, - graphQL: { - resolvers, - schemas - }, - mutations, - policies - }); -} diff --git a/src/plugins/email-smtp/mutations/__snapshots__/verifySMTPEmailSettings.test.js.snap b/src/plugins/email-smtp/mutations/__snapshots__/verifySMTPEmailSettings.test.js.snap deleted file mode 100644 index 8c85cfa891e..00000000000 --- a/src/plugins/email-smtp/mutations/__snapshots__/verifySMTPEmailSettings.test.js.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`throws if permission check fails 1`] = `"Access Denied"`; diff --git a/src/plugins/email-smtp/mutations/index.js b/src/plugins/email-smtp/mutations/index.js deleted file mode 100644 index 2b277340348..00000000000 --- a/src/plugins/email-smtp/mutations/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import verifySMTPEmailSettings from "./verifySMTPEmailSettings.js"; - -export default { - verifySMTPEmailSettings -}; diff --git a/src/plugins/email-smtp/mutations/verifySMTPEmailSettings.js b/src/plugins/email-smtp/mutations/verifySMTPEmailSettings.js deleted file mode 100644 index 91dac329c62..00000000000 --- a/src/plugins/email-smtp/mutations/verifySMTPEmailSettings.js +++ /dev/null @@ -1,35 +0,0 @@ -import SimpleSchema from "simpl-schema"; -import nodemailer from "@reactioncommerce/nodemailer"; -import ReactionError from "@reactioncommerce/reaction-error"; -import { SMTPConfig } from "../config.js"; - -const inputSchema = new SimpleSchema({ - shopId: String -}); - -/** - * @name verifySMTPEmailSettings - * @summary Verify the current email configuration - * @param {Object} context - an object containing the per-request state - * @param {Object} input - an object of all mutation arguments that were sent by the client - * @param {String} input.shopId - ShopID this setting belongs to. - * @returns {Boolean} - returns true if SMTP connection succeeds - */ -export default async function verifySMTPEmailSettings(context, input) { - inputSchema.validate(input); - - const { shopId } = input; - - await context.validatePermissions("reaction:legacy:emails", "read", { shopId }); - - const transporter = nodemailer.createTransport(SMTPConfig); - - let isVerified; - try { - isVerified = await transporter.verify(); - } catch (error) { - throw new ReactionError(error.responseCode, error.response); - } - - return isVerified; -} diff --git a/src/plugins/email-smtp/mutations/verifySMTPEmailSettings.test.js b/src/plugins/email-smtp/mutations/verifySMTPEmailSettings.test.js deleted file mode 100644 index 9e51266a9c4..00000000000 --- a/src/plugins/email-smtp/mutations/verifySMTPEmailSettings.test.js +++ /dev/null @@ -1,21 +0,0 @@ -import mockContext from "@reactioncommerce/api-utils/tests/mockContext.js"; -import ReactionError from "@reactioncommerce/reaction-error"; -import verifySMTPEmailSettings from "./verifySMTPEmailSettings.js"; - -beforeEach(() => { - jest.resetAllMocks(); -}); - -const shopId = "SHOP_ID"; - -test("throws if permission check fails", async () => { - mockContext.validatePermissions.mockImplementation(() => { - throw new ReactionError("access-denied", "Access Denied"); - }); - - await expect(verifySMTPEmailSettings(mockContext, { - shopId - })).rejects.toThrowErrorMatchingSnapshot(); - - expect(mockContext.validatePermissions).toHaveBeenCalledWith("reaction:legacy:emails", "read", { shopId }); -}); diff --git a/src/plugins/email-smtp/policies.json b/src/plugins/email-smtp/policies.json deleted file mode 100644 index c3c218ef8fc..00000000000 --- a/src/plugins/email-smtp/policies.json +++ /dev/null @@ -1,11 +0,0 @@ -[ - { - "description": "Shop managers acting on email settings.", - "subjects": [ "reaction:groups:shop-managers" ], - "resources": [ "reaction:legacy:emails" ], - "actions": [ - "read" - ], - "effect": "allow" - } -] diff --git a/src/plugins/email-smtp/resolvers/Mutation/index.js b/src/plugins/email-smtp/resolvers/Mutation/index.js deleted file mode 100644 index 2b277340348..00000000000 --- a/src/plugins/email-smtp/resolvers/Mutation/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import verifySMTPEmailSettings from "./verifySMTPEmailSettings.js"; - -export default { - verifySMTPEmailSettings -}; diff --git a/src/plugins/email-smtp/resolvers/Mutation/verifySMTPEmailSettings.js b/src/plugins/email-smtp/resolvers/Mutation/verifySMTPEmailSettings.js deleted file mode 100644 index d72de957b20..00000000000 --- a/src/plugins/email-smtp/resolvers/Mutation/verifySMTPEmailSettings.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @name Mutation/verifySMTPEmailSettings - * @method - * @summary resolver for the verifySMTPEmailSettings GraphQL mutation - * @param {Object} _ - unused - * @param {Object} args.input - an object of all mutation arguments that were sent by the client - * @param {String} input.shopId - ShopID this setting belongs to. - * @param {String} [args.input.clientMutationId] - An optional string identifying the mutation call - * @param {Object} context - an object containing the per-request state - * @returns {Promise} verifySMTPEmailSettingsPayload - */ -export default async function verifySMTPEmailSettings(_, { input }, context) { - const { - clientMutationId = null, - shopId - } = input; - - const isVerified = await context.mutations.verifySMTPEmailSettings(context, { - shopId - }); - - return { - clientMutationId, - isVerified - }; -} diff --git a/src/plugins/email-smtp/resolvers/index.js b/src/plugins/email-smtp/resolvers/index.js deleted file mode 100644 index 6b9c90688a3..00000000000 --- a/src/plugins/email-smtp/resolvers/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import Mutation from "./Mutation/index.js"; - -export default { - Mutation -}; diff --git a/src/plugins/email-smtp/schemas/index.js b/src/plugins/email-smtp/schemas/index.js deleted file mode 100644 index 30096f92e54..00000000000 --- a/src/plugins/email-smtp/schemas/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import importAsString from "@reactioncommerce/api-utils/importAsString.js"; - -const schema = importAsString("./schema.graphql"); - -export default [schema]; diff --git a/src/plugins/email-smtp/schemas/schema.graphql b/src/plugins/email-smtp/schemas/schema.graphql deleted file mode 100644 index 8dd617ff1a5..00000000000 --- a/src/plugins/email-smtp/schemas/schema.graphql +++ /dev/null @@ -1,22 +0,0 @@ -extend type Mutation { - "Use this mutation to verify the SMTP email settings" - verifySMTPEmailSettings( - "Mutation input" - input: VerifySMTPEmailSettingsInput! - ): VerifySMTPEmailSettingsInputPayload! -} - -"Input for an `VerifySMTPEmailSettingsInput`" -input VerifySMTPEmailSettingsInput { - "The ID of the shop this setting belongs to" - shopId: ID! -} - -"Response payload for the verifySMTPEmailSettings mutation" -type VerifySMTPEmailSettingsInputPayload { - "The same string you sent with the mutation params, for matching mutation calls with their responses" - clientMutationId: String - - "True if the SMTP connection was made and authentication was successful." - isVerified: Boolean! -} diff --git a/src/plugins/email-smtp/startup.js b/src/plugins/email-smtp/startup.js deleted file mode 100644 index 82b0ccc29ee..00000000000 --- a/src/plugins/email-smtp/startup.js +++ /dev/null @@ -1,11 +0,0 @@ -import sendSMTPEmail from "./util/sendSMTPEmail.js"; - -/** - * @name startup - * @summary Called on startup. Initializes SMTP email handler. - * @param {Object} context App context - * @returns {undefined} - */ -export default function emailSMTPStartup(context) { - context.appEvents.on("sendEmail", (...args) => sendSMTPEmail(context, ...args)); -} diff --git a/src/plugins/email-smtp/util/getConfigFromMailUrl.js b/src/plugins/email-smtp/util/getConfigFromMailUrl.js deleted file mode 100644 index c389218e429..00000000000 --- a/src/plugins/email-smtp/util/getConfigFromMailUrl.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @summary get email sending config for Nodemailer based on parsing a mail URL - * @param {String} mailUrl The mail URL - * @returns {Object} A mail config object - */ -export default function getConfigFromMailUrl(mailUrl) { - const urlSections = mailUrl.split(":"); - // Prevent URL parsing from breaking due to invalid characters in user/password - // Look for invalid characters in username, - // ignore the first two // as they are port of the protocol in username section. - // Also look for invalid character in password, split with @ delimiter to ignore host and only look at password. - if (urlSections[1].slice(2).indexOf("/") >= 0 || urlSections[2].split("@")[0].indexOf("/") >= 0) { - throw new Error(` - Invalid character detected in environment variable MAIL_URL, - user or password has invalid characters, please replace "/" with "%2F" - `); - } - - const parsedUrl = new URL(mailUrl); - - // create a nodemailer config from the SMTP url string - const config = { - host: parsedUrl.hostname, - port: parsedUrl.port, - secure: parseInt(parsedUrl.port, 10) === 465 - }; - - // add user/pass to the config object if they were found - if (parsedUrl.username && parsedUrl.password) { - config.auth = { - user: decodeURIComponent(parsedUrl.username), - pass: decodeURIComponent(parsedUrl.password) - }; - } - - // don't enforce checking TLS on localhost - if (parsedUrl.hostname === "localhost") { - config.ignoreTLS = true; - } - - return config; -} diff --git a/src/plugins/email-smtp/util/sendSMTPEmail.js b/src/plugins/email-smtp/util/sendSMTPEmail.js deleted file mode 100644 index 989a2cb1623..00000000000 --- a/src/plugins/email-smtp/util/sendSMTPEmail.js +++ /dev/null @@ -1,25 +0,0 @@ -import nodemailer from "@reactioncommerce/nodemailer"; -import { SMTPConfig } from "../config.js"; - -/** - * @name sendSMTPEmail - * @summary Responds to the "sendEmail" app event to send an email via SMTP - * @param {Object} context App context - * @param {Object} job Current sendEmail job being processed - * @param {Function} sendEmailCompleted Called when email was successfully sent - * @param {Function} sendEmailFailed Called on error - * @returns {undefined} Calls one of the callbacks with a return - */ -export default async function sendSMTPEmail(context, { job, sendEmailCompleted, sendEmailFailed }) { - const { to, shopId, ...otherEmailFields } = job.data; - - const transport = nodemailer.createTransport(SMTPConfig); - - transport.sendMail({ to, shopId, ...otherEmailFields }, (error) => { - if (error) { - sendEmailFailed(job, `Email job failed: ${error.toString()}`); - } else { - sendEmailCompleted(job, `Successfully sent email to ${to}`); - } - }); -}