0
Fork 0
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:
IceHe.xyz 2022-03-15 16:46:23 +08:00 committed by GitHub
parent bb467a518f
commit 9f3fc5a5cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 87 additions and 136 deletions

View file

@ -21,11 +21,11 @@ describe('sign-in-experience query', () => {
const { table, fields } = convertToIdentifiers(SignInExperiences); const { table, fields } = convertToIdentifiers(SignInExperiences);
const dbvalue = { const dbvalue = {
...mockSignInExperience, ...mockSignInExperience,
companyInfo: JSON.stringify(mockSignInExperience.companyInfo),
branding: JSON.stringify(mockSignInExperience.branding), branding: JSON.stringify(mockSignInExperience.branding),
termsOfUse: JSON.stringify(mockSignInExperience.termsOfUse), termsOfUse: JSON.stringify(mockSignInExperience.termsOfUse),
localization: JSON.stringify(mockSignInExperience.localization), languageInfo: JSON.stringify(mockSignInExperience.languageInfo),
signInMethods: JSON.stringify(mockSignInExperience.signInMethods), signInMethods: JSON.stringify(mockSignInExperience.socialSignInConnectorIds),
socialSignInConnectorIds: JSON.stringify(mockSignInExperience.socialSignInConnectorIds),
}; };
it('findDefaultSignInExperience', async () => { it('findDefaultSignInExperience', async () => {

View file

@ -1,4 +1,4 @@
import { SignInExperience, CreateSignInExperience } from '@logto/schemas'; import { SignInExperience, CreateSignInExperience, BrandingStyle, Branding } from '@logto/schemas';
import { mockSignInExperience } from '@/utils/mock'; import { mockSignInExperience } from '@/utils/mock';
import { createRequester } from '@/utils/test-utils'; import { createRequester } from '@/utils/test-utils';
@ -25,38 +25,37 @@ describe('signInExperiences routes', () => {
}); });
it('PATCH /sign-in-ex/:id', async () => { it('PATCH /sign-in-ex/:id', async () => {
const companyInfo = { const branding: Branding = {
name: 'silverhand', primaryColor: '#000',
logo: 'http://silverhand.png', backgroundColor: '#fff',
}; darkMode: true,
const signInMethods = { darkBackgroundColor: '#000',
primary: ['phone'], darkPrimaryColor: '#fff',
secondary: ['email'], style: BrandingStyle.Logo,
disabled: [], logoUrl: 'http://silverhand.png',
slogan: 'silverhand',
}; };
const socialSignInConnectorIds = ['abc', 'def'];
const response = await signInExperienceRequester.patch('/sign-in-ex/default').send({ const response = await signInExperienceRequester.patch('/sign-in-ex/default').send({
companyInfo, branding,
signInMethods, socialSignInConnectorIds,
}); });
expect(response.status).toEqual(200); expect(response.status).toEqual(200);
expect(response.body).toEqual({ expect(response.body).toEqual({
...mockSignInExperience, ...mockSignInExperience,
companyInfo, branding,
signInMethods, socialSignInConnectorIds,
}); });
}); });
it('PATH /sign-in-ex/:id should throw with invalid inputs', async () => { it('PATCH /sign-in-ex/:id should throw with invalid inputs', async () => {
const signInMethods = { const socialSignInConnectorIds = [123, 456];
primary: [],
secondary: ['email'],
disabled: [],
};
const response = await signInExperienceRequester.patch('/sign-in-ex/default').send({ const response = await signInExperienceRequester.patch('/sign-in-ex/default').send({
signInMethods, socialSignInConnectorIds,
}); });
expect(response.status).toEqual(400); expect(response.status).toEqual(400);
}); });

View file

@ -18,6 +18,7 @@ import {
UserLogType, UserLogType,
UserLogResult, UserLogResult,
ConnectorType, ConnectorType,
SignInMethodState,
} from '@logto/schemas'; } from '@logto/schemas';
import pick from 'lodash.pick'; import pick from 'lodash.pick';
@ -155,39 +156,32 @@ export const mockSetting: Setting = {
export const mockSignInExperience: SignInExperience = { export const mockSignInExperience: SignInExperience = {
id: 'foo', id: 'foo',
companyInfo: {
name: 'logto',
logo: 'http://logto.png',
},
branding: { branding: {
primaryColor: '#000', primaryColor: '#000',
backgroundColor: '#fff', backgroundColor: '#fff',
darkMode: true, darkMode: true,
darkBackgroundColor: '#000', darkBackgroundColor: '#000',
darkPrimaryColor: '#fff', darkPrimaryColor: '#fff',
style: BrandingStyle.CompanyLogo_AppLogo_CompanyName_AppName, style: BrandingStyle.Logo,
logoUrl: 'http://logto.png',
slogan: 'logto',
}, },
termsOfUse: { termsOfUse: {
enabled: false, enabled: false,
}, },
forgetPasswordEnabled: true, forgetPasswordEnabled: true,
localization: { languageInfo: {
autoDetect: true, autoDetect: true,
primaryLanguage: Language.chinese,
fallbackLanguage: Language.english, fallbackLanguage: Language.english,
fixedLanguage: Language.chinese,
}, },
signInMethods: { signInMethods: {
primary: ['email'], username: SignInMethodState.primary,
secondary: [], email: SignInMethodState.disabled,
disabled: [], sms: SignInMethodState.disabled,
social: SignInMethodState.secondary,
}, },
}; socialSignInConnectorIds: ['foo', 'bar'],
export const mockConnector: Connector = {
id: 'foo',
enabled: true,
config: {},
createdAt: 1_645_334_775_356,
}; };
export const mockConnectorList: Connector[] = [ export const mockConnectorList: Connector[] = [

View file

@ -10,6 +10,5 @@ export * from './resource';
export * from './role'; export * from './role';
export * from './setting'; export * from './setting';
export * from './sign-in-experience'; export * from './sign-in-experience';
export * from './sign-in-method';
export * from './user-log'; export * from './user-log';
export * from './user'; export * from './user';

View file

@ -3,48 +3,48 @@
import { z } from 'zod'; import { z } from 'zod';
import { import {
CompanyInfo,
companyInfoGuard,
Branding, Branding,
brandingGuard, brandingGuard,
LanguageInfo,
languageInfoGuard,
TermsOfUse, TermsOfUse,
termsOfUseGuard, termsOfUseGuard,
Localization, SignInMethods,
localizationGuard, signInMethodsGuard,
SignInMethodSettings, ConnectorIds,
signInMethodSettingsGuard, connectorIdsGuard,
GeneratedSchema, GeneratedSchema,
Guard, Guard,
} from '../foundations'; } from '../foundations';
export type CreateSignInExperience = { export type CreateSignInExperience = {
id: string; id: string;
companyInfo: CompanyInfo; branding?: Branding;
branding: Branding; languageInfo?: LanguageInfo;
termsOfUse: TermsOfUse; termsOfUse?: TermsOfUse;
forgetPasswordEnabled?: boolean; forgetPasswordEnabled?: boolean;
localization: Localization; signInMethods?: SignInMethods;
signInMethods: SignInMethodSettings; socialSignInConnectorIds?: ConnectorIds;
}; };
export type SignInExperience = { export type SignInExperience = {
id: string; id: string;
companyInfo: CompanyInfo;
branding: Branding; branding: Branding;
languageInfo: LanguageInfo;
termsOfUse: TermsOfUse; termsOfUse: TermsOfUse;
forgetPasswordEnabled: boolean; forgetPasswordEnabled: boolean;
localization: Localization; signInMethods: SignInMethods;
signInMethods: SignInMethodSettings; socialSignInConnectorIds: ConnectorIds;
}; };
const createGuard: Guard<CreateSignInExperience> = z.object({ const createGuard: Guard<CreateSignInExperience> = z.object({
id: z.string(), id: z.string(),
companyInfo: companyInfoGuard, branding: brandingGuard.optional(),
branding: brandingGuard, languageInfo: languageInfoGuard.optional(),
termsOfUse: termsOfUseGuard, termsOfUse: termsOfUseGuard.optional(),
forgetPasswordEnabled: z.boolean().optional(), forgetPasswordEnabled: z.boolean().optional(),
localization: localizationGuard, signInMethods: signInMethodsGuard.optional(),
signInMethods: signInMethodSettingsGuard, socialSignInConnectorIds: connectorIdsGuard.optional(),
}); });
export const SignInExperiences: GeneratedSchema<CreateSignInExperience> = Object.freeze({ export const SignInExperiences: GeneratedSchema<CreateSignInExperience> = Object.freeze({
@ -52,21 +52,21 @@ export const SignInExperiences: GeneratedSchema<CreateSignInExperience> = Object
tableSingular: 'sign_in_experience', tableSingular: 'sign_in_experience',
fields: { fields: {
id: 'id', id: 'id',
companyInfo: 'company_info',
branding: 'branding', branding: 'branding',
languageInfo: 'language_info',
termsOfUse: 'terms_of_use', termsOfUse: 'terms_of_use',
forgetPasswordEnabled: 'forget_password_enabled', forgetPasswordEnabled: 'forget_password_enabled',
localization: 'localization',
signInMethods: 'sign_in_methods', signInMethods: 'sign_in_methods',
socialSignInConnectorIds: 'social_sign_in_connector_ids',
}, },
fieldKeys: [ fieldKeys: [
'id', 'id',
'companyInfo',
'branding', 'branding',
'languageInfo',
'termsOfUse', 'termsOfUse',
'forgetPasswordEnabled', 'forgetPasswordEnabled',
'localization',
'signInMethods', 'signInMethods',
'socialSignInConnectorIds',
], ],
createGuard, createGuard,
}); });

View file

@ -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,
});

View file

@ -79,20 +79,12 @@ export const adminConsoleConfigGuard = z.object({
export type AdminConsoleConfig = z.infer<typeof adminConsoleConfigGuard>; 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 { export enum BrandingStyle {
CompanyLogo_CompanyName_AppName = 'CompanyLogo_CompanyName_AppName', Logo = 'Logo',
CompanyLogo_AppLogo_CompanyName_AppName = 'CompanyLogo_AppLogo_CompanyName_AppName', Logo_Slogan = 'Logo_Slogan',
AppLogo_CompanyName_AppName = 'AppLogo_CompanyName_AppName',
} }
export const brandingGuard = z.object({ export const brandingGuard = z.object({
@ -102,13 +94,14 @@ export const brandingGuard = z.object({
darkPrimaryColor: z.string(), darkPrimaryColor: z.string(),
darkBackgroundColor: z.string(), darkBackgroundColor: z.string(),
style: z.nativeEnum(BrandingStyle), style: z.nativeEnum(BrandingStyle),
logoUrl: z.string().optional(),
slogan: z.string().optional(),
}); });
export type Branding = z.infer<typeof brandingGuard>; export type Branding = z.infer<typeof brandingGuard>;
export const termsOfUseGuard = z.object({ export const termsOfUseGuard = z.object({
enabled: z.boolean(), enabled: z.boolean(),
content: z.string().optional(),
contentUrl: z.string().optional(), contentUrl: z.string().optional(),
}); });
@ -119,21 +112,32 @@ export enum Language {
chinese = 'zh-cn', chinese = 'zh-cn',
} }
export const localizationGuard = z.object({ export const languageInfoGuard = z.object({
autoDetect: z.boolean(), autoDetect: z.boolean(),
primaryLanguage: z.nativeEnum(Language),
fallbackLanguage: 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({ export enum SignInMethodState {
primary: z.string().array().nonempty().max(3), primary = 'primary',
secondary: z.string().array(), secondary = 'secondary',
disabled: z.string().array(), 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 * Commonly Used

View file

@ -1,10 +1,10 @@
create table sign_in_experiences ( create table sign_in_experiences (
id varchar(128) not null, id varchar(128) not null,
company_info jsonb /* @use CompanyInfo */ not null, branding jsonb /* @use Branding */ not null default '{}'::jsonb,
branding jsonb /* @use Branding */ not null, language_info jsonb /* @use LanguageInfo */ not null default '{}'::jsonb,
terms_of_use jsonb /* @use TermsOfUse */ not null, terms_of_use jsonb /* @use TermsOfUse */ not null default '{}'::jsonb,
forget_password_enabled boolean not null default(true), forget_password_enabled boolean not null default false,
localization jsonb /* @use Localization */ not null, sign_in_methods jsonb /* @use SignInMethods */ not null default '{}'::jsonb,
sign_in_methods jsonb /* @use SignInMethodSettings */ not null, social_sign_in_connector_ids jsonb /* @use ConnectorIds */ not null default '[]'::jsonb,
primary key (id) primary key (id)
) );

View file

@ -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
)