@@ -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
@@ -174,6 +174,91 @@ type Feature struct {
174
174
Actual * int64 `json:"actual,omitempty"`
175
175
}
176
176
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
+
177
262
type Entitlements struct {
178
263
Features map [FeatureName ]Feature `json:"features"`
179
264
Warnings []string `json:"warnings"`
@@ -190,48 +275,21 @@ type Entitlements struct {
190
275
//
191
276
// All features should be added as atomic items, and not merged in any way.
192
277
// Merging entitlements could lead to unexpected behavior, like a larger user
193
- // limit in grace period merging with a smaller one in a 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 in an "entitled" state . This
279
+ // could lead to the larger limit being extended as "entitled", which is not correct.
195
280
func (e * Entitlements ) AddFeature (name FeatureName , add Feature ) {
196
281
existing , ok := e .Features [name ]
197
282
if ! ok {
198
283
e .Features [name ] = add
199
284
return
200
285
}
201
286
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 )
207
289
if comparison > 0 {
208
290
e .Features [name ] = add
209
291
return
210
292
}
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
- }
235
293
}
236
294
237
295
func (c * Client ) Entitlements (ctx context.Context ) (Entitlements , error ) {
0 commit comments