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