0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-31 22:51:25 -05:00

feat(core): remove unselected demo social connectors (#3454)

This commit is contained in:
wangsijie 2023-03-17 12:38:57 +08:00 committed by GitHub
parent d909b48649
commit f247ce5f86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 5 deletions

View file

@ -23,6 +23,7 @@ import * as pageLayout from '@/onboarding/scss/layout.module.scss';
import { Authentication, OnboardingPage } from '@/onboarding/types';
import type { OnboardingSieConfig } from '@/onboarding/types';
import { getOnboardingPage } from '@/onboarding/utils';
import { buildUrl } from '@/utils/url';
import { uriValidator } from '@/utils/validator';
import InspireMe from './components/InspireMe';
@ -83,7 +84,7 @@ const SignInExperience = () => {
}
const updatedData = await api
.patch('api/sign-in-exp', {
.patch(buildUrl('api/sign-in-exp', { removeUnusedDemoSocialConnector: '1' }), {
json: parser.onboardSieConfigToSignInExperience(formData, signInExperience),
})
.json<SignInExperienceType>();

View file

@ -2,6 +2,7 @@ import type { ConnectorFactory } from '@logto/cli/lib/connector/index.js';
import { ConnectorPlatform } from '@logto/connector-kit';
import type { Connector } from '@logto/schemas';
import { ConnectorType } from '@logto/schemas';
import { DemoConnector } from '@logto/shared';
import { any } from 'zod';
import type { LogtoConnector } from '#src/utils/connectors/types.js';
@ -222,6 +223,21 @@ export const mockGoogleConnector: LogtoConnector = {
...mockLogtoConnector,
};
export const mockDemoSocialConnector: LogtoConnector = {
dbEntry: {
...mockConnector,
id: 'demo-social',
},
metadata: {
...mockMetadata,
id: DemoConnector.Social,
target: 'github',
platform: null,
},
type: ConnectorType.Social,
...mockLogtoConnector,
};
export const mockLogtoConnectors = [
mockAliyunDmConnector,
mockAliyunSmsConnector,

View file

@ -15,6 +15,7 @@ import {
mockAliyunSmsConnector,
mockTermsOfUseUrl,
mockPrivacyPolicyUrl,
mockDemoSocialConnector,
} from '#src/__mocks__/index.js';
import { MockTenant } from '#src/test-utils/tenant.js';
import { createRequester } from '#src/utils/test-utils.js';
@ -50,13 +51,19 @@ const signInExperiences = {
const { findDefaultSignInExperience } = signInExperiences;
const validateLanguageInfo = jest.fn();
const mockGetLogtoConnectors = jest.fn(async () => logtoConnectors);
const mockDeleteConnectorById = jest.fn();
const tenantContext = new MockTenant(
undefined,
{ signInExperiences, customPhrases: { findAllCustomLanguageTags: async () => [] } },
{
signInExperiences,
customPhrases: { findAllCustomLanguageTags: async () => [] },
connectors: { deleteConnectorById: mockDeleteConnectorById },
},
{
signInExperiences: { validateLanguageInfo },
connectors: { getLogtoConnectors: async () => logtoConnectors },
connectors: { getLogtoConnectors: mockGetLogtoConnectors },
}
);
@ -80,6 +87,10 @@ describe('GET /sign-in-exp', () => {
});
describe('PATCH /sign-in-exp', () => {
afterEach(() => {
mockDeleteConnectorById.mockClear();
});
it('should update social connector targets in correct sorting order', async () => {
const socialSignInConnectorTargets = ['github', 'facebook'];
const signInExperience = {
@ -110,6 +121,40 @@ describe('PATCH /sign-in-exp', () => {
});
});
it('should remove unselected demo social connectors', async () => {
mockGetLogtoConnectors.mockResolvedValueOnce([...logtoConnectors, mockDemoSocialConnector]);
const socialSignInConnectorTargets = ['facebook', 'google'];
const signInExperience = {
socialSignInConnectorTargets,
};
await signInExperienceRequester
.patch('/sign-in-exp?removeUnusedDemoSocialConnector=1')
.send(signInExperience);
expect(mockDeleteConnectorById).toHaveBeenCalledWith(mockDemoSocialConnector.dbEntry.id);
});
it('should remove unselected demo social connectors when removeUnusedDemoSocialConnector is not set', async () => {
mockGetLogtoConnectors.mockResolvedValueOnce([...logtoConnectors, mockDemoSocialConnector]);
const socialSignInConnectorTargets = ['facebook', 'google'];
const signInExperience = {
socialSignInConnectorTargets,
};
await signInExperienceRequester.patch('/sign-in-exp').send(signInExperience);
expect(mockDeleteConnectorById).not.toHaveBeenCalled();
});
it('should not remove selected demo social connectors', async () => {
mockGetLogtoConnectors.mockResolvedValueOnce([...logtoConnectors, mockDemoSocialConnector]);
const socialSignInConnectorTargets = ['github', 'facebook', 'google'];
const signInExperience = {
socialSignInConnectorTargets,
};
await signInExperienceRequester
.patch('/sign-in-exp?removeUnusedDemoSocialConnector=1')
.send(signInExperience);
expect(mockDeleteConnectorById).not.toHaveBeenCalled();
});
it('should succeed to update when the input is valid', async () => {
const socialSignInConnectorTargets = ['github', 'facebook', 'wechat'];

View file

@ -1,5 +1,6 @@
import { ConnectorType, SignInExperiences } from '@logto/schemas';
import { literal, object, string } from 'zod';
import { DemoConnector } from '@logto/shared';
import { literal, object, string, z } from 'zod';
import { validateSignUp, validateSignIn } from '#src/libraries/sign-in-experience/index.js';
import koaGuard from '#src/middleware/koa-guard.js';
@ -10,6 +11,7 @@ export default function signInExperiencesRoutes<T extends AuthedRouter>(
...[router, { queries, libraries }]: RouterInitArgs<T>
) {
const { findDefaultSignInExperience, updateDefaultSignInExperience } = queries.signInExperiences;
const { deleteConnectorById } = queries.connectors;
const {
signInExperiences: { validateLanguageInfo },
connectors: { getLogtoConnectors },
@ -28,6 +30,7 @@ export default function signInExperiencesRoutes<T extends AuthedRouter>(
router.patch(
'/sign-in-exp',
koaGuard({
query: z.object({ removeUnusedDemoSocialConnector: z.string().optional() }),
body: SignInExperiences.createGuard
.omit({ id: true, termsOfUseUrl: true, privacyPolicyUrl: true })
.merge(
@ -39,7 +42,10 @@ export default function signInExperiencesRoutes<T extends AuthedRouter>(
.partial(),
}),
async (ctx, next) => {
const { socialSignInConnectorTargets, ...rest } = ctx.guard.body;
const {
query: { removeUnusedDemoSocialConnector },
body: { socialSignInConnectorTargets, ...rest },
} = ctx.guard;
const { languageInfo, signUp, signIn } = rest;
if (languageInfo) {
@ -66,6 +72,22 @@ export default function signInExperiencesRoutes<T extends AuthedRouter>(
const signInExperience = await findDefaultSignInExperience();
validateSignIn(signIn, signInExperience.signUp, connectors);
}
// Remove unused demo social connectors, those that are not selected in onboarding SIE config.
if (removeUnusedDemoSocialConnector && filteredSocialSignInConnectorTargets) {
await Promise.all(
connectors
.filter((connector) => {
return (
connector.type === ConnectorType.Social &&
connector.metadata.id === DemoConnector.Social &&
!filteredSocialSignInConnectorTargets.includes(connector.metadata.target)
);
})
.map(async (connector) => deleteConnectorById(connector.dbEntry.id))
);
}
ctx.body = await updateDefaultSignInExperience(
filteredSocialSignInConnectorTargets
? {