Skip to content

feat: send the entitlement window, warning threshold and grant allowance - #5

Merged
Alex (Alexkuva) merged 2 commits into
mainfrom
feat/entitlement-window-and-overage
Oct 12, 2026
Merged

Alex (Alexkuva) merged 2 commits into
mainfrom
feat/entitlement-window-and-overage

Conversation

@Alexkuva

@Alexkuva Alex (Alexkuva) commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR change?

  • EntitlementInput gains ResetPeriod, ResetAnchor, WarningThresholdPercent and the sale-unit trio (SaleUnitSingular, SaleUnitPlural, SaleUnitFactor), with EntitlementResetPeriod* and EntitlementResetAnchor* constants.
  • Licenses.AssociateEntitlement and Licenses.UpdateEntitlement take an optional ...LicenseEntitlementOption. WithOveragePercent(n) sends the grant's limitCapExceededOveragePercent.
  • The doc comments on EntitlementInput, Entitlements.Update and Licenses.UpdateEntitlement, and the README, say what each PUT does with an omitted field.

Why?

The Core request bodies declare these fields and internal/gen already reads them back, but no input could send them. Both PUTs replace rather than patch, so the gap was a set of bugs and not only missing features:

Call Before this PR
Entitlements.Update on a periodic entitlement Always refused with a 400, UpdateEntitlement.ImmutableResetPeriod: the API reads an omitted period as an attempt to remove it
Entitlements.Update on any entitlement Warning threshold reset to 0 (off), sale unit cleared
Licenses.UpdateEntitlement on a soft limit Allowance reset to the server default, 0: a hard limit

It is also what keeps a dataset seeded over the API from matching the OSS demo profile: the monthly window on monthly-orders, and the per-License allowances of 0, 10 and 20 %. The other SDKs already have all of this. sdk-python 1.0.0 exposes reset_period, reset_anchor, warning_threshold_percent, the sale units and overage_percent; @kaitencloud/server gets them through its generated EntitlementWritable and LicenseEntitlementWritable.

Server behavior this is written against, from kaiten main (c04b6b86):

  • entitlements/updateentitlement/handler.go: the immutability checks, and an absent threshold resets to 0.
  • entitlement.sql, UpdateEntitlement: COALESCE on the window only; every other optional column is overwritten.
  • licenses/associateentitlementwithlicense/repository.go, resolveOveragePercent: the default is derived from the value, and -1 is accepted exactly when the value is unlimited. The update path is the same, with 400 …InvalidLimitCapExceededOveragePercent.
  • pkg/apierrors: KindValidation answers 400.

Compatibility

task release:check (gorelease) reports:

  • Incompatible: the two method signatures, because of the added variadic. Every existing call site compiles unchanged; only a method value or an interface declaring the old signature would break.
  • Compatible: the new fields, types and constants, LicenseEntitlementOption and WithOveragePercent.
  • Suggested version: v0.1.0.

Without the option, the grant bodies are byte for byte what they were (pinned by TestGrantWritesRenderTheOveragePercentOnlyWhenSet). Consumers checked: kaitencloud/cli builds against this branch, and every other call site we know of uses the four-argument form.

Testing

  • task test (race, shuffle), task lint (0 issues), task fmt (no change), task deadcode.
  • TestRequestBodiesMatchTheirSpecBodies: six new cases, covering a periodic entitlement's create and update, and a grant's associate and update with and without an allowance.
  • New TestEntitlementAndGrantWritesReachEveryWritableKey.
    • It reads the body keys off gen.CreateEntitlementJSONRequestBody and the three other request types, minus the spec's readOnly fields, so a regenerated client with a new writable key fails until an input sends it.
    • Mutation-checked: dropping warningThresholdPercent from the update payload fails it.
  • TestEntitlementWritesRenderTheWindowAndThresholdOnlyWhenSet, plus TestEntitlementUpdateCarriesThePeriodBack for the Get-then-Update round trip.
  • TestGrantWritesRenderTheOveragePercentOnlyWhenSet: 0 and -1 are rendered, a nil option is skipped, the last option wins.
  • Not run against a live kaiten.

