From 283dce11143c3bc3e7f3196afbc44f5387545ccb Mon Sep 17 00:00:00 2001 From: Sudhanshu Jha Date: Fri, 13 Dec 2024 19:36:27 +0530 Subject: [PATCH] feat: i18nify-go sdk with get_currency_symbol, convert_to_major & minor_unit functionality (#187) --- packages/i18nify-go/country.go | 5 +- packages/i18nify-go/example/example.go | 4 +- .../modules/currency/conversion_test.go | 100 ++++++++++++++++++ .../modules/currency/convert_to_major.go | 36 +++++++ .../modules/currency/convert_to_minor.go | 32 ++++++ .../i18nify-go/modules/currency/currency.go | 44 ++++++-- .../modules/currency/currency_test.go | 70 +++++++++++- packages/i18nify-go/modules/currency/utils.go | 29 +++++ 8 files changed, 309 insertions(+), 11 deletions(-) create mode 100644 packages/i18nify-go/modules/currency/conversion_test.go create mode 100644 packages/i18nify-go/modules/currency/convert_to_major.go create mode 100644 packages/i18nify-go/modules/currency/convert_to_minor.go create mode 100644 packages/i18nify-go/modules/currency/utils.go diff --git a/packages/i18nify-go/country.go b/packages/i18nify-go/country.go index 1f941785..49c826e6 100644 --- a/packages/i18nify-go/country.go +++ b/packages/i18nify-go/country.go @@ -38,7 +38,10 @@ func (c *Country) GetCountryCurrency() []currency.CurrencyInformation { // Iterate through currency codes for the country. for _, cur := range countryMetadata.SupportedCurrency { // Retrieve currency information for each currency code. - curInfoList = append(curInfoList, currency.GetCurrencyInformation(cur)) + curr, err := currency.GetCurrencyInformation(cur) + if err == nil { + curInfoList = append(curInfoList, curr) + } } return curInfoList } diff --git a/packages/i18nify-go/example/example.go b/packages/i18nify-go/example/example.go index 7b4f157b..f34d8391 100644 --- a/packages/i18nify-go/example/example.go +++ b/packages/i18nify-go/example/example.go @@ -56,7 +56,9 @@ func main() { } //USD - currencyUS := currency.GetCurrencyInformation("USD") + currencyUS, _ := currency.GetCurrencyInformation("USD") fmt.Println(currencyUS.Name) //US Dollar fmt.Println(currencyUS.Symbol) //$ + + // add convert to major unit and minor unit examples } diff --git a/packages/i18nify-go/modules/currency/conversion_test.go b/packages/i18nify-go/modules/currency/conversion_test.go new file mode 100644 index 00000000..ba1a3e03 --- /dev/null +++ b/packages/i18nify-go/modules/currency/conversion_test.go @@ -0,0 +1,100 @@ +package currency + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConvertCurrency(t *testing.T) { + tests := []struct { + name string + currencyCode string + amount interface{} + minorExpectedValue float64 + majorExpectedValue float64 + expectedError string + }{ + { + name: "Valid USD Conversion", + currencyCode: "USD", + amount: 1234.0, + minorExpectedValue: 123400, + majorExpectedValue: 12.34, + expectedError: "", + }, + { + name: "Valid EUR Conversion", + currencyCode: "EUR", + amount: 4568, + minorExpectedValue: 456800, + majorExpectedValue: 45.68, + expectedError: "", + }, + { + name: "Valid GBP Conversion", + currencyCode: "GBP", + amount: 100.0, + minorExpectedValue: 10000.00, + majorExpectedValue: 1.00, + expectedError: "", + }, + { + name: "Valid Bahraini Dinar Conversion", + currencyCode: "BHD", + amount: 1000.0, + minorExpectedValue: 1000000.00, + majorExpectedValue: 1.00, + expectedError: "", + }, + { + name: "Valid Iraqi Dinar Conversion", + currencyCode: "IQD", + amount: 1000.0, + minorExpectedValue: 1000000.00, + majorExpectedValue: 1.00, + expectedError: "", + }, + { + name: "Valid Unidad Previsional Conversion", + currencyCode: "UYW", + amount: 10000.0, + minorExpectedValue: 100000000.00, + majorExpectedValue: 1.00, + expectedError: "", + }, + { + name: "Invalid Currency Code", + currencyCode: "INR0", + amount: 100.0, + minorExpectedValue: 0, + majorExpectedValue: 0, + expectedError: "currency code 'INR0' not found", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Call both functions and capture errors + majorActualValue, majorError := ConvertToMajorUnit(tt.currencyCode, tt.amount) + minorActualValue, minorError := ConvertToMinorUnit(tt.currencyCode, tt.amount) + + // Compare the result values + assert.InDelta(t, tt.majorExpectedValue, majorActualValue, 0.001, "unexpected major unit conversion") + assert.InDelta(t, tt.minorExpectedValue, minorActualValue, 0.001, "unexpected minor unit conversion") + + // Check for errors + if tt.expectedError != "" { + // Ensure both major and minor conversions return the same error + assert.Error(t, majorError) + assert.Error(t, minorError) + assert.EqualError(t, majorError, tt.expectedError) + assert.EqualError(t, minorError, tt.expectedError) + } else { + // Ensure no errors are returned + assert.NoError(t, majorError) + assert.NoError(t, minorError) + } + }) + } +} diff --git a/packages/i18nify-go/modules/currency/convert_to_major.go b/packages/i18nify-go/modules/currency/convert_to_major.go new file mode 100644 index 00000000..ab84820c --- /dev/null +++ b/packages/i18nify-go/modules/currency/convert_to_major.go @@ -0,0 +1,36 @@ +package currency + +import ( + "fmt" + "math" + "strconv" +) + +// Converts given amount from minor (e.g., paise) to major unit (e.g., rupees) for a specified currency + +func ConvertToMajorUnit(code string, amount interface{}) (float64, error) { + + amountValue, err := ValidateAndConvertAmount(amount) + if err != nil { + return 0, err + } + + currencyInfo, err := GetCurrencyInformation(code) + if err != nil { + return 0, err + } + + minorUnit, err := strconv.ParseInt(currencyInfo.MinorUnit, 10, 64) + if err != nil { + return 0, fmt.Errorf("invalid minor unit for currency code '%s': %v", code, err) + } + + minorUnitMultiplier := math.Pow(10, float64(minorUnit)) + if minorUnitMultiplier <= 0 { + return 0, fmt.Errorf("invalid minor unit multiplier for currency code '%s'", code) + } + + majorUnitAmount := amountValue / minorUnitMultiplier + + return majorUnitAmount, nil +} diff --git a/packages/i18nify-go/modules/currency/convert_to_minor.go b/packages/i18nify-go/modules/currency/convert_to_minor.go new file mode 100644 index 00000000..2743ee82 --- /dev/null +++ b/packages/i18nify-go/modules/currency/convert_to_minor.go @@ -0,0 +1,32 @@ +package currency + +import ( + "fmt" + "math" + "strconv" +) + +// Converts a given amount from major currency units (e.g., dollars) to minor units (e.g., cents) for a specified currency + +func ConvertToMinorUnit(code string, amount interface{}) (float64, error) { + + amountValue, err := ValidateAndConvertAmount(amount) + if err != nil { + return 0, err + } + + currencyInfo, err := GetCurrencyInformation(code) + if err != nil { + return 0, err + } + + minorUnit, err := strconv.ParseInt(currencyInfo.MinorUnit, 10, 64) + if err != nil { + return 0, fmt.Errorf("invalid minor unit for currency code '%s': %v", code, err) + } + + minorUnitMultiplier := math.Pow(10, float64(minorUnit)) + minorUnitAmount := amountValue * minorUnitMultiplier + + return minorUnitAmount, nil +} diff --git a/packages/i18nify-go/modules/currency/currency.go b/packages/i18nify-go/modules/currency/currency.go index 6bb3b965..77e792a0 100644 --- a/packages/i18nify-go/modules/currency/currency.go +++ b/packages/i18nify-go/modules/currency/currency.go @@ -43,18 +43,29 @@ func (r *Currency) GetAllCurrencyInformation() map[string]CurrencyInformation { } // GetCurrencyInformation retrieves currency information for a specific currency code. -func GetCurrencyInformation(code string) CurrencyInformation { +func GetCurrencyInformation(code string) (CurrencyInformation, error) { // Read JSON data file containing currency information. currencyJsonData, err := currencyJsonDir.ReadFile(DataFile) if err != nil { - // Handle error reading the file. - fmt.Println("Error reading JSON file:", err) - return CurrencyInformation{} + // Handle error reading the file + return CurrencyInformation{}, fmt.Errorf("error reading JSON file: %v", err) } + // Unmarshal JSON data into SupportedCurrency struct. - allCurrencyData, _ := UnmarshalCurrency(currencyJsonData) + allCurrencyData, err := UnmarshalCurrency(currencyJsonData) + if err != nil { + return CurrencyInformation{}, fmt.Errorf("error unmarshalling JSON data: %v", err) + } + // Retrieve currency information for the specified currency code. - return allCurrencyData.CurrencyInformation[code] + currencyInfo, exists := allCurrencyData.CurrencyInformation[code] + + if !exists { + return CurrencyInformation{}, fmt.Errorf("currency code '%s' not found", code) + } + + return currencyInfo, nil + } // NewCurrency creates a new Currency instance. @@ -85,3 +96,24 @@ func NewCurrencyInformation(minorUnit string, name string, numericCode string, p Symbol: symbol, } } + +// GetCurrencySymbol retrieves the currency symbol for a specific currency code. +func GetCurrencySymbol(currencyCode string) (string, error) { + // Validate the input code. + if currencyCode == "" { + return "", fmt.Errorf("currency code cannot be empty") + } + + // Retrieve currency information for the specified code. + currencyInfo, err := GetCurrencyInformation(currencyCode) + if err != nil { + return "", fmt.Errorf("failed to retrieve currency information for code '%s': %v", currencyCode, err) + } + + // Validate the currency symbol. + if currencyInfo.Symbol == "" { + return "", fmt.Errorf("currency symbol for code '%s' is not available", currencyCode) + } + + return currencyInfo.Symbol, nil +} diff --git a/packages/i18nify-go/modules/currency/currency_test.go b/packages/i18nify-go/modules/currency/currency_test.go index 86a249b2..7a815df3 100644 --- a/packages/i18nify-go/modules/currency/currency_test.go +++ b/packages/i18nify-go/modules/currency/currency_test.go @@ -56,13 +56,77 @@ func TestGetCurrencyInformation(t *testing.T) { if err != nil { return } + // Validate specific details for USD as a sample + result, _ := GetCurrencyInformation("USD") - result := GetCurrencyInformation("USD") - - // Use assert.Equal for assertions with inline expected values assert.Equal(t, "2", result.MinorUnit, "MinorUnit field mismatch") assert.Equal(t, "US Dollar", result.Name, "Name field mismatch") assert.Equal(t, "840", result.NumericCode, "NumericCode field mismatch") assert.Equal(t, []string{"1", "5", "10", "25", "50", "100"}, result.PhysicalCurrencyDenominations, "PhysicalCurrencyDenominations field mismatch") assert.Equal(t, "$", result.Symbol, "Symbol field mismatch") + + // Test multiple countries with table-driven tests + countries := []struct { + code string + name string + symbol string + numeric string + }{ + // Asia + {"CNY", "Yuan Renminbi", "CN¥", "156"}, + {"JPY", "Yen", "¥", "392"}, + {"INR", "Indian Rupee", "₹", "356"}, + {"RUB", "Russian Ruble", "₽", "643"}, + {"AED", "UAE Dirham", "د.إ", "784"}, + // North America + {"USD", "US Dollar", "$", "840"}, + // South America + {"BRL", "Brazilian Real", "R$", "986"}, + // Australia + {"AUD", "Australian Dollar", "$", "36"}, + // Europe + {"EUR", "Euro", "€", "978"}, + // Africa + {"ZAR", "South African Rand", "R", "710"}, + } + + for _, country := range countries { + t.Run(country.code, func(t *testing.T) { + result, err := GetCurrencyInformation(country.code) + //fmt.Println("Here:", country.name, ":", result.Name) + assert.NoError(t, err, "Unexpected error retrieving currency information for %s", country.code) + assert.Equal(t, country.name, result.Name, "Name mismatch for %s", country.code) + assert.Equal(t, country.symbol, result.Symbol, "Symbol mismatch for %s", country.code) + assert.Equal(t, country.numeric, result.NumericCode, "Numeric code mismatch for %s", country.code) + }) + } +} + +func TestGetCurrencySymbol(t *testing.T) { + countries := []struct { + code string + symbol string + }{ + {"CNY", "CN¥"}, + {"JPY", "¥"}, + {"INR", "₹"}, + {"RUB", "₽"}, + {"USD", "$"}, + {"AED", "د.إ"}, + {"BRL", "R$"}, + {"AUD", "A$"}, + {"SAR", "ر.س"}, + {"EUR", "€"}, + {"ZAR", "R"}, + } + + for _, country := range countries { + symbol, err := GetCurrencySymbol(country.code) + + if err != nil { + t.Errorf("Error for code %s: %v", country.code, err) + } else { + assert.Equal(t, country.symbol, symbol, "Symbol mismatch for %s", country.code) + } + } } diff --git a/packages/i18nify-go/modules/currency/utils.go b/packages/i18nify-go/modules/currency/utils.go new file mode 100644 index 00000000..14c7f991 --- /dev/null +++ b/packages/i18nify-go/modules/currency/utils.go @@ -0,0 +1,29 @@ +package currency + +import ( + "fmt" + "reflect" + "strconv" +) + +// Validates and converts the input amount (float64, int, or string) to a float64, ensuring it is non-negative. + +func ValidateAndConvertAmount(amount interface{}) (float64, error) { + var amountValue float64 + switch v := amount.(type) { + case float64: + amountValue = v + case int: + amountValue = float64(v) + case string: + parsedAmount, err := strconv.ParseFloat(v, 64) + if err != nil { + return 0, fmt.Errorf("invalid amount value '%v': %v", v, err) + } + amountValue = parsedAmount + default: + return 0, fmt.Errorf("amount must be a number (float64, int, or string that can be parsed to float64), but got %v", reflect.TypeOf(amount)) + } + + return amountValue, nil +}