mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-23 00:16:25 +01:00
feat: Scheduled change conflict email templates and function (#5547)
Creates a new email template for scheduled change conflicts and a function to send it. Relates to: #[1-1686](https://linear.app/unleash/issue/1-1686/send-an-email-when-the-conflicts-are-detected)  --------- Signed-off-by: andreas-unleash <andreas@getunleash.ai> Co-authored-by: Thomas Heartman <thomas@getunleash.io>
This commit is contained in:
parent
da1a9d4036
commit
12f79f90bb
@ -1,10 +1,11 @@
|
|||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
import { EmailService } from './email-service';
|
import { EmailService } from './email-service';
|
||||||
import noLoggerProvider from '../../test/fixtures/no-logger';
|
import noLoggerProvider from '../../test/fixtures/no-logger';
|
||||||
|
import { IUnleashConfig } from '../types';
|
||||||
|
|
||||||
test('Can send reset email', async () => {
|
test('Can send reset email', async () => {
|
||||||
const emailService = new EmailService(
|
const emailService = new EmailService({
|
||||||
{
|
email: {
|
||||||
host: 'test',
|
host: 'test',
|
||||||
port: 587,
|
port: 587,
|
||||||
secure: false,
|
secure: false,
|
||||||
@ -12,8 +13,8 @@ test('Can send reset email', async () => {
|
|||||||
smtppass: '',
|
smtppass: '',
|
||||||
sender: 'noreply@getunleash.ai',
|
sender: 'noreply@getunleash.ai',
|
||||||
},
|
},
|
||||||
noLoggerProvider,
|
getLogger: noLoggerProvider,
|
||||||
);
|
} as unknown as IUnleashConfig);
|
||||||
const resetLinkUrl =
|
const resetLinkUrl =
|
||||||
'https://unleash-hosted.com/reset-password?token=$2b$10$M06Ysso6KL4ueH/xR6rdSuY5GSymdIwmIkEUJMRkB.Qn26r5Gi5vW';
|
'https://unleash-hosted.com/reset-password?token=$2b$10$M06Ysso6KL4ueH/xR6rdSuY5GSymdIwmIkEUJMRkB.Qn26r5Gi5vW';
|
||||||
|
|
||||||
@ -29,17 +30,17 @@ test('Can send reset email', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Can send welcome mail', async () => {
|
test('Can send welcome mail', async () => {
|
||||||
const emailService = new EmailService(
|
const emailService = new EmailService({
|
||||||
{
|
email: {
|
||||||
host: 'test',
|
host: 'test',
|
||||||
port: 9999,
|
port: 587,
|
||||||
secure: false,
|
secure: false,
|
||||||
sender: 'noreply@getunleash.ai',
|
|
||||||
smtpuser: '',
|
smtpuser: '',
|
||||||
smtppass: '',
|
smtppass: '',
|
||||||
|
sender: 'noreply@getunleash.ai',
|
||||||
},
|
},
|
||||||
noLoggerProvider,
|
getLogger: noLoggerProvider,
|
||||||
);
|
} as unknown as IUnleashConfig);
|
||||||
const content = await emailService.sendGettingStartedMail(
|
const content = await emailService.sendGettingStartedMail(
|
||||||
'Some username',
|
'Some username',
|
||||||
'test@test.com',
|
'test@test.com',
|
||||||
@ -52,8 +53,8 @@ test('Can send welcome mail', async () => {
|
|||||||
test('Can supply additional SMTP transport options', async () => {
|
test('Can supply additional SMTP transport options', async () => {
|
||||||
const spy = jest.spyOn(nodemailer, 'createTransport');
|
const spy = jest.spyOn(nodemailer, 'createTransport');
|
||||||
|
|
||||||
new EmailService(
|
new EmailService({
|
||||||
{
|
email: {
|
||||||
host: 'smtp.unleash.test',
|
host: 'smtp.unleash.test',
|
||||||
port: 9999,
|
port: 9999,
|
||||||
secure: false,
|
secure: false,
|
||||||
@ -64,8 +65,8 @@ test('Can supply additional SMTP transport options', async () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
noLoggerProvider,
|
getLogger: noLoggerProvider,
|
||||||
);
|
} as unknown as IUnleashConfig);
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith({
|
expect(spy).toHaveBeenCalledWith({
|
||||||
auth: {
|
auth: {
|
||||||
@ -82,17 +83,17 @@ test('Can supply additional SMTP transport options', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should strip special characters from email subject', async () => {
|
test('should strip special characters from email subject', async () => {
|
||||||
const emailService = new EmailService(
|
const emailService = new EmailService({
|
||||||
{
|
email: {
|
||||||
host: 'test',
|
host: 'test',
|
||||||
port: 9999,
|
port: 587,
|
||||||
secure: false,
|
secure: false,
|
||||||
sender: 'noreply@getunleash.ai',
|
|
||||||
smtpuser: '',
|
smtpuser: '',
|
||||||
smtppass: '',
|
smtppass: '',
|
||||||
|
sender: 'noreply@getunleash.ai',
|
||||||
},
|
},
|
||||||
noLoggerProvider,
|
getLogger: noLoggerProvider,
|
||||||
);
|
} as unknown as IUnleashConfig);
|
||||||
expect(emailService.stripSpecialCharacters('http://evil.com')).toBe(
|
expect(emailService.stripSpecialCharacters('http://evil.com')).toBe(
|
||||||
'httpevilcom',
|
'httpevilcom',
|
||||||
);
|
);
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { createTransport, Transporter } from 'nodemailer';
|
import { createTransport, Transporter } from 'nodemailer';
|
||||||
import Mustache from 'mustache';
|
import Mustache from 'mustache';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { readFileSync, existsSync } from 'fs';
|
import { existsSync, readFileSync } from 'fs';
|
||||||
import { Logger, LogProvider } from '../logger';
|
import { Logger } from '../logger';
|
||||||
import NotFoundError from '../error/notfound-error';
|
import NotFoundError from '../error/notfound-error';
|
||||||
import { IEmailOption } from '../types/option';
|
import { IUnleashConfig } from '../types/option';
|
||||||
|
|
||||||
export interface IAuthOptions {
|
export interface IAuthOptions {
|
||||||
user: string;
|
user: string;
|
||||||
@ -31,6 +31,8 @@ export interface IEmailEnvelope {
|
|||||||
|
|
||||||
const RESET_MAIL_SUBJECT = 'Unleash - Reset your password';
|
const RESET_MAIL_SUBJECT = 'Unleash - Reset your password';
|
||||||
const GETTING_STARTED_SUBJECT = 'Welcome to Unleash';
|
const GETTING_STARTED_SUBJECT = 'Welcome to Unleash';
|
||||||
|
const SCHEDULED_CHANGE_CONFLICT_SUBJECT =
|
||||||
|
'Unleash - Scheduled changes can no longer be applied';
|
||||||
const SCHEDULED_EXECUTION_FAILED_SUBJECT =
|
const SCHEDULED_EXECUTION_FAILED_SUBJECT =
|
||||||
'Unleash - Scheduled change request could not be applied';
|
'Unleash - Scheduled change request could not be applied';
|
||||||
|
|
||||||
@ -38,13 +40,16 @@ export const MAIL_ACCEPTED = '250 Accepted';
|
|||||||
|
|
||||||
export class EmailService {
|
export class EmailService {
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
private config: IUnleashConfig;
|
||||||
|
|
||||||
private readonly mailer?: Transporter;
|
private readonly mailer?: Transporter;
|
||||||
|
|
||||||
private readonly sender: string;
|
private readonly sender: string;
|
||||||
|
|
||||||
constructor(email: IEmailOption | undefined, getLogger: LogProvider) {
|
constructor(config: IUnleashConfig) {
|
||||||
this.logger = getLogger('services/email-service.ts');
|
this.config = config;
|
||||||
|
this.logger = config.getLogger('services/email-service.ts');
|
||||||
|
const { email } = config;
|
||||||
if (email?.host) {
|
if (email?.host) {
|
||||||
this.sender = email.sender;
|
this.sender = email.sender;
|
||||||
if (email.host === 'test') {
|
if (email.host === 'test') {
|
||||||
@ -138,6 +143,95 @@ export class EmailService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendScheduledChangeConflictEmail(
|
||||||
|
recipient: string,
|
||||||
|
conflictScope: 'flag' | 'strategy',
|
||||||
|
changeRequests: {
|
||||||
|
id: number;
|
||||||
|
scheduledAt: string;
|
||||||
|
link: string;
|
||||||
|
title?: string;
|
||||||
|
}[],
|
||||||
|
flagName: string,
|
||||||
|
project: string,
|
||||||
|
strategyId?: string,
|
||||||
|
) {
|
||||||
|
if (this.configured()) {
|
||||||
|
const year = new Date().getFullYear();
|
||||||
|
const conflict =
|
||||||
|
conflictScope === 'flag'
|
||||||
|
? `The feature flag ${flagName} in ${project} has been archived`
|
||||||
|
: `The strategy with id ${strategyId} for flag ${flagName} in ${project} has been deleted`;
|
||||||
|
|
||||||
|
const conflictResolution =
|
||||||
|
conflictScope === 'flag'
|
||||||
|
? ' unless the flag is revived'
|
||||||
|
: false;
|
||||||
|
|
||||||
|
const conflictResolutionLink = conflictResolution
|
||||||
|
? `${this.config.server.baseUriPath}/projects/${project}/archive?sort=archivedAt&search=${flagName}`
|
||||||
|
: false;
|
||||||
|
|
||||||
|
const bodyHtml = await this.compileTemplate(
|
||||||
|
'scheduled-change-conflict',
|
||||||
|
TemplateFormat.HTML,
|
||||||
|
{
|
||||||
|
conflict,
|
||||||
|
conflictScope,
|
||||||
|
conflictResolution,
|
||||||
|
conflictResolutionLink,
|
||||||
|
changeRequests,
|
||||||
|
year,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const bodyText = await this.compileTemplate(
|
||||||
|
'scheduled-change-conflict',
|
||||||
|
TemplateFormat.PLAIN,
|
||||||
|
{
|
||||||
|
conflict,
|
||||||
|
conflictScope,
|
||||||
|
conflictResolution,
|
||||||
|
conflictResolutionLink,
|
||||||
|
changeRequests,
|
||||||
|
year,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const email = {
|
||||||
|
from: this.sender,
|
||||||
|
to: recipient,
|
||||||
|
subject: SCHEDULED_CHANGE_CONFLICT_SUBJECT,
|
||||||
|
html: bodyHtml,
|
||||||
|
text: bodyText,
|
||||||
|
};
|
||||||
|
process.nextTick(() => {
|
||||||
|
this.mailer!.sendMail(email).then(
|
||||||
|
() =>
|
||||||
|
this.logger.info(
|
||||||
|
'Successfully sent scheduled-change-conflict email',
|
||||||
|
),
|
||||||
|
(e) =>
|
||||||
|
this.logger.warn(
|
||||||
|
'Failed to send scheduled-change-conflict email',
|
||||||
|
e,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return Promise.resolve(email);
|
||||||
|
}
|
||||||
|
return new Promise((res) => {
|
||||||
|
this.logger.warn(
|
||||||
|
'No mailer is configured. Please read the docs on how to configure an email service',
|
||||||
|
);
|
||||||
|
res({
|
||||||
|
from: this.sender,
|
||||||
|
to: recipient,
|
||||||
|
subject: SCHEDULED_CHANGE_CONFLICT_SUBJECT,
|
||||||
|
html: '',
|
||||||
|
text: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async sendResetMail(
|
async sendResetMail(
|
||||||
name: string,
|
name: string,
|
||||||
recipient: string,
|
recipient: string,
|
||||||
|
@ -140,7 +140,7 @@ export const createServices = (
|
|||||||
eventService,
|
eventService,
|
||||||
privateProjectChecker,
|
privateProjectChecker,
|
||||||
);
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const featureTypeService = new FeatureTypeService(
|
const featureTypeService = new FeatureTypeService(
|
||||||
stores,
|
stores,
|
||||||
config,
|
config,
|
||||||
|
@ -32,7 +32,7 @@ test('Should create new user', async () => {
|
|||||||
);
|
);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
{ eventStore, featureTagStore: new FakeFeatureTagStore() },
|
{ eventStore, featureTagStore: new FakeFeatureTagStore() },
|
||||||
config,
|
config,
|
||||||
@ -78,7 +78,7 @@ test('Should create default user - with defaults', async () => {
|
|||||||
{ resetTokenStore },
|
{ resetTokenStore },
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
@ -117,7 +117,7 @@ test('Should create default user - with provided username and password', async (
|
|||||||
{ resetTokenStore },
|
{ resetTokenStore },
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
@ -161,7 +161,7 @@ test('Should not create default user - with `createAdminUser` === false', async
|
|||||||
{ resetTokenStore },
|
{ resetTokenStore },
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
@ -210,7 +210,7 @@ test('Should be a valid password', async () => {
|
|||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
|
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
@ -248,7 +248,7 @@ test('Password must be at least 10 chars', async () => {
|
|||||||
{ resetTokenStore },
|
{ resetTokenStore },
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
@ -288,7 +288,7 @@ test('The password must contain at least one uppercase letter.', async () => {
|
|||||||
{ resetTokenStore },
|
{ resetTokenStore },
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
@ -330,7 +330,7 @@ test('The password must contain at least one number', async () => {
|
|||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
|
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
@ -371,7 +371,7 @@ test('The password must contain at least one special character', async () => {
|
|||||||
{ resetTokenStore },
|
{ resetTokenStore },
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
@ -412,7 +412,7 @@ test('Should be a valid password with special chars', async () => {
|
|||||||
{ resetTokenStore },
|
{ resetTokenStore },
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
@ -450,7 +450,7 @@ test('Should send password reset email if user exists', async () => {
|
|||||||
{ resetTokenStore },
|
{ resetTokenStore },
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
@ -504,7 +504,7 @@ test('Should throttle password reset email', async () => {
|
|||||||
{ resetTokenStore },
|
{ resetTokenStore },
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new FakeSessionStore();
|
const sessionStore = new FakeSessionStore();
|
||||||
const sessionService = new SessionService({ sessionStore }, config);
|
const sessionService = new SessionService({ sessionStore }, config);
|
||||||
const eventService = new EventService(
|
const eventService = new EventService(
|
||||||
|
@ -0,0 +1,527 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<title>*|MC:SUBJECT|*</title>
|
||||||
|
<style type="text/css">
|
||||||
|
/* /\/\/\/\/\/\/\/\/ CLIENT-SPECIFIC STYLES /\/\/\/\/\/\/\/\/ */
|
||||||
|
#outlook a{padding:0;} /* Force Outlook to provide a "view in browser" message */
|
||||||
|
.ReadMsgBody{width:100%;} .ExternalClass{width:100%;} /* Force Hotmail to display emails at full width */
|
||||||
|
.ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;} /* Force Hotmail to display normal line spacing */
|
||||||
|
body, table, td, p, a, li, blockquote{-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%;} /* Prevent WebKit and Windows mobile changing default text sizes */
|
||||||
|
table, td{mso-table-lspace:0pt; mso-table-rspace:0pt;} /* Remove spacing between tables in Outlook 2007 and up */
|
||||||
|
img{-ms-interpolation-mode:bicubic;} /* Allow smoother rendering of resized image in Internet Explorer */
|
||||||
|
|
||||||
|
/* /\/\/\/\/\/\/\/\/ RESET STYLES /\/\/\/\/\/\/\/\/ */
|
||||||
|
body{margin:0; padding:0;}
|
||||||
|
img{border:0; height:auto; line-height:100%; outline:none; text-decoration:none;}
|
||||||
|
table{border-collapse:collapse !important;}
|
||||||
|
body, #bodyTable, #bodyCell{height:100% !important; margin:0; padding:0; width:100% !important;}
|
||||||
|
|
||||||
|
/* /\/\/\/\/\/\/\/\/ TEMPLATE STYLES /\/\/\/\/\/\/\/\/ */
|
||||||
|
|
||||||
|
/* ========== Page Styles ========== */
|
||||||
|
|
||||||
|
#bodyCell{padding:20px;}
|
||||||
|
#templateContainer{width:600px;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Page
|
||||||
|
* @section background style
|
||||||
|
* @tip Set the background color and top border for your email. You may want to choose colors that match your company's branding.
|
||||||
|
* @theme page
|
||||||
|
*/
|
||||||
|
body, #bodyTable{
|
||||||
|
/*@editable*/ background-color:#DEE0E2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Page
|
||||||
|
* @section background style
|
||||||
|
* @tip Set the background color and top border for your email. You may want to choose colors that match your company's branding.
|
||||||
|
* @theme page
|
||||||
|
*/
|
||||||
|
#bodyCell{
|
||||||
|
/*@editable*/ border-top:4px solid #BBBBBB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Page
|
||||||
|
* @section email border
|
||||||
|
* @tip Set the border for your email.
|
||||||
|
*/
|
||||||
|
#templateContainer{
|
||||||
|
/*@editable*/ border:1px solid #BBBBBB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Page
|
||||||
|
* @section heading 1
|
||||||
|
* @tip Set the styling for all first-level headings in your emails. These should be the largest of your headings.
|
||||||
|
* @style heading 1
|
||||||
|
*/
|
||||||
|
h1{
|
||||||
|
/*@editable*/ color:#202020 !important;
|
||||||
|
display:block;
|
||||||
|
/*@editable*/ font-family:Helvetica;
|
||||||
|
/*@editable*/ font-size:26px;
|
||||||
|
/*@editable*/ font-style:normal;
|
||||||
|
/*@editable*/ font-weight:bold;
|
||||||
|
/*@editable*/ line-height:100%;
|
||||||
|
/*@editable*/ letter-spacing:normal;
|
||||||
|
margin-top:0;
|
||||||
|
margin-right:0;
|
||||||
|
margin-bottom:10px;
|
||||||
|
margin-left:0;
|
||||||
|
/*@editable*/ text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Page
|
||||||
|
* @section heading 2
|
||||||
|
* @tip Set the styling for all second-level headings in your emails.
|
||||||
|
* @style heading 2
|
||||||
|
*/
|
||||||
|
h2{
|
||||||
|
/*@editable*/ color:#404040 !important;
|
||||||
|
display:block;
|
||||||
|
/*@editable*/ font-family:Helvetica;
|
||||||
|
/*@editable*/ font-size:20px;
|
||||||
|
/*@editable*/ font-style:normal;
|
||||||
|
/*@editable*/ font-weight:bold;
|
||||||
|
/*@editable*/ line-height:100%;
|
||||||
|
/*@editable*/ letter-spacing:normal;
|
||||||
|
margin-top:0;
|
||||||
|
margin-right:0;
|
||||||
|
margin-bottom:10px;
|
||||||
|
margin-left:0;
|
||||||
|
/*@editable*/ text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Page
|
||||||
|
* @section heading 3
|
||||||
|
* @tip Set the styling for all third-level headings in your emails.
|
||||||
|
* @style heading 3
|
||||||
|
*/
|
||||||
|
h3{
|
||||||
|
/*@editable*/ color:#606060 !important;
|
||||||
|
display:block;
|
||||||
|
/*@editable*/ font-family:Helvetica;
|
||||||
|
/*@editable*/ font-size:16px;
|
||||||
|
/*@editable*/ font-style:italic;
|
||||||
|
/*@editable*/ font-weight:normal;
|
||||||
|
/*@editable*/ line-height:100%;
|
||||||
|
/*@editable*/ letter-spacing:normal;
|
||||||
|
margin-top:0;
|
||||||
|
margin-right:0;
|
||||||
|
margin-bottom:10px;
|
||||||
|
margin-left:0;
|
||||||
|
/*@editable*/ text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Page
|
||||||
|
* @section heading 4
|
||||||
|
* @tip Set the styling for all fourth-level headings in your emails. These should be the smallest of your headings.
|
||||||
|
* @style heading 4
|
||||||
|
*/
|
||||||
|
h4{
|
||||||
|
/*@editable*/ color:#808080 !important;
|
||||||
|
display:block;
|
||||||
|
/*@editable*/ font-family:Helvetica;
|
||||||
|
/*@editable*/ font-size:14px;
|
||||||
|
/*@editable*/ font-style:italic;
|
||||||
|
/*@editable*/ font-weight:normal;
|
||||||
|
/*@editable*/ line-height:100%;
|
||||||
|
/*@editable*/ letter-spacing:normal;
|
||||||
|
margin-top:0;
|
||||||
|
margin-right:0;
|
||||||
|
margin-bottom:10px;
|
||||||
|
margin-left:0;
|
||||||
|
/*@editable*/ text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== Header Styles ========== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Header
|
||||||
|
* @section preheader style
|
||||||
|
* @tip Set the background color and bottom border for your email's preheader area.
|
||||||
|
* @theme header
|
||||||
|
*/
|
||||||
|
#templatePreheader{
|
||||||
|
/*@editable*/ background-color:#fff;
|
||||||
|
/*@editable*/ border-bottom:1px solid #CCCCCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Header
|
||||||
|
* @section preheader text
|
||||||
|
* @tip Set the styling for your email's preheader text. Choose a size and color that is easy to read.
|
||||||
|
*/
|
||||||
|
.preheaderContent{
|
||||||
|
/*@editable*/ color:#808080;
|
||||||
|
/*@editable*/ font-family:Helvetica;
|
||||||
|
/*@editable*/ font-size:10px;
|
||||||
|
/*@editable*/ line-height:125%;
|
||||||
|
/*@editable*/ text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Header
|
||||||
|
* @section preheader link
|
||||||
|
* @tip Set the styling for your email's preheader links. Choose a color that helps them stand out from your text.
|
||||||
|
*/
|
||||||
|
.preheaderContent a:link, .preheaderContent a:visited, /* Yahoo! Mail Override */ .preheaderContent a .yshortcuts /* Yahoo! Mail Override */{
|
||||||
|
/*@editable*/ color:#fff;
|
||||||
|
/*@editable*/ font-weight:normal;
|
||||||
|
/*@editable*/ text-decoration:underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Header
|
||||||
|
* @section header style
|
||||||
|
* @tip Set the background color and borders for your email's header area.
|
||||||
|
* @theme header
|
||||||
|
*/
|
||||||
|
#templateHeader{
|
||||||
|
/*@editable*/ background-color:#fff;
|
||||||
|
/*@editable*/ border-top:1px solid #FFFFFF;
|
||||||
|
/*@editable*/ border-bottom:1px solid #CCCCCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Header
|
||||||
|
* @section header text
|
||||||
|
* @tip Set the styling for your email's header text. Choose a size and color that is easy to read.
|
||||||
|
*/
|
||||||
|
.headerContent{
|
||||||
|
/*@editable*/ color:#505050;
|
||||||
|
/*@editable*/ font-family:Helvetica;
|
||||||
|
/*@editable*/ font-size:20px;
|
||||||
|
/*@editable*/ font-weight:bold;
|
||||||
|
/*@editable*/ line-height:100%;
|
||||||
|
/*@editable*/ padding-top:0;
|
||||||
|
/*@editable*/ padding-right:0;
|
||||||
|
/*@editable*/ padding-bottom:0;
|
||||||
|
/*@editable*/ padding-left:0;
|
||||||
|
/*@editable*/ text-align:left;
|
||||||
|
/*@editable*/ vertical-align:middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Header
|
||||||
|
* @section header link
|
||||||
|
* @tip Set the styling for your email's header links. Choose a color that helps them stand out from your text.
|
||||||
|
*/
|
||||||
|
.headerContent a:link, .headerContent a:visited, /* Yahoo! Mail Override */ .headerContent a .yshortcuts /* Yahoo! Mail Override */{
|
||||||
|
/*@editable*/ color:#fff;
|
||||||
|
/*@editable*/ font-weight:normal;
|
||||||
|
/*@editable*/ text-decoration:underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#headerImage{
|
||||||
|
height:auto;
|
||||||
|
max-width:600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== Body Styles ========== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Body
|
||||||
|
* @section body style
|
||||||
|
* @tip Set the background color and borders for your email's body area.
|
||||||
|
*/
|
||||||
|
#templateBody{
|
||||||
|
/*@editable*/ background-color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Body
|
||||||
|
* @section body text
|
||||||
|
* @tip Set the styling for your email's main content text. Choose a size and color that is easy to read.
|
||||||
|
* @theme main
|
||||||
|
*/
|
||||||
|
.bodyContent{
|
||||||
|
/*@editable*/ color:#505050;
|
||||||
|
/*@editable*/ font-family:Helvetica;
|
||||||
|
/*@editable*/ font-size:14px;
|
||||||
|
/*@editable*/ line-height:150%;
|
||||||
|
/*@editable*/ border-bottom: 1px solid #CCCCCC;
|
||||||
|
padding-top:20px;
|
||||||
|
padding-right:20px;
|
||||||
|
padding-bottom:20px;
|
||||||
|
padding-left:20px;
|
||||||
|
/*@editable*/ text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodyContent img{
|
||||||
|
display:inline;
|
||||||
|
height:auto;
|
||||||
|
max-width:560px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changeRequestLink {
|
||||||
|
text-decoration: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changeRequestLink:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== Footer Styles ========== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Footer
|
||||||
|
* @section footer style
|
||||||
|
* @tip Set the background color and borders for your email's footer area.
|
||||||
|
* @theme footer
|
||||||
|
*/
|
||||||
|
#templateFooter{
|
||||||
|
/*@editable*/ background-color:#fff;
|
||||||
|
/*@editable*/ border-top:1px solid #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Footer
|
||||||
|
* @section footer text
|
||||||
|
* @tip Set the styling for your email's footer text. Choose a size and color that is easy to read.
|
||||||
|
* @theme footer
|
||||||
|
*/
|
||||||
|
.footerContent{
|
||||||
|
/*@editable*/ color:#808080;
|
||||||
|
/*@editable*/ font-family:Helvetica;
|
||||||
|
/*@editable*/ font-size:10px;
|
||||||
|
/*@editable*/ line-height:150%;
|
||||||
|
padding-top:20px;
|
||||||
|
padding-right:20px;
|
||||||
|
padding-bottom:20px;
|
||||||
|
padding-left:20px;
|
||||||
|
/*@editable*/ text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Footer
|
||||||
|
* @section footer link
|
||||||
|
* @tip Set the styling for your email's footer links. Choose a color that helps them stand out from your text.
|
||||||
|
*/
|
||||||
|
.footerContent a:link, .footerContent a:visited, /* Yahoo! Mail Override */ .footerContent a .yshortcuts, .footerContent a span /* Yahoo! Mail Override */{
|
||||||
|
/*@editable*/ color:#606060;
|
||||||
|
/*@editable*/ font-weight:normal;
|
||||||
|
/*@editable*/ text-decoration:underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* /\/\/\/\/\/\/\/\/ MOBILE STYLES /\/\/\/\/\/\/\/\/ */
|
||||||
|
|
||||||
|
@media only screen and (max-width: 480px){
|
||||||
|
/* /\/\/\/\/\/\/ CLIENT-SPECIFIC MOBILE STYLES /\/\/\/\/\/\/ */
|
||||||
|
body, table, td, p, a, li, blockquote{-webkit-text-size-adjust:none !important;} /* Prevent Webkit platforms from changing default text sizes */
|
||||||
|
body{width:100% !important; min-width:100% !important;} /* Prevent iOS Mail from adding padding to the body */
|
||||||
|
|
||||||
|
/* /\/\/\/\/\/\/ MOBILE RESET STYLES /\/\/\/\/\/\/ */
|
||||||
|
#bodyCell{padding:10px !important;}
|
||||||
|
|
||||||
|
/* /\/\/\/\/\/\/ MOBILE TEMPLATE STYLES /\/\/\/\/\/\/ */
|
||||||
|
|
||||||
|
/* ======== Page Styles ======== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Mobile Styles
|
||||||
|
* @section template width
|
||||||
|
* @tip Make the template fluid for portrait or landscape view adaptability. If a fluid layout doesn't work for you, set the width to 300px instead.
|
||||||
|
*/
|
||||||
|
#templateContainer{
|
||||||
|
max-width:600px !important;
|
||||||
|
/*@editable*/ width:100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Mobile Styles
|
||||||
|
* @section heading 1
|
||||||
|
* @tip Make the first-level headings larger in size for better readability on small screens.
|
||||||
|
*/
|
||||||
|
h1{
|
||||||
|
/*@editable*/ font-size:24px !important;
|
||||||
|
/*@editable*/ line-height:100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Mobile Styles
|
||||||
|
* @section heading 2
|
||||||
|
* @tip Make the second-level headings larger in size for better readability on small screens.
|
||||||
|
*/
|
||||||
|
h2{
|
||||||
|
/*@editable*/ font-size:20px !important;
|
||||||
|
/*@editable*/ line-height:100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Mobile Styles
|
||||||
|
* @section heading 3
|
||||||
|
* @tip Make the third-level headings larger in size for better readability on small screens.
|
||||||
|
*/
|
||||||
|
h3{
|
||||||
|
/*@editable*/ font-size:18px !important;
|
||||||
|
/*@editable*/ line-height:100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Mobile Styles
|
||||||
|
* @section heading 4
|
||||||
|
* @tip Make the fourth-level headings larger in size for better readability on small screens.
|
||||||
|
*/
|
||||||
|
h4{
|
||||||
|
/*@editable*/ font-size:16px !important;
|
||||||
|
/*@editable*/ line-height:100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======== Header Styles ======== */
|
||||||
|
|
||||||
|
#templatePreheader{display:none !important;} /* Hide the template preheader to save space */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Mobile Styles
|
||||||
|
* @section header image
|
||||||
|
* @tip Make the main header image fluid for portrait or landscape view adaptability, and set the image's original width as the max-width. If a fluid setting doesn't work, set the image width to half its original size instead.
|
||||||
|
*/
|
||||||
|
#headerImage{
|
||||||
|
height:auto !important;
|
||||||
|
/*@editable*/ max-width:600px !important;
|
||||||
|
/*@editable*/ width:100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Mobile Styles
|
||||||
|
* @section header text
|
||||||
|
* @tip Make the header content text larger in size for better readability on small screens. We recommend a font size of at least 16px.
|
||||||
|
*/
|
||||||
|
.headerContent{
|
||||||
|
/*@editable*/ font-size:20px !important;
|
||||||
|
/*@editable*/ line-height:125% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======== Body Styles ======== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Mobile Styles
|
||||||
|
* @section body text
|
||||||
|
* @tip Make the body content text larger in size for better readability on small screens. We recommend a font size of at least 16px.
|
||||||
|
*/
|
||||||
|
.bodyContent{
|
||||||
|
/*@editable*/ font-size:18px !important;
|
||||||
|
/*@editable*/ line-height:125% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======== Footer Styles ======== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tab Mobile Styles
|
||||||
|
* @section footer text
|
||||||
|
* @tip Make the body content text larger in size for better readability on small screens.
|
||||||
|
*/
|
||||||
|
.footerContent{
|
||||||
|
/*@editable*/ font-size:14px !important;
|
||||||
|
/*@editable*/ line-height:115% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footerContent a{display:block !important;} /* Place footer social and utility links on their own lines, for easier access */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body leftmargin="0" marginwidth="0" topmargin="0" marginheight="0" offset="0">
|
||||||
|
<center>
|
||||||
|
<table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top" id="bodyCell">
|
||||||
|
<!-- BEGIN TEMPLATE // -->
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" id="templateContainer">
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top">
|
||||||
|
<!-- BEGIN PREHEADER // -->
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" id="templatePreheader">
|
||||||
|
<tr>
|
||||||
|
<td valign="top" class="preheaderContent" style="padding-top:10px; padding-right:20px; padding-bottom:10px; padding-left:20px;" mc:edit="preheader_content00">
|
||||||
|
Scheduled changes can no longer be applied
|
||||||
|
</td>
|
||||||
|
<!-- *|IFNOT:ARCHIVE_PAGE|* -->
|
||||||
|
<td valign="top" width="180" class="preheaderContent" style="padding-top:10px; padding-right:20px; padding-bottom:10px; padding-left:0;" mc:edit="preheader_content01">
|
||||||
|
Email not displaying correctly?<br /><a href="*|ARCHIVE|*" target="_blank">View it in your browser</a>.
|
||||||
|
</td>
|
||||||
|
<!-- *|END:IF|* -->
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<!-- // END PREHEADER -->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top">
|
||||||
|
<!-- BEGIN HEADER // -->
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" id="templateHeader">
|
||||||
|
<tr>
|
||||||
|
<td valign="top" class="headerContent">
|
||||||
|
<img src="https://cdn.getunleash.io/unleash_logo_600.png" style="max-width:600px;padding:1rem;" id="headerImage" mc:label="header_image" mc:edit="header_image" mc:allowdesigner mc:allowtext />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<!-- // END HEADER -->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top">
|
||||||
|
<!-- BEGIN BODY // -->
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" id="templateBody">
|
||||||
|
<tr>
|
||||||
|
<td valign="top" class="bodyContent" mc:edit="body_content">
|
||||||
|
<h1>Conflict detected in a scheduled change</h1>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
{{ conflict }}. Scheduled change requests that use this {{ conflictScope }} can no longer be applied and their scheduled applications will fail{{#conflictResolution}}<span><a class="changeRequestLink" href="{{{ conflictResolutionLink }}}" target="_blank" rel="noopener noreferrer">{{.}}</a></span>{{/conflictResolution}}{{^conflictResolution}}.{{/conflictResolution}}
|
||||||
|
<br />
|
||||||
|
For you, this concerns change requests:
|
||||||
|
<br />
|
||||||
|
{{#changeRequests}}
|
||||||
|
<ul>
|
||||||
|
<li><span><a class="changeRequestLink" href="{{{ link }}}" target="_blank" rel="noopener noreferrer">#{{id}} {{#title}}- {{.}}{{/title}} (scheduled for {{scheduledAt}})</a></span></li>
|
||||||
|
</ul>
|
||||||
|
{{/changeRequests}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<!-- // END BODY -->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top">
|
||||||
|
<!-- BEGIN FOOTER // -->
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" id="templateFooter">
|
||||||
|
<tr>
|
||||||
|
<td valign="top" class="footerContent" mc:edit="footer_content00">
|
||||||
|
<a href="https://github.com/Unleash/unleash">Follow us on Github</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td valign="top" class="footerContent" style="padding-top:0;" mc:edit="footer_content01">
|
||||||
|
<em>Copyright © {{ year }} | Bricks Software | All rights reserved.</em>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<strong>Our mailing address is: team@getunleash.io</strong>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<!-- // END FOOTER -->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<!-- // END TEMPLATE -->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</center>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,9 @@
|
|||||||
|
Scheduled changes can no longer be applied
|
||||||
|
|
||||||
|
{{ conflict }}. Scheduled change requests that use this {{ conflictScope }} can no longer be applied and their scheduled applications will fail{{#conflictResolution}}{{.}} ({{conflictResolutionLink}}){{/conflictResolution}}{{^conflictResolution}}.{{/conflictResolution}}
|
||||||
|
|
||||||
|
For you, this concerns change requests:
|
||||||
|
|
||||||
|
{{#changeRequests}}
|
||||||
|
- # {{id}} - {{#title}}- {{.}}{{/title}} (scheduled for {{scheduledAt}}) ({{link}})
|
||||||
|
{{/changeRequests}}
|
@ -57,7 +57,7 @@ beforeAll(async () => {
|
|||||||
groupService,
|
groupService,
|
||||||
eventService,
|
eventService,
|
||||||
);
|
);
|
||||||
const emailService = new EmailService(config.email, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionStore = new SessionStore(
|
const sessionStore = new SessionStore(
|
||||||
db,
|
db,
|
||||||
new EventEmitter(),
|
new EventEmitter(),
|
||||||
|
@ -44,7 +44,7 @@ beforeAll(async () => {
|
|||||||
eventService,
|
eventService,
|
||||||
);
|
);
|
||||||
const resetTokenService = new ResetTokenService(stores, config);
|
const resetTokenService = new ResetTokenService(stores, config);
|
||||||
const emailService = new EmailService(undefined, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const sessionService = new SessionService(stores, config);
|
const sessionService = new SessionService(stores, config);
|
||||||
const settingService = new SettingService(stores, config, eventService);
|
const settingService = new SettingService(stores, config, eventService);
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ beforeAll(async () => {
|
|||||||
);
|
);
|
||||||
resetTokenService = new ResetTokenService(stores, config);
|
resetTokenService = new ResetTokenService(stores, config);
|
||||||
sessionService = new SessionService(stores, config);
|
sessionService = new SessionService(stores, config);
|
||||||
const emailService = new EmailService(undefined, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
const settingService = new SettingService(
|
const settingService = new SettingService(
|
||||||
{
|
{
|
||||||
settingStore: new FakeSettingStore(),
|
settingStore: new FakeSettingStore(),
|
||||||
|
@ -43,7 +43,7 @@ beforeAll(async () => {
|
|||||||
eventService,
|
eventService,
|
||||||
);
|
);
|
||||||
const resetTokenService = new ResetTokenService(stores, config);
|
const resetTokenService = new ResetTokenService(stores, config);
|
||||||
const emailService = new EmailService(undefined, config.getLogger);
|
const emailService = new EmailService(config);
|
||||||
sessionService = new SessionService(stores, config);
|
sessionService = new SessionService(stores, config);
|
||||||
settingService = new SettingService(stores, config, eventService);
|
settingService = new SettingService(stores, config, eventService);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user