After merge

Tag v0.1.0 (there is no release workflow, so the tag is the release). Then bump the consumers, the CLI among them. It merges cleanly with #1 (feat/billing).

Checklist

  • I have read CONTRIBUTING.md.
  • Every commit in this PR includes a valid DCO Signed-off-by line.
  • I have the right to submit all material in this PR.
  • I have not included secrets or confidential data.
  • I have updated tests where appropriate.
  • I have updated documentation where appropriate.
  • I have preserved required third-party licenses and attributions.

🤖 Generated with Claude Code · ✅ Tested and approved by Alex (@Alexkuva), maintainer

EntitlementInput gains ResetPeriod, ResetAnchor, WarningThresholdPercent
and the sale-unit trio, with constants for the reset periods and anchors.
Licenses.AssociateEntitlement and UpdateEntitlement take an optional
WithOveragePercent. The two bodies declare these fields and internal/gen
already reads them back, but no input could send them. Both PUTs replace
rather than patch, so the gap was more than missing features:

- Entitlements.Update failed on every periodic entitlement: once a reset
  period is set, the API refuses an update that does not carry it back,
  400 UpdateEntitlement.ImmutableResetPeriod.
- Entitlements.Update turned the warning threshold off and cleared the
  sale unit on every call.
- Licenses.UpdateEntitlement turned every soft limit into a hard one: an
  omitted allowance is the server's default, 0 for a finite value.

Without the option the grant bodies are byte for byte what they were, so
every call site compiles and behaves as before. The doc comments say what
each PUT does with an omitted field.

TestEntitlementAndGrantWritesReachEveryWritableKey reads the body keys off
the generated request types, so a field the spec adds to these bodies
fails the tests until an input can send it.

Signed-off-by: Alexandre Bergere <alexandre.bergere@kaiten.sh>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Alexandre Bergere <alexandre.bergere@kaiten.sh>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread inputs_test.go
}

// jsonKeys returns the JSON names of a generated struct's fields, sorted.
func jsonKeys(t *testing.T, structType reflect.Type) []string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be moved up to a common go package maybe

@Alexkuva
Alex (Alexkuva) merged commit 6288964 into main Oct 12, 2026
7 checks passed
@Alexkuva
Alex (Alexkuva) deleted the feat/entitlement-window-and-overage branch October 12, 2026 21:32
Alex (Alexkuva) added a commit to kaitencloud/cli that referenced this pull request Oct 13, 2026
The API replaces an entitlement or a grant with what it is sent, and the
two update commands sent only what their flags held:

- `entitlements update --description ...` reset the warning threshold, the
  icon, the unit and sale-unit labels, userFacing and the display order,
  and on a periodic entitlement it was refused outright (400
  UpdateEntitlement.ImmutableResetPeriod): the window has to be sent back.
- `licenses entitlements update --number ...` turned a soft limit into a
  hard one, since the API derives an omitted allowance from the value.

With inline flags, `entitlements update` now reads the entitlement first
and sends back every field the flags do not name; --name is no longer
required there. --file and --payload are still sent as written. `licenses
entitlements update` reads the grant and keeps its allowance when the new
value is a finite number, unless --overage-percent gives one.

New flags, on create and update: --reset-period, --reset-anchor,
--warning-threshold-percent, --icon, --unit-singular, --unit-plural,
--sale-unit-singular, --sale-unit-plural, --sale-unit-factor,
--user-facing and --display-order; an empty label or icon removes it.
--overage-percent on `licenses entitlements associate` and `update`.

This needs kaitencloud/sdk-go#5, which adds these fields and the
allowance to the SDK: go.mod is pinned to its commit until it is tagged
v0.1.0.

Signed-off-by: Alexandre Bergere <alexandre.bergere@kaiten.sh>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants