0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat(core,schemas): add phrases schema and GET /custom-phrases/:languageKey route (#1905)

This commit is contained in:
IceHe 2022-09-13 17:36:37 +08:00 committed by GitHub
parent 03fe4beb43
commit 7242aa8c2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 121 additions and 0 deletions

View file

@ -0,0 +1,14 @@
import { CustomPhrase, CustomPhrases } from '@logto/schemas';
import { sql } from 'slonik';
import { convertToIdentifiers } from '@/database/utils';
import envSet from '@/env-set';
const { table, fields } = convertToIdentifiers(CustomPhrases);
export const findCustomPhraseByLanguageKey = async (languageKey: string): Promise<CustomPhrase> =>
envSet.pool.one<CustomPhrase>(sql`
select ${sql.join(Object.values(fields), sql`, `)}
from ${table}
where ${fields.languageKey} = ${languageKey}
`);

View file

@ -0,0 +1,62 @@
import { CustomPhrase } from '@logto/schemas';
import RequestError from '@/errors/RequestError';
import phraseRoutes from '@/routes/custom-phrase';
import { createRequester } from '@/utils/test-utils';
const mockLanguageKey = 'en-US';
const mockCustomPhrases: Record<string, CustomPhrase> = {
[mockLanguageKey]: {
languageKey: mockLanguageKey,
translation: {
input: {
username: 'Username',
password: 'Password',
email: 'Email',
phone_number: 'Phone number',
confirm_password: 'Confirm password',
},
},
},
};
const findCustomPhraseByLanguageKey = jest.fn(async (languageKey: string) => {
const mockCustomPhrase = mockCustomPhrases[languageKey];
if (!mockCustomPhrase) {
throw new RequestError({ code: 'entity.not_found', status: 404 });
}
return mockCustomPhrase;
});
jest.mock('@/queries/custom-phrase', () => ({
findCustomPhraseByLanguageKey: async (key: string) => findCustomPhraseByLanguageKey(key),
}));
describe('customPhraseRoutes', () => {
const phraseRequest = createRequester({ authedRoutes: phraseRoutes });
afterEach(() => {
jest.clearAllMocks();
});
describe('GET /custom-phrases/:languageKey', () => {
it('should call findCustomPhraseByLanguageKey once', async () => {
await phraseRequest.get(`/custom-phrases/${mockLanguageKey}`);
expect(findCustomPhraseByLanguageKey).toBeCalledTimes(1);
});
it('should return the specified custom phrase existing in the database', async () => {
const response = await phraseRequest.get(`/custom-phrases/${mockLanguageKey}`);
expect(response.status).toEqual(200);
expect(response.body).toEqual(mockCustomPhrases[mockLanguageKey]);
});
it('should return 404 status code when there is no specified custom phrase in the database', async () => {
const response = await phraseRequest.get('/custom-phrases/en-UK');
expect(response.status).toEqual(404);
});
});
});

View file

@ -0,0 +1,26 @@
import { CustomPhrases } from '@logto/schemas';
import koaGuard from '@/middleware/koa-guard';
import { findCustomPhraseByLanguageKey } from '@/queries/custom-phrase';
import { AuthedRouter } from './types';
export default function phraseRoutes<T extends AuthedRouter>(router: T) {
router.get(
'/custom-phrases/:languageKey',
koaGuard({
// Next up: guard languageKey by enum LanguageKey (that will be provided by @sijie later.)
params: CustomPhrases.createGuard.pick({ languageKey: true }),
response: CustomPhrases.guard,
}),
async (ctx, next) => {
const {
params: { languageKey },
} = ctx.guard;
ctx.body = await findCustomPhraseByLanguageKey(languageKey);
return next();
}
);
}

View file

@ -10,6 +10,7 @@ import adminUserRoutes from '@/routes/admin-user';
import applicationRoutes from '@/routes/application';
import authnRoutes from '@/routes/authn';
import connectorRoutes from '@/routes/connector';
import customPhraseRoutes from '@/routes/custom-phrase';
import dashboardRoutes from '@/routes/dashboard';
import logRoutes from '@/routes/log';
import resourceRoutes from '@/routes/resource';
@ -39,6 +40,7 @@ const createRouters = (provider: Provider) => {
logRoutes(managementRouter);
roleRoutes(managementRouter);
dashboardRoutes(managementRouter);
customPhraseRoutes(managementRouter);
const anonymousRouter: AnonymousRouter = new Router();
wellKnownRoutes(anonymousRouter, provider);

View file

@ -155,3 +155,15 @@ export const adminConsoleConfigGuard = z.object({
});
export type AdminConsoleConfig = z.infer<typeof adminConsoleConfigGuard>;
/**
* Phrases
*/
export type Translation = {
[key: string]: string | Translation;
};
export const translationGuard: z.ZodType<Translation> = z.lazy(() =>
z.record(z.string().or(translationGuard))
);

View file

@ -0,0 +1,5 @@
create table custom_phrases (
language_key varchar(16) not null,
translation jsonb /* @use Translation */ not null,
primary key(language_key)
);