mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(toolkit,core,connector): add connector switch for WIP features
This commit is contained in:
parent
3efd6485fc
commit
26466e6542
10 changed files with 51 additions and 27 deletions
|
@ -1,5 +1,3 @@
|
||||||
import { getEnv } from '@silverhand/essentials';
|
|
||||||
|
|
||||||
import type { ConnectorMetadata } from '@logto/connector-kit';
|
import type { ConnectorMetadata } from '@logto/connector-kit';
|
||||||
import { ConnectorConfigFormItemType } from '@logto/connector-kit';
|
import { ConnectorConfigFormItemType } from '@logto/connector-kit';
|
||||||
|
|
||||||
|
@ -102,11 +100,9 @@ export const defaultMetadata: ConnectorMetadata = {
|
||||||
|
|
||||||
export const scope = ['send:email'];
|
export const scope = ['send:email'];
|
||||||
|
|
||||||
export const defaultTimeout = 5000;
|
export const defaultTimeout = 10_000;
|
||||||
|
|
||||||
export const oldEmailEndpoint = '/services/send-email';
|
export const oldEmailEndpoint = '/services/send-email';
|
||||||
export const newEmailEndpoint = '/services/mails';
|
export const newEmailEndpoint = '/services/mails';
|
||||||
export const emailEndpoint =
|
|
||||||
getEnv('NODE_ENV') === 'production' ? oldEmailEndpoint : newEmailEndpoint;
|
|
||||||
|
|
||||||
export const usageEndpoint = '/services/mails/usage';
|
export const usageEndpoint = '/services/mails/usage';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import nock from 'nock';
|
||||||
|
|
||||||
import { VerificationCodeType } from '@logto/connector-kit';
|
import { VerificationCodeType } from '@logto/connector-kit';
|
||||||
|
|
||||||
import { emailEndpoint } from './constant.js';
|
import { newEmailEndpoint } from './constant.js';
|
||||||
import { mockedAccessTokenResponse, mockedConfig } from './mock.js';
|
import { mockedAccessTokenResponse, mockedConfig } from './mock.js';
|
||||||
|
|
||||||
const { jest } = import.meta;
|
const { jest } = import.meta;
|
||||||
|
@ -17,14 +17,18 @@ describe('sendMessage()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should send message successfully', async () => {
|
it('should send message successfully', async () => {
|
||||||
nock(mockedConfig.endpoint).post(emailEndpoint).reply(200);
|
nock(mockedConfig.endpoint).post(newEmailEndpoint).reply(200);
|
||||||
const connector = await createConnector({ getConfig });
|
const connector = await createConnector({ getConfig });
|
||||||
await expect(
|
await expect(
|
||||||
connector.sendMessage({
|
connector.sendMessage(
|
||||||
to: 'wangsijie94@gmail.com',
|
{
|
||||||
type: VerificationCodeType.SignIn,
|
to: 'wangsijie94@gmail.com',
|
||||||
payload: { code: '1234' },
|
type: VerificationCodeType.SignIn,
|
||||||
})
|
payload: { code: '1234' },
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
false // Set to `false` since this is not production env.
|
||||||
|
)
|
||||||
).resolves.not.toThrow();
|
).resolves.not.toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,7 +15,13 @@ import {
|
||||||
ConnectorErrorCodes,
|
ConnectorErrorCodes,
|
||||||
} from '@logto/connector-kit';
|
} from '@logto/connector-kit';
|
||||||
|
|
||||||
import { defaultMetadata, defaultTimeout, emailEndpoint, usageEndpoint } from './constant.js';
|
import {
|
||||||
|
defaultMetadata,
|
||||||
|
defaultTimeout,
|
||||||
|
oldEmailEndpoint,
|
||||||
|
newEmailEndpoint,
|
||||||
|
usageEndpoint,
|
||||||
|
} from './constant.js';
|
||||||
import { grantAccessToken } from './grant-access-token.js';
|
import { grantAccessToken } from './grant-access-token.js';
|
||||||
import type { LogtoEmailConfig } from './types.js';
|
import type { LogtoEmailConfig } from './types.js';
|
||||||
import { logtoEmailConfigGuard } from './types.js';
|
import { logtoEmailConfigGuard } from './types.js';
|
||||||
|
@ -24,9 +30,10 @@ export type { EmailServiceBasicConfig } from './types.js';
|
||||||
|
|
||||||
const sendMessage =
|
const sendMessage =
|
||||||
(getConfig: GetConnectorConfig): SendMessageFunction =>
|
(getConfig: GetConnectorConfig): SendMessageFunction =>
|
||||||
async (data, inputConfig) => {
|
async (data, inputConfig, isDevelopment) => {
|
||||||
const config = inputConfig ?? (await getConfig(defaultMetadata.id));
|
const config = inputConfig ?? (await getConfig(defaultMetadata.id));
|
||||||
validateConfig<LogtoEmailConfig>(config, logtoEmailConfigGuard);
|
validateConfig<LogtoEmailConfig>(config, logtoEmailConfigGuard);
|
||||||
|
const emailEndpoint = isDevelopment ? oldEmailEndpoint : newEmailEndpoint;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
endpoint,
|
endpoint,
|
||||||
|
|
|
@ -15,7 +15,8 @@ export type ConnectorLibrary = ReturnType<typeof createConnectorLibrary>;
|
||||||
|
|
||||||
export const createConnectorLibrary = (
|
export const createConnectorLibrary = (
|
||||||
queries: Queries,
|
queries: Queries,
|
||||||
cloudConnection: CloudConnectionLibrary
|
cloudConnection: CloudConnectionLibrary,
|
||||||
|
isProduction: boolean
|
||||||
) => {
|
) => {
|
||||||
const { findAllConnectors, findAllConnectorsWellKnown } = queries.connectors;
|
const { findAllConnectors, findAllConnectorsWellKnown } = queries.connectors;
|
||||||
const { getCloudConnectionData } = cloudConnection;
|
const { getCloudConnectionData } = cloudConnection;
|
||||||
|
@ -99,6 +100,7 @@ export const createConnectorLibrary = (
|
||||||
validateConfig(config, rawConnector.configGuard);
|
validateConfig(config, rawConnector.configGuard);
|
||||||
},
|
},
|
||||||
dbEntry: databaseConnector,
|
dbEntry: databaseConnector,
|
||||||
|
isDevelopment: isProduction && ServiceConnector.Email === connectorFactory.metadata.id,
|
||||||
};
|
};
|
||||||
} catch {}
|
} catch {}
|
||||||
})
|
})
|
||||||
|
|
|
@ -83,7 +83,7 @@ export const createPasscodeLibrary = (queries: Queries, connectorLibrary: Connec
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const { dbEntry, metadata, sendMessage } = connector;
|
const { dbEntry, metadata, sendMessage, isDevelopment } = connector;
|
||||||
|
|
||||||
const messageTypeResult = verificationCodeTypeGuard.safeParse(passcode.type);
|
const messageTypeResult = verificationCodeTypeGuard.safeParse(passcode.type);
|
||||||
|
|
||||||
|
@ -91,13 +91,17 @@ export const createPasscodeLibrary = (queries: Queries, connectorLibrary: Connec
|
||||||
throw new ConnectorError(ConnectorErrorCodes.InvalidConfig);
|
throw new ConnectorError(ConnectorErrorCodes.InvalidConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await sendMessage({
|
const response = await sendMessage(
|
||||||
to: emailOrPhone,
|
{
|
||||||
type: messageTypeResult.data,
|
to: emailOrPhone,
|
||||||
payload: {
|
type: messageTypeResult.data,
|
||||||
code: passcode.code,
|
payload: {
|
||||||
|
code: passcode.code,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
undefined,
|
||||||
|
isDevelopment
|
||||||
|
);
|
||||||
|
|
||||||
return { dbEntry, metadata, response };
|
return { dbEntry, metadata, response };
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { phoneRegEx, emailRegEx } from '@logto/core-kit';
|
||||||
import { jsonObjectGuard, ConnectorType } from '@logto/schemas';
|
import { jsonObjectGuard, ConnectorType } from '@logto/schemas';
|
||||||
import { string, object } from 'zod';
|
import { string, object } from 'zod';
|
||||||
|
|
||||||
|
import { EnvSet } from '#src/env-set/index.js';
|
||||||
import RequestError from '#src/errors/RequestError/index.js';
|
import RequestError from '#src/errors/RequestError/index.js';
|
||||||
import koaGuard from '#src/middleware/koa-guard.js';
|
import koaGuard from '#src/middleware/koa-guard.js';
|
||||||
import assertThat from '#src/utils/assert-that.js';
|
import assertThat from '#src/utils/assert-that.js';
|
||||||
|
@ -84,7 +85,8 @@ export default function connectorConfigTestingRoutes<T extends AuthedRouter>(
|
||||||
code: '000000',
|
code: '000000',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
config
|
config,
|
||||||
|
ServiceConnector.Email === connectorFactory.metadata.id && EnvSet.values.isProduction
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.status = 204;
|
ctx.status = 204;
|
||||||
|
|
|
@ -56,7 +56,11 @@ export default class Tenant implements TenantContext {
|
||||||
public readonly queries = new Queries(envSet.pool, wellKnownCache),
|
public readonly queries = new Queries(envSet.pool, wellKnownCache),
|
||||||
public readonly logtoConfigs = createLogtoConfigLibrary(queries),
|
public readonly logtoConfigs = createLogtoConfigLibrary(queries),
|
||||||
public readonly cloudConnection = createCloudConnectionLibrary(logtoConfigs),
|
public readonly cloudConnection = createCloudConnectionLibrary(logtoConfigs),
|
||||||
public readonly connectors = createConnectorLibrary(queries, cloudConnection),
|
public readonly connectors = createConnectorLibrary(
|
||||||
|
queries,
|
||||||
|
cloudConnection,
|
||||||
|
EnvSet.values.isProduction
|
||||||
|
),
|
||||||
public readonly libraries = new Libraries(id, queries, connectors)
|
public readonly libraries = new Libraries(id, queries, connectors)
|
||||||
) {
|
) {
|
||||||
const isAdminTenant = id === adminTenantId;
|
const isAdminTenant = id === adminTenantId;
|
||||||
|
|
|
@ -76,7 +76,8 @@ export class MockTenant implements TenantContext {
|
||||||
this.logtoConfigs = createLogtoConfigLibrary(this.queries);
|
this.logtoConfigs = createLogtoConfigLibrary(this.queries);
|
||||||
this.cloudConnection = createCloudConnectionLibrary(this.logtoConfigs);
|
this.cloudConnection = createCloudConnectionLibrary(this.logtoConfigs);
|
||||||
this.connectors = {
|
this.connectors = {
|
||||||
...createConnectorLibrary(this.queries, this.cloudConnection),
|
// Manually set the third argument `isDevelopment` to `false` since this is not a production environment.
|
||||||
|
...createConnectorLibrary(this.queries, this.cloudConnection, false),
|
||||||
...connectorsOverride,
|
...connectorsOverride,
|
||||||
};
|
};
|
||||||
this.libraries = new Libraries(this.id, this.queries, this.connectors);
|
this.libraries = new Libraries(this.id, this.queries, this.connectors);
|
||||||
|
|
|
@ -9,7 +9,7 @@ export { ConnectorType } from '@logto/schemas';
|
||||||
*/
|
*/
|
||||||
export type LogtoConnector<T extends AllConnector = AllConnector> = T & {
|
export type LogtoConnector<T extends AllConnector = AllConnector> = T & {
|
||||||
validateConfig: (config: unknown) => void;
|
validateConfig: (config: unknown) => void;
|
||||||
} & { dbEntry: Connector };
|
} & { dbEntry: Connector; isDevelopment: boolean };
|
||||||
|
|
||||||
export const connectorWellKnownGuard = Connectors.guard.pick({
|
export const connectorWellKnownGuard = Connectors.guard.pick({
|
||||||
id: true,
|
id: true,
|
||||||
|
|
|
@ -240,7 +240,11 @@ export const sendMessagePayloadGuard = z.object({
|
||||||
|
|
||||||
export type SendMessagePayload = z.infer<typeof sendMessagePayloadGuard>;
|
export type SendMessagePayload = z.infer<typeof sendMessagePayloadGuard>;
|
||||||
|
|
||||||
export type SendMessageFunction = (data: SendMessagePayload, config?: unknown) => Promise<unknown>;
|
export type SendMessageFunction = (
|
||||||
|
data: SendMessagePayload,
|
||||||
|
config?: unknown,
|
||||||
|
isDevelopment?: boolean
|
||||||
|
) => Promise<unknown>;
|
||||||
|
|
||||||
export type GetUsageFunction = (startFrom?: Date) => Promise<number>;
|
export type GetUsageFunction = (startFrom?: Date) => Promise<number>;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue