Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refactoring currency module to use central geo data [ATLAS-167] #119

Merged
merged 51 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
04be70f
[chore]: migrate convertToMajorUnit from modular data to geo data
RgnDunes Mar 30, 2024
0a31e95
[chore]: migrate convertToMinorUnit from modular data to geo data
RgnDunes Mar 30, 2024
0a99b1e
[chore]: migrate getCurrencySymbol, getCurrencyList from modular data…
RgnDunes Mar 30, 2024
cecfc5e
[fix]: minor typo issues
RgnDunes Mar 30, 2024
94a60b4
[chore]: remove module currency data
RgnDunes Mar 30, 2024
a1bd63f
[fix]: fixed exception message and respective UT for currency module
RgnDunes Apr 1, 2024
f94e5ce
[feat]: add getters for currency name and denominations and their UTs
RgnDunes Apr 1, 2024
f4a42e4
Merge remote-tracking branch 'origin/master' into feat/currency-geo-d…
RgnDunes Apr 1, 2024
803a5fc
[feat]: i18nify-data alias path added for static imports
tarun-khanna Apr 2, 2024
a8665df
Merge branch 'master' of github.com:razorpay/i18nify into feat/i18nif…
tarun-khanna Apr 2, 2024
345bbcb
Create six-seals-matter.md
tarun-khanna Apr 2, 2024
18fb57b
[chore]: Merge remote-tracking branch 'origin/feat/i18nify-data-alias…
RgnDunes Apr 2, 2024
fb69967
[fix]: update i18nify-data imports
RgnDunes Apr 2, 2024
31116f1
[chore]: alias mapping added in jest config
tarun-khanna Apr 2, 2024
a33b6ed
Merge branch 'feat/i18nify-data-alias' of github.com:razorpay/i18nify…
tarun-khanna Apr 2, 2024
155bcdb
Merge remote-tracking branch 'origin/feat/i18nify-data-alias' into fe…
RgnDunes Apr 2, 2024
e8e64d2
[fix]: fix e2e
RgnDunes Apr 2, 2024
8e1cdbe
Create chilly-timers-impress.md
RgnDunes Apr 2, 2024
0a37540
[chore]: removed extra changeset file
RgnDunes Apr 2, 2024
dff98c0
[fix]: remove unused code
RgnDunes Apr 2, 2024
010849b
[fix]: jst coverage
RgnDunes Apr 2, 2024
2104a53
[chore]: Merge remote-tracking branch 'origin/master' into feat/curre…
RgnDunes Apr 2, 2024
883fd57
[chore]: add config for build time support
RgnDunes Apr 2, 2024
6568461
[chore]: modify existing currency apis to use json subset created dur…
RgnDunes Apr 2, 2024
58fd032
[fix]: fix build issues
RgnDunes Apr 2, 2024
92cfe26
[fix]: fix build issues
RgnDunes Apr 2, 2024
d969053
[fix]: fix build issues
RgnDunes Apr 2, 2024
847cc99
[feat]: add prebuild command in validate.yml
RgnDunes Apr 2, 2024
051550d
[fix]: update script.js
RgnDunes Apr 2, 2024
8ba0688
[fix]: update script.js
RgnDunes Apr 2, 2024
4fd7677
[fix]: update script.js
RgnDunes Apr 2, 2024
684fff0
[fix]: fix ts errors
RgnDunes Apr 2, 2024
3f0646f
[feat]: add codeCov GH action prebuild step
RgnDunes Apr 3, 2024
719e25c
[feat]: add working-directory for prebuild step in codeCov
RgnDunes Apr 3, 2024
08c270a
[chore]: modifies script.js to script.ts
RgnDunes Apr 3, 2024
e6c1a19
[fix]: remove script.js
RgnDunes Apr 3, 2024
550f6e4
[resolve]: resolve review comments
RgnDunes Apr 3, 2024
6f2c12d
[chore]: migrated scripts to cript folder
RgnDunes Apr 5, 2024
2190465
[prettier]: apply prettier formatting
RgnDunes Apr 5, 2024
b0e180e
Merge remote-tracking branch 'origin/master' into feat/currency-geo-d…
RgnDunes Apr 9, 2024
31db929
Merge remote-tracking branch 'origin/master' into feat/currency-geo-d…
RgnDunes Apr 12, 2024
de316a9
Merge remote-tracking branch 'origin/master' into feat/currency-geo-d…
RgnDunes Apr 12, 2024
42b8869
[fix]: rollup json import fix
RgnDunes Apr 12, 2024
77eefa5
Merge remote-tracking branch 'origin/master' into feat/currency-geo-d…
RgnDunes Apr 29, 2024
b7af662
Merge branch 'master' into feat/currency-geo-data-migration
RgnDunes Apr 29, 2024
dfdb503
[chore]: resolve review comments
RgnDunes May 1, 2024
da3d1cd
Merge branch 'master' into feat/currency-geo-data-migration
RgnDunes May 1, 2024
07cef04
[test]: dummy commit
RgnDunes May 1, 2024
45c43ca
[chore]: remove redundant type assertions
RgnDunes May 2, 2024
9349cee
[chore]: remove redundant type assertions
RgnDunes May 2, 2024
6f68562
[chore]: resolve review comments
RgnDunes May 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/chilly-timers-impress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@razorpay/i18nify-js': patch
---

