| package httpdriver |
|
|
| import ( |
| "context" |
| "errors" |
| "fmt" |
| "slices" |
| "time" |
|
|
| "github.com/samber/lo" |
|
|
| "github.com/openmeterio/openmeter/api" |
| "github.com/openmeterio/openmeter/openmeter/apiconverter" |
| "github.com/openmeterio/openmeter/openmeter/customer" |
| "github.com/openmeterio/openmeter/openmeter/meter" |
| "github.com/openmeterio/openmeter/openmeter/streaming" |
| "github.com/openmeterio/openmeter/pkg/filter" |
| "github.com/openmeterio/openmeter/pkg/models" |
| modelshttp "github.com/openmeterio/openmeter/pkg/models/http" |
| ) |
|
|
| |
| func ToAPIMeter(m meter.Meter) api.Meter { |
| apiMeter := api.Meter{ |
| Id: m.ID, |
| Name: &m.Name, |
| Description: m.Description, |
| Slug: m.Key, |
| EventType: m.EventType, |
| EventFrom: m.EventFrom, |
| Aggregation: api.MeterAggregation(m.Aggregation), |
| ValueProperty: m.ValueProperty, |
| CreatedAt: m.CreatedAt, |
| UpdatedAt: m.UpdatedAt, |
| DeletedAt: m.DeletedAt, |
| Metadata: modelshttp.FromMetadata(m.Metadata), |
| Annotations: modelshttp.FromAnnotations(m.Annotations), |
| } |
|
|
| if len(m.GroupBy) > 0 { |
| apiMeter.GroupBy = &m.GroupBy |
| } |
|
|
| return apiMeter |
| } |
|
|
| |
| func ToAPIMeterQueryResult(from *time.Time, to *time.Time, windowSize *api.WindowSize, rows []meter.MeterQueryRow) api.MeterQueryResult { |
| return api.MeterQueryResult{ |
| From: from, |
| To: to, |
| WindowSize: windowSize, |
| Data: ToAPIMeterQueryRowList(rows), |
| } |
| } |
|
|
| |
| func ToAPIMeterQueryRow(row meter.MeterQueryRow) api.MeterQueryRow { |
| apiRow := api.MeterQueryRow{ |
| CustomerId: row.CustomerID, |
| Subject: row.Subject, |
| GroupBy: row.GroupBy, |
| WindowStart: row.WindowStart, |
| WindowEnd: row.WindowEnd, |
| Value: row.Value, |
| } |
|
|
| return apiRow |
| } |
|
|
| |
| func ToAPIMeterQueryRowList(rows []meter.MeterQueryRow) []api.MeterQueryRow { |
| apiRows := make([]api.MeterQueryRow, len(rows)) |
| for i, row := range rows { |
| apiRows[i] = ToAPIMeterQueryRow(row) |
| } |
|
|
| return apiRows |
| } |
|
|
| |
| |
| func ToRequestFromQueryParamsPOSTBody(apiParams api.QueryMeterParams) api.QueryMeterPostJSONRequestBody { |
| |
| request := api.QueryMeterPostJSONRequestBody{ |
| ClientId: apiParams.ClientId, |
| From: apiParams.From, |
| To: apiParams.To, |
| Subject: apiParams.Subject, |
| GroupBy: apiParams.GroupBy, |
| FilterCustomerId: apiParams.FilterCustomerId, |
| WindowSize: apiParams.WindowSize, |
| WindowTimeZone: apiParams.WindowTimeZone, |
| AdvancedMeterGroupByFilters: (*map[string]api.FilterString)(apiParams.AdvancedMeterGroupByFilters), |
| } |
|
|
| if apiParams.FilterGroupBy != nil { |
| filterGroupBy := map[string][]string{} |
| for k, v := range *apiParams.FilterGroupBy { |
| filterGroupBy[k] = []string{v} |
| } |
| request.FilterGroupBy = &filterGroupBy |
| } |
|
|
| return request |
| } |
|
|
| |
| |
| func (h *handler) toQueryParamsFromRequest(ctx context.Context, m meter.Meter, request api.QueryMeterPostJSONRequestBody) (streaming.QueryParams, error) { |
| params := streaming.QueryParams{ |
| ClientID: request.ClientId, |
| From: request.From, |
| To: request.To, |
| } |
|
|
| if request.WindowSize != nil { |
| params.WindowSize = lo.ToPtr(meter.WindowSize(*request.WindowSize)) |
| } |
|
|
| if request.GroupBy != nil { |
| for _, groupBy := range *request.GroupBy { |
| |
| if ok := groupBy == "subject" || groupBy == "customer_id" || m.GroupBy[groupBy] != ""; !ok { |
| err := fmt.Errorf("invalid group by: %s", groupBy) |
| return params, models.NewGenericValidationError(err) |
| } |
|
|
| params.GroupBy = append(params.GroupBy, groupBy) |
| } |
| } |
|
|
| |
| if request.Subject != nil { |
| params.FilterSubject = *request.Subject |
|
|
| |
| if !slices.Contains(params.GroupBy, "subject") { |
| params.GroupBy = append(params.GroupBy, "subject") |
| } |
| } |
|
|
| |
| if request.FilterCustomerId != nil { |
| filterCustomer, err := h.getFilterCustomer(ctx, m.Namespace, *request.FilterCustomerId) |
| if err != nil { |
| return params, fmt.Errorf("failed to get filter customer: %w", err) |
| } |
|
|
| params.FilterCustomer = lo.Map(filterCustomer, func(c customer.Customer, _ int) streaming.Customer { |
| return c |
| }) |
|
|
| |
| if len(filterCustomer) > 0 && !slices.Contains(params.GroupBy, "customer_id") { |
| params.GroupBy = append(params.GroupBy, "customer_id") |
| } |
| } |
|
|
| if request.WindowTimeZone != nil { |
| tz, err := time.LoadLocation(*request.WindowTimeZone) |
| if err != nil { |
| err := fmt.Errorf("invalid time zone: %w", err) |
| return params, models.NewGenericValidationError(err) |
| } |
| params.WindowTimeZone = tz |
| } |
|
|
| if request.AdvancedMeterGroupByFilters != nil && len(*request.AdvancedMeterGroupByFilters) > 0 { |
| params.FilterGroupBy = apiconverter.ConvertStringMap(*request.AdvancedMeterGroupByFilters) |
| } |
|
|
| if request.FilterGroupBy != nil && len(*request.FilterGroupBy) > 0 { |
| if len(params.FilterGroupBy) > 0 { |
| return params, models.NewGenericValidationError(errors.New("advanced meter group by filters and filter group by cannot be used together")) |
| } |
|
|
| params.FilterGroupBy = map[string]filter.FilterString{} |
| for k, v := range *request.FilterGroupBy { |
| |
| if _, ok := m.GroupBy[k]; ok { |
| |
| if len(v) > 0 { |
| |
| params.FilterGroupBy[k] = filter.FilterString{ |
| In: lo.ToPtr(v), |
| } |
| } |
| continue |
| } else { |
| err := fmt.Errorf("invalid group by filter: %s", k) |
| return params, models.NewGenericValidationError(err) |
| } |
| } |
| } |
|
|
| return params, nil |
| } |
|
|
| |
| func (h *handler) getFilterCustomer(ctx context.Context, namespace string, filterCustomerIds []string) ([]customer.Customer, error) { |
| var filterCustomer []customer.Customer |
|
|
| if len(filterCustomerIds) == 0 { |
| return filterCustomer, nil |
| } |
|
|
| |
| customers, err := h.customerService.ListCustomers(ctx, customer.ListCustomersInput{ |
| Namespace: namespace, |
| CustomerIDs: filterCustomerIds, |
| }) |
| if err != nil { |
| return nil, fmt.Errorf("failed to list customers: %w", err) |
| } |
|
|
| customersById := lo.KeyBy(customers.Items, func(c customer.Customer) string { |
| return c.ID |
| }) |
|
|
| |
| for _, customerId := range filterCustomerIds { |
| var errs []error |
|
|
| if _, ok := customersById[customerId]; !ok { |
| errs = append(errs, fmt.Errorf("customer with id %s not found", customerId)) |
| } |
|
|
| if len(errs) > 0 { |
| return nil, models.NewGenericNotFoundError(errors.Join(errs...)) |
| } |
| } |
|
|
| return customers.Items, nil |
| } |
|
|