@@ -44,9 +44,9 @@ func entitlementWeight(e Entitlement) int {
44
44
case EntitlementGracePeriod :
45
45
return 1
46
46
case EntitlementNotEntitled :
47
- return 0
48
- default :
49
47
return - 1
48
+ default :
49
+ return - 2
50
50
}
51
51
}
52
52
@@ -176,6 +176,91 @@ type Feature struct {
176
176
Actual * int64 `json:"actual,omitempty"`
177
177
}
178
178
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
+
179
264
type Entitlements struct {
180
265
Features map [FeatureName ]Feature `json:"features"`
181
266
Warnings []string `json:"warnings"`
@@ -192,48 +277,21 @@ type Entitlements struct {
192
277
//
193
278
// All features should be added as atomic items, and not merged in any way.
194
279
// Merging entitlements could lead to unexpected behavior, like a larger user
195
- // limit in grace period merging with a smaller one in a 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 in an "entitled" state . This
281
+ // could lead to the larger limit being extended as "entitled", which is not correct.
197
282
func (e * Entitlements ) AddFeature (name FeatureName , add Feature ) {
198
283
existing , ok := e .Features [name ]
199
284
if ! ok {
200
285
e .Features [name ] = add
201
286
return
202
287
}
203
288
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 )
209
291
if comparison > 0 {
210
292
e .Features [name ] = add
211
293
return
212
294
}
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
- }
237
295
}
238
296
239
297
func (c * Client ) Entitlements (ctx context.Context ) (Entitlements , error ) {
0 commit comments