diff --git a/packages/connector-aliyun-dm/src/constant.ts b/packages/connector-aliyun-dm/src/constant.ts index 804230f10..c86ae73ee 100644 --- a/packages/connector-aliyun-dm/src/constant.ts +++ b/packages/connector-aliyun-dm/src/constant.ts @@ -3,22 +3,6 @@ import path from 'path'; import { ConnectorType, ConnectorMetadata } from '@logto/connector-types'; import { getFileContents } from '@logto/shared'; -/** - * @doc https://help.aliyun.com/document_detail/29444.html - */ -export interface SingleSendMail { - AccountName: string; - AddressType: '0' | '1'; - ClickTrace?: '0' | '1'; - FromAlias?: string; - HtmlBody?: string; - ReplyToAddress: 'true' | 'false'; - Subject: string; - TagName?: string; - TextBody?: string; - ToAddress: string; -} - export const endpoint = 'https://dm.aliyuncs.com/'; export const staticConfigs = { diff --git a/packages/connector-aliyun-dm/src/index.test.ts b/packages/connector-aliyun-dm/src/index.test.ts index 974e87178..48f5de4b1 100644 --- a/packages/connector-aliyun-dm/src/index.test.ts +++ b/packages/connector-aliyun-dm/src/index.test.ts @@ -1,8 +1,9 @@ import { GetConnectorConfig } from '@logto/connector-types'; -import { AliyunDmConnector, AliyunDmConfig } from '.'; +import { AliyunDmConnector } from '.'; import { mockedConfig } from './mock'; import { singleSendMail } from './single-send-mail'; +import { AliyunDmConfig } from './types'; const getConnectorConfig = jest.fn() as GetConnectorConfig; diff --git a/packages/connector-aliyun-dm/src/index.ts b/packages/connector-aliyun-dm/src/index.ts index 8024d0efb..02233edf7 100644 --- a/packages/connector-aliyun-dm/src/index.ts +++ b/packages/connector-aliyun-dm/src/index.ts @@ -9,31 +9,10 @@ import { } from '@logto/connector-types'; import { assert } from '@silverhand/essentials'; import { Response } from 'got'; -import { z } from 'zod'; import { defaultMetadata } from './constant'; import { singleSendMail } from './single-send-mail'; -import { SendEmailResponse } from './utils'; - -/** - * UsageType here is used to specify the use case of the template, can be either - * 'Register', 'SignIn', 'ForgotPassword' or 'Test'. - */ -const templateGuard = z.object({ - usageType: z.string(), - subject: z.string(), - content: z.string(), // With variable {{code}}, support HTML -}); - -const configGuard = z.object({ - accessKeyId: z.string(), - accessKeySecret: z.string(), - accountName: z.string(), - fromAlias: z.string().optional(), - templates: z.array(templateGuard), -}); - -export type AliyunDmConfig = z.infer; +import { SendEmailResponse, AliyunDmConfig, aliyunDmConfigGuard } from './types'; export class AliyunDmConnector implements EmailConnector { public metadata: ConnectorMetadata = defaultMetadata; @@ -45,7 +24,7 @@ export class AliyunDmConnector implements EmailConnector { } public validateConfig: ValidateConfig = async (config: unknown) => { - const result = configGuard.safeParse(config); + const result = aliyunDmConfigGuard.safeParse(config); if (!result.success) { throw new ConnectorError(ConnectorErrorCodes.InvalidConfig, result.error.message); diff --git a/packages/connector-aliyun-dm/src/single-send-mail.ts b/packages/connector-aliyun-dm/src/single-send-mail.ts index a002f4546..e8c0cb5c4 100644 --- a/packages/connector-aliyun-dm/src/single-send-mail.ts +++ b/packages/connector-aliyun-dm/src/single-send-mail.ts @@ -1,7 +1,8 @@ import { Response } from 'got'; -import { SingleSendMail, endpoint, staticConfigs } from './constant'; -import { PublicParameters, request, SendEmailResponse } from './utils'; +import { endpoint, staticConfigs } from './constant'; +import { PublicParameters, SendEmailResponse, SingleSendMail } from './types'; +import { request } from './utils'; /** * @doc https://help.aliyun.com/document_detail/29444.html diff --git a/packages/connector-aliyun-dm/src/types.ts b/packages/connector-aliyun-dm/src/types.ts new file mode 100644 index 000000000..17c486491 --- /dev/null +++ b/packages/connector-aliyun-dm/src/types.ts @@ -0,0 +1,53 @@ +import { z } from 'zod'; + +export type { Response } from 'got'; + +export type SendEmailResponse = { EnvId: string; RequestId: string }; + +/** + * UsageType here is used to specify the use case of the template, can be either + * 'Register', 'SignIn', 'ForgotPassword' or 'Test'. + */ +const templateGuard = z.object({ + usageType: z.string(), + subject: z.string(), + content: z.string(), // With variable {{code}}, support HTML +}); + +export const aliyunDmConfigGuard = z.object({ + accessKeyId: z.string(), + accessKeySecret: z.string(), + accountName: z.string(), + fromAlias: z.string().optional(), + templates: z.array(templateGuard), +}); + +export type AliyunDmConfig = z.infer; + +/** + * @doc https://help.aliyun.com/document_detail/29444.html + */ +export type SingleSendMail = { + AccountName: string; + AddressType: '0' | '1'; + ClickTrace?: '0' | '1'; + FromAlias?: string; + HtmlBody?: string; + ReplyToAddress: 'true' | 'false'; + Subject: string; + TagName?: string; + TextBody?: string; + ToAddress: string; +}; + +export type PublicParameters = { + AccessKeyId: string; + Format?: string; // 'json' or 'xml', default: 'json' + RegionId?: string; // 'cn-hangzhou' | 'ap-southeast-1' | 'ap-southeast-2' + Signature?: string; + SignatureMethod?: string; + SignatureNonce?: string; + SignatureVersion?: string; + Timestamp?: string; + Version?: string; +}; diff --git a/packages/connector-aliyun-dm/src/utils.ts b/packages/connector-aliyun-dm/src/utils.ts index 62d32b488..75b13014d 100644 --- a/packages/connector-aliyun-dm/src/utils.ts +++ b/packages/connector-aliyun-dm/src/utils.ts @@ -2,8 +2,7 @@ import { createHmac } from 'crypto'; import got from 'got'; -export type { Response } from 'got'; -export type SendEmailResponse = { EnvId: string; RequestId: string }; +import { PublicParameters } from './types'; // Aliyun has special escape rules. // https://help.aliyun.com/document_detail/29442.html @@ -38,18 +37,6 @@ export const getSignature = ( return createHmac('sha1', `${secret}&`).update(stringToSign).digest('base64'); }; -export interface PublicParameters { - AccessKeyId: string; - Format?: string; // 'json' or 'xml', default: 'json' - RegionId?: string; // 'cn-hangzhou' | 'ap-southeast-1' | 'ap-southeast-2' - Signature?: string; - SignatureMethod?: string; - SignatureNonce?: string; - SignatureVersion?: string; - Timestamp?: string; - Version?: string; -} - export const request = async ( url: string, parameters: PublicParameters & Record, diff --git a/packages/connector-aliyun-sms/src/constant.ts b/packages/connector-aliyun-sms/src/constant.ts index 3ce2bd1a5..423ddce7b 100644 --- a/packages/connector-aliyun-sms/src/constant.ts +++ b/packages/connector-aliyun-sms/src/constant.ts @@ -3,18 +3,6 @@ import path from 'path'; import { ConnectorMetadata, ConnectorType } from '@logto/connector-types'; import { getFileContents } from '@logto/shared'; -/** - * @doc https://help.aliyun.com/document_detail/101414.html - */ -export interface SendSms { - OutId?: string; - PhoneNumbers: string; // 11 digits w/o prefix (can be multiple phone numbers with separator `,`) - SignName: string; // Name of SMS signature - SmsUpExtendCode?: string; - TemplateCode: string; // Text message template ID - TemplateParam?: string; // Stringified JSON (used to fill in text template) -} - export const endpoint = 'https://dysmsapi.aliyuncs.com/'; export const staticConfigs = { @@ -25,6 +13,21 @@ export const staticConfigs = { Version: '2017-05-25', }; +/** + * Details of SmsTemplateType can be found at: + * https://next.api.aliyun.com/document/Dysmsapi/2017-05-25/QuerySmsTemplateList. + * + * For our use case is to send passcode sms for passwordless sign-in/up as well as + * reset password, the default value of type code is set to be 2. + */ +export enum SmsTemplateType { + Notification = 0, + Promotion = 1, + Passcode = 2, + InternationalMessage = 6, + PureNumber = 7, +} + // eslint-disable-next-line unicorn/prefer-module const currentPath = __dirname; const pathToReadmeFile = path.join(currentPath, '..', 'README.md'); diff --git a/packages/connector-aliyun-sms/src/index.test.ts b/packages/connector-aliyun-sms/src/index.test.ts index e713bd380..e97d83423 100644 --- a/packages/connector-aliyun-sms/src/index.test.ts +++ b/packages/connector-aliyun-sms/src/index.test.ts @@ -1,8 +1,9 @@ import { GetConnectorConfig } from '@logto/connector-types'; -import { AliyunSmsConnector, AliyunSmsConfig } from '.'; +import { AliyunSmsConnector } from '.'; import { mockedConnectorConfig, mockedValidConnectorConfig, phoneTest, codeTest } from './mock'; import { sendSms } from './single-send-text'; +import { AliyunSmsConfig } from './types'; const getConnectorConfig = jest.fn() as GetConnectorConfig; diff --git a/packages/connector-aliyun-sms/src/index.ts b/packages/connector-aliyun-sms/src/index.ts index 68a35c141..aac33c984 100644 --- a/packages/connector-aliyun-sms/src/index.ts +++ b/packages/connector-aliyun-sms/src/index.ts @@ -9,51 +9,10 @@ import { } from '@logto/connector-types'; import { assert } from '@silverhand/essentials'; import { Response } from 'got'; -import { z } from 'zod'; import { defaultMetadata } from './constant'; import { sendSms } from './single-send-text'; -import { SendSmsResponse } from './utils'; -/** - * Details of SmsTemplateType can be found at: - * https://next.api.aliyun.com/document/Dysmsapi/2017-05-25/QuerySmsTemplateList. - * - * For our use case is to send passcode sms for passwordless sign-in/up as well as - * reset password, the default value of type code is set to be 2. - */ -enum SmsTemplateType { - Notification = 0, - Promotion = 1, - Passcode = 2, - InternationalMessage = 6, - PureNumber = 7, -} - -/** - * UsageType here is used to specify the use case of the template, can be either - * 'Register', 'SignIn', 'ForgotPassword' or 'Test'. - * - * Type here in the template is used to specify the purpose of sending the sms, - * can be either item in SmsTemplateType. - * As the SMS is applied for sending passcode, the value should always be 2 in our case. - */ -const templateGuard = z.object({ - type: z.nativeEnum(SmsTemplateType).default(2), - usageType: z.string(), - code: z.string(), - name: z.string().min(1).max(30), - content: z.string().min(1).max(500), - remark: z.string(), -}); - -const configGuard = z.object({ - accessKeyId: z.string(), - accessKeySecret: z.string(), - signName: z.string(), - templates: z.array(templateGuard), -}); - -export type AliyunSmsConfig = z.infer; +import { aliyunSmsConfigGuard, AliyunSmsConfig, SendSmsResponse } from './types'; export class AliyunSmsConnector implements SmsConnector { public metadata: ConnectorMetadata = defaultMetadata; @@ -65,7 +24,7 @@ export class AliyunSmsConnector implements SmsConnector { } public validateConfig: ValidateConfig = async (config: unknown) => { - const result = configGuard.safeParse(config); + const result = aliyunSmsConfigGuard.safeParse(config); if (!result.success) { throw new ConnectorError(ConnectorErrorCodes.InvalidConfig, result.error.message); diff --git a/packages/connector-aliyun-sms/src/single-send-text.ts b/packages/connector-aliyun-sms/src/single-send-text.ts index 0e5bb5cbd..f0c3c3242 100644 --- a/packages/connector-aliyun-sms/src/single-send-text.ts +++ b/packages/connector-aliyun-sms/src/single-send-text.ts @@ -1,7 +1,8 @@ import { Response } from 'got'; -import { SendSms, endpoint, staticConfigs } from './constant'; -import { PublicParameters, request, SendSmsResponse } from './utils'; +import { endpoint, staticConfigs } from './constant'; +import { PublicParameters, SendSms, SendSmsResponse } from './types'; +import { request } from './utils'; /** * @doc https://help.aliyun.com/document_detail/101414.html diff --git a/packages/connector-aliyun-sms/src/types.ts b/packages/connector-aliyun-sms/src/types.ts new file mode 100644 index 000000000..cb536d18f --- /dev/null +++ b/packages/connector-aliyun-sms/src/types.ts @@ -0,0 +1,57 @@ +import { z } from 'zod'; + +import { SmsTemplateType } from './constant'; + +export type { Response } from 'got'; + +export type SendSmsResponse = { BizId: string; Code: string; Message: string; RequestId: string }; + +/** + * @doc https://help.aliyun.com/document_detail/101414.html + */ +export type SendSms = { + OutId?: string; + PhoneNumbers: string; // 11 digits w/o prefix (can be multiple phone numbers with separator `,`) + SignName: string; // Name of SMS signature + SmsUpExtendCode?: string; + TemplateCode: string; // Text message template ID + TemplateParam?: string; // Stringified JSON (used to fill in text template) +}; + +export type PublicParameters = { + AccessKeyId: string; + Format?: string; // 'json' or 'xml', default: 'json' + RegionId?: string; // 'cn-hangzhou' | 'ap-southeast-1' | 'ap-southeast-2' + Signature?: string; + SignatureMethod?: string; + SignatureNonce?: string; + SignatureVersion?: string; + Timestamp?: string; + Version?: string; +}; + +/** + * UsageType here is used to specify the use case of the template, can be either + * 'Register', 'SignIn', 'ForgotPassword' or 'Test'. + * + * Type here in the template is used to specify the purpose of sending the sms, + * can be either item in SmsTemplateType. + * As the SMS is applied for sending passcode, the value should always be 2 in our case. + */ +const templateGuard = z.object({ + type: z.nativeEnum(SmsTemplateType).default(2), + usageType: z.string(), + code: z.string(), + name: z.string().min(1).max(30), + content: z.string().min(1).max(500), + remark: z.string(), +}); + +export const aliyunSmsConfigGuard = z.object({ + accessKeyId: z.string(), + accessKeySecret: z.string(), + signName: z.string(), + templates: z.array(templateGuard), +}); + +export type AliyunSmsConfig = z.infer; diff --git a/packages/connector-aliyun-sms/src/utils.ts b/packages/connector-aliyun-sms/src/utils.ts index 18614325c..75b13014d 100644 --- a/packages/connector-aliyun-sms/src/utils.ts +++ b/packages/connector-aliyun-sms/src/utils.ts @@ -2,8 +2,7 @@ import { createHmac } from 'crypto'; import got from 'got'; -export type { Response } from 'got'; -export type SendSmsResponse = { BizId: string; Code: string; Message: string; RequestId: string }; +import { PublicParameters } from './types'; // Aliyun has special escape rules. // https://help.aliyun.com/document_detail/29442.html @@ -38,18 +37,6 @@ export const getSignature = ( return createHmac('sha1', `${secret}&`).update(stringToSign).digest('base64'); }; -export interface PublicParameters { - AccessKeyId: string; - Format?: string; // 'json' or 'xml', default: 'json' - RegionId?: string; // 'cn-hangzhou' | 'ap-southeast-1' | 'ap-southeast-2' - Signature?: string; - SignatureMethod?: string; - SignatureNonce?: string; - SignatureVersion?: string; - Timestamp?: string; - Version?: string; -} - export const request = async ( url: string, parameters: PublicParameters & Record, diff --git a/packages/connector-facebook/src/constant.ts b/packages/connector-facebook/src/constant.ts index cbd7e92ff..447560e5e 100644 --- a/packages/connector-facebook/src/constant.ts +++ b/packages/connector-facebook/src/constant.ts @@ -2,7 +2,6 @@ import path from 'path'; import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types'; import { getFileContents } from '@logto/shared'; -import { z } from 'zod'; /** * Note: If you do not include a version number we will default to the oldest available version, so it's recommended to include the version number in your requests. @@ -18,13 +17,6 @@ export const accessTokenEndpoint = 'https://graph.facebook.com/v13.0/oauth/acces export const userInfoEndpoint = 'https://graph.facebook.com/v13.0/me'; export const scope = 'email,public_profile'; -export const facebookConfigGuard = z.object({ - clientId: z.string(), - clientSecret: z.string(), -}); - -export type FacebookConfig = z.infer; - // eslint-disable-next-line unicorn/prefer-module const currentPath = __dirname; const pathToReadmeFile = path.join(currentPath, '..', 'README.md'); diff --git a/packages/connector-facebook/src/index.test.ts b/packages/connector-facebook/src/index.test.ts index 708906f60..778824431 100644 --- a/packages/connector-facebook/src/index.test.ts +++ b/packages/connector-facebook/src/index.test.ts @@ -2,13 +2,9 @@ import { ConnectorError, ConnectorErrorCodes, GetConnectorConfig } from '@logto/ import nock from 'nock'; import { FacebookConnector } from '.'; -import { - FacebookConfig, - accessTokenEndpoint, - authorizationEndpoint, - userInfoEndpoint, -} from './constant'; +import { accessTokenEndpoint, authorizationEndpoint, userInfoEndpoint } from './constant'; import { clientId, clientSecret, code, dummyRedirectUri, fields, mockedConfig } from './mock'; +import { FacebookConfig } from './types'; const getConnectorConfig = jest.fn() as GetConnectorConfig; diff --git a/packages/connector-facebook/src/index.ts b/packages/connector-facebook/src/index.ts index da65c5d46..b35d70627 100644 --- a/packages/connector-facebook/src/index.ts +++ b/packages/connector-facebook/src/index.ts @@ -24,9 +24,13 @@ import { userInfoEndpoint, defaultMetadata, defaultTimeout, - facebookConfigGuard, - FacebookConfig, } from './constant'; +import { + facebookConfigGuard, + AccessTokenResponse, + FacebookConfig, + UserInfoResponse, +} from './types'; export class FacebookConnector implements SocialConnector { public metadata: ConnectorMetadata = defaultMetadata; @@ -60,12 +64,6 @@ export class FacebookConnector implements SocialConnector { }; public getAccessToken: GetAccessToken = async (code, redirectUri) => { - type AccessTokenResponse = { - access_token: string; - token_type: string; - expires_in: number; - }; - const { clientId: client_id, clientSecret: client_secret } = await this.getConfig( this.metadata.target, this.metadata.platform @@ -89,13 +87,6 @@ export class FacebookConnector implements SocialConnector { }; public getUserInfo: GetUserInfo = async (accessTokenObject) => { - type UserInfoResponse = { - id: string; - email?: string; - name?: string; - picture?: { data: { url: string } }; - }; - const { accessToken } = accessTokenObject; try { diff --git a/packages/connector-facebook/src/types.ts b/packages/connector-facebook/src/types.ts new file mode 100644 index 000000000..101530d2c --- /dev/null +++ b/packages/connector-facebook/src/types.ts @@ -0,0 +1,21 @@ +import { z } from 'zod'; + +export const facebookConfigGuard = z.object({ + clientId: z.string(), + clientSecret: z.string(), +}); + +export type FacebookConfig = z.infer; + +export type AccessTokenResponse = { + access_token: string; + token_type: string; + expires_in: number; +}; + +export type UserInfoResponse = { + id: string; + email?: string; + name?: string; + picture?: { data: { url: string } }; +}; diff --git a/packages/connector-github/src/constant.ts b/packages/connector-github/src/constant.ts index fd028c5c6..f258cb43c 100644 --- a/packages/connector-github/src/constant.ts +++ b/packages/connector-github/src/constant.ts @@ -2,20 +2,12 @@ import path from 'path'; import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types'; import { getFileContents } from '@logto/shared'; -import { z } from 'zod'; export const authorizationEndpoint = 'https://github.com/login/oauth/authorize'; export const scope = 'read:user'; export const accessTokenEndpoint = 'https://github.com/login/oauth/access_token'; export const userInfoEndpoint = 'https://api.github.com/user'; -export const githubConfigGuard = z.object({ - clientId: z.string(), - clientSecret: z.string(), -}); - -export type GithubConfig = z.infer; - // eslint-disable-next-line unicorn/prefer-module const currentPath = __dirname; const pathToReadmeFile = path.join(currentPath, '..', 'README.md'); diff --git a/packages/connector-github/src/index.test.ts b/packages/connector-github/src/index.test.ts index 2c1aaee37..5993a2746 100644 --- a/packages/connector-github/src/index.test.ts +++ b/packages/connector-github/src/index.test.ts @@ -2,13 +2,9 @@ import { ConnectorError, ConnectorErrorCodes, GetConnectorConfig } from '@logto/ import nock from 'nock'; import { GithubConnector } from '.'; -import { - GithubConfig, - accessTokenEndpoint, - authorizationEndpoint, - userInfoEndpoint, -} from './constant'; +import { accessTokenEndpoint, authorizationEndpoint, userInfoEndpoint } from './constant'; import { mockedConfig } from './mock'; +import { GithubConfig } from './types'; const getConnectorConfig = jest.fn() as GetConnectorConfig; diff --git a/packages/connector-github/src/index.ts b/packages/connector-github/src/index.ts index 15e5a9efb..a48e95807 100644 --- a/packages/connector-github/src/index.ts +++ b/packages/connector-github/src/index.ts @@ -17,11 +17,10 @@ import { accessTokenEndpoint, scope, userInfoEndpoint, - githubConfigGuard, - GithubConfig, defaultMetadata, defaultTimeout, } from './constant'; +import { githubConfigGuard, AccessTokenResponse, GithubConfig, UserInfoResponse } from './types'; export class GithubConnector implements SocialConnector { public metadata: ConnectorMetadata = defaultMetadata; @@ -54,12 +53,6 @@ export class GithubConnector implements SocialConnector { }; getAccessToken: GetAccessToken = async (code) => { - type AccessTokenResponse = { - access_token: string; - scope: string; - token_type: string; - }; - const { clientId: client_id, clientSecret: client_secret } = await this.getConfig( this.metadata.target, this.metadata.platform @@ -83,13 +76,6 @@ export class GithubConnector implements SocialConnector { }; public getUserInfo: GetUserInfo = async (accessTokenObject) => { - type UserInfoResponse = { - id: number; - avatar_url?: string; - email?: string; - name?: string; - }; - const { accessToken } = accessTokenObject; try { diff --git a/packages/connector-github/src/types.ts b/packages/connector-github/src/types.ts new file mode 100644 index 000000000..85def7965 --- /dev/null +++ b/packages/connector-github/src/types.ts @@ -0,0 +1,21 @@ +import { z } from 'zod'; + +export const githubConfigGuard = z.object({ + clientId: z.string(), + clientSecret: z.string(), +}); + +export type GithubConfig = z.infer; + +export type AccessTokenResponse = { + access_token: string; + scope: string; + token_type: string; +}; + +export type UserInfoResponse = { + id: number; + avatar_url?: string; + email?: string; + name?: string; +}; diff --git a/packages/connector-google/src/constant.ts b/packages/connector-google/src/constant.ts index 35d4180ac..065147262 100644 --- a/packages/connector-google/src/constant.ts +++ b/packages/connector-google/src/constant.ts @@ -2,20 +2,12 @@ import path from 'path'; import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types'; import { getFileContents } from '@logto/shared'; -import { z } from 'zod'; export const authorizationEndpoint = 'https://accounts.google.com/o/oauth2/v2/auth'; export const accessTokenEndpoint = 'https://oauth2.googleapis.com/token'; export const userInfoEndpoint = 'https://openidconnect.googleapis.com/v1/userinfo'; export const scope = 'openid profile email'; -export const googleConfigGuard = z.object({ - clientId: z.string(), - clientSecret: z.string(), -}); - -export type GoogleConfig = z.infer; - // eslint-disable-next-line unicorn/prefer-module const currentPath = __dirname; const pathToReadmeFile = path.join(currentPath, '..', 'README.md'); diff --git a/packages/connector-google/src/index.test.ts b/packages/connector-google/src/index.test.ts index adaebd41a..1ad54efbf 100644 --- a/packages/connector-google/src/index.test.ts +++ b/packages/connector-google/src/index.test.ts @@ -2,13 +2,9 @@ import { ConnectorError, ConnectorErrorCodes, GetConnectorConfig } from '@logto/ import nock from 'nock'; import { GoogleConnector } from '.'; -import { - GoogleConfig, - accessTokenEndpoint, - authorizationEndpoint, - userInfoEndpoint, -} from './constant'; +import { accessTokenEndpoint, authorizationEndpoint, userInfoEndpoint } from './constant'; import { mockedConfig } from './mock'; +import { GoogleConfig } from './types'; const getConnectorConfig = jest.fn() as GetConnectorConfig; diff --git a/packages/connector-google/src/index.ts b/packages/connector-google/src/index.ts index f467a6cee..fd128b108 100644 --- a/packages/connector-google/src/index.ts +++ b/packages/connector-google/src/index.ts @@ -21,11 +21,10 @@ import { authorizationEndpoint, scope, userInfoEndpoint, - googleConfigGuard, - GoogleConfig, defaultMetadata, defaultTimeout, } from './constant'; +import { googleConfigGuard, AccessTokenResponse, GoogleConfig, UserInfoResponse } from './types'; export class GoogleConnector implements SocialConnector { public metadata: ConnectorMetadata = defaultMetadata; @@ -59,12 +58,6 @@ export class GoogleConnector implements SocialConnector { }; public getAccessToken: GetAccessToken = async (code, redirectUri) => { - type AccessTokenResponse = { - access_token: string; - scope: string; - token_type: string; - }; - const { clientId, clientSecret } = await this.getConfig( this.metadata.target, this.metadata.platform @@ -92,17 +85,6 @@ export class GoogleConnector implements SocialConnector { }; public getUserInfo: GetUserInfo = async (accessTokenObject) => { - type UserInfoResponse = { - sub: string; - name?: string; - given_name?: string; - family_name?: string; - picture?: string; - email?: string; - email_verified?: boolean; - locale?: string; - }; - const { accessToken } = accessTokenObject; try { diff --git a/packages/connector-google/src/types.ts b/packages/connector-google/src/types.ts new file mode 100644 index 000000000..7ad8fdc79 --- /dev/null +++ b/packages/connector-google/src/types.ts @@ -0,0 +1,25 @@ +import { z } from 'zod'; + +export const googleConfigGuard = z.object({ + clientId: z.string(), + clientSecret: z.string(), +}); + +export type GoogleConfig = z.infer; + +export type AccessTokenResponse = { + access_token: string; + scope: string; + token_type: string; +}; + +export type UserInfoResponse = { + sub: string; + name?: string; + given_name?: string; + family_name?: string; + picture?: string; + email?: string; + email_verified?: boolean; + locale?: string; +}; diff --git a/packages/connector-wechat-native/src/constant.ts b/packages/connector-wechat-native/src/constant.ts index 8484fe6fd..e9d6c9e64 100644 --- a/packages/connector-wechat-native/src/constant.ts +++ b/packages/connector-wechat-native/src/constant.ts @@ -2,17 +2,12 @@ import path from 'path'; import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types'; import { getFileContents } from '@logto/shared'; -import { z } from 'zod'; export const authorizationEndpoint = 'https://wechat.native/'; // This is used to arouse the native WeChat App export const accessTokenEndpoint = 'https://api.weixin.qq.com/sns/oauth2/access_token'; export const userInfoEndpoint = 'https://api.weixin.qq.com/sns/userinfo'; export const scope = 'snsapi_userinfo'; -export const weChatNativeConfigGuard = z.object({ appId: z.string(), appSecret: z.string() }); - -export type WeChatNativeConfig = z.infer; - // eslint-disable-next-line unicorn/prefer-module const currentPath = __dirname; const pathToReadmeFile = path.join(currentPath, '..', 'README.md'); diff --git a/packages/connector-wechat-native/src/index.test.ts b/packages/connector-wechat-native/src/index.test.ts index 4c3d83a5a..098537871 100644 --- a/packages/connector-wechat-native/src/index.test.ts +++ b/packages/connector-wechat-native/src/index.test.ts @@ -2,13 +2,9 @@ import { ConnectorError, ConnectorErrorCodes, GetConnectorConfig } from '@logto/ import nock from 'nock'; import { WeChatNativeConnector } from '.'; -import { - WeChatNativeConfig, - accessTokenEndpoint, - authorizationEndpoint, - userInfoEndpoint, -} from './constant'; +import { accessTokenEndpoint, authorizationEndpoint, userInfoEndpoint } from './constant'; import { mockedConfig } from './mock'; +import { WeChatNativeConfig } from './types'; const getConnectorConfig = jest.fn() as GetConnectorConfig; diff --git a/packages/connector-wechat-native/src/index.ts b/packages/connector-wechat-native/src/index.ts index 88e3682d1..4d899a649 100644 --- a/packages/connector-wechat-native/src/index.ts +++ b/packages/connector-wechat-native/src/index.ts @@ -24,9 +24,13 @@ import { scope, defaultMetadata, defaultTimeout, - weChatNativeConfigGuard, - WeChatNativeConfig, } from './constant'; +import { + weChatNativeConfigGuard, + AccessTokenResponse, + UserInfoResponse, + WeChatNativeConfig, +} from './types'; // As creating a WeChat Web/Mobile application needs a real App or Website record, the real test is temporarily not finished. // TODO: test with our own wechat mobile/web application (LOG-1910), already tested with other verified wechat web application @@ -62,15 +66,6 @@ export class WeChatNativeConnector implements SocialConnector { }; public getAccessToken: GetAccessToken = async (code) => { - type AccessTokenResponse = { - access_token?: string; - openid?: string; - expires_in?: number; // In seconds - refresh_token?: string; - scope?: string; - errcode?: number; - }; - const { appId: appid, appSecret: secret } = await this.getConfig( this.metadata.target, this.metadata.platform @@ -98,14 +93,6 @@ export class WeChatNativeConnector implements SocialConnector { // FIXME: // eslint-disable-next-line complexity public getUserInfo: GetUserInfo = async (accessTokenObject) => { - type UserInfoResponse = { - unionid?: string; - headimgurl?: string; - nickname?: string; - errcode?: number; - errmsg?: string; - }; - const { accessToken, openid } = accessTokenObject; try { diff --git a/packages/connector-wechat-native/src/types.ts b/packages/connector-wechat-native/src/types.ts new file mode 100644 index 000000000..df4f69b7a --- /dev/null +++ b/packages/connector-wechat-native/src/types.ts @@ -0,0 +1,22 @@ +import { z } from 'zod'; + +export const weChatNativeConfigGuard = z.object({ appId: z.string(), appSecret: z.string() }); + +export type WeChatNativeConfig = z.infer; + +export type AccessTokenResponse = { + access_token?: string; + openid?: string; + expires_in?: number; // In seconds + refresh_token?: string; + scope?: string; + errcode?: number; +}; + +export type UserInfoResponse = { + unionid?: string; + headimgurl?: string; + nickname?: string; + errcode?: number; + errmsg?: string; +}; diff --git a/packages/connector-wechat/src/constant.ts b/packages/connector-wechat/src/constant.ts index b5638d78c..9b412b702 100644 --- a/packages/connector-wechat/src/constant.ts +++ b/packages/connector-wechat/src/constant.ts @@ -2,17 +2,12 @@ import path from 'path'; import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types'; import { getFileContents } from '@logto/shared'; -import { z } from 'zod'; export const authorizationEndpoint = 'https://open.weixin.qq.com/connect/qrconnect'; export const accessTokenEndpoint = 'https://api.weixin.qq.com/sns/oauth2/access_token'; export const userInfoEndpoint = 'https://api.weixin.qq.com/sns/userinfo'; export const scope = 'snsapi_login'; -export const weChatConfigGuard = z.object({ appId: z.string(), appSecret: z.string() }); - -export type WeChatConfig = z.infer; - // eslint-disable-next-line unicorn/prefer-module const currentPath = __dirname; const pathToReadmeFile = path.join(currentPath, '..', 'README.md'); diff --git a/packages/connector-wechat/src/index.test.ts b/packages/connector-wechat/src/index.test.ts index 669331193..4d8ca3095 100644 --- a/packages/connector-wechat/src/index.test.ts +++ b/packages/connector-wechat/src/index.test.ts @@ -2,13 +2,9 @@ import { ConnectorError, ConnectorErrorCodes, GetConnectorConfig } from '@logto/ import nock from 'nock'; import { WeChatConnector } from '.'; -import { - WeChatConfig, - accessTokenEndpoint, - authorizationEndpoint, - userInfoEndpoint, -} from './constant'; +import { accessTokenEndpoint, authorizationEndpoint, userInfoEndpoint } from './constant'; import { mockedConfig } from './mock'; +import { WeChatConfig } from './types'; const getConnectorConfig = jest.fn() as GetConnectorConfig; diff --git a/packages/connector-wechat/src/index.ts b/packages/connector-wechat/src/index.ts index a19b84030..aadc6fd4d 100644 --- a/packages/connector-wechat/src/index.ts +++ b/packages/connector-wechat/src/index.ts @@ -24,9 +24,8 @@ import { scope, defaultMetadata, defaultTimeout, - weChatConfigGuard, - WeChatConfig, } from './constant'; +import { weChatConfigGuard, AccessTokenResponse, UserInfoResponse, WeChatConfig } from './types'; // As creating a WeChat Web/Mobile application needs a real App or Website record, the real test is temporarily not finished. // TODO: test with our own wechat mobile/web application (LOG-1910), already tested with other verified wechat web application @@ -63,15 +62,6 @@ export class WeChatConnector implements SocialConnector { }; public getAccessToken: GetAccessToken = async (code) => { - type AccessTokenResponse = { - access_token?: string; - openid?: string; - expires_in?: number; // In seconds - refresh_token?: string; - scope?: string; - errcode?: number; - }; - const { appId: appid, appSecret: secret } = await this.getConfig( this.metadata.target, this.metadata.platform @@ -99,14 +89,6 @@ export class WeChatConnector implements SocialConnector { // FIXME: // eslint-disable-next-line complexity public getUserInfo: GetUserInfo = async (accessTokenObject) => { - type UserInfoResponse = { - unionid?: string; - headimgurl?: string; - nickname?: string; - errcode?: number; - errmsg?: string; - }; - const { accessToken, openid } = accessTokenObject; try { diff --git a/packages/connector-wechat/src/types.ts b/packages/connector-wechat/src/types.ts new file mode 100644 index 000000000..ef41cbaa6 --- /dev/null +++ b/packages/connector-wechat/src/types.ts @@ -0,0 +1,22 @@ +import { z } from 'zod'; + +export const weChatConfigGuard = z.object({ appId: z.string(), appSecret: z.string() }); + +export type WeChatConfig = z.infer; + +export type AccessTokenResponse = { + access_token?: string; + openid?: string; + expires_in?: number; // In seconds + refresh_token?: string; + scope?: string; + errcode?: number; +}; + +export type UserInfoResponse = { + unionid?: string; + headimgurl?: string; + nickname?: string; + errcode?: number; + errmsg?: string; +};