Fix for real email tests failing silently
This commit is contained in:
parent
d3a7286711
commit
2d993adec8
|
@ -1,3 +1,4 @@
|
||||||
|
jest.unmock("node-fetch")
|
||||||
import { TestConfiguration } from "../../../../tests"
|
import { TestConfiguration } from "../../../../tests"
|
||||||
import { EmailTemplatePurpose } from "../../../../constants"
|
import { EmailTemplatePurpose } from "../../../../constants"
|
||||||
const nodemailer = require("nodemailer")
|
const nodemailer = require("nodemailer")
|
||||||
|
|
|
@ -13,6 +13,7 @@ export class EmailAPI extends TestAPI {
|
||||||
email: "test@test.com",
|
email: "test@test.com",
|
||||||
purpose,
|
purpose,
|
||||||
tenantId: this.config.getTenantId(),
|
tenantId: this.config.getTenantId(),
|
||||||
|
userId: this.config.user?._id!,
|
||||||
})
|
})
|
||||||
.set(this.config.defaultHeaders())
|
.set(this.config.defaultHeaders())
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
|
|
|
@ -55,8 +55,8 @@ export function smtpEthereal() {
|
||||||
host: "smtp.ethereal.email",
|
host: "smtp.ethereal.email",
|
||||||
secure: false,
|
secure: false,
|
||||||
auth: {
|
auth: {
|
||||||
user: "don.bahringer@ethereal.email",
|
user: "wyatt.zulauf29@ethereal.email",
|
||||||
pass: "yCKSH8rWyUPbnhGYk9",
|
pass: "tEwDtHBWWxusVWAPfa",
|
||||||
},
|
},
|
||||||
connectionTimeout: 1000, // must be less than the jest default of 5000
|
connectionTimeout: 1000, // must be less than the jest default of 5000
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { EmailTemplatePurpose, TemplateType, Config } from "../constants"
|
import { EmailTemplatePurpose, TemplateType } from "../constants"
|
||||||
import { getTemplateByPurpose } from "../constants/templates"
|
import { getTemplateByPurpose } from "../constants/templates"
|
||||||
import { getSettingsTemplateContext } from "./templates"
|
import { getSettingsTemplateContext } from "./templates"
|
||||||
import { processString } from "@budibase/string-templates"
|
import { processString } from "@budibase/string-templates"
|
||||||
import { getResetPasswordCode, getInviteCode } from "./redis"
|
import { getResetPasswordCode, getInviteCode } from "./redis"
|
||||||
import { User, Database } from "@budibase/types"
|
import { User, SMTPInnerConfig } from "@budibase/types"
|
||||||
import { tenancy, db as dbCore } from "@budibase/backend-core"
|
import { configs } from "@budibase/backend-core"
|
||||||
const nodemailer = require("nodemailer")
|
const nodemailer = require("nodemailer")
|
||||||
|
|
||||||
type SendEmailOpts = {
|
type SendEmailOpts = {
|
||||||
|
@ -36,24 +36,24 @@ const FULL_EMAIL_PURPOSES = [
|
||||||
EmailTemplatePurpose.CUSTOM,
|
EmailTemplatePurpose.CUSTOM,
|
||||||
]
|
]
|
||||||
|
|
||||||
function createSMTPTransport(config: any) {
|
function createSMTPTransport(config?: SMTPInnerConfig) {
|
||||||
let options: any
|
let options: any
|
||||||
let secure = config.secure
|
let secure = config?.secure
|
||||||
// default it if not specified
|
// default it if not specified
|
||||||
if (secure == null) {
|
if (secure == null) {
|
||||||
secure = config.port === 465
|
secure = config?.port === 465
|
||||||
}
|
}
|
||||||
if (!TEST_MODE) {
|
if (!TEST_MODE) {
|
||||||
options = {
|
options = {
|
||||||
port: config.port,
|
port: config?.port,
|
||||||
host: config.host,
|
host: config?.host,
|
||||||
secure: secure,
|
secure: secure,
|
||||||
auth: config.auth,
|
auth: config?.auth,
|
||||||
}
|
}
|
||||||
options.tls = {
|
options.tls = {
|
||||||
rejectUnauthorized: false,
|
rejectUnauthorized: false,
|
||||||
}
|
}
|
||||||
if (config.connectionTimeout) {
|
if (config?.connectionTimeout) {
|
||||||
options.connectionTimeout = config.connectionTimeout
|
options.connectionTimeout = config.connectionTimeout
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -134,57 +134,16 @@ async function buildEmail(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility function for finding most valid SMTP configuration.
|
|
||||||
* @param {object} db The CouchDB database which is to be looked up within.
|
|
||||||
* @param {string|null} workspaceId If using finer grain control of configs a workspace can be used.
|
|
||||||
* @param {boolean|null} automation Whether or not the configuration is being fetched for an email automation.
|
|
||||||
* @return {Promise<object|null>} returns the SMTP configuration if it exists
|
|
||||||
*/
|
|
||||||
async function getSmtpConfiguration(
|
|
||||||
db: Database,
|
|
||||||
workspaceId?: string,
|
|
||||||
automation?: boolean
|
|
||||||
) {
|
|
||||||
const params: any = {
|
|
||||||
type: Config.SMTP,
|
|
||||||
}
|
|
||||||
if (workspaceId) {
|
|
||||||
params.workspace = workspaceId
|
|
||||||
}
|
|
||||||
|
|
||||||
const customConfig = await dbCore.getScopedConfig(db, params)
|
|
||||||
|
|
||||||
if (customConfig) {
|
|
||||||
return customConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use an SMTP fallback configuration from env variables
|
|
||||||
if (!automation && env.SMTP_FALLBACK_ENABLED) {
|
|
||||||
return {
|
|
||||||
port: env.SMTP_PORT,
|
|
||||||
host: env.SMTP_HOST,
|
|
||||||
secure: false,
|
|
||||||
from: env.SMTP_FROM_ADDRESS,
|
|
||||||
auth: {
|
|
||||||
user: env.SMTP_USER,
|
|
||||||
pass: env.SMTP_PASSWORD,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a SMTP config exists based on passed in parameters.
|
* Checks if a SMTP config exists based on passed in parameters.
|
||||||
* @return {Promise<boolean>} returns true if there is a configuration that can be used.
|
* @return {Promise<boolean>} returns true if there is a configuration that can be used.
|
||||||
*/
|
*/
|
||||||
export async function isEmailConfigured(workspaceId?: string) {
|
export async function isEmailConfigured() {
|
||||||
// when "testing" or smtp fallback is enabled simply return true
|
// when "testing" or smtp fallback is enabled simply return true
|
||||||
if (TEST_MODE || env.SMTP_FALLBACK_ENABLED) {
|
if (TEST_MODE || env.SMTP_FALLBACK_ENABLED) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
const db = tenancy.getGlobalDB()
|
const config = await configs.getSMTPConfig()
|
||||||
const config = await getSmtpConfiguration(db, workspaceId)
|
|
||||||
return config != null
|
return config != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,22 +161,17 @@ export async function sendEmail(
|
||||||
purpose: EmailTemplatePurpose,
|
purpose: EmailTemplatePurpose,
|
||||||
opts: SendEmailOpts
|
opts: SendEmailOpts
|
||||||
) {
|
) {
|
||||||
const db = tenancy.getGlobalDB()
|
const config = await configs.getSMTPConfig(opts?.automation)
|
||||||
let config =
|
if (!config && !TEST_MODE) {
|
||||||
(await getSmtpConfiguration(db, opts?.workspaceId, opts?.automation)) || {}
|
|
||||||
if (Object.keys(config).length === 0 && !TEST_MODE) {
|
|
||||||
throw "Unable to find SMTP configuration."
|
throw "Unable to find SMTP configuration."
|
||||||
}
|
}
|
||||||
const transport = createSMTPTransport(config)
|
const transport = createSMTPTransport(config)
|
||||||
// if there is a link code needed this will retrieve it
|
// if there is a link code needed this will retrieve it
|
||||||
const code = await getLinkCode(purpose, email, opts.user, opts?.info)
|
const code = await getLinkCode(purpose, email, opts.user, opts?.info)
|
||||||
let context
|
let context = await getSettingsTemplateContext(purpose, code)
|
||||||
if (code) {
|
|
||||||
context = await getSettingsTemplateContext(purpose, code)
|
|
||||||
}
|
|
||||||
|
|
||||||
let message: any = {
|
let message: any = {
|
||||||
from: opts?.from || config.from,
|
from: opts?.from || config?.from,
|
||||||
html: await buildEmail(purpose, email, context, {
|
html: await buildEmail(purpose, email, context, {
|
||||||
user: opts?.user,
|
user: opts?.user,
|
||||||
contents: opts?.contents,
|
contents: opts?.contents,
|
||||||
|
@ -231,9 +185,9 @@ export async function sendEmail(
|
||||||
bcc: opts?.bcc,
|
bcc: opts?.bcc,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts?.subject || config.subject) {
|
if (opts?.subject || config?.subject) {
|
||||||
message.subject = await processString(
|
message.subject = await processString(
|
||||||
opts?.subject || config.subject,
|
(opts?.subject || config?.subject) as string,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { db as dbCore, tenancy } from "@budibase/backend-core"
|
import { tenancy, configs } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
Config,
|
|
||||||
InternalTemplateBinding,
|
InternalTemplateBinding,
|
||||||
LOGO_URL,
|
LOGO_URL,
|
||||||
EmailTemplatePurpose,
|
EmailTemplatePurpose,
|
||||||
|
@ -10,20 +9,16 @@ const BASE_COMPANY = "Budibase"
|
||||||
|
|
||||||
export async function getSettingsTemplateContext(
|
export async function getSettingsTemplateContext(
|
||||||
purpose: EmailTemplatePurpose,
|
purpose: EmailTemplatePurpose,
|
||||||
code?: string
|
code?: string | null
|
||||||
) {
|
) {
|
||||||
const db = tenancy.getGlobalDB()
|
let settings = await configs.getSettingsConfig()
|
||||||
// TODO: use more granular settings in the future if required
|
|
||||||
let settings =
|
|
||||||
(await dbCore.getScopedConfig(db, { type: Config.SETTINGS })) || {}
|
|
||||||
const URL = settings.platformUrl
|
const URL = settings.platformUrl
|
||||||
const context: any = {
|
const context: any = {
|
||||||
[InternalTemplateBinding.LOGO_URL]:
|
[InternalTemplateBinding.LOGO_URL]:
|
||||||
checkSlashesInUrl(`${URL}/${settings.logoUrl}`) || LOGO_URL,
|
checkSlashesInUrl(`${URL}/${settings.logoUrl}`) || LOGO_URL,
|
||||||
[InternalTemplateBinding.PLATFORM_URL]: URL,
|
[InternalTemplateBinding.PLATFORM_URL]: URL,
|
||||||
[InternalTemplateBinding.COMPANY]: settings.company || BASE_COMPANY,
|
[InternalTemplateBinding.COMPANY]: settings.company || BASE_COMPANY,
|
||||||
[InternalTemplateBinding.DOCS_URL]:
|
[InternalTemplateBinding.DOCS_URL]: "https://docs.budibase.com/",
|
||||||
settings.docsUrl || "https://docs.budibase.com/",
|
|
||||||
[InternalTemplateBinding.LOGIN_URL]: checkSlashesInUrl(
|
[InternalTemplateBinding.LOGIN_URL]: checkSlashesInUrl(
|
||||||
tenancy.addTenantToUrl(`${URL}/login`)
|
tenancy.addTenantToUrl(`${URL}/login`)
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in New Issue