mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(core): optimize translation strictly partial check (#2333)
This commit is contained in:
parent
d42a19133d
commit
494c4ae483
4 changed files with 30 additions and 43 deletions
|
@ -40,13 +40,13 @@ jest.mock('@/queries/custom-phrase', () => ({
|
|||
upsertCustomPhrase: async (customPhrase: CustomPhrase) => upsertCustomPhrase(customPhrase),
|
||||
}));
|
||||
|
||||
const isValidStructure = jest.fn(
|
||||
const isStrictlyPartial = jest.fn(
|
||||
(fullTranslation: Translation, partialTranslation: Partial<Translation>) => true
|
||||
);
|
||||
|
||||
jest.mock('@/utils/translation', () => ({
|
||||
isValidStructure: (fullTranslation: Translation, partialTranslation: Translation) =>
|
||||
isValidStructure(fullTranslation, partialTranslation),
|
||||
isStrictlyPartial: (fullTranslation: Translation, partialTranslation: Translation) =>
|
||||
isStrictlyPartial(fullTranslation, partialTranslation),
|
||||
}));
|
||||
|
||||
const mockFallbackLanguage = trTrTag;
|
||||
|
@ -130,13 +130,13 @@ describe('customPhraseRoutes', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should call isValidStructure', async () => {
|
||||
it('should call isStrictlyPartial', async () => {
|
||||
await customPhraseRequest.put(`/custom-phrases/${mockLanguageTag}`).send(translation);
|
||||
expect(isValidStructure).toBeCalledWith(en.translation, translation);
|
||||
expect(isStrictlyPartial).toBeCalledWith(en.translation, translation);
|
||||
});
|
||||
|
||||
it('should fail when the input translation structure is invalid', async () => {
|
||||
isValidStructure.mockReturnValueOnce(false);
|
||||
isStrictlyPartial.mockReturnValueOnce(false);
|
||||
const response = await customPhraseRequest
|
||||
.put(`/custom-phrases/${mockLanguageTag}`)
|
||||
.send(translation);
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
} from '@/queries/custom-phrase';
|
||||
import { findDefaultSignInExperience } from '@/queries/sign-in-experience';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
import { isValidStructure } from '@/utils/translation';
|
||||
import { isStrictlyPartial } from '@/utils/translation';
|
||||
|
||||
import type { AuthedRouter } from './types';
|
||||
|
||||
|
@ -70,7 +70,7 @@ export default function customPhraseRoutes<T extends AuthedRouter>(router: T) {
|
|||
const translation = cleanDeepTranslation(body);
|
||||
|
||||
assertThat(
|
||||
isValidStructure(resource.en.translation, translation),
|
||||
isStrictlyPartial(resource.en.translation, translation),
|
||||
new RequestError('localization.invalid_translation_structure')
|
||||
);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import en from '@logto/phrases-ui/lib/locales/en';
|
||||
import fr from '@logto/phrases-ui/lib/locales/fr';
|
||||
|
||||
import { isValidStructure } from '@/utils/translation';
|
||||
import { isStrictlyPartial } from '@/utils/translation';
|
||||
|
||||
const customizedFrTranslation = {
|
||||
secondary: {
|
||||
|
@ -10,15 +10,15 @@ const customizedFrTranslation = {
|
|||
},
|
||||
};
|
||||
|
||||
describe('isValidStructure', () => {
|
||||
describe('isStrictlyPartial', () => {
|
||||
it('should be true when its structure is valid', () => {
|
||||
expect(isValidStructure(en.translation, fr.translation)).toBeTruthy();
|
||||
expect(isValidStructure(en.translation, customizedFrTranslation)).toBeTruthy();
|
||||
expect(isStrictlyPartial(en.translation, fr.translation)).toBeTruthy();
|
||||
expect(isStrictlyPartial(en.translation, customizedFrTranslation)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should be true when the structure is partial and the existing key-value pairs are correct', () => {
|
||||
expect(
|
||||
isValidStructure(en.translation, {
|
||||
isStrictlyPartial(en.translation, {
|
||||
secondary: {
|
||||
sign_in_with: 'Se connecter avec {{methods, list(type: disjunction;)}}',
|
||||
// Missing 'secondary.social_bind_with' key-value pair
|
||||
|
@ -29,7 +29,7 @@ describe('isValidStructure', () => {
|
|||
|
||||
it('should be false when there is an unexpected key-value pair', () => {
|
||||
expect(
|
||||
isValidStructure(en.translation, {
|
||||
isStrictlyPartial(en.translation, {
|
||||
secondary: {
|
||||
sign_in_with: 'Se connecter avec {{methods, list(type: disjunction;)}}',
|
||||
social_bind_with:
|
||||
|
|
|
@ -1,38 +1,25 @@
|
|||
import type { Translation } from '@logto/schemas';
|
||||
|
||||
// LOG-4385: Refactor me
|
||||
// eslint-disable-next-line complexity
|
||||
export const isValidStructure = (fullTranslation: Translation, partialTranslation: Translation) => {
|
||||
const fullKeys = new Set(Object.keys(fullTranslation));
|
||||
const partialKeys = Object.keys(partialTranslation);
|
||||
|
||||
if (fullKeys.size === 0 || partialKeys.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (partialKeys.some((key) => !fullKeys.has(key))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(fullTranslation)) {
|
||||
const targetValue = partialTranslation[key];
|
||||
|
||||
if (targetValue === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
if (typeof targetValue === 'string') {
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* @param fullTranslation The translation with full keys
|
||||
* @param partialTranslation The translation to check
|
||||
* @returns If the flatten keys of `partialTranslation` is a subset of `fullTranslation`
|
||||
*/
|
||||
export const isStrictlyPartial = (
|
||||
fullTranslation: Translation,
|
||||
partialTranslation: Translation
|
||||
): boolean => {
|
||||
return Object.entries(partialTranslation).every(([key, value]) => {
|
||||
const fullValue = fullTranslation[key];
|
||||
|
||||
if (!fullValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof targetValue === 'string' || !isValidStructure(value, targetValue)) {
|
||||
return false;
|
||||
if (typeof fullValue === 'object' && typeof value === 'object') {
|
||||
return isStrictlyPartial(fullValue, value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return typeof fullValue === typeof value;
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue