mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(schemas): sign-in-experiences (#361)
* chore(schemas): remove useless table sign_in_methods * feat(schemas): sign-in-experiences * chore(core): update mock.ts and sign-in-experience.test.ts about sign-in-experiences schema * chore(core): fix typo in test
This commit is contained in:
parent
bb467a518f
commit
9f3fc5a5cc
9 changed files with 87 additions and 136 deletions
|
@ -21,11 +21,11 @@ describe('sign-in-experience query', () => {
|
|||
const { table, fields } = convertToIdentifiers(SignInExperiences);
|
||||
const dbvalue = {
|
||||
...mockSignInExperience,
|
||||
companyInfo: JSON.stringify(mockSignInExperience.companyInfo),
|
||||
branding: JSON.stringify(mockSignInExperience.branding),
|
||||
termsOfUse: JSON.stringify(mockSignInExperience.termsOfUse),
|
||||
localization: JSON.stringify(mockSignInExperience.localization),
|
||||
signInMethods: JSON.stringify(mockSignInExperience.signInMethods),
|
||||
languageInfo: JSON.stringify(mockSignInExperience.languageInfo),
|
||||
signInMethods: JSON.stringify(mockSignInExperience.socialSignInConnectorIds),
|
||||
socialSignInConnectorIds: JSON.stringify(mockSignInExperience.socialSignInConnectorIds),
|
||||
};
|
||||
|
||||
it('findDefaultSignInExperience', async () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { SignInExperience, CreateSignInExperience } from '@logto/schemas';
|
||||
import { SignInExperience, CreateSignInExperience, BrandingStyle, Branding } from '@logto/schemas';
|
||||
|
||||
import { mockSignInExperience } from '@/utils/mock';
|
||||
import { createRequester } from '@/utils/test-utils';
|
||||
|
@ -25,38 +25,37 @@ describe('signInExperiences routes', () => {
|
|||
});
|
||||
|
||||
it('PATCH /sign-in-ex/:id', async () => {
|
||||
const companyInfo = {
|
||||
name: 'silverhand',
|
||||
logo: 'http://silverhand.png',
|
||||
};
|
||||
const signInMethods = {
|
||||
primary: ['phone'],
|
||||
secondary: ['email'],
|
||||
disabled: [],
|
||||
const branding: Branding = {
|
||||
primaryColor: '#000',
|
||||
backgroundColor: '#fff',
|
||||
darkMode: true,
|
||||
darkBackgroundColor: '#000',
|
||||
darkPrimaryColor: '#fff',
|
||||
style: BrandingStyle.Logo,
|
||||
logoUrl: 'http://silverhand.png',
|
||||
slogan: 'silverhand',
|
||||
};
|
||||
|
||||
const socialSignInConnectorIds = ['abc', 'def'];
|
||||
|
||||
const response = await signInExperienceRequester.patch('/sign-in-ex/default').send({
|
||||
companyInfo,
|
||||
signInMethods,
|
||||
branding,
|
||||
socialSignInConnectorIds,
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({
|
||||
...mockSignInExperience,
|
||||
companyInfo,
|
||||
signInMethods,
|
||||
branding,
|
||||
socialSignInConnectorIds,
|
||||
});
|
||||
});
|
||||
|
||||
it('PATH /sign-in-ex/:id should throw with invalid inputs', async () => {
|
||||
const signInMethods = {
|
||||
primary: [],
|
||||
secondary: ['email'],
|
||||
disabled: [],
|
||||
};
|
||||
it('PATCH /sign-in-ex/:id should throw with invalid inputs', async () => {
|
||||
const socialSignInConnectorIds = [123, 456];
|
||||
|
||||
const response = await signInExperienceRequester.patch('/sign-in-ex/default').send({
|
||||
signInMethods,
|
||||
socialSignInConnectorIds,
|
||||
});
|
||||
expect(response.status).toEqual(400);
|
||||
});
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
UserLogType,
|
||||
UserLogResult,
|
||||
ConnectorType,
|
||||
SignInMethodState,
|
||||
} from '@logto/schemas';
|
||||
import pick from 'lodash.pick';
|
||||
|
||||
|
@ -155,39 +156,32 @@ export const mockSetting: Setting = {
|
|||
|
||||
export const mockSignInExperience: SignInExperience = {
|
||||
id: 'foo',
|
||||
companyInfo: {
|
||||
name: 'logto',
|
||||
logo: 'http://logto.png',
|
||||
},
|
||||
branding: {
|
||||
primaryColor: '#000',
|
||||
backgroundColor: '#fff',
|
||||
darkMode: true,
|
||||
darkBackgroundColor: '#000',
|
||||
darkPrimaryColor: '#fff',
|
||||
style: BrandingStyle.CompanyLogo_AppLogo_CompanyName_AppName,
|
||||
style: BrandingStyle.Logo,
|
||||
logoUrl: 'http://logto.png',
|
||||
slogan: 'logto',
|
||||
},
|
||||
termsOfUse: {
|
||||
enabled: false,
|
||||
},
|
||||
forgetPasswordEnabled: true,
|
||||
localization: {
|
||||
languageInfo: {
|
||||
autoDetect: true,
|
||||
primaryLanguage: Language.chinese,
|
||||
fallbackLanguage: Language.english,
|
||||
fixedLanguage: Language.chinese,
|
||||
},
|
||||
signInMethods: {
|
||||
primary: ['email'],
|
||||
secondary: [],
|
||||
disabled: [],
|
||||
username: SignInMethodState.primary,
|
||||
email: SignInMethodState.disabled,
|
||||
sms: SignInMethodState.disabled,
|
||||
social: SignInMethodState.secondary,
|
||||
},
|
||||
};
|
||||
|
||||
export const mockConnector: Connector = {
|
||||
id: 'foo',
|
||||
enabled: true,
|
||||
config: {},
|
||||
createdAt: 1_645_334_775_356,
|
||||
socialSignInConnectorIds: ['foo', 'bar'],
|
||||
};
|
||||
|
||||
export const mockConnectorList: Connector[] = [
|
||||
|
|
|
@ -10,6 +10,5 @@ export * from './resource';
|
|||
export * from './role';
|
||||
export * from './setting';
|
||||
export * from './sign-in-experience';
|
||||
export * from './sign-in-method';
|
||||
export * from './user-log';
|
||||
export * from './user';
|
||||
|
|
|
@ -3,48 +3,48 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
CompanyInfo,
|
||||
companyInfoGuard,
|
||||
Branding,
|
||||
brandingGuard,
|
||||
LanguageInfo,
|
||||
languageInfoGuard,
|
||||
TermsOfUse,
|
||||
termsOfUseGuard,
|
||||
Localization,
|
||||
localizationGuard,
|
||||
SignInMethodSettings,
|
||||
signInMethodSettingsGuard,
|
||||
SignInMethods,
|
||||
signInMethodsGuard,
|
||||
ConnectorIds,
|
||||
connectorIdsGuard,
|
||||
GeneratedSchema,
|
||||
Guard,
|
||||
} from '../foundations';
|
||||
|
||||
export type CreateSignInExperience = {
|
||||
id: string;
|
||||
companyInfo: CompanyInfo;
|
||||
branding: Branding;
|
||||
termsOfUse: TermsOfUse;
|
||||
branding?: Branding;
|
||||
languageInfo?: LanguageInfo;
|
||||
termsOfUse?: TermsOfUse;
|
||||
forgetPasswordEnabled?: boolean;
|
||||
localization: Localization;
|
||||
signInMethods: SignInMethodSettings;
|
||||
signInMethods?: SignInMethods;
|
||||
socialSignInConnectorIds?: ConnectorIds;
|
||||
};
|
||||
|
||||
export type SignInExperience = {
|
||||
id: string;
|
||||
companyInfo: CompanyInfo;
|
||||
branding: Branding;
|
||||
languageInfo: LanguageInfo;
|
||||
termsOfUse: TermsOfUse;
|
||||
forgetPasswordEnabled: boolean;
|
||||
localization: Localization;
|
||||
signInMethods: SignInMethodSettings;
|
||||
signInMethods: SignInMethods;
|
||||
socialSignInConnectorIds: ConnectorIds;
|
||||
};
|
||||
|
||||
const createGuard: Guard<CreateSignInExperience> = z.object({
|
||||
id: z.string(),
|
||||
companyInfo: companyInfoGuard,
|
||||
branding: brandingGuard,
|
||||
termsOfUse: termsOfUseGuard,
|
||||
branding: brandingGuard.optional(),
|
||||
languageInfo: languageInfoGuard.optional(),
|
||||
termsOfUse: termsOfUseGuard.optional(),
|
||||
forgetPasswordEnabled: z.boolean().optional(),
|
||||
localization: localizationGuard,
|
||||
signInMethods: signInMethodSettingsGuard,
|
||||
signInMethods: signInMethodsGuard.optional(),
|
||||
socialSignInConnectorIds: connectorIdsGuard.optional(),
|
||||
});
|
||||
|
||||
export const SignInExperiences: GeneratedSchema<CreateSignInExperience> = Object.freeze({
|
||||
|
@ -52,21 +52,21 @@ export const SignInExperiences: GeneratedSchema<CreateSignInExperience> = Object
|
|||
tableSingular: 'sign_in_experience',
|
||||
fields: {
|
||||
id: 'id',
|
||||
companyInfo: 'company_info',
|
||||
branding: 'branding',
|
||||
languageInfo: 'language_info',
|
||||
termsOfUse: 'terms_of_use',
|
||||
forgetPasswordEnabled: 'forget_password_enabled',
|
||||
localization: 'localization',
|
||||
signInMethods: 'sign_in_methods',
|
||||
socialSignInConnectorIds: 'social_sign_in_connector_ids',
|
||||
},
|
||||
fieldKeys: [
|
||||
'id',
|
||||
'companyInfo',
|
||||
'branding',
|
||||
'languageInfo',
|
||||
'termsOfUse',
|
||||
'forgetPasswordEnabled',
|
||||
'localization',
|
||||
'signInMethods',
|
||||
'socialSignInConnectorIds',
|
||||
],
|
||||
createGuard,
|
||||
});
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { GeneratedSchema, Guard } from '../foundations';
|
||||
|
||||
export type CreateSignInMethod = {
|
||||
id: string;
|
||||
name: string;
|
||||
connectorId?: string | null;
|
||||
};
|
||||
|
||||
export type SignInMethod = {
|
||||
id: string;
|
||||
name: string;
|
||||
connectorId: string | null;
|
||||
};
|
||||
|
||||
const createGuard: Guard<CreateSignInMethod> = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
connectorId: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export const SignInMethods: GeneratedSchema<CreateSignInMethod> = Object.freeze({
|
||||
table: 'sign_in_methods',
|
||||
tableSingular: 'sign_in_method',
|
||||
fields: {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
connectorId: 'connector_id',
|
||||
},
|
||||
fieldKeys: ['id', 'name', 'connectorId'],
|
||||
createGuard,
|
||||
});
|
|
@ -79,20 +79,12 @@ export const adminConsoleConfigGuard = z.object({
|
|||
export type AdminConsoleConfig = z.infer<typeof adminConsoleConfigGuard>;
|
||||
|
||||
/**
|
||||
* SignIn Experience
|
||||
* SignIn Experiences
|
||||
*/
|
||||
|
||||
export const companyInfoGuard = z.object({
|
||||
name: z.string(),
|
||||
logo: z.string(),
|
||||
});
|
||||
|
||||
export type CompanyInfo = z.infer<typeof companyInfoGuard>;
|
||||
|
||||
export enum BrandingStyle {
|
||||
CompanyLogo_CompanyName_AppName = 'CompanyLogo_CompanyName_AppName',
|
||||
CompanyLogo_AppLogo_CompanyName_AppName = 'CompanyLogo_AppLogo_CompanyName_AppName',
|
||||
AppLogo_CompanyName_AppName = 'AppLogo_CompanyName_AppName',
|
||||
Logo = 'Logo',
|
||||
Logo_Slogan = 'Logo_Slogan',
|
||||
}
|
||||
|
||||
export const brandingGuard = z.object({
|
||||
|
@ -102,13 +94,14 @@ export const brandingGuard = z.object({
|
|||
darkPrimaryColor: z.string(),
|
||||
darkBackgroundColor: z.string(),
|
||||
style: z.nativeEnum(BrandingStyle),
|
||||
logoUrl: z.string().optional(),
|
||||
slogan: z.string().optional(),
|
||||
});
|
||||
|
||||
export type Branding = z.infer<typeof brandingGuard>;
|
||||
|
||||
export const termsOfUseGuard = z.object({
|
||||
enabled: z.boolean(),
|
||||
content: z.string().optional(),
|
||||
contentUrl: z.string().optional(),
|
||||
});
|
||||
|
||||
|
@ -119,21 +112,32 @@ export enum Language {
|
|||
chinese = 'zh-cn',
|
||||
}
|
||||
|
||||
export const localizationGuard = z.object({
|
||||
export const languageInfoGuard = z.object({
|
||||
autoDetect: z.boolean(),
|
||||
primaryLanguage: z.nativeEnum(Language),
|
||||
fallbackLanguage: z.nativeEnum(Language),
|
||||
fixedLanguage: z.nativeEnum(Language),
|
||||
});
|
||||
|
||||
export type Localization = z.infer<typeof localizationGuard>;
|
||||
export type LanguageInfo = z.infer<typeof languageInfoGuard>;
|
||||
|
||||
export const signInMethodSettingsGuard = z.object({
|
||||
primary: z.string().array().nonempty().max(3),
|
||||
secondary: z.string().array(),
|
||||
disabled: z.string().array(),
|
||||
export enum SignInMethodState {
|
||||
primary = 'primary',
|
||||
secondary = 'secondary',
|
||||
disabled = 'disabled',
|
||||
}
|
||||
|
||||
export const signInMethodsGuard = z.object({
|
||||
username: z.nativeEnum(SignInMethodState),
|
||||
email: z.nativeEnum(SignInMethodState),
|
||||
sms: z.nativeEnum(SignInMethodState),
|
||||
social: z.nativeEnum(SignInMethodState),
|
||||
});
|
||||
|
||||
export type SignInMethodSettings = z.infer<typeof signInMethodSettingsGuard>;
|
||||
export type SignInMethods = z.infer<typeof signInMethodsGuard>;
|
||||
|
||||
export const connectorIdsGuard = z.string().array();
|
||||
|
||||
export type ConnectorIds = z.infer<typeof connectorIdsGuard>;
|
||||
|
||||
/**
|
||||
* Commonly Used
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
create table sign_in_experiences (
|
||||
id varchar(128) not null,
|
||||
company_info jsonb /* @use CompanyInfo */ not null,
|
||||
branding jsonb /* @use Branding */ not null,
|
||||
terms_of_use jsonb /* @use TermsOfUse */ not null,
|
||||
forget_password_enabled boolean not null default(true),
|
||||
localization jsonb /* @use Localization */ not null,
|
||||
sign_in_methods jsonb /* @use SignInMethodSettings */ not null,
|
||||
branding jsonb /* @use Branding */ not null default '{}'::jsonb,
|
||||
language_info jsonb /* @use LanguageInfo */ not null default '{}'::jsonb,
|
||||
terms_of_use jsonb /* @use TermsOfUse */ not null default '{}'::jsonb,
|
||||
forget_password_enabled boolean not null default false,
|
||||
sign_in_methods jsonb /* @use SignInMethods */ not null default '{}'::jsonb,
|
||||
social_sign_in_connector_ids jsonb /* @use ConnectorIds */ not null default '[]'::jsonb,
|
||||
primary key (id)
|
||||
)
|
||||
);
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
create table sign_in_methods (
|
||||
id varchar(128) not null,
|
||||
name varchar(128) not null,
|
||||
connector_id varchar(128),
|
||||
primary key (id),
|
||||
constraint fk__sign_in_methods__connector_id
|
||||
foreign key (connector_id)
|
||||
references connectors(id)
|
||||
on delete cascade
|
||||
)
|
Loading…
Reference in a new issue