Skip to content

Commit

Permalink
feat: i18nify-go sdk with get_currency_symbol, convert_to_major & min…
Browse files Browse the repository at this point in the history
…or_unit functionality (#187)
  • Loading branch information
isudhanshukrjha authored Dec 13, 2024
1 parent e43a8cb commit 283dce1
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 11 deletions.
5 changes: 4 additions & 1 deletion packages/i18nify-go/country.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 3 additions & 1 deletion packages/i18nify-go/example/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
100 changes: 100 additions & 0 deletions packages/i18nify-go/modules/currency/conversion_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}
36 changes: 36 additions & 0 deletions packages/i18nify-go/modules/currency/convert_to_major.go
Original file line number Diff line number Diff line change
@@ -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
}
32 changes: 32 additions & 0 deletions packages/i18nify-go/modules/currency/convert_to_minor.go
Original file line number Diff line number Diff line change
@@ -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
}
44 changes: 38 additions & 6 deletions packages/i18nify-go/modules/currency/currency.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
70 changes: 67 additions & 3 deletions packages/i18nify-go/modules/currency/currency_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
29 changes: 29 additions & 0 deletions packages/i18nify-go/modules/currency/utils.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 283dce1

Please sign in to comment.