mirror of
https://github.com/logto-io/logto.git
synced 2025-02-10 21:58:23 -05:00
refactor(connector): fix UTs
This commit is contained in:
parent
0a73c837cf
commit
0185629352
34 changed files with 361 additions and 291 deletions
|
@ -9,7 +9,7 @@
|
|||
* https://opendocs.alipay.com/open/204/105296/
|
||||
*/
|
||||
|
||||
import { SocialConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SocialConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
AuthResponseParser,
|
||||
ConnectorError,
|
||||
|
@ -46,10 +46,7 @@ import { signingParameters } from './utils';
|
|||
|
||||
export type { AlipayNativeConfig } from './types';
|
||||
|
||||
export default class AlipayNativeConnector<T> extends SocialConnectorInstance<
|
||||
AlipayNativeConfig,
|
||||
T
|
||||
> {
|
||||
export default class AlipayNativeConnector extends SocialConnector<AlipayNativeConfig> {
|
||||
private readonly signingParameters = signingParameters;
|
||||
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* https://opendocs.alipay.com/open/263/105808
|
||||
* https://opendocs.alipay.com/open/01emu5
|
||||
*/
|
||||
import { SocialConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SocialConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
AuthResponseParser,
|
||||
ConnectorError,
|
||||
|
@ -43,7 +43,7 @@ import { signingParameters } from './utils';
|
|||
|
||||
export type { AlipayConfig } from './types';
|
||||
|
||||
export default class AlipayConnector<T> extends SocialConnectorInstance<AlipayConfig, T> {
|
||||
export default class AlipayConnector extends SocialConnector<AlipayConfig> {
|
||||
private readonly signingParameters = signingParameters;
|
||||
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { EmailConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { EmailConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
|
@ -17,7 +17,7 @@ import {
|
|||
sendMailErrorResponseGuard,
|
||||
} from './types';
|
||||
|
||||
export default class AliyunDmConnector<T> extends EmailConnectorInstance<AliyunDmConfig, T> {
|
||||
export default class AliyunDmConnector extends EmailConnector<AliyunDmConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
@ -32,7 +32,7 @@ export default class AliyunDmConnector<T> extends EmailConnectorInstance<AliyunD
|
|||
}
|
||||
}
|
||||
|
||||
public readonly sendMessageBy: EmailSendMessageByFunction<AliyunDmConfig> = async (
|
||||
protected readonly sendMessageBy: EmailSendMessageByFunction<AliyunDmConfig> = async (
|
||||
config,
|
||||
address,
|
||||
type,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { SmsConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SmsConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
|
@ -12,7 +12,7 @@ import { defaultMetadata } from './constant';
|
|||
import { sendSms } from './single-send-text';
|
||||
import { aliyunSmsConfigGuard, AliyunSmsConfig, sendSmsResponseGuard } from './types';
|
||||
|
||||
export default class AliyunSmsConnector<T> extends SmsConnectorInstance<AliyunSmsConfig, T> {
|
||||
export default class AliyunSmsConnector extends SmsConnector<AliyunSmsConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
@ -27,7 +27,7 @@ export default class AliyunSmsConnector<T> extends SmsConnectorInstance<AliyunSm
|
|||
}
|
||||
}
|
||||
|
||||
public readonly sendMessageBy: SmsSendMessageByFunction<AliyunSmsConfig> = async (
|
||||
protected readonly sendMessageBy: SmsSendMessageByFunction<AliyunSmsConfig> = async (
|
||||
config,
|
||||
phone,
|
||||
type,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { SocialConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SocialConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
AuthResponseParser,
|
||||
GetAuthorizationUri,
|
||||
|
@ -13,7 +13,7 @@ import { scope, defaultMetadata, jwksUri, issuer, authorizationEndpoint } from '
|
|||
import { appleConfigGuard, authResponseGuard, AppleConfig, AuthResponse } from './types';
|
||||
|
||||
// TO-DO: support nonce validation
|
||||
export default class AppleConnector<T> extends SocialConnectorInstance<AppleConfig, T> {
|
||||
export default class AppleConnector extends SocialConnector<AppleConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
AuthorizationUrlRequest,
|
||||
CryptoProvider,
|
||||
} from '@azure/msal-node';
|
||||
import { SocialConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SocialConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
|
@ -27,7 +27,7 @@ import {
|
|||
userInfoResponseGuard,
|
||||
} from './types';
|
||||
|
||||
export default class AzureADConnector<T> extends SocialConnectorInstance<AzureADConfig, T> {
|
||||
export default class AzureADConnector extends SocialConnector<AzureADConfig> {
|
||||
public clientApplication!: ConfidentialClientApplication;
|
||||
public authCodeUrlParams!: AuthorizationUrlRequest;
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ import path from 'path';
|
|||
import {
|
||||
ConnectorMetadata,
|
||||
GetConnectorConfig,
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
EmailSendMessageFunction,
|
||||
EmailSendTestMessageFunction,
|
||||
EmailSendMessageByFunction,
|
||||
|
@ -17,24 +15,10 @@ import {
|
|||
AuthResponseParser,
|
||||
} from '@logto/connector-types';
|
||||
|
||||
export class BaseConnectorInstance<T, U> {
|
||||
export class BaseConnector<T> {
|
||||
public metadata!: ConnectorMetadata;
|
||||
public getConfig: GetConnectorConfig;
|
||||
|
||||
private _connector?: U;
|
||||
|
||||
public get connector() {
|
||||
if (!this._connector) {
|
||||
throw new ConnectorError(ConnectorErrorCodes.General);
|
||||
}
|
||||
|
||||
return this._connector;
|
||||
}
|
||||
|
||||
public set connector(input: U) {
|
||||
this._connector = input;
|
||||
}
|
||||
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
this.getConfig = getConnectorConfig;
|
||||
}
|
||||
|
@ -43,7 +27,7 @@ export class BaseConnectorInstance<T, U> {
|
|||
public validateConfig(config: unknown): asserts config is T {}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
public metadataParser = () => {
|
||||
protected metadataParser = () => {
|
||||
// eslint-disable-next-line unicorn/prefer-module
|
||||
const currentPath = __dirname;
|
||||
|
||||
|
@ -83,8 +67,8 @@ export class BaseConnectorInstance<T, U> {
|
|||
};
|
||||
}
|
||||
|
||||
export class SmsConnectorInstance<T, U> extends BaseConnectorInstance<T, U> {
|
||||
public readonly sendMessageBy!: EmailSendMessageByFunction<T>;
|
||||
export class SmsConnector<T> extends BaseConnector<T> {
|
||||
protected readonly sendMessageBy!: EmailSendMessageByFunction<T>;
|
||||
|
||||
public sendMessage: EmailSendMessageFunction = async (address, type, data) => {
|
||||
const config = await this.getConfig(this.metadata.id);
|
||||
|
@ -93,15 +77,15 @@ export class SmsConnectorInstance<T, U> extends BaseConnectorInstance<T, U> {
|
|||
return this.sendMessageBy(config, address, type, data);
|
||||
};
|
||||
|
||||
public sendTestMessage: EmailSendTestMessageFunction = async (config, address, type, data) => {
|
||||
public sendTestMessage?: EmailSendTestMessageFunction = async (config, address, type, data) => {
|
||||
this.validateConfig(config);
|
||||
|
||||
return this.sendMessageBy(config, address, type, data);
|
||||
};
|
||||
}
|
||||
|
||||
export class EmailConnectorInstance<T, U> extends BaseConnectorInstance<T, U> {
|
||||
public readonly sendMessageBy!: SmsSendMessageByFunction<T>;
|
||||
export class EmailConnector<T> extends BaseConnector<T> {
|
||||
protected readonly sendMessageBy!: SmsSendMessageByFunction<T>;
|
||||
|
||||
public sendMessage: SmsSendMessageFunction = async (address, type, data) => {
|
||||
const config = await this.getConfig(this.metadata.id);
|
||||
|
@ -110,22 +94,17 @@ export class EmailConnectorInstance<T, U> extends BaseConnectorInstance<T, U> {
|
|||
return this.sendMessageBy(config, address, type, data);
|
||||
};
|
||||
|
||||
public sendTestMessage: SmsSendTestMessageFunction = async (config, address, type, data) => {
|
||||
public sendTestMessage?: SmsSendTestMessageFunction = async (config, address, type, data) => {
|
||||
this.validateConfig(config);
|
||||
|
||||
return this.sendMessageBy(config, address, type, data);
|
||||
};
|
||||
}
|
||||
|
||||
export class SocialConnectorInstance<T, U> extends BaseConnectorInstance<T, U> {
|
||||
export class SocialConnector<T> extends BaseConnector<T> {
|
||||
public getAuthorizationUri!: GetAuthorizationUri;
|
||||
|
||||
public getUserInfo!: GetUserInfo;
|
||||
|
||||
protected authResponseParser!: AuthResponseParser;
|
||||
}
|
||||
|
||||
export type ConnectorInstance =
|
||||
| InstanceType<typeof SmsConnectorInstance>
|
||||
| InstanceType<typeof EmailConnectorInstance>
|
||||
| InstanceType<typeof SocialConnectorInstance>;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow
|
||||
*/
|
||||
|
||||
import { SocialConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SocialConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
AuthResponseParser,
|
||||
ConnectorError,
|
||||
|
@ -33,7 +33,7 @@ import {
|
|||
userInfoResponseGuard,
|
||||
} from './types';
|
||||
|
||||
export default class FacebookConnector<T> extends SocialConnectorInstance<FacebookConfig, T> {
|
||||
export default class FacebookConnector extends SocialConnector<FacebookConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { SocialConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SocialConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
AuthResponseParser,
|
||||
GetAuthorizationUri,
|
||||
|
@ -29,7 +29,7 @@ import {
|
|||
userInfoResponseGuard,
|
||||
} from './types';
|
||||
|
||||
export default class GithubConnector<T> extends SocialConnectorInstance<GithubConfig, T> {
|
||||
export default class GithubConnector extends SocialConnector<GithubConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* The Implementation of OpenID Connect of Google Identity Platform.
|
||||
* https://developers.google.com/identity/protocols/oauth2/openid-connect
|
||||
*/
|
||||
import { SocialConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SocialConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
AuthResponseParser,
|
||||
ConnectorError,
|
||||
|
@ -31,7 +31,7 @@ import {
|
|||
userInfoResponseGuard,
|
||||
} from './types';
|
||||
|
||||
export default class GoogleConnector<T> extends SocialConnectorInstance<GoogleConfig, T> {
|
||||
export default class GoogleConnector extends SocialConnector<GoogleConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
import { EmailConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { EmailConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
|
@ -13,7 +13,7 @@ import { assert } from '@silverhand/essentials';
|
|||
import { defaultMetadata } from './constant';
|
||||
import { mockMailConfigGuard, MockMailConfig } from './types';
|
||||
|
||||
export default class MockMailConnector<T> extends EmailConnectorInstance<MockMailConfig, T> {
|
||||
export default class MockMailConnector extends EmailConnector<MockMailConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
@ -28,7 +28,7 @@ export default class MockMailConnector<T> extends EmailConnectorInstance<MockMai
|
|||
}
|
||||
}
|
||||
|
||||
public readonly sendMessageBy: EmailSendMessageByFunction<MockMailConfig> = async (
|
||||
protected readonly sendMessageBy: EmailSendMessageByFunction<MockMailConfig> = async (
|
||||
config,
|
||||
address,
|
||||
type,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
import { SmsConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SmsConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
|
@ -13,7 +13,7 @@ import { assert } from '@silverhand/essentials';
|
|||
import { defaultMetadata } from './constant';
|
||||
import { mockSmsConfigGuard, MockSmsConfig } from './types';
|
||||
|
||||
export default class MockSmsConnector<T> extends SmsConnectorInstance<MockSmsConfig, T> {
|
||||
export default class MockSmsConnector extends SmsConnector<MockSmsConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { randomUUID } from 'crypto';
|
||||
|
||||
import { SocialConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SocialConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
|
@ -13,7 +13,7 @@ import { z } from 'zod';
|
|||
import { defaultMetadata } from './constant';
|
||||
import { mockSocialConfigGuard, MockSocialConfig } from './types';
|
||||
|
||||
export default class MockSocialConnector<T> extends SocialConnectorInstance<MockSocialConfig, T> {
|
||||
export default class MockSocialConnector extends SocialConnector<MockSocialConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { EmailConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { EmailConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
|
@ -18,10 +18,7 @@ import {
|
|||
PublicParameters,
|
||||
} from './types';
|
||||
|
||||
export default class SendGridMailConnector<T> extends EmailConnectorInstance<
|
||||
SendGridMailConfig,
|
||||
T
|
||||
> {
|
||||
export default class SendGridMailConnector extends EmailConnector<SendGridMailConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
@ -36,7 +33,7 @@ export default class SendGridMailConnector<T> extends EmailConnectorInstance<
|
|||
}
|
||||
}
|
||||
|
||||
public readonly sendMessageBy: EmailSendMessageByFunction<SendGridMailConfig> = async (
|
||||
protected readonly sendMessageBy: EmailSendMessageByFunction<SendGridMailConfig> = async (
|
||||
config,
|
||||
address,
|
||||
type,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { EmailConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { EmailConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
|
@ -12,7 +12,7 @@ import SMTPTransport from 'nodemailer/lib/smtp-transport';
|
|||
import { defaultMetadata } from './constant';
|
||||
import { ContextType, smtpConfigGuard, SmtpConfig } from './types';
|
||||
|
||||
export default class SmtpConnector<T> extends EmailConnectorInstance<SmtpConfig, T> {
|
||||
export default class SmtpConnector extends EmailConnector<SmtpConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
@ -27,7 +27,7 @@ export default class SmtpConnector<T> extends EmailConnectorInstance<SmtpConfig,
|
|||
}
|
||||
}
|
||||
|
||||
public readonly sendMessageBy: EmailSendMessageByFunction<SmtpConfig> = async (
|
||||
protected readonly sendMessageBy: EmailSendMessageByFunction<SmtpConfig> = async (
|
||||
config,
|
||||
address,
|
||||
type,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { SmsConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SmsConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
|
@ -11,7 +11,7 @@ import got, { HTTPError } from 'got';
|
|||
import { defaultMetadata, endpoint } from './constant';
|
||||
import { twilioSmsConfigGuard, TwilioSmsConfig, PublicParameters } from './types';
|
||||
|
||||
export default class TwilioSmsConnector<T> extends SmsConnectorInstance<TwilioSmsConfig, T> {
|
||||
export default class TwilioSmsConnector extends SmsConnector<TwilioSmsConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
@ -26,7 +26,7 @@ export default class TwilioSmsConnector<T> extends SmsConnectorInstance<TwilioSm
|
|||
}
|
||||
}
|
||||
|
||||
public readonly sendMessageBy: SmsSendMessageByFunction<TwilioSmsConfig> = async (
|
||||
protected readonly sendMessageBy: SmsSendMessageByFunction<TwilioSmsConfig> = async (
|
||||
config,
|
||||
phone,
|
||||
type,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
|
||||
*/
|
||||
|
||||
import { SocialConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SocialConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
GetAuthorizationUri,
|
||||
GetUserInfo,
|
||||
|
@ -35,10 +35,7 @@ import {
|
|||
WechatNativeConfig,
|
||||
} from './types';
|
||||
|
||||
export default class WechatNativeConnector<T> extends SocialConnectorInstance<
|
||||
WechatNativeConfig,
|
||||
T
|
||||
> {
|
||||
export default class WechatNativeConnector extends SocialConnector<WechatNativeConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
|
||||
*/
|
||||
|
||||
import { SocialConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { SocialConnector } from '@logto/connector-base-classes';
|
||||
import {
|
||||
AuthResponseParser,
|
||||
GetAuthorizationUri,
|
||||
|
@ -36,7 +36,7 @@ import {
|
|||
WechatConfig,
|
||||
} from './types';
|
||||
|
||||
export default class WechatConnector<T> extends SocialConnectorInstance<WechatConfig, T> {
|
||||
export default class WechatConnector extends SocialConnector<WechatConfig> {
|
||||
constructor(getConnectorConfig: GetConnectorConfig) {
|
||||
super(getConnectorConfig);
|
||||
this.metadata = defaultMetadata;
|
||||
|
|
|
@ -148,35 +148,35 @@ export const mockConnectorList: Connector[] = [
|
|||
|
||||
export const mockConnectorInstanceList: Array<{
|
||||
connector: Connector;
|
||||
metadata: ConnectorMetadata;
|
||||
instance: { metadata: ConnectorMetadata };
|
||||
}> = [
|
||||
{
|
||||
connector: mockConnector0,
|
||||
metadata: { ...mockMetadata0, type: ConnectorType.Social },
|
||||
instance: { metadata: { ...mockMetadata0, type: ConnectorType.Social } },
|
||||
},
|
||||
{
|
||||
connector: mockConnector1,
|
||||
metadata: mockMetadata1,
|
||||
instance: { metadata: mockMetadata1 },
|
||||
},
|
||||
{
|
||||
connector: mockConnector2,
|
||||
metadata: mockMetadata2,
|
||||
instance: { metadata: mockMetadata2 },
|
||||
},
|
||||
{
|
||||
connector: mockConnector3,
|
||||
metadata: mockMetadata3,
|
||||
instance: { metadata: mockMetadata3 },
|
||||
},
|
||||
{
|
||||
connector: mockConnector4,
|
||||
metadata: { ...mockMetadata4, type: ConnectorType.Email, platform: null },
|
||||
instance: { metadata: { ...mockMetadata4, type: ConnectorType.Email, platform: null } },
|
||||
},
|
||||
{
|
||||
connector: mockConnector5,
|
||||
metadata: { ...mockMetadata5, type: ConnectorType.SMS, platform: null },
|
||||
instance: { metadata: { ...mockMetadata5, type: ConnectorType.SMS, platform: null } },
|
||||
},
|
||||
{
|
||||
connector: mockConnector6,
|
||||
metadata: { ...mockMetadata6, type: ConnectorType.Email, platform: null },
|
||||
instance: { metadata: { ...mockMetadata6, type: ConnectorType.Email, platform: null } },
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -185,12 +185,14 @@ export const mockAliyunDmConnectorInstance = {
|
|||
...mockConnector,
|
||||
id: 'aliyun-dm',
|
||||
},
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'aliyun-dm',
|
||||
target: 'aliyun-dm',
|
||||
type: ConnectorType.Email,
|
||||
platform: null,
|
||||
instance: {
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'aliyun-dm',
|
||||
target: 'aliyun-dm',
|
||||
type: ConnectorType.Email,
|
||||
platform: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -199,12 +201,14 @@ export const mockAliyunSmsConnectorInstance = {
|
|||
...mockConnector,
|
||||
id: 'aliyun-sms',
|
||||
},
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'aliyun-sms',
|
||||
target: 'aliyun-sms',
|
||||
type: ConnectorType.SMS,
|
||||
platform: null,
|
||||
instance: {
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'aliyun-sms',
|
||||
target: 'aliyun-sms',
|
||||
type: ConnectorType.SMS,
|
||||
platform: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -213,12 +217,14 @@ export const mockFacebookConnectorInstance = {
|
|||
...mockConnector,
|
||||
id: 'facebook',
|
||||
},
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'facebook',
|
||||
target: 'facebook',
|
||||
type: ConnectorType.Social,
|
||||
platform: ConnectorPlatform.Web,
|
||||
instance: {
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'facebook',
|
||||
target: 'facebook',
|
||||
type: ConnectorType.Social,
|
||||
platform: ConnectorPlatform.Web,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -227,12 +233,14 @@ export const mockGithubConnectorInstance = {
|
|||
...mockConnector,
|
||||
id: 'github',
|
||||
},
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'github',
|
||||
target: 'github',
|
||||
type: ConnectorType.Social,
|
||||
platform: ConnectorPlatform.Web,
|
||||
instance: {
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'github',
|
||||
target: 'github',
|
||||
type: ConnectorType.Social,
|
||||
platform: ConnectorPlatform.Web,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -241,12 +249,14 @@ export const mockWechatConnectorInstance = {
|
|||
...mockConnector,
|
||||
id: 'wechat-web',
|
||||
},
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'wechat-web',
|
||||
target: 'wechat',
|
||||
type: ConnectorType.Social,
|
||||
platform: ConnectorPlatform.Web,
|
||||
instance: {
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'wechat-web',
|
||||
target: 'wechat',
|
||||
type: ConnectorType.Social,
|
||||
platform: ConnectorPlatform.Web,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -255,12 +265,14 @@ export const mockWechatNativeConnectorInstance = {
|
|||
...mockConnector,
|
||||
id: 'wechat-native',
|
||||
},
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'wechat-native',
|
||||
target: 'wechat',
|
||||
type: ConnectorType.Social,
|
||||
platform: ConnectorPlatform.Native,
|
||||
instance: {
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'wechat-native',
|
||||
target: 'wechat',
|
||||
type: ConnectorType.Social,
|
||||
platform: ConnectorPlatform.Native,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -270,12 +282,14 @@ export const mockGoogleConnectorInstance = {
|
|||
id: 'google',
|
||||
enabled: false,
|
||||
},
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'google',
|
||||
target: 'google',
|
||||
type: ConnectorType.Social,
|
||||
platform: ConnectorPlatform.Web,
|
||||
instance: {
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
id: 'google',
|
||||
target: 'google',
|
||||
type: ConnectorType.Social,
|
||||
platform: ConnectorPlatform.Web,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
import { ConnectorInstance, SocialConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { Connector } from '@logto/schemas';
|
||||
|
||||
import envSet from '@/env-set';
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import { findAllConnectors, insertConnector } from '@/queries/connector';
|
||||
|
||||
import { defaultConnectorPackages } from './consts';
|
||||
import { ConnectorType } from './types';
|
||||
import { ConnectorType, ConnectorInstance, Instance, SocialConnectorInstance } from './types';
|
||||
import { getConnectorConfig } from './utilities';
|
||||
|
||||
// eslint-disable-next-line @silverhand/fp/no-let
|
||||
let cachedConnectors: ConnectorInstance[] | undefined;
|
||||
let cachedConnectors: Instance[] | undefined;
|
||||
|
||||
const loadConnectors = async () => {
|
||||
if (cachedConnectors) {
|
||||
|
@ -28,8 +24,8 @@ const loadConnectors = async () => {
|
|||
connectorPackages.map(async (packageName) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { default: Builder } = await import(packageName);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
|
||||
const instance: ConnectorInstance = new Builder<Connector>(getConnectorConfig);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
||||
const instance: Instance = new Builder(getConnectorConfig);
|
||||
|
||||
return instance;
|
||||
})
|
||||
|
@ -42,20 +38,17 @@ export const getConnectorInstances = async (): Promise<ConnectorInstance[]> => {
|
|||
const connectors = await findAllConnectors();
|
||||
const connectorMap = new Map(connectors.map((connector) => [connector.id, connector]));
|
||||
|
||||
const allConnectors = await loadConnectors();
|
||||
const allInstances = await loadConnectors();
|
||||
|
||||
return allConnectors.map((element) => {
|
||||
const { id } = element.metadata;
|
||||
return allInstances.map((instance) => {
|
||||
const { id } = instance.metadata;
|
||||
const connector = connectorMap.get(id);
|
||||
|
||||
if (!connector) {
|
||||
throw new RequestError({ code: 'entity.not_found', id, status: 404 });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||
element.connector = connector;
|
||||
|
||||
return element;
|
||||
return { instance, connector };
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -76,13 +69,13 @@ export const getConnectorInstanceById = async (id: string): Promise<ConnectorIns
|
|||
|
||||
const isSocialConnectorInstance = (
|
||||
connector: ConnectorInstance
|
||||
): connector is InstanceType<typeof SocialConnectorInstance> => {
|
||||
return connector.metadata.type === ConnectorType.Social;
|
||||
): connector is SocialConnectorInstance => {
|
||||
return connector.instance.metadata.type === ConnectorType.Social;
|
||||
};
|
||||
|
||||
export const getSocialConnectorInstanceById = async (
|
||||
id: string
|
||||
): Promise<InstanceType<typeof SocialConnectorInstance>> => {
|
||||
): Promise<SocialConnectorInstance> => {
|
||||
const connector = await getConnectorInstanceById(id);
|
||||
|
||||
if (!isSocialConnectorInstance(connector)) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { PasscodeType } from '@logto/schemas';
|
||||
import { SmsConnector, EmailConnector, SocialConnector } from '@logto/connector-base-classes';
|
||||
import { PasscodeType, Connector } from '@logto/schemas';
|
||||
import { z } from 'zod';
|
||||
|
||||
export { ConnectorType } from '@logto/schemas';
|
||||
|
@ -15,3 +16,29 @@ export const socialUserInfoGuard = z.object({
|
|||
});
|
||||
|
||||
export type SocialUserInfo = z.infer<typeof socialUserInfoGuard>;
|
||||
|
||||
export type Instance =
|
||||
| InstanceType<typeof SmsConnector>
|
||||
| InstanceType<typeof EmailConnector>
|
||||
| InstanceType<typeof SocialConnector>;
|
||||
|
||||
export type SmsConnectorInstance = {
|
||||
instance: InstanceType<typeof SmsConnector>;
|
||||
connector: Connector;
|
||||
};
|
||||
|
||||
export type EmailConnectorInstance = {
|
||||
instance: InstanceType<typeof EmailConnector>;
|
||||
connector: Connector;
|
||||
};
|
||||
|
||||
export type SocialConnectorInstance = {
|
||||
instance: InstanceType<typeof SocialConnector>;
|
||||
connector: Connector;
|
||||
};
|
||||
|
||||
export type ConnectorInstance =
|
||||
| SmsConnectorInstance
|
||||
| EmailConnectorInstance
|
||||
| SocialConnectorInstance
|
||||
| { instance: Instance; connector: Connector };
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { ConnectorType } from '@logto/connector-types';
|
||||
import { Passcode, PasscodeType } from '@logto/schemas';
|
||||
import { ConnectorType, ValidateConfig, GetConnectorConfig } from '@logto/connector-types';
|
||||
import { Passcode, PasscodeType, Connector } from '@logto/schemas';
|
||||
|
||||
import { mockConnector, mockMetadata } from '@/__mocks__';
|
||||
import { getConnectorInstances } from '@/connectors';
|
||||
import { ConnectorMetadata } from '@/connectors/types';
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import {
|
||||
consumePasscode,
|
||||
|
@ -25,6 +25,17 @@ import {
|
|||
jest.mock('@/queries/passcode');
|
||||
jest.mock('@/connectors');
|
||||
|
||||
type ConnectorInstance = {
|
||||
connector: Connector;
|
||||
instance: {
|
||||
metadata: ConnectorMetadata;
|
||||
validateConfig?: ValidateConfig<unknown>;
|
||||
getConfig?: GetConnectorConfig;
|
||||
sendMessage?: unknown;
|
||||
sendTestMessage?: unknown;
|
||||
};
|
||||
};
|
||||
|
||||
const mockedFindUnconsumedPasscodesByJtiAndType =
|
||||
findUnconsumedPasscodesByJtiAndType as jest.MockedFunction<
|
||||
typeof findUnconsumedPasscodesByJtiAndType
|
||||
|
@ -37,14 +48,19 @@ const mockedDeletePasscodesByIds = deletePasscodesByIds as jest.MockedFunction<
|
|||
typeof deletePasscodesByIds
|
||||
>;
|
||||
const mockedInsertPasscode = insertPasscode as jest.MockedFunction<typeof insertPasscode>;
|
||||
const mockedGetConnectorInstances = getConnectorInstances as jest.MockedFunction<
|
||||
typeof getConnectorInstances
|
||||
>;
|
||||
const mockedConsumePasscode = consumePasscode as jest.MockedFunction<typeof consumePasscode>;
|
||||
const mockedIncreasePasscodeTryCount = increasePasscodeTryCount as jest.MockedFunction<
|
||||
typeof increasePasscodeTryCount
|
||||
>;
|
||||
|
||||
const getConnectorInstancesPlaceHolder = jest.fn() as jest.MockedFunction<
|
||||
() => Promise<ConnectorInstance[]>
|
||||
>;
|
||||
|
||||
jest.mock('@/connectors', () => ({
|
||||
getConnectorInstances: async () => getConnectorInstancesPlaceHolder(),
|
||||
}));
|
||||
|
||||
beforeAll(() => {
|
||||
mockedFindUnconsumedPasscodesByJtiAndType.mockResolvedValue([]);
|
||||
mockedInsertPasscode.mockImplementation(async (data): Promise<Passcode> => {
|
||||
|
@ -125,24 +141,27 @@ describe('sendPasscode', () => {
|
|||
|
||||
it('should throw error when email or sms connector can not be found', async () => {
|
||||
const sendMessage = jest.fn();
|
||||
const validateConfig = jest.fn();
|
||||
const getConfig = jest.fn();
|
||||
mockedGetConnectorInstances.mockResolvedValueOnce([
|
||||
const validateConfig = jest.fn() as ValidateConfig<unknown>;
|
||||
const getConfig = jest.fn() as GetConnectorConfig;
|
||||
const mockConnectorInstances: ConnectorInstance[] = [
|
||||
{
|
||||
connector: {
|
||||
...mockConnector,
|
||||
id: 'id1',
|
||||
},
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
type: ConnectorType.Email,
|
||||
platform: null,
|
||||
instance: {
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
type: ConnectorType.Email,
|
||||
platform: null,
|
||||
},
|
||||
sendMessage,
|
||||
validateConfig,
|
||||
getConfig,
|
||||
},
|
||||
sendMessage,
|
||||
validateConfig,
|
||||
getConfig,
|
||||
},
|
||||
]);
|
||||
];
|
||||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce(mockConnectorInstances);
|
||||
const passcode: Passcode = {
|
||||
id: 'id',
|
||||
interactionJti: 'jti',
|
||||
|
@ -164,38 +183,42 @@ describe('sendPasscode', () => {
|
|||
|
||||
it('should call sendPasscode with params matching', async () => {
|
||||
const sendMessage = jest.fn();
|
||||
const validateConfig = jest.fn();
|
||||
const getConfig = jest.fn();
|
||||
mockedGetConnectorInstances.mockResolvedValueOnce([
|
||||
const validateConfig = jest.fn() as ValidateConfig<unknown>;
|
||||
const getConfig = jest.fn() as GetConnectorConfig;
|
||||
const mockConnectorInstances: ConnectorInstance[] = [
|
||||
{
|
||||
connector: {
|
||||
...mockConnector,
|
||||
id: 'id0',
|
||||
},
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
type: ConnectorType.SMS,
|
||||
platform: null,
|
||||
instance: {
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
type: ConnectorType.SMS,
|
||||
platform: null,
|
||||
},
|
||||
sendMessage,
|
||||
validateConfig,
|
||||
getConfig,
|
||||
},
|
||||
sendMessage,
|
||||
validateConfig,
|
||||
getConfig,
|
||||
},
|
||||
{
|
||||
connector: {
|
||||
...mockConnector,
|
||||
id: 'id1',
|
||||
},
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
type: ConnectorType.Email,
|
||||
platform: null,
|
||||
instance: {
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
type: ConnectorType.Email,
|
||||
platform: null,
|
||||
},
|
||||
validateConfig,
|
||||
getConfig,
|
||||
},
|
||||
sendMessage,
|
||||
validateConfig,
|
||||
getConfig,
|
||||
},
|
||||
]);
|
||||
];
|
||||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce(mockConnectorInstances);
|
||||
const passcode: Passcode = {
|
||||
id: 'passcode_id',
|
||||
interactionJti: 'jti',
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { EmailConnectorInstance, SmsConnectorInstance } from '@logto/connector-base-classes';
|
||||
import { Passcode, PasscodeType } from '@logto/schemas';
|
||||
import { customAlphabet, nanoid } from 'nanoid';
|
||||
|
||||
import { getConnectorInstances } from '@/connectors';
|
||||
import { ConnectorType } from '@/connectors/types';
|
||||
import { ConnectorType, EmailConnectorInstance, SmsConnectorInstance } from '@/connectors/types';
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import {
|
||||
consumePasscode,
|
||||
|
@ -49,12 +48,12 @@ export const sendPasscode = async (passcode: Passcode) => {
|
|||
const connectorInstances = await getConnectorInstances();
|
||||
|
||||
const emailConnectorInstance = connectorInstances.find(
|
||||
(connector): connector is InstanceType<typeof EmailConnectorInstance> =>
|
||||
connector.connector.enabled && connector.metadata.type === ConnectorType.Email
|
||||
(connector): connector is EmailConnectorInstance =>
|
||||
connector.connector.enabled && connector.instance.metadata.type === ConnectorType.Email
|
||||
);
|
||||
const smsConnectorInstance = connectorInstances.find(
|
||||
(connector): connector is InstanceType<typeof SmsConnectorInstance> =>
|
||||
connector.connector.enabled && connector.metadata.type === ConnectorType.SMS
|
||||
(connector): connector is SmsConnectorInstance =>
|
||||
connector.connector.enabled && connector.instance.metadata.type === ConnectorType.SMS
|
||||
);
|
||||
|
||||
const connectorInstance = passcode.email ? emailConnectorInstance : smsConnectorInstance;
|
||||
|
@ -67,7 +66,10 @@ export const sendPasscode = async (passcode: Passcode) => {
|
|||
})
|
||||
);
|
||||
|
||||
const { connector, metadata, sendMessage } = connectorInstance;
|
||||
const {
|
||||
connector,
|
||||
instance: { metadata, sendMessage },
|
||||
} = connectorInstance;
|
||||
|
||||
const response = await sendMessage(emailOrPhone, passcode.type, {
|
||||
code: passcode.code,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { ConnectorInstance } from '@logto/connector-types';
|
||||
import { BrandingStyle, SignInMethodState, ConnectorType } from '@logto/schemas';
|
||||
|
||||
import {
|
||||
|
@ -8,6 +7,7 @@ import {
|
|||
mockBranding,
|
||||
mockSignInMethods,
|
||||
} from '@/__mocks__';
|
||||
import { ConnectorInstance } from '@/connectors/types';
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import {
|
||||
isEnabled,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { ConnectorInstance } from '@logto/connector-base-classes';
|
||||
import {
|
||||
Branding,
|
||||
BrandingStyle,
|
||||
|
@ -8,7 +7,7 @@ import {
|
|||
} from '@logto/schemas';
|
||||
import { Optional } from '@silverhand/essentials';
|
||||
|
||||
import { ConnectorType } from '@/connectors/types';
|
||||
import { ConnectorInstance, ConnectorType } from '@/connectors/types';
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
|
||||
|
@ -42,7 +41,7 @@ export const validateSignInMethods = (
|
|||
|
||||
if (isEnabled(signInMethods.email)) {
|
||||
assertThat(
|
||||
enabledConnectorInstances.some((item) => item.metadata.type === ConnectorType.Email),
|
||||
enabledConnectorInstances.some((item) => item.instance.metadata.type === ConnectorType.Email),
|
||||
new RequestError({
|
||||
code: 'sign_in_experiences.enabled_connector_not_found',
|
||||
type: ConnectorType.Email,
|
||||
|
@ -52,7 +51,7 @@ export const validateSignInMethods = (
|
|||
|
||||
if (isEnabled(signInMethods.sms)) {
|
||||
assertThat(
|
||||
enabledConnectorInstances.some((item) => item.metadata.type === ConnectorType.SMS),
|
||||
enabledConnectorInstances.some((item) => item.instance.metadata.type === ConnectorType.SMS),
|
||||
new RequestError({
|
||||
code: 'sign_in_experiences.enabled_connector_not_found',
|
||||
type: ConnectorType.SMS,
|
||||
|
@ -62,7 +61,9 @@ export const validateSignInMethods = (
|
|||
|
||||
if (isEnabled(signInMethods.social)) {
|
||||
assertThat(
|
||||
enabledConnectorInstances.some((item) => item.metadata.type === ConnectorType.Social),
|
||||
enabledConnectorInstances.some(
|
||||
(item) => item.instance.metadata.type === ConnectorType.Social
|
||||
),
|
||||
new RequestError({
|
||||
code: 'sign_in_experiences.enabled_connector_not_found',
|
||||
type: ConnectorType.Social,
|
||||
|
|
|
@ -41,7 +41,7 @@ export const getUserInfoByAuthCode = async (
|
|||
): Promise<SocialUserInfo> => {
|
||||
const connector = await getConnector(connectorId);
|
||||
|
||||
return connector.getUserInfo(data);
|
||||
return connector.instance.getUserInfo(data);
|
||||
};
|
||||
|
||||
export const getUserInfoFromInteractionResult = async (
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
import {
|
||||
ValidateConfig,
|
||||
EmailConnectorInstance,
|
||||
SmsConnectorInstance,
|
||||
} from '@logto/connector-types';
|
||||
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-types';
|
||||
import { Connector, ConnectorType } from '@logto/schemas';
|
||||
|
||||
import { mockConnectorInstanceList, mockMetadata, mockConnector } from '@/__mocks__';
|
||||
|
@ -15,9 +11,13 @@ import connectorRoutes from './connector';
|
|||
|
||||
type ConnectorInstance = {
|
||||
connector: Connector;
|
||||
metadata: ConnectorMetadata;
|
||||
validateConfig?: ValidateConfig;
|
||||
sendMessage?: unknown;
|
||||
instance: {
|
||||
metadata: ConnectorMetadata;
|
||||
validateConfig?: ValidateConfig<unknown>;
|
||||
getConfig?: GetConnectorConfig;
|
||||
sendMessage?: unknown;
|
||||
sendTestMessage?: unknown;
|
||||
};
|
||||
};
|
||||
|
||||
const getConnectorInstancesPlaceHolder = jest.fn() as jest.MockedFunction<
|
||||
|
@ -59,7 +59,7 @@ describe('connector route', () => {
|
|||
it('throws if more than one SMS connector is enabled', async () => {
|
||||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce(
|
||||
mockConnectorInstanceList.filter(
|
||||
(connectorInstance) => connectorInstance.metadata.type !== ConnectorType.Email
|
||||
(connectorInstance) => connectorInstance.instance.metadata.type !== ConnectorType.Email
|
||||
)
|
||||
);
|
||||
const response = await connectorRequest.get('/connectors').send({});
|
||||
|
@ -69,7 +69,7 @@ describe('connector route', () => {
|
|||
it('shows all connectors', async () => {
|
||||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce(
|
||||
mockConnectorInstanceList.filter(
|
||||
(connectorInstance) => connectorInstance.metadata.type === ConnectorType.Social
|
||||
(connectorInstance) => connectorInstance.instance.metadata.type === ConnectorType.Social
|
||||
)
|
||||
);
|
||||
const response = await connectorRequest.get('/connectors').send({});
|
||||
|
@ -107,46 +107,50 @@ describe('connector route', () => {
|
|||
});
|
||||
|
||||
it('should get SMS connector and send test message', async () => {
|
||||
const mockSendTestMessage = jest.fn();
|
||||
const mockedMetadata = {
|
||||
...mockMetadata,
|
||||
type: ConnectorType.SMS,
|
||||
};
|
||||
const mockedSmsConnectorInstance: SmsConnectorInstance = {
|
||||
const mockedSmsConnectorInstance: ConnectorInstance = {
|
||||
connector: mockConnector,
|
||||
metadata: mockedMetadata,
|
||||
validateConfig: jest.fn(),
|
||||
getConfig: jest.fn(),
|
||||
sendMessage: jest.fn(),
|
||||
sendTestMessage: jest.fn(),
|
||||
instance: {
|
||||
metadata: mockedMetadata,
|
||||
validateConfig: jest.fn(),
|
||||
getConfig: jest.fn(),
|
||||
sendMessage: jest.fn(),
|
||||
sendTestMessage: mockSendTestMessage,
|
||||
},
|
||||
};
|
||||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([mockedSmsConnectorInstance]);
|
||||
const sendMessageSpy = jest.spyOn(mockedSmsConnectorInstance, 'sendTestMessage');
|
||||
const response = await connectorRequest
|
||||
.post('/connectors/id/test')
|
||||
.send({ phone: '12345678901', config: { test: 123 } });
|
||||
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(sendMessageSpy).toHaveBeenCalledWith({ test: 123 }, '12345678901', 'Test', {
|
||||
expect(mockSendTestMessage).toHaveBeenCalledTimes(1);
|
||||
expect(mockSendTestMessage).toHaveBeenCalledWith({ test: 123 }, '12345678901', 'Test', {
|
||||
code: '123456',
|
||||
});
|
||||
expect(response).toHaveProperty('statusCode', 204);
|
||||
});
|
||||
|
||||
it('should get email connector and send test message', async () => {
|
||||
const mockedEmailConnector: EmailConnectorInstance = {
|
||||
const sendTestMessage = jest.fn();
|
||||
const mockedEmailConnector: ConnectorInstance = {
|
||||
connector: mockConnector,
|
||||
metadata: mockMetadata,
|
||||
validateConfig: jest.fn(),
|
||||
getConfig: jest.fn(),
|
||||
sendMessage: jest.fn(),
|
||||
sendTestMessage: jest.fn(),
|
||||
instance: {
|
||||
metadata: mockMetadata,
|
||||
validateConfig: jest.fn(),
|
||||
getConfig: jest.fn(),
|
||||
sendMessage: jest.fn(),
|
||||
sendTestMessage,
|
||||
},
|
||||
};
|
||||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([mockedEmailConnector]);
|
||||
const sendMessageSpy = jest.spyOn(mockedEmailConnector, 'sendTestMessage');
|
||||
const response = await connectorRequest
|
||||
.post('/connectors/id/test')
|
||||
.send({ email: 'test@email.com', config: { test: 123 } });
|
||||
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(sendMessageSpy).toHaveBeenCalledWith({ test: 123 }, 'test@email.com', 'Test', {
|
||||
expect(sendTestMessage).toHaveBeenCalledTimes(1);
|
||||
expect(sendTestMessage).toHaveBeenCalledWith({ test: 123 }, 'test@email.com', 'Test', {
|
||||
code: 'email-test',
|
||||
});
|
||||
expect(response).toHaveProperty('statusCode', 204);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import {
|
||||
ConnectorInstance,
|
||||
EmailConnectorInstance,
|
||||
SmsConnectorInstance,
|
||||
} from '@logto/connector-base-classes';
|
||||
import { arbitraryObjectGuard, ConnectorDto, Connectors, ConnectorType } from '@logto/schemas';
|
||||
import { emailRegEx, phoneRegEx } from '@logto/shared';
|
||||
import { object, string } from 'zod';
|
||||
|
||||
import { getConnectorInstances, getConnectorInstanceById } from '@/connectors';
|
||||
import {
|
||||
ConnectorInstance,
|
||||
EmailConnectorInstance,
|
||||
SmsConnectorInstance,
|
||||
} from '@/connectors/types';
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import koaGuard from '@/middleware/koa-guard';
|
||||
import { updateConnector } from '@/queries/connector';
|
||||
|
@ -15,7 +15,10 @@ import assertThat from '@/utils/assert-that';
|
|||
|
||||
import { AuthedRouter } from './types';
|
||||
|
||||
const transpileConnectorInstance = ({ connector, metadata }: ConnectorInstance): ConnectorDto => ({
|
||||
const transpileConnectorInstance = ({
|
||||
connector,
|
||||
instance: { metadata },
|
||||
}: ConnectorInstance): ConnectorDto => ({
|
||||
...connector,
|
||||
...metadata,
|
||||
});
|
||||
|
@ -35,20 +38,26 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
|
|||
assertThat(
|
||||
connectorInstances.filter(
|
||||
(connector) =>
|
||||
connector.connector.enabled && connector.metadata.type === ConnectorType.Email
|
||||
connector.connector.enabled && connector.instance.metadata.type === ConnectorType.Email
|
||||
).length <= 1,
|
||||
'connector.more_than_one_email'
|
||||
);
|
||||
assertThat(
|
||||
connectorInstances.filter(
|
||||
(connector) =>
|
||||
connector.connector.enabled && connector.metadata.type === ConnectorType.SMS
|
||||
connector.connector.enabled && connector.instance.metadata.type === ConnectorType.SMS
|
||||
).length <= 1,
|
||||
'connector.more_than_one_sms'
|
||||
);
|
||||
|
||||
const filteredInstances = filterTarget
|
||||
? connectorInstances.filter(({ metadata: { target } }) => target === filterTarget)
|
||||
? connectorInstances.filter(
|
||||
({
|
||||
instance: {
|
||||
metadata: { target },
|
||||
},
|
||||
}) => target === filterTarget
|
||||
)
|
||||
: connectorInstances;
|
||||
|
||||
ctx.body = filteredInstances.map((connectorInstance) =>
|
||||
|
@ -88,8 +97,7 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
|
|||
const connectorInstance = await getConnectorInstanceById(id);
|
||||
const {
|
||||
connector: { config },
|
||||
validateConfig,
|
||||
metadata,
|
||||
instance: { validateConfig, metadata },
|
||||
} = connectorInstance;
|
||||
|
||||
/**
|
||||
|
@ -113,7 +121,7 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
|
|||
connectors
|
||||
.filter(
|
||||
(connector) =>
|
||||
connector.metadata.type === metadata.type && connector.connector.enabled
|
||||
connector.instance.metadata.type === metadata.type && connector.connector.enabled
|
||||
)
|
||||
.map(async ({ connector: { id } }) =>
|
||||
updateConnector({ set: { enabled: false }, where: { id }, jsonbMode: 'merge' })
|
||||
|
@ -144,7 +152,9 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
|
|||
body,
|
||||
} = ctx.guard;
|
||||
|
||||
const { metadata, validateConfig } = await getConnectorInstanceById(id);
|
||||
const {
|
||||
instance: { metadata, validateConfig },
|
||||
} = await getConnectorInstanceById(id);
|
||||
|
||||
/**
|
||||
* Assertion functions always need explicit annotations.
|
||||
|
@ -184,17 +194,16 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
|
|||
const subject = phone ?? email;
|
||||
assertThat(subject, new RequestError({ code: 'guard.invalid_input' }));
|
||||
|
||||
const connector:
|
||||
| InstanceType<typeof SmsConnectorInstance>
|
||||
| InstanceType<typeof EmailConnectorInstance>
|
||||
| undefined = phone
|
||||
const connector: SmsConnectorInstance | EmailConnectorInstance | undefined = phone
|
||||
? connectorInstances.find(
|
||||
(connector): connector is InstanceType<typeof SmsConnectorInstance> =>
|
||||
connector.metadata.id === id && connector.metadata.type === ConnectorType.SMS
|
||||
(connector): connector is SmsConnectorInstance =>
|
||||
connector.instance.metadata.id === id &&
|
||||
connector.instance.metadata.type === ConnectorType.SMS
|
||||
)
|
||||
: connectorInstances.find(
|
||||
(connector): connector is InstanceType<typeof EmailConnectorInstance> =>
|
||||
connector.metadata.id === id && connector.metadata.type === ConnectorType.Email
|
||||
(connector): connector is EmailConnectorInstance =>
|
||||
connector.instance.metadata.id === id &&
|
||||
connector.instance.metadata.type === ConnectorType.Email
|
||||
);
|
||||
|
||||
assertThat(
|
||||
|
@ -205,7 +214,9 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
|
|||
})
|
||||
);
|
||||
|
||||
const { sendTestMessage } = connector;
|
||||
const {
|
||||
instance: { sendTestMessage },
|
||||
} = connector;
|
||||
assertThat(
|
||||
sendTestMessage,
|
||||
new RequestError({
|
||||
|
|
|
@ -12,9 +12,11 @@ import connectorRoutes from './connector';
|
|||
|
||||
type ConnectorInstance = {
|
||||
connector: Connector;
|
||||
metadata: ConnectorMetadata;
|
||||
validateConfig?: ValidateConfig;
|
||||
sendMessage?: unknown;
|
||||
instance: {
|
||||
metadata: ConnectorMetadata;
|
||||
validateConfig?: ValidateConfig<unknown>;
|
||||
sendMessage?: unknown;
|
||||
};
|
||||
};
|
||||
|
||||
const getConnectorInstancesPlaceHolder = jest.fn() as jest.MockedFunction<
|
||||
|
@ -22,9 +24,11 @@ const getConnectorInstancesPlaceHolder = jest.fn() as jest.MockedFunction<
|
|||
>;
|
||||
const getConnectorInstanceByIdPlaceHolder = jest.fn(async (connectorId: string) => {
|
||||
const connectorInstances = await getConnectorInstancesPlaceHolder();
|
||||
const connector = connectorInstances.find(({ connector }) => connector.id === connectorId);
|
||||
const connectorInstance = connectorInstances.find(
|
||||
({ connector }) => connector.id === connectorId
|
||||
);
|
||||
assertThat(
|
||||
connector,
|
||||
connectorInstance,
|
||||
new RequestError({
|
||||
code: 'entity.not_found',
|
||||
connectorId,
|
||||
|
@ -32,13 +36,18 @@ const getConnectorInstanceByIdPlaceHolder = jest.fn(async (connectorId: string)
|
|||
})
|
||||
);
|
||||
|
||||
const { instance, connector } = connectorInstance;
|
||||
|
||||
return {
|
||||
...connector,
|
||||
validateConfig: validateConfigPlaceHolder,
|
||||
sendMessage: sendMessagePlaceHolder,
|
||||
connector,
|
||||
instance: {
|
||||
...instance,
|
||||
validateConfig: validateConfigPlaceHolder,
|
||||
sendMessage: sendMessagePlaceHolder,
|
||||
},
|
||||
};
|
||||
});
|
||||
const validateConfigPlaceHolder = jest.fn() as jest.MockedFunction<ValidateConfig>;
|
||||
const validateConfigPlaceHolder = jest.fn() as jest.MockedFunction<ValidateConfig<unknown>>;
|
||||
const sendMessagePlaceHolder = jest.fn();
|
||||
|
||||
jest.mock('@/queries/connector', () => ({
|
||||
|
@ -78,7 +87,7 @@ describe('connector PATCH routes', () => {
|
|||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
|
||||
{
|
||||
connector: mockConnector,
|
||||
metadata: { ...mockMetadata, type: ConnectorType.Social },
|
||||
instance: { metadata: { ...mockMetadata, type: ConnectorType.Social } },
|
||||
},
|
||||
]);
|
||||
const response = await connectorRequest
|
||||
|
@ -104,7 +113,7 @@ describe('connector PATCH routes', () => {
|
|||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
|
||||
{
|
||||
connector: mockConnector,
|
||||
metadata: mockMetadata,
|
||||
instance: { metadata: mockMetadata },
|
||||
},
|
||||
]);
|
||||
const response = await connectorRequest
|
||||
|
@ -117,7 +126,7 @@ describe('connector PATCH routes', () => {
|
|||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
|
||||
{
|
||||
connector: mockConnector,
|
||||
metadata: mockMetadata,
|
||||
instance: { metadata: mockMetadata },
|
||||
},
|
||||
]);
|
||||
const response = await connectorRequest
|
||||
|
@ -149,9 +158,7 @@ describe('connector PATCH routes', () => {
|
|||
};
|
||||
getConnectorInstanceByIdPlaceHolder.mockResolvedValueOnce({
|
||||
connector: mockedConnector,
|
||||
metadata: mockedMetadata,
|
||||
validateConfig: jest.fn(),
|
||||
sendMessage: jest.fn(),
|
||||
instance: { metadata: mockedMetadata, validateConfig: jest.fn(), sendMessage: jest.fn() },
|
||||
});
|
||||
const response = await connectorRequest
|
||||
.patch('/connectors/id1/enabled')
|
||||
|
@ -193,9 +200,11 @@ describe('connector PATCH routes', () => {
|
|||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
|
||||
{
|
||||
connector: mockConnector,
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
type: ConnectorType.SMS,
|
||||
instance: {
|
||||
metadata: {
|
||||
...mockMetadata,
|
||||
type: ConnectorType.SMS,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
@ -209,7 +218,7 @@ describe('connector PATCH routes', () => {
|
|||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
|
||||
{
|
||||
connector: mockConnector,
|
||||
metadata: mockMetadata,
|
||||
instance: { metadata: mockMetadata },
|
||||
},
|
||||
]);
|
||||
const response = await connectorRequest
|
||||
|
@ -253,7 +262,7 @@ describe('connector PATCH routes', () => {
|
|||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
|
||||
{
|
||||
connector: mockConnector,
|
||||
metadata: mockMetadata,
|
||||
instance: { metadata: mockMetadata },
|
||||
},
|
||||
]);
|
||||
const response = await connectorRequest
|
||||
|
@ -266,7 +275,7 @@ describe('connector PATCH routes', () => {
|
|||
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
|
||||
{
|
||||
connector: mockConnector,
|
||||
metadata: mockMetadata,
|
||||
instance: { metadata: mockMetadata },
|
||||
},
|
||||
]);
|
||||
const response = await connectorRequest
|
||||
|
|
|
@ -61,13 +61,13 @@ const getConnectorInstanceByIdHelper = jest.fn(async (connectorId: string) => {
|
|||
type: connectorId.startsWith('social') ? ConnectorType.Social : ConnectorType.SMS,
|
||||
};
|
||||
|
||||
return { connector, metadata, getAuthorizationUri: jest.fn(async () => '') };
|
||||
return { connector, instance: { metadata, getAuthorizationUri: jest.fn(async () => '') } };
|
||||
});
|
||||
jest.mock('@/connectors', () => ({
|
||||
getSocialConnectorInstanceById: async (connectorId: string) => {
|
||||
const connectorInstance = await getConnectorInstanceByIdHelper(connectorId);
|
||||
|
||||
if (connectorInstance.metadata.type !== ConnectorType.Social) {
|
||||
if (connectorInstance.instance.metadata.type !== ConnectorType.Social) {
|
||||
throw new RequestError({
|
||||
code: 'entity.not_found',
|
||||
status: 404,
|
||||
|
@ -170,7 +170,7 @@ describe('sessionSocialRoutes', () => {
|
|||
|
||||
it('throw error when auth code is wrong', async () => {
|
||||
(getConnectorInstanceById as jest.Mock).mockResolvedValueOnce({
|
||||
metadata: { target: connectorTarget },
|
||||
instance: { metadata: { target: connectorTarget } },
|
||||
});
|
||||
const response = await sessionRequest.post('/session/sign-in/social/auth').send({
|
||||
connectorId: 'connectorId',
|
||||
|
@ -183,7 +183,7 @@ describe('sessionSocialRoutes', () => {
|
|||
|
||||
it('throw error when code is provided but connector can not be found', async () => {
|
||||
(getConnectorInstanceById as jest.Mock).mockResolvedValueOnce({
|
||||
metadata: { target: connectorTarget },
|
||||
instance: { metadata: { target: connectorTarget } },
|
||||
});
|
||||
const response = await sessionRequest.post('/session/sign-in/social/auth').send({
|
||||
connectorId: '_connectorId',
|
||||
|
@ -196,7 +196,7 @@ describe('sessionSocialRoutes', () => {
|
|||
|
||||
it('get and add user info with auth code, as well as assign result and redirect', async () => {
|
||||
(getConnectorInstanceById as jest.Mock).mockResolvedValueOnce({
|
||||
metadata: { target: connectorTarget },
|
||||
instance: { metadata: { target: connectorTarget } },
|
||||
});
|
||||
const response = await sessionRequest.post('/session/sign-in/social/auth').send({
|
||||
connectorId: 'connectorId',
|
||||
|
@ -224,7 +224,7 @@ describe('sessionSocialRoutes', () => {
|
|||
it('throw error when identity exists', async () => {
|
||||
const wrongConnectorTarget = 'wrongConnectorTarget';
|
||||
(getConnectorInstanceById as jest.Mock).mockResolvedValueOnce({
|
||||
metadata: { target: wrongConnectorTarget },
|
||||
instance: { metadata: { target: wrongConnectorTarget } },
|
||||
});
|
||||
const response = await sessionRequest.post('/session/sign-in/social/auth').send({
|
||||
connectorId: '_connectorId_',
|
||||
|
@ -250,7 +250,7 @@ describe('sessionSocialRoutes', () => {
|
|||
beforeEach(() => {
|
||||
const mockGetConnectorInstanceById = getConnectorInstanceById as jest.Mock;
|
||||
mockGetConnectorInstanceById.mockResolvedValueOnce({
|
||||
metadata: { target: 'connectorTarget' },
|
||||
instance: { metadata: { target: 'connectorTarget' } },
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
|
@ -312,7 +312,7 @@ describe('sessionSocialRoutes', () => {
|
|||
beforeEach(() => {
|
||||
const mockGetConnectorInstanceById = getConnectorInstanceById as jest.Mock;
|
||||
mockGetConnectorInstanceById.mockResolvedValueOnce({
|
||||
metadata: { target: 'connectorTarget' },
|
||||
instance: { metadata: { target: 'connectorTarget' } },
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
|
@ -378,7 +378,7 @@ describe('sessionSocialRoutes', () => {
|
|||
beforeEach(() => {
|
||||
const mockGetConnectorInstanceById = getConnectorInstanceById as jest.Mock;
|
||||
mockGetConnectorInstanceById.mockResolvedValueOnce({
|
||||
metadata: { target: 'connectorTarget' },
|
||||
instance: { metadata: { target: 'connectorTarget' } },
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
|
|
|
@ -45,7 +45,7 @@ export default function sessionSocialRoutes<T extends AnonymousRouter>(
|
|||
assertThat(state && redirectUri, 'session.insufficient_info');
|
||||
const connector = await getSocialConnectorInstanceById(connectorId);
|
||||
assertThat(connector.connector.enabled, 'connector.not_enabled');
|
||||
const redirectTo = await connector.getAuthorizationUri({ state, redirectUri });
|
||||
const redirectTo = await connector.instance.getAuthorizationUri({ state, redirectUri });
|
||||
ctx.body = { redirectTo };
|
||||
|
||||
return next();
|
||||
|
@ -67,7 +67,9 @@ export default function sessionSocialRoutes<T extends AnonymousRouter>(
|
|||
const type = 'SignInSocial';
|
||||
ctx.log(type, { connectorId, data });
|
||||
const {
|
||||
metadata: { target },
|
||||
instance: {
|
||||
metadata: { target },
|
||||
},
|
||||
} = await getConnectorInstanceById(connectorId);
|
||||
|
||||
const userInfo = await getUserInfoByAuthCode(connectorId, data);
|
||||
|
@ -118,7 +120,9 @@ export default function sessionSocialRoutes<T extends AnonymousRouter>(
|
|||
const type = 'SignInSocialBind';
|
||||
ctx.log(type, { connectorId });
|
||||
const {
|
||||
metadata: { target },
|
||||
instance: {
|
||||
metadata: { target },
|
||||
},
|
||||
} = await getConnectorInstanceById(connectorId);
|
||||
|
||||
const userInfo = await getUserInfoFromInteractionResult(connectorId, result);
|
||||
|
@ -158,7 +162,9 @@ export default function sessionSocialRoutes<T extends AnonymousRouter>(
|
|||
const type = 'RegisterSocial';
|
||||
ctx.log(type, { connectorId });
|
||||
const {
|
||||
metadata: { target },
|
||||
instance: {
|
||||
metadata: { target },
|
||||
},
|
||||
} = await getConnectorInstanceById(connectorId);
|
||||
|
||||
const userInfo = await getUserInfoFromInteractionResult(connectorId, result);
|
||||
|
@ -203,7 +209,9 @@ export default function sessionSocialRoutes<T extends AnonymousRouter>(
|
|||
const type = 'RegisterSocialBind';
|
||||
ctx.log(type, { connectorId, userId });
|
||||
const {
|
||||
metadata: { target },
|
||||
instance: {
|
||||
metadata: { target },
|
||||
},
|
||||
} = await getConnectorInstanceById(connectorId);
|
||||
|
||||
const userInfo = await getUserInfoFromInteractionResult(connectorId, result);
|
||||
|
|
|
@ -52,7 +52,8 @@ export default function signInExperiencesRoutes<T extends AuthedRouter>(router:
|
|||
const filteredSocialSignInConnectorTargets = socialSignInConnectorTargets?.filter((target) =>
|
||||
enabledConnectorInstances.some(
|
||||
(connector) =>
|
||||
connector.metadata.target === target && connector.metadata.type === ConnectorType.Social
|
||||
connector.instance.metadata.target === target &&
|
||||
connector.instance.metadata.type === ConnectorType.Social
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ jest.mock('@/connectors', () => ({
|
|||
getSocialConnectorInstanceById: async (connectorId: string) => {
|
||||
const connectorInstance = await getConnectorInstanceById(connectorId);
|
||||
|
||||
if (connectorInstance.metadata.type !== ConnectorType.Social) {
|
||||
if (connectorInstance.instance.metadata.type !== ConnectorType.Social) {
|
||||
throw new RequestError({
|
||||
code: 'entity.not_found',
|
||||
status: 404,
|
||||
|
@ -92,19 +92,19 @@ describe('GET /.well-known/sign-in-exp', () => {
|
|||
...mockSignInExperience,
|
||||
socialConnectors: [
|
||||
{
|
||||
...mockGithubConnectorInstance.metadata,
|
||||
...mockGithubConnectorInstance.instance.metadata,
|
||||
id: mockGithubConnectorInstance.connector.id,
|
||||
},
|
||||
{
|
||||
...mockFacebookConnectorInstance.metadata,
|
||||
...mockFacebookConnectorInstance.instance.metadata,
|
||||
id: mockFacebookConnectorInstance.connector.id,
|
||||
},
|
||||
{
|
||||
...mockWechatConnectorInstance.metadata,
|
||||
...mockWechatConnectorInstance.instance.metadata,
|
||||
id: mockWechatConnectorInstance.connector.id,
|
||||
},
|
||||
{
|
||||
...mockWechatNativeConnectorInstance.metadata,
|
||||
...mockWechatNativeConnectorInstance.instance.metadata,
|
||||
id: mockWechatNativeConnectorInstance.connector.id,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -55,13 +55,20 @@ export default function wellKnownRoutes<T extends AnonymousRouter>(router: T, pr
|
|||
Array<ConnectorMetadata & { id: string }>
|
||||
>((previous, connectorTarget) => {
|
||||
const connectors = connectorInstances.filter(
|
||||
({ metadata: { target }, connector: { enabled } }) =>
|
||||
target === connectorTarget && enabled
|
||||
({
|
||||
instance: {
|
||||
metadata: { target },
|
||||
},
|
||||
connector: { enabled },
|
||||
}) => target === connectorTarget && enabled
|
||||
);
|
||||
|
||||
return [
|
||||
...previous,
|
||||
...connectors.map(({ metadata, connector: { id } }) => ({ ...metadata, id })),
|
||||
...connectors.map(({ instance: { metadata }, connector: { id } }) => ({
|
||||
...metadata,
|
||||
id,
|
||||
})),
|
||||
];
|
||||
}, []);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue