Skip to content

Commit 268c2bf

Browse files
authored
feat(api): add code filter to currency and sort option (#4348)
1 parent 4ba4bfe commit 268c2bf

10 files changed

Lines changed: 808 additions & 238 deletions

File tree

api/spec/packages/aip/src/currencies/operations.tsp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ namespace Currencies;
1717
*/
1818
@friendlyName("ListCurrenciesParamsFilter")
1919
model ListCurrenciesParamsFilter {
20-
/**
21-
* Filter currencies by type.
22-
*/
20+
#suppress "@openmeter/api-spec-aip/doc-decorator" "filter field"
2321
type?: CurrencyType;
22+
#suppress "@openmeter/api-spec-aip/doc-decorator" "filter field"
23+
code?: Common.StringFieldFilter;
2424
}
2525

2626
interface CurrenciesOperations {
@@ -35,6 +35,18 @@ interface CurrenciesOperations {
3535
list(
3636
...Common.PagePaginationQuery,
3737

38+
/**
39+
* Sort currencies returned in the response. Supported sort attributes are:
40+
*
41+
* - `code` (default)
42+
* - `name`
43+
*
44+
* The `asc` suffix is optional as the default sort order is ascending. The `desc`
45+
* suffix is used to specify a descending order.
46+
*/
47+
@query(#{ name: "sort" })
48+
sort?: Common.SortQuery,
49+
3850
/**
3951
* Filter currencies returned in the response.
4052
*

api/v3/api.gen.go

Lines changed: 240 additions & 218 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/v3/handlers/currencies/list.go

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ import (
99

1010
v3 "github.com/openmeterio/openmeter/api/v3"
1111
"github.com/openmeterio/openmeter/api/v3/apierrors"
12+
"github.com/openmeterio/openmeter/api/v3/filters"
13+
"github.com/openmeterio/openmeter/api/v3/request"
1214
"github.com/openmeterio/openmeter/api/v3/response"
1315
"github.com/openmeterio/openmeter/openmeter/currencies"
1416
"github.com/openmeterio/openmeter/pkg/framework/commonhttp"
1517
"github.com/openmeterio/openmeter/pkg/framework/transport/httptransport"
1618
"github.com/openmeterio/openmeter/pkg/pagination"
19+
"github.com/openmeterio/openmeter/pkg/sortx"
1720
)
1821

1922
type (
@@ -49,17 +52,42 @@ func (h *handler) ListCurrencies() ListCurrenciesHandler {
4952
})
5053
}
5154

52-
var filterType *currencies.CurrencyType
53-
if params.Filter != nil && params.Filter.Type != nil {
54-
ft := FromAPIBillingCurrencyType(*params.Filter.Type)
55-
filterType = &ft
55+
var orderBy string
56+
var order sortx.Order
57+
if params.Sort != nil {
58+
sort, err := request.ParseSortBy(*params.Sort)
59+
if err != nil {
60+
return ListCurrenciesRequest{}, apierrors.NewBadRequestError(ctx, err, apierrors.InvalidParameters{
61+
{Field: "sort", Reason: err.Error(), Source: apierrors.InvalidParamSourceQuery},
62+
})
63+
}
64+
orderBy = sort.Field
65+
order = sort.Order.ToSortxOrder()
66+
}
67+
68+
req := ListCurrenciesRequest{
69+
Page: page,
70+
Namespace: ns,
71+
OrderBy: currencies.OrderBy(orderBy),
72+
Order: order,
73+
}
74+
75+
if params.Filter != nil {
76+
if params.Filter.Type != nil {
77+
ft := FromAPIBillingCurrencyType(*params.Filter.Type)
78+
req.FilterType = &ft
79+
}
80+
81+
code, err := filters.FromAPIFilterString(params.Filter.Code)
82+
if err != nil {
83+
return ListCurrenciesRequest{}, apierrors.NewBadRequestError(ctx, err, apierrors.InvalidParameters{
84+
{Field: "filter[code]", Reason: err.Error(), Source: apierrors.InvalidParamSourceQuery},
85+
})
86+
}
87+
req.Code = code
5688
}
5789

58-
return ListCurrenciesRequest{
59-
Page: page,
60-
Namespace: ns,
61-
FilterType: filterType,
62-
}, nil
90+
return req, nil
6391
},
6492
func(ctx context.Context, request ListCurrenciesRequest) (ListCurrenciesResponse, error) {
6593
result, err := h.currencyService.ListCurrencies(ctx, request)

api/v3/openapi.yaml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,21 @@ paths:
333333
description: List currencies supported by the billing system.
334334
parameters:
335335
- $ref: '#/components/parameters/PagePaginationQuery'
336+
- name: sort
337+
in: query
338+
required: false
339+
description: |-
340+
Sort currencies returned in the response. Supported sort attributes are:
341+
342+
- `code` (default)
343+
- `name`
344+
345+
The `asc` suffix is optional as the default sort order is ascending. The `desc`
346+
suffix is used to specify a descending order.
347+
schema:
348+
$ref: '#/components/schemas/SortQuery'
349+
explode: false
350+
style: form
336351
- name: filter
337352
in: query
338353
required: false
@@ -8232,9 +8247,9 @@ components:
82328247
type: object
82338248
properties:
82348249
type:
8235-
allOf:
8236-
- $ref: '#/components/schemas/BillingCurrencyType'
8237-
description: Filter currencies by type.
8250+
$ref: '#/components/schemas/BillingCurrencyType'
8251+
code:
8252+
$ref: '#/components/schemas/StringFieldFilter'
82388253
additionalProperties: false
82398254
description: Filter options for listing currencies.
82408255
ListCustomerEntitlementAccessResponseData:

openmeter/currencies/adapter/currencies.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import (
1313
"github.com/openmeterio/openmeter/openmeter/ent/db/currencycostbasis"
1414
"github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency"
1515
"github.com/openmeterio/openmeter/pkg/currencyx"
16+
"github.com/openmeterio/openmeter/pkg/filter"
1617
"github.com/openmeterio/openmeter/pkg/framework/entutils"
1718
"github.com/openmeterio/openmeter/pkg/models"
1819
"github.com/openmeterio/openmeter/pkg/pagination"
20+
"github.com/openmeterio/openmeter/pkg/sortx"
1921
)
2022

2123
var _ currencies.Adapter = (*adapter)(nil)
@@ -44,8 +46,20 @@ func mapCostBasisFromDB(c *entdb.CurrencyCostBasis) currencies.CostBasis {
4446
func (a *adapter) ListCustomCurrencies(ctx context.Context, params currencies.ListCurrenciesInput) (pagination.Result[currencies.Currency], error) {
4547
return entutils.TransactingRepo(ctx, a, func(ctx context.Context, tx *adapter) (pagination.Result[currencies.Currency], error) {
4648
q := a.db.CustomCurrency.Query().
47-
Where(customcurrency.Namespace(params.Namespace)).
48-
Order(entdb.Asc(customcurrency.FieldCode))
49+
Where(customcurrency.Namespace(params.Namespace))
50+
51+
q = filter.ApplyToQuery(q, params.Code, customcurrency.FieldCode)
52+
53+
order := entutils.GetOrdering(sortx.OrderDefault)
54+
if !params.Order.IsDefaultValue() {
55+
order = entutils.GetOrdering(params.Order)
56+
}
57+
switch params.OrderBy {
58+
case currencies.OrderByName:
59+
q = q.Order(customcurrency.ByName(order...))
60+
default:
61+
q = q.Order(customcurrency.ByCode(order...))
62+
}
4963

5064
total, err := q.Count(ctx)
5165
if err != nil {

openmeter/currencies/models.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import (
77

88
"github.com/alpacahq/alpacadecimal"
99

10+
"github.com/openmeterio/openmeter/pkg/filter"
1011
"github.com/openmeterio/openmeter/pkg/models"
1112
"github.com/openmeterio/openmeter/pkg/pagination"
13+
"github.com/openmeterio/openmeter/pkg/sortx"
1214
)
1315

1416
type Currency struct {
@@ -19,6 +21,22 @@ type Currency struct {
1921
Symbol string `json:"symbol"`
2022
}
2123

24+
// OrderBy specifies the field to sort currencies by.
25+
type OrderBy string
26+
27+
const (
28+
OrderByCode OrderBy = "code"
29+
OrderByName OrderBy = "name"
30+
)
31+
32+
func (o OrderBy) Validate() error {
33+
switch o {
34+
case OrderByCode, OrderByName, "":
35+
return nil
36+
}
37+
return fmt.Errorf("invalid order by: %s", o)
38+
}
39+
2240
var _ models.Validator = (*ListCurrenciesInput)(nil)
2341

2442
type ListCurrenciesInput struct {
@@ -28,6 +46,11 @@ type ListCurrenciesInput struct {
2846

2947
// FilterType filters currencies by type: "custom" or "fiat". Nil means no filter.
3048
FilterType *CurrencyType `json:"filter_type,omitempty"`
49+
// Code filters currencies by code field. Nil means no filter.
50+
Code *filter.FilterString `json:"code,omitempty"`
51+
52+
OrderBy OrderBy
53+
Order sortx.Order
3154
}
3255

3356
func (i ListCurrenciesInput) Validate() error {
@@ -43,6 +66,16 @@ func (i ListCurrenciesInput) Validate() error {
4366
}
4467
}
4568

69+
if i.Code != nil {
70+
if err := i.Code.Validate(); err != nil {
71+
errs = append(errs, fmt.Errorf("code: %w", err))
72+
}
73+
}
74+
75+
if err := i.OrderBy.Validate(); err != nil {
76+
errs = append(errs, err)
77+
}
78+
4679
return errors.Join(errs...)
4780
}
4881

openmeter/currencies/service/service.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package service
33
import (
44
"context"
55
"fmt"
6+
"slices"
7+
"strings"
68
"time"
79

810
"github.com/invopop/gobl/currency"
@@ -12,6 +14,7 @@ import (
1214
"github.com/openmeterio/openmeter/pkg/framework/transaction"
1315
"github.com/openmeterio/openmeter/pkg/models"
1416
"github.com/openmeterio/openmeter/pkg/pagination"
17+
"github.com/openmeterio/openmeter/pkg/sortx"
1518
)
1619

1720
var _ currencies.CurrencyService = (*Service)(nil)
@@ -55,10 +58,18 @@ func (s *Service) ListCurrencies(ctx context.Context, params currencies.ListCurr
5558
}
5659

5760
if includeFiat {
58-
for _, def := range lo.Filter(currency.Definitions(), func(def *currency.Def, _ int) bool {
61+
matchCode := params.Code.LoFilterPredicate()
62+
filteredMatchCode, err := lo.FilterErr(currency.Definitions(), func(def *currency.Def, _ int) (bool, error) {
5963
// NOTE: this filters out non-iso currencies such as crypto
60-
return def.ISONumeric != ""
61-
}) {
64+
if def.ISONumeric == "" {
65+
return false, nil
66+
}
67+
return matchCode(def.ISOCode.String(), 0)
68+
})
69+
if err != nil {
70+
return pagination.Result[currencies.Currency]{}, fmt.Errorf("filtering fiat currencies by code: %w", err)
71+
}
72+
for _, def := range filteredMatchCode {
6273
items = append(items, currencies.Currency{
6374
Code: def.ISOCode.String(),
6475
Name: def.Name,
@@ -67,6 +78,19 @@ func (s *Service) ListCurrencies(ctx context.Context, params currencies.ListCurr
6778
}
6879
}
6980

81+
slices.SortFunc(items, func(a, b currencies.Currency) int {
82+
result := 0
83+
if params.OrderBy == currencies.OrderByName {
84+
result = strings.Compare(a.Name, b.Name)
85+
} else {
86+
result = strings.Compare(a.Code, b.Code)
87+
}
88+
if params.Order == sortx.OrderDesc {
89+
return -result
90+
}
91+
return result
92+
})
93+
7094
total := len(items)
7195

7296
pageSize := params.Page.PageSize

0 commit comments

Comments
 (0)