mirror of https://github.com/buster-so/buster.git
move currency around to dictionary
This commit is contained in:
parent
ec2a3f6238
commit
95e0c8d7b5
|
@ -1,4 +1,4 @@
|
||||||
import type { Currency } from '@buster/server-shared/currency';
|
import type { Currency } from '@buster/server-shared/dictionary';
|
||||||
|
|
||||||
const codeToFlag = {
|
const codeToFlag = {
|
||||||
AED: '🇦🇪',
|
AED: '🇦🇪',
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { CurrencyResponse } from '@buster/server-shared/currency';
|
import type { CurrencyResponse } from '@buster/server-shared/dictionary';
|
||||||
import { Hono } from 'hono';
|
import { Hono } from 'hono';
|
||||||
import { CURRENCIES_MAP } from './config';
|
import { CURRENCIES_MAP } from './config';
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export * from './queryRequests';
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { queryKeys } from '@/api/query_keys';
|
|
||||||
import { QueryClient, usePrefetchQuery, useQuery } from '@tanstack/react-query';
|
|
||||||
import { mainApiV2 } from '../instances';
|
|
||||||
import { Currency } from '@buster/server-shared/currency';
|
|
||||||
|
|
||||||
export const useGetCurrencies = () => {
|
|
||||||
return useQuery({
|
|
||||||
...queryKeys.getCurrencies,
|
|
||||||
queryFn: async () => {
|
|
||||||
return mainApiV2.get<Currency[]>('/dictionaries/currency').then(async (res) => res.data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const prefetchGetCurrencies = async (queryClientProp?: QueryClient) => {
|
|
||||||
const queryClient = queryClientProp || new QueryClient();
|
|
||||||
|
|
||||||
await queryClient.prefetchQuery({
|
|
||||||
...queryKeys.getCurrencies,
|
|
||||||
queryFn: async () => {
|
|
||||||
return mainApiV2.get<Currency[]>('/dictionaries/currency').then(async (res) => res.data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return queryClient;
|
|
||||||
};
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { useQuery, QueryClient } from '@tanstack/react-query';
|
import { useQuery, QueryClient } from '@tanstack/react-query';
|
||||||
import { getColorThemes } from './requests';
|
import { getColorThemes, getCurrencies } from './requests';
|
||||||
import { dictionariesQueryKeys } from '../../query_keys/dictionaries';
|
import { dictionariesQueryKeys } from '@/api/query_keys/dictionaries';
|
||||||
|
|
||||||
export const useColorDictionaryThemes = () => {
|
export const useColorDictionaryThemes = () => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
...dictionariesQueryKeys.colorThemes,
|
...dictionariesQueryKeys.colorThemes,
|
||||||
initialData: [],
|
|
||||||
queryFn: getColorThemes
|
queryFn: getColorThemes
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -20,3 +19,21 @@ export const prefetchColorThemes = async (queryClientProp?: QueryClient) => {
|
||||||
|
|
||||||
return queryClient;
|
return queryClient;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetCurrencies = () => {
|
||||||
|
return useQuery({
|
||||||
|
...dictionariesQueryKeys.getCurrencies,
|
||||||
|
queryFn: getCurrencies
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const prefetchGetCurrencies = async (queryClientProp?: QueryClient) => {
|
||||||
|
const queryClient = queryClientProp || new QueryClient();
|
||||||
|
|
||||||
|
await queryClient.prefetchQuery({
|
||||||
|
...dictionariesQueryKeys.getCurrencies,
|
||||||
|
queryFn: getCurrencies
|
||||||
|
});
|
||||||
|
|
||||||
|
return queryClient;
|
||||||
|
};
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
import type { ColorThemeDictionariesResponse } from '@buster/server-shared/dictionary';
|
import type { ColorThemeDictionariesResponse } from '@buster/server-shared/dictionary';
|
||||||
import { mainApiV2 } from '../instances';
|
import { mainApiV2 } from '../instances';
|
||||||
|
import type { CurrencyResponse } from '@buster/server-shared/dictionary';
|
||||||
|
|
||||||
export const getColorThemes = async () => {
|
export const getColorThemes = async () => {
|
||||||
return await mainApiV2
|
return await mainApiV2
|
||||||
.get<ColorThemeDictionariesResponse>('/dictionaries/color-themes')
|
.get<ColorThemeDictionariesResponse>('/dictionaries/color-themes')
|
||||||
.then((res) => res.data);
|
.then((res) => res.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCurrencies = async () => {
|
||||||
|
return await mainApiV2.get<CurrencyResponse>('/dictionaries/currency').then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
|
||||||
import { CurrencyResponse } from '@buster/server-shared/currency';
|
|
||||||
|
|
||||||
export const getCurrencies = queryOptions<CurrencyResponse>({
|
|
||||||
queryKey: ['nextjs', 'list', 'currencies'],
|
|
||||||
initialData: [],
|
|
||||||
initialDataUpdatedAt: 0,
|
|
||||||
staleTime: 1000 * 60 * 60 * 24 * 7 //7 days
|
|
||||||
});
|
|
||||||
|
|
||||||
export const currencyQueryKeys = {
|
|
||||||
getCurrencies
|
|
||||||
};
|
|
|
@ -1,11 +1,22 @@
|
||||||
|
import type { CurrencyResponse } from '@buster/server-shared/dictionary';
|
||||||
import type { ColorThemeDictionariesResponse } from '@buster/server-shared/dictionary';
|
import type { ColorThemeDictionariesResponse } from '@buster/server-shared/dictionary';
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
|
|
||||||
export const colorThemes = queryOptions<ColorThemeDictionariesResponse>({
|
export const colorThemes = queryOptions<ColorThemeDictionariesResponse>({
|
||||||
queryKey: ['color-themes', 'list'],
|
queryKey: ['dictionaries', 'color-themes', 'list'] as const,
|
||||||
staleTime: 60 * 1000 * 60 * 24 * 3 // 3 days
|
initialData: [],
|
||||||
|
initialDataUpdatedAt: 0,
|
||||||
|
staleTime: 1000 * 60 * 60 * 24 * 7 // 7 days
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getCurrencies = queryOptions<CurrencyResponse>({
|
||||||
|
queryKey: ['dictionaries', 'currencies', 'list'] as const,
|
||||||
|
initialData: [],
|
||||||
|
initialDataUpdatedAt: 0,
|
||||||
|
staleTime: 1000 * 60 * 60 * 24 * 7 // 7 days
|
||||||
});
|
});
|
||||||
|
|
||||||
export const dictionariesQueryKeys = {
|
export const dictionariesQueryKeys = {
|
||||||
colorThemes
|
colorThemes,
|
||||||
|
getCurrencies
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { datasetGroupQueryKeys } from './dataset_groups';
|
||||||
import { datasetQueryKeys } from './datasets';
|
import { datasetQueryKeys } from './datasets';
|
||||||
import { datasourceQueryKeys } from './datasources';
|
import { datasourceQueryKeys } from './datasources';
|
||||||
import { metricsQueryKeys } from './metric';
|
import { metricsQueryKeys } from './metric';
|
||||||
import { currencyQueryKeys } from './currency';
|
|
||||||
import { permissionGroupQueryKeys } from './permission_groups';
|
import { permissionGroupQueryKeys } from './permission_groups';
|
||||||
import { searchQueryKeys } from './search';
|
import { searchQueryKeys } from './search';
|
||||||
import { termsQueryKeys } from './terms';
|
import { termsQueryKeys } from './terms';
|
||||||
|
@ -26,7 +25,6 @@ export const queryKeys = {
|
||||||
...datasourceQueryKeys,
|
...datasourceQueryKeys,
|
||||||
...datasetGroupQueryKeys,
|
...datasetGroupQueryKeys,
|
||||||
...permissionGroupQueryKeys,
|
...permissionGroupQueryKeys,
|
||||||
...currencyQueryKeys,
|
|
||||||
...securityQueryKeys,
|
...securityQueryKeys,
|
||||||
...slackQueryKeys,
|
...slackQueryKeys,
|
||||||
...dictionariesQueryKeys
|
...dictionariesQueryKeys
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DefaultThemeSelectorBase } from './DefaultThemeSelectorBase';
|
import { DefaultThemeSelectorBase } from './DefaultThemeSelectorBase';
|
||||||
import { useGetMyUserInfo } from '@/api/buster_rest/users/queryRequests';
|
|
||||||
import { useColorDictionaryThemes } from '@/api/buster_rest/dictionaries';
|
|
||||||
import { StatusCard } from '@/components/ui/card/StatusCard';
|
import { StatusCard } from '@/components/ui/card/StatusCard';
|
||||||
import { CircleSpinnerLoader } from '../../../ui/loaders';
|
import { CircleSpinnerLoader } from '../../../ui/loaders';
|
||||||
import { useThemeOperations } from '@/context-hooks/useThemeOperations';
|
import { useThemeOperations } from '@/context-hooks/useThemeOperations';
|
||||||
|
@ -9,11 +7,8 @@ import { useGetPalettes } from '@/context-hooks/usePalettes';
|
||||||
|
|
||||||
export const DefaultThemeSelector = React.memo(
|
export const DefaultThemeSelector = React.memo(
|
||||||
({ className, themeListClassName }: { className?: string; themeListClassName?: string }) => {
|
({ className, themeListClassName }: { className?: string; themeListClassName?: string }) => {
|
||||||
const { data: userData } = useGetMyUserInfo();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isErrorDictionaryPalettes,
|
isErrorDictionaryPalettes,
|
||||||
isFetchedDictionaryPalettes,
|
|
||||||
organizationPalettes,
|
organizationPalettes,
|
||||||
dictionaryPalettes,
|
dictionaryPalettes,
|
||||||
selectedPaletteId
|
selectedPaletteId
|
||||||
|
@ -22,7 +17,12 @@ export const DefaultThemeSelector = React.memo(
|
||||||
const { onCreateCustomTheme, onDeleteCustomTheme, onModifyCustomTheme, onSelectTheme } =
|
const { onCreateCustomTheme, onDeleteCustomTheme, onModifyCustomTheme, onSelectTheme } =
|
||||||
useThemeOperations();
|
useThemeOperations();
|
||||||
|
|
||||||
if (!isFetchedDictionaryPalettes) return <CircleSpinnerLoader />;
|
if (dictionaryPalettes.length === 0 && !isErrorDictionaryPalettes)
|
||||||
|
return (
|
||||||
|
<div className="flex h-24 w-full min-w-24 items-center justify-center">
|
||||||
|
<CircleSpinnerLoader size={24} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
if (isErrorDictionaryPalettes)
|
if (isErrorDictionaryPalettes)
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -40,10 +40,6 @@ const PickButton = React.memo(() => {
|
||||||
|
|
||||||
const hasDefaultPalette = !!defaultPalette;
|
const hasDefaultPalette = !!defaultPalette;
|
||||||
|
|
||||||
useMount(() => {
|
|
||||||
prefetchColorThemes();
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Popover
|
||||||
className="p-0"
|
className="p-0"
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { ColorPalette } from '@buster/server-shared/organization';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useColorDictionaryThemes } from '../api/buster_rest/dictionaries';
|
import { useColorDictionaryThemes } from '../api/buster_rest/dictionaries';
|
||||||
import { DEFAULT_CHART_THEME } from '@buster/server-shared/metrics';
|
import { DEFAULT_CHART_THEME } from '@buster/server-shared/metrics';
|
||||||
|
import { useGetCurrencies } from '../api/buster_rest/dictionaries';
|
||||||
|
|
||||||
const useGetOrganizationPalettes = () => {
|
const useGetOrganizationPalettes = () => {
|
||||||
const { data: userData } = useGetMyUserInfo();
|
const { data: userData } = useGetMyUserInfo();
|
||||||
|
@ -27,11 +28,11 @@ const useGetOrganizationPalettes = () => {
|
||||||
|
|
||||||
export const useGetPalettes = () => {
|
export const useGetPalettes = () => {
|
||||||
const { organizationPalettes, selectedPaletteId } = useGetOrganizationPalettes();
|
const { organizationPalettes, selectedPaletteId } = useGetOrganizationPalettes();
|
||||||
const {
|
const { data: dictionaryPalettes, isError: isErrorDictionaryPalettes } =
|
||||||
data: dictionaryPalettes,
|
useColorDictionaryThemes();
|
||||||
isFetched: isFetchedDictionaryPalettes,
|
const { data: currencies } = useGetCurrencies();
|
||||||
isError: isErrorDictionaryPalettes
|
|
||||||
} = useColorDictionaryThemes();
|
console.log(currencies, dictionaryPalettes);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const allPalettes = [...dictionaryPalettes, ...organizationPalettes];
|
const allPalettes = [...dictionaryPalettes, ...organizationPalettes];
|
||||||
|
@ -43,10 +44,10 @@ export const useGetPalettes = () => {
|
||||||
dictionaryPalettes,
|
dictionaryPalettes,
|
||||||
selectedPaletteId,
|
selectedPaletteId,
|
||||||
defaultPalette,
|
defaultPalette,
|
||||||
isFetchedDictionaryPalettes,
|
|
||||||
isErrorDictionaryPalettes
|
isErrorDictionaryPalettes
|
||||||
};
|
};
|
||||||
}, [dictionaryPalettes, organizationPalettes, selectedPaletteId, isFetchedDictionaryPalettes]);
|
}, [dictionaryPalettes, organizationPalettes, selectedPaletteId]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSelectedColorPalette = (colors: string[] | undefined | null): string[] => {
|
export const useSelectedColorPalette = (colors: string[] | undefined | null): string[] => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import type { ColumnLabelFormat } from '@buster/server-shared/metrics';
|
import type { ColumnLabelFormat } from '@buster/server-shared/metrics';
|
||||||
import { useGetCurrencies } from '@/api/buster_rest/currency';
|
import { useGetCurrencies } from '@/api/buster_rest/dictionaries';
|
||||||
import { Select, type SelectItem } from '@/components/ui/select';
|
import { Select, type SelectItem } from '@/components/ui/select';
|
||||||
import { Text } from '@/components/ui/typography';
|
import { Text } from '@/components/ui/typography';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useMemo } from 'react';
|
||||||
import type { ChartConfigProps } from '@buster/server-shared/metrics';
|
import type { ChartConfigProps } from '@buster/server-shared/metrics';
|
||||||
import { type ChartEncodes, type ColumnSettings } from '@buster/server-shared/metrics';
|
import { type ChartEncodes, type ColumnSettings } from '@buster/server-shared/metrics';
|
||||||
import type { ColumnLabelFormat } from '@buster/server-shared/metrics';
|
import type { ColumnLabelFormat } from '@buster/server-shared/metrics';
|
||||||
import { prefetchGetCurrencies } from '@/api/buster_rest/currency';
|
import { prefetchGetCurrencies } from '@/api/buster_rest/dictionaries';
|
||||||
import { ErrorBoundary } from '@/components/ui/error';
|
import { ErrorBoundary } from '@/components/ui/error';
|
||||||
import { Text } from '@/components/ui/typography';
|
import { Text } from '@/components/ui/typography';
|
||||||
import { useUpdateMetricChart } from '@/context/Metrics';
|
import { useUpdateMetricChart } from '@/context/Metrics';
|
||||||
|
|
|
@ -36,10 +36,6 @@
|
||||||
"types": "./dist/dashboards/index.d.ts",
|
"types": "./dist/dashboards/index.d.ts",
|
||||||
"default": "./dist/dashboards/index.js"
|
"default": "./dist/dashboards/index.js"
|
||||||
},
|
},
|
||||||
"./currency": {
|
|
||||||
"types": "./dist/currency/index.d.ts",
|
|
||||||
"default": "./dist/currency/index.js"
|
|
||||||
},
|
|
||||||
"./slack": {
|
"./slack": {
|
||||||
"types": "./dist/slack/index.d.ts",
|
"types": "./dist/slack/index.d.ts",
|
||||||
"default": "./dist/slack/index.js"
|
"default": "./dist/slack/index.js"
|
||||||
|
|
|
@ -1,192 +0,0 @@
|
||||||
import { describe, expect, it } from 'vitest';
|
|
||||||
import { CurrencySchema } from './currency.types';
|
|
||||||
|
|
||||||
describe('CurrencySchema', () => {
|
|
||||||
it('should parse valid currency object', () => {
|
|
||||||
const validCurrency = {
|
|
||||||
code: 'USD',
|
|
||||||
description: 'United States Dollar',
|
|
||||||
flag: '🇺🇸',
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = CurrencySchema.safeParse(validCurrency);
|
|
||||||
expect(result.success).toBe(true);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
expect(result.data.code).toBe('USD');
|
|
||||||
expect(result.data.description).toBe('United States Dollar');
|
|
||||||
expect(result.data.flag).toBe('🇺🇸');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require all fields', () => {
|
|
||||||
const incompleteObjects = [
|
|
||||||
{
|
|
||||||
// missing code
|
|
||||||
description: 'Euro',
|
|
||||||
flag: '🇪🇺',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'EUR',
|
|
||||||
// missing description
|
|
||||||
flag: '🇪🇺',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'EUR',
|
|
||||||
description: 'Euro',
|
|
||||||
// missing flag
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const obj of incompleteObjects) {
|
|
||||||
const result = CurrencySchema.safeParse(obj);
|
|
||||||
expect(result.success).toBe(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate that all fields are strings', () => {
|
|
||||||
const invalidTypes = [
|
|
||||||
{
|
|
||||||
code: 123, // should be string
|
|
||||||
description: 'Invalid Code',
|
|
||||||
flag: '🇺🇸',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'USD',
|
|
||||||
description: true, // should be string
|
|
||||||
flag: '🇺🇸',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'USD',
|
|
||||||
description: 'United States Dollar',
|
|
||||||
flag: null, // should be string
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const obj of invalidTypes) {
|
|
||||||
const result = CurrencySchema.safeParse(obj);
|
|
||||||
expect(result.success).toBe(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle various currency examples', () => {
|
|
||||||
const currencies = [
|
|
||||||
{
|
|
||||||
code: 'EUR',
|
|
||||||
description: 'Euro',
|
|
||||||
flag: '🇪🇺',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'GBP',
|
|
||||||
description: 'British Pound Sterling',
|
|
||||||
flag: '🇬🇧',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'JPY',
|
|
||||||
description: 'Japanese Yen',
|
|
||||||
flag: '🇯🇵',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'CAD',
|
|
||||||
description: 'Canadian Dollar',
|
|
||||||
flag: '🇨🇦',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'AUD',
|
|
||||||
description: 'Australian Dollar',
|
|
||||||
flag: '🇦🇺',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const currency of currencies) {
|
|
||||||
const result = CurrencySchema.safeParse(currency);
|
|
||||||
expect(result.success).toBe(true);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
expect(result.data.code).toBe(currency.code);
|
|
||||||
expect(result.data.description).toBe(currency.description);
|
|
||||||
expect(result.data.flag).toBe(currency.flag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle empty strings', () => {
|
|
||||||
const currencyWithEmptyStrings = {
|
|
||||||
code: '',
|
|
||||||
description: '',
|
|
||||||
flag: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = CurrencySchema.safeParse(currencyWithEmptyStrings);
|
|
||||||
expect(result.success).toBe(true);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
expect(result.data.code).toBe('');
|
|
||||||
expect(result.data.description).toBe('');
|
|
||||||
expect(result.data.flag).toBe('');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle long strings', () => {
|
|
||||||
const currencyWithLongStrings = {
|
|
||||||
code: 'VERYLONGCURRENCYCODE',
|
|
||||||
description:
|
|
||||||
'This is a very long description for a currency that might not exist in real life but should still be valid according to our schema',
|
|
||||||
flag: '🏳️🌈🏳️⚧️🇺🇳', // Multiple flag emojis
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = CurrencySchema.safeParse(currencyWithLongStrings);
|
|
||||||
expect(result.success).toBe(true);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
expect(result.data.code).toBe('VERYLONGCURRENCYCODE');
|
|
||||||
expect(result.data.description).toBe(
|
|
||||||
'This is a very long description for a currency that might not exist in real life but should still be valid according to our schema'
|
|
||||||
);
|
|
||||||
expect(result.data.flag).toBe('🏳️🌈🏳️⚧️🇺🇳');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle special characters and unicode', () => {
|
|
||||||
const currencyWithSpecialChars = {
|
|
||||||
code: 'BTC-₿',
|
|
||||||
description: 'Bitcoin (₿) - Digital Currency with special chars: @#$%^&*()[]{}',
|
|
||||||
flag: '₿',
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = CurrencySchema.safeParse(currencyWithSpecialChars);
|
|
||||||
expect(result.success).toBe(true);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
expect(result.data.code).toBe('BTC-₿');
|
|
||||||
expect(result.data.description).toBe(
|
|
||||||
'Bitcoin (₿) - Digital Currency with special chars: @#$%^&*()[]{}'
|
|
||||||
);
|
|
||||||
expect(result.data.flag).toBe('₿');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reject additional properties', () => {
|
|
||||||
const currencyWithExtraProps = {
|
|
||||||
code: 'USD',
|
|
||||||
description: 'United States Dollar',
|
|
||||||
flag: '🇺🇸',
|
|
||||||
extraProperty: 'This should not be allowed',
|
|
||||||
anotherExtra: 123,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = CurrencySchema.safeParse(currencyWithExtraProps);
|
|
||||||
// Zod by default allows additional properties unless .strict() is used
|
|
||||||
// Since the schema doesn't use .strict(), extra properties are allowed but ignored
|
|
||||||
expect(result.success).toBe(true);
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
// Extra properties should not be included in the result
|
|
||||||
expect('extraProperty' in result.data).toBe(false);
|
|
||||||
expect('anotherExtra' in result.data).toBe(false);
|
|
||||||
expect(result.data.code).toBe('USD');
|
|
||||||
expect(result.data.description).toBe('United States Dollar');
|
|
||||||
expect(result.data.flag).toBe('🇺🇸');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,2 +0,0 @@
|
||||||
export * from './currency.types';
|
|
||||||
export * from './responses';
|
|
|
@ -1,6 +0,0 @@
|
||||||
import { z } from 'zod';
|
|
||||||
import { CurrencySchema } from './currency.types';
|
|
||||||
|
|
||||||
export const CurrencyResponseSchema = z.array(CurrencySchema);
|
|
||||||
|
|
||||||
export type CurrencyResponse = z.infer<typeof CurrencyResponseSchema>;
|
|
|
@ -7,3 +7,7 @@ export const CurrencySchema = z.object({
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Currency = z.infer<typeof CurrencySchema>;
|
export type Currency = z.infer<typeof CurrencySchema>;
|
||||||
|
|
||||||
|
export const CurrencyResponseSchema = z.array(CurrencySchema);
|
||||||
|
|
||||||
|
export type CurrencyResponse = z.infer<typeof CurrencyResponseSchema>;
|
|
@ -1 +1,2 @@
|
||||||
export * from './color-themes';
|
export * from './color-themes';
|
||||||
|
export * from './currency';
|
||||||
|
|
Loading…
Reference in New Issue