mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
feat: update available social targets after connector deletion (#2524)
This commit is contained in:
parent
1d64ded7eb
commit
efd404e9aa
4 changed files with 84 additions and 2 deletions
|
@ -10,6 +10,7 @@ import { expectSqlAssert } from '#src/utils/test-utils.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
findAllConnectors,
|
findAllConnectors,
|
||||||
|
findConnectorById,
|
||||||
countConnectorByConnectorId,
|
countConnectorByConnectorId,
|
||||||
deleteConnectorById,
|
deleteConnectorById,
|
||||||
insertConnector,
|
insertConnector,
|
||||||
|
@ -47,6 +48,28 @@ describe('connector queries', () => {
|
||||||
await expect(findAllConnectors()).resolves.toEqual([rowData]);
|
await expect(findAllConnectors()).resolves.toEqual([rowData]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('findConnectorById', async () => {
|
||||||
|
const row = {
|
||||||
|
...mockConnector,
|
||||||
|
config: JSON.stringify(mockConnector.config),
|
||||||
|
metadata: JSON.stringify(mockConnector.metadata),
|
||||||
|
};
|
||||||
|
const expectSql = sql`
|
||||||
|
select ${sql.join(Object.values(fields), sql`,`)}
|
||||||
|
from ${table}
|
||||||
|
where ${fields.id}=$1
|
||||||
|
`;
|
||||||
|
|
||||||
|
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||||
|
expectSqlAssert(sql, expectSql.sql);
|
||||||
|
expect(values).toEqual([mockConnector.id]);
|
||||||
|
|
||||||
|
return createMockQueryResult([row]);
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(findConnectorById(mockConnector.id)).resolves.toEqual(row);
|
||||||
|
});
|
||||||
|
|
||||||
it('countConnectorsByConnectorId', async () => {
|
it('countConnectorsByConnectorId', async () => {
|
||||||
const rowData = { id: 'foo', connectorId: 'bar' };
|
const rowData = { id: 'foo', connectorId: 'bar' };
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,13 @@ export const findAllConnectors = async () =>
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const findConnectorById = async (id: string) =>
|
||||||
|
envSet.pool.one<Connector>(sql`
|
||||||
|
select ${sql.join(Object.values(fields), sql`,`)}
|
||||||
|
from ${table}
|
||||||
|
where ${fields.id}=${id}
|
||||||
|
`);
|
||||||
|
|
||||||
export const countConnectorByConnectorId = async (connectorId: string) =>
|
export const countConnectorByConnectorId = async (connectorId: string) =>
|
||||||
envSet.pool.one<{ count: number }>(sql`
|
envSet.pool.one<{ count: number }>(sql`
|
||||||
select count(*)
|
select count(*)
|
||||||
|
|
|
@ -16,7 +16,12 @@ import {
|
||||||
import { defaultConnectorMethods } from '#src/connectors/consts.js';
|
import { defaultConnectorMethods } from '#src/connectors/consts.js';
|
||||||
import type { ConnectorFactory, LogtoConnector } from '#src/connectors/types.js';
|
import type { ConnectorFactory, LogtoConnector } from '#src/connectors/types.js';
|
||||||
import RequestError from '#src/errors/RequestError/index.js';
|
import RequestError from '#src/errors/RequestError/index.js';
|
||||||
import { countConnectorByConnectorId, deleteConnectorById } from '#src/queries/connector.js';
|
import { removeUnavailableSocialConnectorTargets } from '#src/lib/sign-in-experience/index.js';
|
||||||
|
import {
|
||||||
|
findConnectorById,
|
||||||
|
countConnectorByConnectorId,
|
||||||
|
deleteConnectorById,
|
||||||
|
} from '#src/queries/connector.js';
|
||||||
import assertThat from '#src/utils/assert-that.js';
|
import assertThat from '#src/utils/assert-that.js';
|
||||||
import { createRequester } from '#src/utils/test-utils.js';
|
import { createRequester } from '#src/utils/test-utils.js';
|
||||||
|
|
||||||
|
@ -29,7 +34,12 @@ const getLogtoConnectorsPlaceHolder = jest.fn() as jest.MockedFunction<
|
||||||
() => Promise<LogtoConnector[]>
|
() => Promise<LogtoConnector[]>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
jest.mock('#src/lib/sign-in-experience/index.js', () => ({
|
||||||
|
removeUnavailableSocialConnectorTargets: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
jest.mock('#src/queries/connector.js', () => ({
|
jest.mock('#src/queries/connector.js', () => ({
|
||||||
|
findConnectorById: jest.fn(),
|
||||||
countConnectorByConnectorId: jest.fn(),
|
countConnectorByConnectorId: jest.fn(),
|
||||||
deleteConnectorById: jest.fn(),
|
deleteConnectorById: jest.fn(),
|
||||||
insertConnector: jest.fn(async (body: unknown) => body),
|
insertConnector: jest.fn(async (body: unknown) => body),
|
||||||
|
@ -300,13 +310,44 @@ describe('connector route', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /connectors/:id', () => {
|
describe('DELETE /connectors/:id', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('delete connector instance', async () => {
|
it('delete connector instance and remove unavailable social connector targets', async () => {
|
||||||
|
(findConnectorById as jest.Mock).mockResolvedValueOnce(mockConnector);
|
||||||
|
loadConnectorFactoriesPlaceHolder.mockResolvedValueOnce([mockConnectorFactory]);
|
||||||
await connectorRequest.delete('/connectors/id').send({});
|
await connectorRequest.delete('/connectors/id').send({});
|
||||||
expect(deleteConnectorById).toHaveBeenCalledTimes(1);
|
expect(deleteConnectorById).toHaveBeenCalledTimes(1);
|
||||||
|
expect(removeUnavailableSocialConnectorTargets).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('delete connector instance (connector factory is not social type)', async () => {
|
||||||
|
(findConnectorById as jest.Mock).mockResolvedValueOnce(mockConnector);
|
||||||
|
loadConnectorFactoriesPlaceHolder.mockResolvedValueOnce([
|
||||||
|
{ ...mockConnectorFactory, type: ConnectorType.Sms },
|
||||||
|
]);
|
||||||
|
await connectorRequest.delete('/connectors/id').send({});
|
||||||
|
expect(deleteConnectorById).toHaveBeenCalledTimes(1);
|
||||||
|
expect(removeUnavailableSocialConnectorTargets).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('delete connector instance (connector factory is not found)', async () => {
|
||||||
|
(findConnectorById as jest.Mock).mockResolvedValueOnce(mockConnector);
|
||||||
|
loadConnectorFactoriesPlaceHolder.mockResolvedValueOnce([]);
|
||||||
|
await connectorRequest.delete('/connectors/id').send({});
|
||||||
|
expect(deleteConnectorById).toHaveBeenCalledTimes(1);
|
||||||
|
expect(removeUnavailableSocialConnectorTargets).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws when connector not exists with `id`', async () => {
|
||||||
|
// eslint-disable-next-line unicorn/no-useless-undefined
|
||||||
|
(findConnectorById as jest.Mock).mockResolvedValueOnce(undefined);
|
||||||
|
const response = await connectorRequest.delete('/connectors/id').send({});
|
||||||
|
expect(response).toHaveProperty('statusCode', 500);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,6 +15,7 @@ import RequestError from '#src/errors/RequestError/index.js';
|
||||||
import { removeUnavailableSocialConnectorTargets } from '#src/lib/sign-in-experience/index.js';
|
import { removeUnavailableSocialConnectorTargets } from '#src/lib/sign-in-experience/index.js';
|
||||||
import koaGuard from '#src/middleware/koa-guard.js';
|
import koaGuard from '#src/middleware/koa-guard.js';
|
||||||
import {
|
import {
|
||||||
|
findConnectorById,
|
||||||
countConnectorByConnectorId,
|
countConnectorByConnectorId,
|
||||||
deleteConnectorById,
|
deleteConnectorById,
|
||||||
insertConnector,
|
insertConnector,
|
||||||
|
@ -291,8 +292,18 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
|
||||||
params: { id },
|
params: { id },
|
||||||
} = ctx.guard;
|
} = ctx.guard;
|
||||||
|
|
||||||
|
const { connectorId } = await findConnectorById(id);
|
||||||
|
const connectorFactories = await loadConnectorFactories();
|
||||||
|
const connectorFactory = connectorFactories.find(
|
||||||
|
({ metadata }) => metadata.id === connectorId
|
||||||
|
);
|
||||||
|
|
||||||
await deleteConnectorById(id);
|
await deleteConnectorById(id);
|
||||||
|
|
||||||
|
if (connectorFactory?.type === ConnectorType.Social) {
|
||||||
|
await removeUnavailableSocialConnectorTargets();
|
||||||
|
}
|
||||||
|
|
||||||
ctx.status = 204;
|
ctx.status = 204;
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
|
|
Loading…
Reference in a new issue