0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00

refactor(connector): refactor passwordless connector

This commit is contained in:
Darcy Ye 2022-07-12 15:41:12 +08:00
parent e09a4703cc
commit 0439ce9dc8
No known key found for this signature in database
GPG key ID: B46F4C07EDEFC610
9 changed files with 142 additions and 35 deletions

View file

@ -2,6 +2,7 @@ import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
EmailMessageTypes,
EmailSendMessageFunction,
ValidateConfig,
EmailConnector,
@ -32,12 +33,30 @@ export default class AliyunDmConnector implements EmailConnector {
}
};
// eslint-disable-next-line complexity
public sendMessage: EmailSendMessageFunction = async (address, type, data, config) => {
const emailConfig =
(config as AliyunDmConfig | undefined) ?? (await this.getConfig(this.metadata.id));
public sendMessage: EmailSendMessageFunction = async (address, type, data) => {
const emailConfig = await this.getConfig(this.metadata.id);
await this.validateConfig(emailConfig);
const { accessKeyId, accessKeySecret, accountName, fromAlias, templates } = emailConfig;
return this.sendMessageCore(address, type, data, emailConfig);
};
public sendTestMessage: EmailSendMessageFunction = async (address, type, data, config) => {
if (!config) {
throw new ConnectorError(ConnectorErrorCodes.InsufficientRequestParameters);
}
await this.validateConfig(config);
return this.sendMessageCore(address, type, data, config as AliyunDmConfig);
};
private readonly sendMessageCore = async (
address: string,
type: keyof EmailMessageTypes,
data: EmailMessageTypes[typeof type],
config: AliyunDmConfig
) => {
const { accessKeyId, accessKeySecret, accountName, fromAlias, templates } = config;
const template = templates.find((template) => template.usageType === type);
assert(

View file

@ -2,6 +2,7 @@ import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
SmsMessageTypes,
SmsSendMessageFunction,
ValidateConfig,
SmsConnector,
@ -27,11 +28,30 @@ export default class AliyunSmsConnector implements SmsConnector {
}
};
public sendMessage: SmsSendMessageFunction = async (phone, type, { code }, config) => {
const smsConfig =
(config as AliyunSmsConfig | undefined) ?? (await this.getConfig(this.metadata.id));
public sendMessage: SmsSendMessageFunction = async (phone, type, { code }) => {
const smsConfig = await this.getConfig(this.metadata.id);
await this.validateConfig(smsConfig);
const { accessKeyId, accessKeySecret, signName, templates } = smsConfig;
return this.sendMessageCore(phone, type, { code }, smsConfig);
};
public sendTestMessage: SmsSendMessageFunction = async (phone, type, { code }, config) => {
if (!config) {
throw new ConnectorError(ConnectorErrorCodes.InsufficientRequestParameters);
}
await this.validateConfig(config);
return this.sendMessageCore(phone, type, { code }, config as AliyunSmsConfig);
};
private readonly sendMessageCore = async (
phone: string,
type: keyof SmsMessageTypes,
data: SmsMessageTypes[typeof type],
config: AliyunSmsConfig
) => {
const { accessKeyId, accessKeySecret, signName, templates } = config;
const template = templates.find(({ usageType }) => usageType === type);
assert(
@ -46,7 +66,7 @@ export default class AliyunSmsConnector implements SmsConnector {
PhoneNumbers: phone,
SignName: signName,
TemplateCode: template.templateCode,
TemplateParam: JSON.stringify({ code }),
TemplateParam: JSON.stringify(data),
},
accessKeySecret
);

View file

@ -2,6 +2,7 @@ import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
EmailMessageTypes,
EmailSendMessageFunction,
ValidateConfig,
EmailConnector,
@ -33,12 +34,30 @@ export default class SendGridMailConnector implements EmailConnector {
}
};
// eslint-disable-next-line complexity
public sendMessage: EmailSendMessageFunction = async (address, type, data, config) => {
const emailConfig =
(config as SendGridMailConfig | undefined) ?? (await this.getConfig(this.metadata.id));
public sendMessage: EmailSendMessageFunction = async (address, type, data) => {
const emailConfig = await this.getConfig(this.metadata.id);
await this.validateConfig(emailConfig);
const { apiKey, fromEmail, fromName, templates } = emailConfig;
return this.sendMessageCore(address, type, data, emailConfig);
};
public sendTestMessage: EmailSendMessageFunction = async (address, type, data, config) => {
if (!config) {
throw new ConnectorError(ConnectorErrorCodes.InsufficientRequestParameters);
}
await this.validateConfig(config);
return this.sendMessageCore(address, type, data, config as SendGridMailConfig);
};
private readonly sendMessageCore = async (
address: string,
type: keyof EmailMessageTypes,
data: EmailMessageTypes[typeof type],
config: SendGridMailConfig
) => {
const { apiKey, fromEmail, fromName, templates } = config;
const template = templates.find((template) => template.usageType === type);
assert(

View file

@ -2,6 +2,7 @@ import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
EmailMessageTypes,
EmailSendMessageFunction,
ValidateConfig,
EmailConnector,
@ -27,11 +28,30 @@ export default class SmtpConnector implements EmailConnector {
}
};
public sendMessage: EmailSendMessageFunction = async (address, type, data, config) => {
const emailConfig =
(config as SmtpConfig | undefined) ?? (await this.getConfig(this.metadata.id));
public sendMessage: EmailSendMessageFunction = async (address, type, data) => {
const emailConfig = await this.getConfig(this.metadata.id);
await this.validateConfig(emailConfig);
const { host, port, username, password, fromEmail, replyTo, templates } = emailConfig;
return this.sendMessageCore(address, type, data, emailConfig);
};
public sendTestMessage: EmailSendMessageFunction = async (address, type, data, config) => {
if (!config) {
throw new ConnectorError(ConnectorErrorCodes.InsufficientRequestParameters);
}
await this.validateConfig(config);
return this.sendMessageCore(address, type, data, config as SmtpConfig);
};
private readonly sendMessageCore = async (
address: string,
type: keyof EmailMessageTypes,
data: EmailMessageTypes[typeof type],
config: SmtpConfig
) => {
const { host, port, username, password, fromEmail, replyTo, templates } = config;
const template = templates.find((template) => template.usageType === type);
assert(

View file

@ -2,6 +2,7 @@ import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
SmsMessageTypes,
SmsSendMessageFunction,
ValidateConfig,
SmsConnector,
@ -26,11 +27,30 @@ export default class TwilioSmsConnector implements SmsConnector {
}
};
public sendMessage: SmsSendMessageFunction = async (address, type, data, config) => {
const smsConfig =
(config as TwilioSmsConfig | undefined) ?? (await this.getConfig(this.metadata.id));
public sendMessage: SmsSendMessageFunction = async (address, type, data) => {
const smsConfig = await this.getConfig(this.metadata.id);
await this.validateConfig(smsConfig);
const { accountSID, authToken, fromMessagingServiceSID, templates } = smsConfig;
return this.sendMessageCore(address, type, data, smsConfig);
};
public sendTestMessage: SmsSendMessageFunction = async (address, type, data, config) => {
if (!config) {
throw new ConnectorError(ConnectorErrorCodes.InsufficientRequestParameters);
}
await this.validateConfig(config);
return this.sendMessageCore(address, type, data, config as TwilioSmsConfig);
};
private readonly sendMessageCore = async (
phone: string,
type: keyof SmsMessageTypes,
data: SmsMessageTypes[typeof type],
config: TwilioSmsConfig
) => {
const { accountSID, authToken, fromMessagingServiceSID, templates } = config;
const template = templates.find((template) => template.usageType === type);
assert(
@ -42,7 +62,7 @@ export default class TwilioSmsConnector implements SmsConnector {
);
const parameters: PublicParameters = {
To: address,
To: phone,
MessagingServiceSid: fromMessagingServiceSID,
Body:
typeof data.code === 'string'

View file

@ -66,19 +66,19 @@ export type EmailMessageTypes = {
export type SmsMessageTypes = EmailMessageTypes;
export type EmailSendMessageFunction<T = unknown> = (
export type EmailSendMessageFunction<T = Record<string, unknown>, U = unknown> = (
address: string,
type: keyof EmailMessageTypes,
payload: EmailMessageTypes[typeof type],
config?: Record<string, unknown>
) => Promise<T>;
config?: T
) => Promise<U>;
export type SmsSendMessageFunction<T = unknown> = (
export type SmsSendMessageFunction<T = Record<string, unknown>, U = unknown> = (
phone: string,
type: keyof SmsMessageTypes,
payload: SmsMessageTypes[typeof type],
config?: Record<string, unknown>
) => Promise<T>;
config?: T
) => Promise<U>;
export interface BaseConnector {
metadata: ConnectorMetadata;
@ -88,10 +88,12 @@ export interface BaseConnector {
export interface SmsConnector extends BaseConnector {
sendMessage: SmsSendMessageFunction;
sendTestMessage: SmsSendMessageFunction;
}
export interface EmailConnector extends BaseConnector {
sendMessage: EmailSendMessageFunction;
sendTestMessage: EmailSendMessageFunction;
}
export interface SocialConnector extends BaseConnector {

View file

@ -119,6 +119,7 @@ describe('sendPasscode', () => {
it('should throw error when email or sms connector can not be found', async () => {
const sendMessage = jest.fn();
const sendTestMessage = jest.fn();
const validateConfig = jest.fn();
const getConfig = jest.fn();
mockedGetConnectorInstances.mockResolvedValueOnce([
@ -133,6 +134,7 @@ describe('sendPasscode', () => {
platform: null,
},
sendMessage,
sendTestMessage,
validateConfig,
getConfig,
},
@ -158,6 +160,7 @@ describe('sendPasscode', () => {
it('should call sendPasscode with params matching', async () => {
const sendMessage = jest.fn();
const sendTestMessage = jest.fn();
const validateConfig = jest.fn();
const getConfig = jest.fn();
mockedGetConnectorInstances.mockResolvedValueOnce([
@ -172,6 +175,7 @@ describe('sendPasscode', () => {
platform: null,
},
sendMessage,
sendTestMessage,
validateConfig,
getConfig,
},
@ -186,6 +190,7 @@ describe('sendPasscode', () => {
platform: null,
},
sendMessage,
sendTestMessage,
validateConfig,
getConfig,
},

View file

@ -116,15 +116,16 @@ describe('connector route', () => {
metadata: mockedMetadata,
validateConfig: jest.fn(),
getConfig: jest.fn(),
sendMessage: async (
sendTestMessage: async (
address: string,
type: keyof EmailMessageTypes,
_payload: EmailMessageTypes[typeof type]
// eslint-disable-next-line @typescript-eslint/no-empty-function
): Promise<any> => {},
sendMessage: jest.fn(),
};
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([mockedSmsConnectorInstance]);
const sendMessageSpy = jest.spyOn(mockedSmsConnectorInstance, 'sendMessage');
const sendMessageSpy = jest.spyOn(mockedSmsConnectorInstance, 'sendTestMessage');
const response = await connectorRequest
.post('/connectors/id/test')
.send({ phone: '12345678901', config: { test: 123 } });
@ -146,15 +147,16 @@ describe('connector route', () => {
metadata: mockMetadata,
validateConfig: jest.fn(),
getConfig: jest.fn(),
sendMessage: async (
sendTestMessage: async (
address: string,
type: keyof EmailMessageTypes,
_payload: EmailMessageTypes[typeof type]
// eslint-disable-next-line @typescript-eslint/no-empty-function
): Promise<any> => {},
sendMessage: jest.fn(),
};
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([mockedEmailConnector]);
const sendMessageSpy = jest.spyOn(mockedEmailConnector, 'sendMessage');
const sendMessageSpy = jest.spyOn(mockedEmailConnector, 'sendTestMessage');
const response = await connectorRequest
.post('/connectors/id/test')
.send({ email: 'test@email.com', config: { test: 123 } });

View file

@ -191,7 +191,7 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
await connector.validateConfig(config);
}
await connector.sendMessage(
await connector.sendTestMessage(
subject,
'Test',
{