|
1 | 1 | package governance |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "github.com/oapi-codegen/nullable" |
| 5 | + "github.com/samber/lo" |
| 6 | + |
4 | 7 | api "github.com/openmeterio/openmeter/api/v3" |
5 | | - "github.com/openmeterio/openmeter/openmeter/entitlement" |
6 | | - booleanentitlement "github.com/openmeterio/openmeter/openmeter/entitlement/boolean" |
7 | | - meteredentitlement "github.com/openmeterio/openmeter/openmeter/entitlement/metered" |
8 | | - staticentitlement "github.com/openmeterio/openmeter/openmeter/entitlement/static" |
| 8 | + customershandler "github.com/openmeterio/openmeter/api/v3/handlers/customers" |
| 9 | + "github.com/openmeterio/openmeter/openmeter/governance" |
9 | 10 | ) |
10 | 11 |
|
11 | | -// mapEntitlementToAccess converts an entitlement value to a governance feature access result. |
12 | | -// When has_access is false, the reason code is derived from the entitlement type. |
13 | | -func mapEntitlementToAccess(v entitlement.EntitlementValue) api.GovernanceFeatureAccess { |
14 | | - switch ent := v.(type) { |
15 | | - case *meteredentitlement.MeteredEntitlementValue: |
16 | | - if ent.HasAccess() { |
17 | | - return api.GovernanceFeatureAccess{HasAccess: true} |
18 | | - } |
19 | | - return api.GovernanceFeatureAccess{ |
20 | | - HasAccess: false, |
21 | | - Reason: &api.GovernanceFeatureAccessReason{ |
22 | | - Code: api.GovernanceFeatureAccessReasonCodeUsageLimitReached, |
23 | | - Message: "usage limit for feature reached", |
24 | | - }, |
| 12 | +// ToAPIGovernanceQueryResponse maps a domain QueryResult to the API response. |
| 13 | +func ToAPIGovernanceQueryResponse(res governance.QueryResult, pageSize int) api.GovernanceQueryResponse { |
| 14 | + data := make([]api.GovernanceQueryResult, 0, len(res.Customers)) |
| 15 | + for _, c := range res.Customers { |
| 16 | + features := make(map[string]api.GovernanceFeatureAccess, len(c.Features)) |
| 17 | + for key, fa := range c.Features { |
| 18 | + features[key] = toAPIFeatureAccess(fa) |
25 | 19 | } |
26 | 20 |
|
27 | | - case *booleanentitlement.BooleanEntitlementValue: |
28 | | - if ent.HasAccess() { |
29 | | - return api.GovernanceFeatureAccess{HasAccess: true} |
30 | | - } |
31 | | - return api.GovernanceFeatureAccess{ |
32 | | - HasAccess: false, |
33 | | - Reason: &api.GovernanceFeatureAccessReason{ |
34 | | - Code: api.GovernanceFeatureAccessReasonCodeFeatureUnavailable, |
35 | | - Message: "feature is not available for customer", |
36 | | - }, |
37 | | - } |
| 21 | + data = append(data, api.GovernanceQueryResult{ |
| 22 | + Matched: c.Matched, |
| 23 | + Customer: customershandler.ToAPIBillingCustomer(c.Customer), |
| 24 | + Features: features, |
| 25 | + UpdatedAt: c.UpdatedAt, |
| 26 | + }) |
| 27 | + } |
38 | 28 |
|
39 | | - case *staticentitlement.StaticEntitlementValue: |
40 | | - if ent.HasAccess() { |
41 | | - return api.GovernanceFeatureAccess{HasAccess: true} |
42 | | - } |
43 | | - return api.GovernanceFeatureAccess{ |
44 | | - HasAccess: false, |
45 | | - Reason: &api.GovernanceFeatureAccessReason{ |
46 | | - Code: api.GovernanceFeatureAccessReasonCodeFeatureUnavailable, |
47 | | - Message: "feature is not available for customer", |
48 | | - }, |
| 29 | + errs := make([]api.GovernanceQueryError, 0, len(res.Errors)) |
| 30 | + for _, e := range res.Errors { |
| 31 | + errs = append(errs, api.GovernanceQueryError{ |
| 32 | + Customer: lo.ToPtr(e.CustomerKey), |
| 33 | + Code: toAPIQueryErrorCode(e.Code), |
| 34 | + Message: e.Message, |
| 35 | + }) |
| 36 | + } |
| 37 | + |
| 38 | + return api.GovernanceQueryResponse{ |
| 39 | + Data: data, |
| 40 | + Errors: errs, |
| 41 | + Meta: toAPICursorMeta(res, pageSize), |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +func toAPIFeatureAccess(fa governance.FeatureAccess) api.GovernanceFeatureAccess { |
| 46 | + out := api.GovernanceFeatureAccess{HasAccess: fa.HasAccess} |
| 47 | + if fa.Reason != nil { |
| 48 | + out.Reason = &api.GovernanceFeatureAccessReason{ |
| 49 | + Code: toAPIReasonCode(fa.Reason.Code), |
| 50 | + Message: fa.Reason.Message, |
49 | 51 | } |
| 52 | + } |
| 53 | + return out |
| 54 | +} |
50 | 55 |
|
| 56 | +func toAPIReasonCode(code governance.ReasonCode) api.GovernanceFeatureAccessReasonCode { |
| 57 | + switch code { |
| 58 | + case governance.ReasonUsageLimitReached: |
| 59 | + return api.GovernanceFeatureAccessReasonCodeUsageLimitReached |
| 60 | + case governance.ReasonFeatureUnavailable: |
| 61 | + return api.GovernanceFeatureAccessReasonCodeFeatureUnavailable |
| 62 | + case governance.ReasonFeatureNotFound: |
| 63 | + return api.GovernanceFeatureAccessReasonCodeFeatureNotFound |
| 64 | + case governance.ReasonNoCreditAvailable: |
| 65 | + return api.GovernanceFeatureAccessReasonCodeNoCreditAvailable |
51 | 66 | default: |
52 | | - // NoAccessValue or unknown type |
53 | | - return api.GovernanceFeatureAccess{ |
54 | | - HasAccess: false, |
55 | | - Reason: &api.GovernanceFeatureAccessReason{ |
56 | | - Code: api.GovernanceFeatureAccessReasonCodeFeatureUnavailable, |
57 | | - Message: "feature is not available for customer", |
58 | | - }, |
| 67 | + return api.GovernanceFeatureAccessReasonCodeUnknown |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +func toAPIQueryErrorCode(code governance.QueryErrorCode) api.GovernanceQueryErrorCode { |
| 72 | + switch code { |
| 73 | + case governance.QueryErrorCustomerNotFound: |
| 74 | + return api.GovernanceQueryErrorCodeCustomerNotFound |
| 75 | + default: |
| 76 | + return api.GovernanceQueryErrorCodeUnknown |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +// toAPICursorMeta builds cursor pagination metadata from the domain result. |
| 81 | +func toAPICursorMeta(res governance.QueryResult, pageSize int) api.CursorMeta { |
| 82 | + meta := api.CursorMeta{ |
| 83 | + Page: api.CursorMetaPage{ |
| 84 | + Next: nullable.NewNullNullable[string](), |
| 85 | + Previous: nullable.NewNullNullable[string](), |
| 86 | + Size: float32(pageSize), |
| 87 | + }, |
| 88 | + } |
| 89 | + |
| 90 | + if res.First != nil { |
| 91 | + meta.Page.First = lo.ToPtr(res.First.Encode()) |
| 92 | + if res.HasPrev { |
| 93 | + meta.Page.Previous = nullable.NewNullableWithValue(res.First.Encode()) |
59 | 94 | } |
60 | 95 | } |
| 96 | + if res.Last != nil { |
| 97 | + meta.Page.Last = lo.ToPtr(res.Last.Encode()) |
| 98 | + if res.HasNext { |
| 99 | + meta.Page.Next = nullable.NewNullableWithValue(res.Last.Encode()) |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + return meta |
61 | 104 | } |
0 commit comments