feat: refactoring currency module to use central geo data
4 changes: 2 additions & 2 deletions packages/i18nify-js/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const declarationTypes = modules.map((_module) => ({
file: `lib/esm/${_module.name}/index.d.ts`,
format: 'es',
},
plugins: [dts()],
plugins: [dts(), json()],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this required in declaration types ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it fixes below error that occurs while creating build, during declaration file creation (src/modules/currency/index.ts → lib/esm/currency/index.d.ts).

RollupError: Expected ';', '}' or <eof> (Note that you need @rollup/plugin-json to import JSON files)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still required ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes tried removing it, and above error pops again while creating build.
Screenshot 2024-05-02 at 3 10 23 PM

}));

export default [
Expand Down Expand Up @@ -148,6 +148,6 @@ export default [
file: 'lib/types/index.d.ts',
format: 'es',
},
plugins: [dts()],
plugins: [dts(), json()],
RgnDunes marked this conversation as resolved.
Show resolved Hide resolved
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Creates a smaller json currency configuration from parent i18nify-data.
*
* @example
* {"AFN": {
"name": "Afghani",
"numeric_code": "971",
"minor_unit": "2",
"symbol": "؋",
"physical_currency_denominations": [
"1",
"2",
"5",
"10",
"20",
"50",
"100",
"500",
"1000"
]
}
transforms to
{"AFN": {
"name": "Afghani",
"minor_unit": "2",
"symbol": "؋"
}
}

*
*/

export default () => {
const DATA = require('#/i18nify-data/currency/data.json');

const currencyInfo = DATA.currency_information;

const currencyConfigSubset = Object.keys(currencyInfo).reduce(
(acc: any, curr: any) => {
acc[curr] = {
name: currencyInfo[curr].name,
minor_unit: currencyInfo[curr].minor_unit,
symbol: currencyInfo[curr].symbol,
};
return acc;
},
{},
);

return {
data: currencyConfigSubset,
subsetFilePath: './src/modules/currency/data/currencyConfig.json',
};
};
2 changes: 2 additions & 0 deletions packages/i18nify-js/scripts/jsonSubsets/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import transformFormatterConfig from './phoneNumber/transformFormatterConfig';
import transformRegexConfig from './phoneNumber/transformRegexConfig';
import transformCurrencyConfig from './currency/transformCurrencyConfig';
import createModuleSubsetFile from './createFile';

createModuleSubsetFile(transformFormatterConfig());
createModuleSubsetFile(transformRegexConfig());
createModuleSubsetFile(transformCurrencyConfig());
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('currency - convertToMajorUnit', () => {
];

testCases.forEach(({ amount, currency, expectedResult }) => {
it(`should correctly convert ${amount} of minor unit ${currency} to ${expectedResult}`, () => {
it(`should correctly convert ${amount} of minor unit ${String(currency)} to ${expectedResult}`, () => {
const result = convertToMajorUnit(amount, { currency: currency });
expect(result).toBe(expectedResult);
});
Expand All @@ -21,8 +21,9 @@ describe('currency - convertToMajorUnit', () => {
it('should throw an error for unsupported currency codes', () => {
const unsupportedCurrencyCode = 'XXX';
expect(() => {
// @ts-expect-error intented invalid currencyCode for testing
convertToMajorUnit(100, { currency: unsupportedCurrencyCode });
convertToMajorUnit(100, {
currency: unsupportedCurrencyCode as CurrencyCodeType,
});
}).toThrow('Unsupported currency XXX');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('currency - convertToMinorUnit', () => {
];

testCases.forEach(({ amount, currency, expectedResult }) => {
it(`should correctly convert ${amount} of minor unit ${currency} to ${expectedResult}`, () => {
it(`should correctly convert ${amount} of minor unit ${String(currency)} to ${expectedResult}`, () => {
const result = convertToMinorUnit(amount, { currency: currency });
expect(result).toBe(expectedResult);
});
Expand All @@ -21,8 +21,9 @@ describe('currency - convertToMinorUnit', () => {
it('should throw an error for unsupported currency codes', () => {
const unsupportedCurrencyCode = 'XXX';
expect(() => {
// @ts-expect-error intented invalid currencyCode for testing
convertToMinorUnit(100, { currency: unsupportedCurrencyCode });
convertToMinorUnit(100, {
currency: unsupportedCurrencyCode as CurrencyCodeType,
});
}).toThrow('Unsupported currency XXX');
});
});
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { getLocale } from '../../.internal/utils';
import { setState } from '../../core';
import { formatNumber } from '../index';
import { CurrencyCodeType, formatNumber } from '../index';

const nbsp = String.fromCharCode(160);
const nnsp = String.fromCharCode(8239);

describe('formatNumber', () => {
it('should format the amount with default options', () => {
const result = formatNumber('1000.5', { currency: 'USD' });
const result = formatNumber('1000.5', {
currency: 'USD',
});
expect(result).toBe('$1,000.50');
});

Expand Down Expand Up @@ -55,11 +57,15 @@ describe('formatNumber', () => {
formatNumber('invalid-amount', {
currency: 'USD',
}),
).toThrow('Parameter `amount` is not a number!');
).toThrow(
`Error: Parameter 'amount' is not a number. typeof amount: string`,
);
});

it('should format a negative amount', () => {
const result = formatNumber('-500', { currency: 'USD' });
const result = formatNumber('-500', {
currency: 'USD',
});
expect(result).toBe('-$500.00');
});

Expand All @@ -86,7 +92,7 @@ describe('formatNumber', () => {

it('should throw error with thousands separators', () => {
expect(() => formatNumber('1,234,567.89', { currency: 'USD' })).toThrow(
'Parameter `amount` is not a number!',
`Error: Parameter 'amount' is not a number. typeof amount: string`,
);
});

Expand All @@ -96,7 +102,9 @@ describe('formatNumber', () => {
currency: 'USD',
intlOptions: { useGrouping: false },
}),
).toThrow('Parameter `amount` is not a number!');
).toThrow(
`Error: Parameter 'amount' is not a number. typeof amount: string`,
);
});

it('should handle extremely large numbers with precision', () => {
Expand All @@ -108,8 +116,7 @@ describe('formatNumber', () => {

it('should handle custom currency symbol and placement', () => {
const result = formatNumber('1000', {
// @ts-expect-error invalid currency code for testing
currency: 'XYZ',
currency: 'XYZ' as CurrencyCodeType,
intlOptions: {
style: 'currency',
currencyDisplay: 'symbol',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { formatNumberByParts } from '../index';
import { CurrencyCodeType, formatNumberByParts } from '../index';

const nbsp = String.fromCharCode(160);

Expand Down Expand Up @@ -50,7 +50,9 @@ describe('formatNumberByParts', () => {
currency: 'USD',
locale: 'en-US',
}),
).toThrow('Error: Parameter `amount` is not a number!');
).toThrow(
`Error: Parameter 'amount' is not a number. typeof amount: string`,
);
});

it('should use the default locale if locale is not provided', () => {
Expand All @@ -63,8 +65,7 @@ describe('formatNumberByParts', () => {

it('should handle invalid currency code', () => {
const result = formatNumberByParts(12345.67, {
// @ts-expect-error invalid currency for testing
currency: 'XYZ',
currency: 'XYZ' as CurrencyCodeType,
locale: 'en-US',
});
expect(result).toEqual({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ test.describe('getCurrencyList', () => {
test('should print name correctly for a given currency', async ({ page }) => {
await injectScript(page, `(await getCurrencyList())['USD'].name`);

await assertScriptText(page, 'United States Dollar');
await assertScriptText(page, 'US Dollar');
});
});
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { getCurrencyList } from '../index';
import { CURRENCIES } from '../data/currencies';
import CURRENCY_INFO from '../data/currencyConfig.json';
import { Currency } from '../types';

describe('getCurrencyList', () => {
it('should return the correct currency list', () => {
const currencyList = getCurrencyList();
expect(currencyList).toEqual(CURRENCIES);
expect(currencyList).toEqual(CURRENCY_INFO);
});

it("check properties 'symbol' and 'name' for a sample currency", () => {
Expand All @@ -17,10 +19,12 @@ describe('getCurrencyList', () => {
it("check the values of 'symbol' and 'name' properties for a sample currency", () => {
const currencyList = getCurrencyList();
const sampleCurrencyCode = 'USD';
const sampleCurrency = currencyList[sampleCurrencyCode];
const sampleCurrency: Currency[string] = currencyList[sampleCurrencyCode];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't this translate to string ?
Also, is it needed ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it's needed, since currency interface looks like below:

export interface Currency {
  [key: string]: {
    name: string;
    minor_unit: string;
    symbol: string;
  };
}

and sampleCurrency is supposed to store value like below:

{
    name: "Indian Rupee",
    minor_unit: "2",
    symbol: "₹"
  }


// Assert that the 'symbol' and 'name' properties have the expected values
expect(sampleCurrency.symbol).toBe(CURRENCIES[sampleCurrencyCode].symbol);
expect(sampleCurrency.name).toBe(CURRENCIES[sampleCurrencyCode].name);
expect(sampleCurrency.symbol).toBe(
CURRENCY_INFO[sampleCurrencyCode].symbol,
);
expect(sampleCurrency.name).toBe(CURRENCY_INFO[sampleCurrencyCode].name);
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getCurrencySymbol } from '../index';
import { CurrencyCodeType, getCurrencySymbol } from '../index';

describe('getCurrencySymbol', () => {
it('should return the correct symbol for a valid currency code', () => {
Expand All @@ -10,17 +10,15 @@ describe('getCurrencySymbol', () => {

it('should throw Error for an invalid currency code', () => {
const currencyCode = 'XYZ'; // An invalid code
// @ts-expect-error invalid currency code for testing
expect(() => getCurrencySymbol(currencyCode)).toThrow(
'Invalid currencyCode!',
expect(() => getCurrencySymbol(currencyCode as CurrencyCodeType)).toThrow(
'Error: Invalid currencyCode: XYZ',
);
});

it('should throw Error for an empty string', () => {
const currencyCode = '';
// @ts-expect-error invalid currency code for testing
expect(() => getCurrencySymbol(currencyCode)).toThrow(
'Invalid currencyCode!',
expect(() => getCurrencySymbol(currencyCode as CurrencyCodeType)).toThrow(
'Error: Invalid currencyCode: ',
);
});
});
15 changes: 8 additions & 7 deletions packages/i18nify-js/src/modules/currency/convertToMajorUnit.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { withErrorBoundary } from '../../common/errorBoundary';
import { CURRENCIES } from './data/currencies';
import { CurrencyCodeType, CurrencyType } from './types';
import { CurrencyCodeType } from './types';
import CURRENCY_INFO from './data/currencyConfig.json';

/**
* Converts an amount from a minor currency unit to a major currency unit.
*
* The function takes an amount in the minor unit (e.g., cents, pence) and a currency code,
* then converts the amount to the major unit (e.g., dollars, pounds) using the conversion rate
* defined in the CURRENCIES object. If the currency code is not supported, it throws an error.
* defined in the CURRENCY_INFO object. If the currency code is not supported, it throws an error.
*
* @param {number} amount - The amount in the minor currency unit.
* @param {object} options - The options object
Expand All @@ -20,14 +20,15 @@ const convertToMajorUnit = (
currency: CurrencyCodeType;
},
): number => {
const currencyInfo = CURRENCIES[options.currency] as CurrencyType;
const currencyInfo = CURRENCY_INFO[options.currency];

if (!currencyInfo)
throw new Error(`Unsupported currency ${options.currency}`);
throw new Error(`Unsupported currency ${String(options.currency)}`);

const minorUnitMultiplier = currencyInfo.minorUnitMultiplier || 100;
const majorUnitMultiplier =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either name this minorUnitMultiplier or majorUnitDivider

Math.pow(10, Number(currencyInfo.minor_unit)) || 100;

const higherCurrencyValue = amount / minorUnitMultiplier;
const higherCurrencyValue = amount / majorUnitMultiplier;
return higherCurrencyValue;
};

Expand Down
13 changes: 7 additions & 6 deletions packages/i18nify-js/src/modules/currency/convertToMinorUnit.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { withErrorBoundary } from '../../common/errorBoundary';
import { CURRENCIES } from './data/currencies';
import { CurrencyCodeType, CurrencyType } from './types';
import { CurrencyCodeType } from './types';
import CURRENCY_INFO from './data/currencyConfig.json';

/**
* Converts an amount from a major currency unit to a minor currency unit.
*
* The function takes an amount in the major unit (e.g., dollars, pounds) and a currency code,
* then converts the amount to the minor unit (e.g., cents, pence) using the conversion rate
* defined in the CURRENCIES object. If the currency code is not supported, it throws an error.
* defined in the CURRENCY_INFO object. If the currency code is not supported, it throws an error.
*
* @param {number} amount - The amount in the major currency unit.
* @param {object} options - The options object
Expand All @@ -20,12 +20,13 @@ const convertToMinorUnit = (
currency: CurrencyCodeType;
},
): number => {
const currencyInfo = CURRENCIES[options.currency] as CurrencyType;
const currencyInfo = CURRENCY_INFO[options.currency];

if (!currencyInfo)
throw new Error(`Unsupported currency ${options.currency}`);
throw new Error(`Unsupported currency ${String(options.currency)}`);

const minorUnitMultiplier = currencyInfo.minorUnitMultiplier || 100;
const minorUnitMultiplier =
Math.pow(10, Number(currencyInfo.minor_unit)) || 100;

const lowerCurrencyValue = amount * minorUnitMultiplier;
return lowerCurrencyValue;
Expand Down