From c16f49cb2f949610eec9e6d7a057591dea585e1a Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Fri, 12 Aug 2022 10:53:58 +0800 Subject: [PATCH] refactor(connector): add @logto/connector-schemas --- packages/connector-schemas/package.json | 37 ++++++ packages/connector-schemas/src/class.ts | 124 ++++++++++++++++++ packages/connector-schemas/src/error.ts | 13 ++ packages/connector-schemas/src/index.ts | 3 + packages/connector-schemas/src/types.ts | 44 +++++++ .../connector-schemas/tsconfig.build.json | 5 + packages/connector-schemas/tsconfig.json | 11 ++ pnpm-lock.yaml | 21 +++ 8 files changed, 258 insertions(+) create mode 100644 packages/connector-schemas/package.json create mode 100644 packages/connector-schemas/src/class.ts create mode 100644 packages/connector-schemas/src/error.ts create mode 100644 packages/connector-schemas/src/index.ts create mode 100644 packages/connector-schemas/src/types.ts create mode 100644 packages/connector-schemas/tsconfig.build.json create mode 100644 packages/connector-schemas/tsconfig.json diff --git a/packages/connector-schemas/package.json b/packages/connector-schemas/package.json new file mode 100644 index 000000000..63b93838b --- /dev/null +++ b/packages/connector-schemas/package.json @@ -0,0 +1,37 @@ +{ + "name": "@logto/connector-schemas", + "version": "1.0.0-beta.4", + "main": "lib/index.js", + "author": "Silverhand Inc. ", + "license": "MPL-2.0", + "private": true, + "files": [ + "lib" + ], + "scripts": { + "precommit": "lint-staged", + "build": "rm -rf lib/ && tsc --p tsconfig.build.json", + "lint": "eslint --ext .ts src", + "lint:report": "pnpm lint --format json --output-file report.json", + "prepack": "pnpm build" + }, + "engines": { + "node": "^16.0.0" + }, + "dependencies": { + "@logto/phrases": "^1.0.0-beta.4" + }, + "devDependencies": { + "@silverhand/eslint-config": "1.0.0-rc.2", + "@silverhand/essentials": "^1.1.6", + "@silverhand/ts-config": "1.0.0-rc.2", + "eslint": "^8.21.0", + "lint-staged": "^13.0.0", + "prettier": "^2.7.1", + "typescript": "^4.7.4" + }, + "eslintConfig": { + "extends": "@silverhand" + }, + "prettier": "@silverhand/eslint-config/.prettierrc" +} diff --git a/packages/connector-schemas/src/class.ts b/packages/connector-schemas/src/class.ts new file mode 100644 index 000000000..296240ac9 --- /dev/null +++ b/packages/connector-schemas/src/class.ts @@ -0,0 +1,124 @@ +import { ConnectorMetadata } from './types'; + +export type EmailMessageTypes = { + SignIn: { + code: string; + }; + Register: { + code: string; + }; + ForgotPassword: { + code: string; + }; + Test: Record; +}; + +export type SmsMessageTypes = EmailMessageTypes; + +export type EmailSendMessageFunction = ( + address: string, + type: keyof EmailMessageTypes, + payload: EmailMessageTypes[typeof type] +) => Promise; + +export type EmailSendTestMessageFunction = ( + config: Record, + address: string, + type: keyof EmailMessageTypes, + payload: EmailMessageTypes[typeof type] +) => Promise; + +export type EmailSendMessageByFunction = ( + config: T, + address: string, + type: keyof EmailMessageTypes, + payload: EmailMessageTypes[typeof type] +) => Promise; + +export type SmsSendMessageFunction = ( + phone: string, + type: keyof SmsMessageTypes, + payload: SmsMessageTypes[typeof type] +) => Promise; + +export type SmsSendTestMessageFunction = ( + config: Record, + phone: string, + type: keyof SmsMessageTypes, + payload: SmsMessageTypes[typeof type] +) => Promise; + +export type SmsSendMessageByFunction = ( + config: T, + phone: string, + type: keyof SmsMessageTypes, + payload: SmsMessageTypes[typeof type] +) => Promise; + +export type ValidateConfig = (config: unknown) => asserts config is T; + +export type GetAuthorizationUri = (payload: { + state: string; + redirectUri: string; +}) => Promise; + +export type GetUserInfo = ( + data: unknown +) => Promise<{ id: string } & Record>; + +export type GetConnectorConfig = (id: string) => Promise; + +export type AuthResponseParser> = (response: unknown) => Promise; + +abstract class BaseConnector { + public getConfig: GetConnectorConfig; + public metadata!: ConnectorMetadata; + + constructor(getConnectorConfig: GetConnectorConfig) { + this.getConfig = getConnectorConfig; + } + + public abstract validateConfig(config: unknown): asserts config is T; +} + +export abstract class SmsConnector extends BaseConnector { + protected abstract readonly sendMessageBy: EmailSendMessageByFunction; + + public sendMessage: EmailSendMessageFunction = async (address, type, data) => { + const config = await this.getConfig(this.metadata.id); + this.validateConfig(config); + + return this.sendMessageBy(config, address, type, data); + }; + + public sendTestMessage?: EmailSendTestMessageFunction = async (config, address, type, data) => { + this.validateConfig(config); + + return this.sendMessageBy(config, address, type, data); + }; +} + +export abstract class EmailConnector extends BaseConnector { + protected abstract readonly sendMessageBy: SmsSendMessageByFunction; + + public sendMessage: SmsSendMessageFunction = async (address, type, data) => { + const config = await this.getConfig(this.metadata.id); + this.validateConfig(config); + + return this.sendMessageBy(config, address, type, data); + }; + + public sendTestMessage?: SmsSendTestMessageFunction = async (config, address, type, data) => { + this.validateConfig(config); + + return this.sendMessageBy(config, address, type, data); + }; +} + +export abstract class SocialConnector extends BaseConnector { + public abstract getAuthorizationUri: GetAuthorizationUri; + + public abstract getUserInfo: GetUserInfo; + + protected authResponseParser?: AuthResponseParser; +} diff --git a/packages/connector-schemas/src/error.ts b/packages/connector-schemas/src/error.ts new file mode 100644 index 000000000..94fad7a67 --- /dev/null +++ b/packages/connector-schemas/src/error.ts @@ -0,0 +1,13 @@ +import { ConnectorErrorCodes } from './types'; + +export class ConnectorError extends Error { + public code: ConnectorErrorCodes; + public data: unknown; + + constructor(code: ConnectorErrorCodes, data?: unknown) { + const message = typeof data === 'string' ? data : 'Connector error occurred.'; + super(message); + this.code = code; + this.data = typeof data === 'string' ? { message: data } : data; + } +} diff --git a/packages/connector-schemas/src/index.ts b/packages/connector-schemas/src/index.ts new file mode 100644 index 000000000..b03dd6ab6 --- /dev/null +++ b/packages/connector-schemas/src/index.ts @@ -0,0 +1,3 @@ +export * from './types'; +export * from './error'; +export * from './class'; diff --git a/packages/connector-schemas/src/types.ts b/packages/connector-schemas/src/types.ts new file mode 100644 index 000000000..5bd1e8ad5 --- /dev/null +++ b/packages/connector-schemas/src/types.ts @@ -0,0 +1,44 @@ +import type { Language } from '@logto/phrases'; +import { Nullable } from '@silverhand/essentials'; + +export enum ConnectorType { + Email = 'Email', + SMS = 'SMS', + Social = 'Social', +} + +export enum ConnectorPlatform { + Native = 'Native', + Universal = 'Universal', + Web = 'Web', +} + +export enum ConnectorErrorCodes { + General, + InsufficientRequestParameters, + InvalidConfig, + InvalidResponse, + TemplateNotFound, + NotImplemented, + SocialAuthCodeInvalid, + SocialAccessTokenInvalid, + SocialIdTokenInvalid, + AuthorizationFailed, +} + +type i18nPhrases = { [Language.English]: string } & { + [key in Exclude]?: string; +}; + +export type ConnectorMetadata = { + id: string; + target: string; + type: ConnectorType; + platform: Nullable; + name: i18nPhrases; + logo: string; + logoDark: Nullable; + description: i18nPhrases; + readme: string; + configTemplate: string; +}; diff --git a/packages/connector-schemas/tsconfig.build.json b/packages/connector-schemas/tsconfig.build.json new file mode 100644 index 000000000..665256ab7 --- /dev/null +++ b/packages/connector-schemas/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig", + "include": ["src"], + "exclude": ["src/**/*.test.ts"] +} diff --git a/packages/connector-schemas/tsconfig.json b/packages/connector-schemas/tsconfig.json new file mode 100644 index 000000000..f333e0b36 --- /dev/null +++ b/packages/connector-schemas/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@silverhand/ts-config/tsconfig.base", + "compilerOptions": { + "outDir": "lib", + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a5f7c3e7..18fe48065 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -534,6 +534,27 @@ importers: tsc-watch: 5.0.3_typescript@4.7.4 typescript: 4.7.4 + packages/connector-schemas: + specifiers: + '@logto/phrases': ^1.0.0-beta.4 + '@silverhand/eslint-config': 1.0.0-rc.2 + '@silverhand/essentials': ^1.1.6 + '@silverhand/ts-config': 1.0.0-rc.2 + eslint: ^8.21.0 + lint-staged: ^13.0.0 + prettier: ^2.7.1 + typescript: ^4.7.4 + dependencies: + '@logto/phrases': link:../phrases + devDependencies: + '@silverhand/eslint-config': 1.0.0-rc.2_swk2g7ygmfleszo5c33j4vooni + '@silverhand/essentials': 1.1.7 + '@silverhand/ts-config': 1.0.0-rc.2_typescript@4.7.4 + eslint: 8.21.0 + lint-staged: 13.0.0 + prettier: 2.7.1 + typescript: 4.7.4 + packages/connector-sendgrid-mail: specifiers: '@jest/types': ^28.1.3