From 71465ca999b89b69f6e0aaa11aa12e6a933421bc Mon Sep 17 00:00:00 2001 From: "IceHe.xyz" Date: Wed, 6 Apr 2022 12:11:06 +0800 Subject: [PATCH] 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 --- packages/core/src/connectors/index.test.ts | 11 +++++++++-- packages/core/src/connectors/index.ts | 14 +++++--------- .../core/src/connectors/utilities/index.test.ts | 3 +++ packages/core/src/lib/passcode.test.ts | 1 + packages/core/src/queries/connector.test.ts | 9 +++++---- packages/core/src/routes/connector.test.ts | 17 ++++++++++++++--- packages/core/src/utils/mock.ts | 14 ++++++++++++++ packages/phrases/src/locales/en.ts | 1 + packages/phrases/src/locales/zh-cn.ts | 1 + packages/schemas/src/db-entries/connector.ts | 7 ++++++- packages/schemas/src/db-entries/custom-types.ts | 5 +++++ packages/schemas/src/types/connector.ts | 7 +------ packages/schemas/tables/connectors.sql | 3 +++ 13 files changed, 68 insertions(+), 25 deletions(-) diff --git a/packages/core/src/connectors/index.test.ts b/packages/core/src/connectors/index.test.ts index 534169430..6ec15a172 100644 --- a/packages/core/src/connectors/index.test.ts +++ b/packages/core/src/connectors/index.test.ts @@ -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 }); } }); diff --git a/packages/core/src/connectors/index.ts b/packages/core/src/connectors/index.ts index cf798b0ce..9f89cd7da 100644 --- a/packages/core/src/connectors/index.ts +++ b/packages/core/src/connectors/index.ts @@ -104,15 +104,11 @@ export const getConnectorInstanceByType = async ( 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 })) ); }; diff --git a/packages/core/src/connectors/utilities/index.test.ts b/packages/core/src/connectors/utilities/index.test.ts index 09ecd3b95..7e994e70e 100644 --- a/packages/core/src/connectors/utilities/index.test.ts +++ b/packages/core/src/connectors/utilities/index.test.ts @@ -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).mockResolvedValueOnce({ id: 'id', + type: ConnectorType.Social, enabled: true, config: { foo: 'bar' }, createdAt: 0, diff --git a/packages/core/src/lib/passcode.test.ts b/packages/core/src/lib/passcode.test.ts index f851edf2d..240bd3437 100644 --- a/packages/core/src/lib/passcode.test.ts +++ b/packages/core/src/lib/passcode.test.ts @@ -124,6 +124,7 @@ describe('sendPasscode', () => { mockedGetConnectorInstanceByType.mockResolvedValue({ connector: { id: 'id', + type: ConnectorType.SMS, enabled: true, config: {}, createdAt: Date.now(), diff --git a/packages/core/src/queries/connector.test.ts b/packages/core/src/queries/connector.test.ts index aa4eeb8bd..ef354af51 100644 --- a/packages/core/src/queries/connector.test.ts +++ b/packages/core/src/queries/connector.test.ts @@ -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]); }); diff --git a/packages/core/src/routes/connector.test.ts b/packages/core/src/routes/connector.test.ts index 4dfa3c9c3..9c8ea54ec 100644 --- a/packages/core/src/routes/connector.test.ts +++ b/packages/core/src/routes/connector.test.ts @@ -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' }); diff --git a/packages/core/src/utils/mock.ts b/packages/core/src/utils/mock.ts index 152cab777..d3cb7caf1 100644 --- a/packages/core/src/utils/mock.ts +++ b/packages/core/src/utils/mock.ts @@ -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, diff --git a/packages/phrases/src/locales/en.ts b/packages/phrases/src/locales/en.ts index d1fa6983d..dcc5462f7 100644 --- a/packages/phrases/src/locales/en.ts +++ b/packages/phrases/src/locales/en.ts @@ -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.', diff --git a/packages/phrases/src/locales/zh-cn.ts b/packages/phrases/src/locales/zh-cn.ts index 47204b3e9..ccb9aa1d2 100644 --- a/packages/phrases/src/locales/zh-cn.ts +++ b/packages/phrases/src/locales/zh-cn.ts @@ -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: '手机号与邮箱地址均为空。', diff --git a/packages/schemas/src/db-entries/connector.ts b/packages/schemas/src/db-entries/connector.ts index d853732c9..1f20df298 100644 --- a/packages/schemas/src/db-entries/connector.ts +++ b/packages/schemas/src/db-entries/connector.ts @@ -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 = 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 = 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, }); diff --git a/packages/schemas/src/db-entries/custom-types.ts b/packages/schemas/src/db-entries/custom-types.ts index a87dbc9f9..e9a686cd0 100644 --- a/packages/schemas/src/db-entries/custom-types.ts +++ b/packages/schemas/src/db-entries/custom-types.ts @@ -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', diff --git a/packages/schemas/src/types/connector.ts b/packages/schemas/src/types/connector.ts index 045c98c88..b78c2865c 100644 --- a/packages/schemas/src/types/connector.ts +++ b/packages/schemas/src/types/connector.ts @@ -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; diff --git a/packages/schemas/tables/connectors.sql b/packages/schemas/tables/connectors.sql index cc54e6ffc..114549ac3 100644 --- a/packages/schemas/tables/connectors.sql +++ b/packages/schemas/tables/connectors.sql @@ -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()),