0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

feat(schemas,core,phrases)!: add type to connector schema and sync to DB (#491)

* feat(schemas,core,phrases)!: add type in connector schema and sync to DB

* chore(core): fix code about connector type
This commit is contained in:
IceHe.xyz 2022-04-06 12:11:06 +08:00 committed by GitHub
parent b098dd5f59
commit 71465ca999
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 68 additions and 25 deletions

View file

@ -13,42 +13,49 @@ import RequestError from '@/errors/RequestError';
const aliyunDmConnector = {
id: 'aliyun-dm',
type: ConnectorType.Email,
enabled: true,
config: {},
createdAt: 1_646_382_233_911,
};
const aliyunSmsConnector = {
id: 'aliyun-sms',
type: ConnectorType.SMS,
enabled: false,
config: {},
createdAt: 1_646_382_233_666,
};
const facebookConnector = {
id: 'facebook',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_646_382_233_333,
};
const githubConnector = {
id: 'github',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_646_382_233_555,
};
const googleConnector = {
id: 'google',
type: ConnectorType.Social,
enabled: false,
config: {},
createdAt: 1_646_382_233_000,
};
const wechatConnector = {
id: 'wechat',
type: ConnectorType.Social,
enabled: false,
config: {},
createdAt: 1_646_382_233_000,
};
const wechatNativeConnector = {
id: 'wechat-native',
type: ConnectorType.Social,
enabled: false,
config: {},
createdAt: 1_646_382_233_000,
@ -170,8 +177,8 @@ describe('initConnectors', () => {
expect(insertConnector).toHaveBeenCalledTimes(connectors.length);
for (const [i, connector] of connectors.entries()) {
const { id } = connector;
expect(insertConnector).toHaveBeenNthCalledWith(i + 1, { id });
const { id, type } = connector;
expect(insertConnector).toHaveBeenNthCalledWith(i + 1, { id, type });
}
});

View file

@ -104,15 +104,11 @@ export const getConnectorInstanceByType = async <T extends ConnectorInstance>(
export const initConnectors = async () => {
const connectors = await findAllConnectors();
const existingConnectorIds = new Set(connectors.map((connector) => connector.id));
const existingConnectors = new Map(connectors.map((connector) => [connector.id, connector]));
const newConnectors = allConnectors.filter(
({ metadata: { id, type } }) => existingConnectors.get(id)?.type !== type
);
await Promise.all(
allConnectors.map(async ({ metadata: { id } }) => {
if (existingConnectorIds.has(id)) {
return;
}
await insertConnector({ id });
})
newConnectors.map(async ({ metadata: { id, type } }) => insertConnector({ id, type }))
);
};

View file

@ -1,3 +1,5 @@
import { ConnectorType } from '@logto/schemas';
import { findConnectorById, updateConnector } from '@/queries/connector';
import { getConnectorConfig, updateConnectorConfig } from '.';
@ -7,6 +9,7 @@ jest.mock('@/queries/connector');
it('getConnectorConfig()', async () => {
(findConnectorById as jest.MockedFunction<typeof findConnectorById>).mockResolvedValueOnce({
id: 'id',
type: ConnectorType.Social,
enabled: true,
config: { foo: 'bar' },
createdAt: 0,

View file

@ -124,6 +124,7 @@ describe('sendPasscode', () => {
mockedGetConnectorInstanceByType.mockResolvedValue({
connector: {
id: 'id',
type: ConnectorType.SMS,
enabled: true,
config: {},
createdAt: Date.now(),

View file

@ -1,4 +1,4 @@
import { Connectors, CreateConnector } from '@logto/schemas';
import { Connectors, ConnectorType, CreateConnector } from '@logto/schemas';
import { createMockPool, createMockQueryResult, sql, QueryResultRowType } from 'slonik';
import { convertToIdentifiers } from '@/database/utils';
@ -65,19 +65,20 @@ describe('connector queries', () => {
it('insertConnector', async () => {
const connector: CreateConnector & QueryResultRowType = {
id: 'foo',
type: ConnectorType.Social,
enabled: true,
};
const expectSql = `
insert into "connectors" ("id", "enabled")
values ($1, $2)
insert into "connectors" ("id", "type", "enabled")
values ($1, $2, $3)
returning *
`;
mockQuery.mockImplementationOnce(async (sql, values) => {
expectSqlAssert(sql, expectSql);
expect(values).toEqual([connector.id, connector.enabled]);
expect(values).toEqual([connector.id, connector.type, connector.enabled]);
return createMockQueryResult([connector]);
});

View file

@ -7,6 +7,7 @@ import {
ConnectorMetadata,
EmailConnectorInstance,
EmailMessageTypes,
SmsConnectorInstance,
ValidateConfig,
} from '@/connectors/types';
import RequestError from '@/errors/RequestError';
@ -187,6 +188,7 @@ describe('connector route', () => {
return {
connector: {
id: 'connector_0',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
@ -229,6 +231,7 @@ describe('connector route', () => {
return {
connector: {
id: 'connector_0',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
@ -257,6 +260,7 @@ describe('connector route', () => {
return {
connector: {
id: 'connector_0',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
@ -299,6 +303,7 @@ describe('connector route', () => {
return {
connector: {
id: 'connector_1',
type: ConnectorType.SMS,
enabled: true,
config: {},
createdAt: 1_234_567_890_234,
@ -357,6 +362,7 @@ describe('connector route', () => {
return {
connector: {
id: 'connector_1',
type: ConnectorType.SMS,
enabled: true,
config: {},
createdAt: 1_234_567_890_234,
@ -385,6 +391,7 @@ describe('connector route', () => {
return {
connector: {
id: 'connector_4',
type: ConnectorType.Email,
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
@ -464,6 +471,7 @@ describe('connector route', () => {
return {
connector: {
id: 'connector_0',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
@ -492,6 +500,7 @@ describe('connector route', () => {
return {
connector: {
id: 'connector_0',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
@ -539,6 +548,7 @@ describe('connector route', () => {
const mockedEmailConnector: EmailConnectorInstance = {
connector: {
id: 'connector_0',
type: ConnectorType.Email,
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
@ -580,9 +590,10 @@ describe('connector route', () => {
});
it('should get SMS connector and send message', async () => {
const mockedEmailConnector: EmailConnectorInstance = {
const mockedSmsConnector: SmsConnectorInstance = {
connector: {
id: 'connector_0',
type: ConnectorType.SMS,
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
@ -605,10 +616,10 @@ describe('connector route', () => {
};
getConnectorInstanceByTypePlaceHolder.mockImplementationOnce(async (_: ConnectorType) => {
return mockedEmailConnector;
return mockedSmsConnector;
});
const sendMessageSpy = jest.spyOn(mockedEmailConnector, 'sendMessage');
const sendMessageSpy = jest.spyOn(mockedSmsConnector, 'sendMessage');
const response = await connectorRequest
.post('/connectors/test/sms')
.send({ phone: '12345678901' });

View file

@ -192,42 +192,49 @@ export const mockSignInExperience: SignInExperience = {
export const mockConnectorList: Connector[] = [
{
id: 'connector_0',
type: ConnectorType.Email,
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
},
{
id: 'connector_1',
type: ConnectorType.SMS,
enabled: true,
config: {},
createdAt: 1_234_567_890_234,
},
{
id: 'connector_2',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_345,
},
{
id: 'connector_3',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_456,
},
{
id: 'connector_4',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
},
{
id: 'connector_5',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
},
{
id: 'connector_6',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
@ -241,6 +248,7 @@ export const mockConnectorInstanceList: Array<{
{
connector: {
id: 'connector_0',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_123,
@ -257,6 +265,7 @@ export const mockConnectorInstanceList: Array<{
{
connector: {
id: 'connector_1',
type: ConnectorType.SMS,
enabled: true,
config: {},
createdAt: 1_234_567_890_234,
@ -273,6 +282,7 @@ export const mockConnectorInstanceList: Array<{
{
connector: {
id: 'connector_2',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_345,
@ -289,6 +299,7 @@ export const mockConnectorInstanceList: Array<{
{
connector: {
id: 'connector_3',
type: ConnectorType.Social,
enabled: true,
config: {},
createdAt: 1_234_567_890_456,
@ -305,6 +316,7 @@ export const mockConnectorInstanceList: Array<{
{
connector: {
id: 'connector_4',
type: ConnectorType.Email,
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
@ -321,6 +333,7 @@ export const mockConnectorInstanceList: Array<{
{
connector: {
id: 'connector_5',
type: ConnectorType.SMS,
enabled: true,
config: {},
createdAt: 1_234_567_890_567,
@ -337,6 +350,7 @@ export const mockConnectorInstanceList: Array<{
{
connector: {
id: 'connector_6',
type: ConnectorType.Email,
enabled: true,
config: {},
createdAt: 1_234_567_890_567,

View file

@ -336,6 +336,7 @@ const errors = {
oauth_code_invalid: 'Unable to get access token, please check authorization code.',
more_than_one_sms: 'The number of SMS connectors is larger then 1.',
more_than_one_email: 'The number of Email connectors is larger then 1.',
db_connector_type_mismatch: 'There is a connector in the DB that does not match the type.',
},
passcode: {
phone_email_empty: 'Both phone and email are empty.',

View file

@ -334,6 +334,7 @@ const errors = {
oauth_code_invalid: '无法获取 access_token请检查授权 code 是否有效。',
more_than_one_sms: '同时存在超过 1 个短信连接器。',
more_than_one_email: '同时存在超过 1 个邮件连接器。',
db_connector_type_mismatch: '数据库中存在一个类型不匹配的连接器。',
},
passcode: {
phone_email_empty: '手机号与邮箱地址均为空。',

View file

@ -3,9 +3,11 @@
import { z } from 'zod';
import { ArbitraryObject, arbitraryObjectGuard, GeneratedSchema, Guard } from '../foundations';
import { ConnectorType } from './custom-types';
export type CreateConnector = {
id: string;
type: ConnectorType;
enabled?: boolean;
config?: ArbitraryObject;
createdAt?: number;
@ -13,6 +15,7 @@ export type CreateConnector = {
export type Connector = {
id: string;
type: ConnectorType;
enabled: boolean;
config: ArbitraryObject;
createdAt: number;
@ -20,6 +23,7 @@ export type Connector = {
const createGuard: Guard<CreateConnector> = z.object({
id: z.string(),
type: z.nativeEnum(ConnectorType),
enabled: z.boolean().optional(),
config: arbitraryObjectGuard.optional(),
createdAt: z.number().optional(),
@ -30,10 +34,11 @@ export const Connectors: GeneratedSchema<CreateConnector> = Object.freeze({
tableSingular: 'connector',
fields: {
id: 'id',
type: 'type',
enabled: 'enabled',
config: 'config',
createdAt: 'created_at',
},
fieldKeys: ['id', 'enabled', 'config', 'createdAt'],
fieldKeys: ['id', 'type', 'enabled', 'config', 'createdAt'],
createGuard,
});

View file

@ -5,6 +5,11 @@ export enum ApplicationType {
SPA = 'SPA',
Traditional = 'Traditional',
}
export enum ConnectorType {
Email = 'Email',
SMS = 'SMS',
Social = 'Social',
}
export enum PasscodeType {
SignIn = 'SignIn',
Register = 'Register',

View file

@ -1,12 +1,7 @@
import { Languages } from '@logto/phrases';
import { Connector } from '../db-entries';
import { Connector, ConnectorType } from '../db-entries';
export enum ConnectorType {
SMS = 'SMS',
Email = 'Email',
Social = 'Social',
}
export interface ConnectorMetadata {
id: string;
type: ConnectorType;

View file

@ -1,5 +1,8 @@
create type connector_type as enum ('Email', 'SMS', 'Social');
create table connectors (
id varchar(128) not null,
type connector_type not null,
enabled boolean not null default TRUE,
config jsonb /* @use ArbitraryObject */ not null default '{}'::jsonb,
created_at timestamptz not null default(now()),