@@ -44,9 +44,9 @@ func entitlementWeight(e Entitlement) int {
4444case EntitlementGracePeriod :
4545return 1
4646case EntitlementNotEntitled :
47- return 0
48- default :
4947return - 1
48+ default :
49+ return - 2
5050}
5151}
5252
@@ -176,6 +176,91 @@ type Feature struct {
176176Actual * int64 `json:"actual,omitempty"`
177177}
178178
179+ // CompareFeatures compares two features and returns an integer representing
180+ // if the first feature is greater than, equal to, or less than the second feature.
181+ // "Greater than" means the first feature has more functionality than the
182+ // second feature. It is assumed the features are for the same FeatureName.
183+ //
184+ // A feature is considered greater than another feature if:
185+ // - Graceful & capable > Entitled & not capable
186+ // - The entitlement is greater
187+ // - The limit is greater
188+ // - Enabled is greater than disabled
189+ // - The actual is greater
190+ func CompareFeatures (a ,b Feature )int {
191+ if ! a .Capable ()|| ! b .Capable () {
192+ // If either is incapable, then it is possible a grace period
193+ // feature can be "greater" than an entitled.
194+ // If either is "NotEntitled" then we can defer to a strict entitlement
195+ // check.
196+ if entitlementWeight (a .Entitlement )>= 0 && entitlementWeight (b .Entitlement )>= 0 {
197+ if a .Capable ()&& ! b .Capable () {
198+ return 1
199+ }
200+ if b .Capable ()&& ! a .Capable () {
201+ return - 1
202+ }
203+ }
204+ }
205+
206+ entitlement := CompareEntitlements (a .Entitlement ,b .Entitlement )
207+ if entitlement > 0 {
208+ return 1
209+ }
210+ if entitlement < 0 {
211+ return - 1
212+ }
213+
214+ // If the entitlement is the same, then we can compare the limits.
215+ if a .Limit == nil && b .Limit != nil {
216+ return - 1
217+ }
218+ if a .Limit != nil && b .Limit == nil {
219+ return 1
220+ }
221+ if a .Limit != nil && b .Limit != nil {
222+ difference := * a .Limit - * b .Limit
223+ if * a .Limit - * b .Limit != 0 {
224+ return int (difference )
225+ }
226+ }
227+
228+ // Enabled is better than disabled.
229+ if a .Enabled && ! b .Enabled {
230+ return 1
231+ }
232+ if ! a .Enabled && b .Enabled {
233+ return - 1
234+ }
235+
236+ // Higher actual is better
237+ if a .Actual == nil && b .Actual != nil {
238+ return - 1
239+ }
240+ if a .Actual != nil && b .Actual == nil {
241+ return 1
242+ }
243+ if a .Actual != nil && b .Actual != nil {
244+ difference := * a .Actual - * b .Actual
245+ if * a .Actual - * b .Actual != 0 {
246+ return int (difference )
247+ }
248+ }
249+
250+ return 0
251+ }
252+
253+ // Capable is a helper function that returns if a given feature has a limit
254+ // that is greater than or equal to the actual.
255+ // If this condition is not true, then the feature is not capable of being used
256+ // since the limit is not high enough.
257+ func (f Feature )Capable ()bool {
258+ if f .Limit != nil && f .Actual != nil {
259+ return * f .Limit >= * f .Actual
260+ }
261+ return true
262+ }
263+
179264type Entitlements struct {
180265Features map [FeatureName ]Feature `json:"features"`
181266Warnings []string `json:"warnings"`
@@ -192,48 +277,21 @@ type Entitlements struct {
192277//
193278// All features should be added as atomic items, and not merged in any way.
194279// Merging entitlements could lead to unexpected behavior, like a larger user
195- // limit in grace period merging with a smaller one ina grace period . This could
196- // lead to the larger limit being extended as "entitled", which is not correct.
280+ // limit in grace period merging with a smaller one inan "entitled" state . This
281+ //could lead to the larger limit being extended as "entitled", which is not correct.
197282func (e * Entitlements )AddFeature (name FeatureName ,add Feature ) {
198283existing ,ok := e .Features [name ]
199284if ! ok {
200285e .Features [name ]= add
201286return
202287}
203288
204- comparison := CompareEntitlements (add .Entitlement ,existing .Entitlement )
205- // If the new entitlement is greater than the existing entitlement, replace it.
206- // The edge case is if the previous entitlement is in a grace period with a
207- // higher value.
208- // TODO: Address the edge case.
289+ // Compare the features, keep the one that is "better"
290+ comparison := CompareFeatures (add ,existing )
209291if comparison > 0 {
210292e .Features [name ]= add
211293return
212294}
213-
214- // If they have the same entitlement, then we can compare the limits.
215- if comparison == 0 {
216- if add .Limit != nil {
217- if existing .Limit == nil || * add .Limit > * existing .Limit {
218- e .Features [name ]= add
219- return
220- }
221- }
222-
223- // Enabled is better than disabled.
224- if add .Enabled && ! existing .Enabled {
225- e .Features [name ]= add
226- return
227- }
228-
229- // If the actual value is greater than the existing actual value, replace it.
230- if add .Actual != nil {
231- if existing .Actual == nil || * add .Actual > * existing .Actual {
232- e .Features [name ]= add
233- return
234- }
235- }
236- }
237295}
238296
239297func (c * Client )Entitlements (ctx context.Context ) (Entitlements ,error ) {