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