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 { 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 () => {
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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[] = [
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>;
|
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
|
||||||
|
|
|
@ -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)
|
||||||
)
|
);
|
||||||
|
|
|
@ -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