[usage] Cost centers get nextBillingTime#12913
Conversation
b5bf52b to
6e9c65e
Compare
6e9c65e to
e3ed2c6
Compare
nextBillingTime
|
started the job as gitpod-build-se-cost-center-next-billing.4 because the annotations in the pull request description changed |
ab2cfcb to
23a3f31
Compare
23a3f31 to
dedd727
Compare
| } | ||
| BillingStrategy billing_strategy = 3; | ||
|
|
||
| // next_billing_time specifies when the next billing cycle happens. Only set when billing strategy is 'other'. This property is readonly. |
There was a problem hiding this comment.
Nice use of proto comments to describe semantics!
| "defaultSpendingLimitForUsers": 5000, | ||
| "defaultSpendingLimitForTeams": 0, |
| DefaultSpendingLimitForUsers int32 `json:"defaultSpendingLimitForUsers,omitempty"` | ||
|
|
||
| DefaultSpendingLimitForTeams int32 `json:"defaultSpendingLimitForTeams,omitempty"` |
There was a problem hiding this comment.
| DefaultSpendingLimitForUsers int32 `json:"defaultSpendingLimitForUsers,omitempty"` | |
| DefaultSpendingLimitForTeams int32 `json:"defaultSpendingLimitForTeams,omitempty"` | |
| DefaultSpendingLimitForUsers int `json:"defaultSpendingLimitForUsers,omitempty"` | |
| DefaultSpendingLimitForTeams int `json:"defaultSpendingLimitForTeams,omitempty"` |
This should be sufficient. int is actually compiler target dependant but often it's easier to use int and let the compiler deal with it.
|
|
||
| Server *baseserver.Configuration `json:"server,omitempty"` | ||
|
|
||
| apiv1.UsageConfig |
There was a problem hiding this comment.
I'd name this explicitly. It also allows you to be explicit about the json field name which guards against issues when you rename the apiv1.UsageConfig in the future.
dedd727 to
cda8e7a
Compare
cda8e7a to
f78a64d
Compare
easyCZ
left a comment
There was a problem hiding this comment.
Looking good, left a couple of questions.
/hold
We can tackle these in a follow-up if we want.
I'd also recommend isolating DB migrations away from application changes which make use of those values. When we introduced #12873 and rolled it out, we actually saw requests fail as the migration was not compatible with the application changes - while this can happen with migrations being independent as well, it makes it easier to reason about it in the given PR.
| require.Equal(t, costCenter.SpendingLimit, read.SpendingLimit) | ||
| } | ||
|
|
||
| func TestGetCostCenter(t *testing.T) { |
There was a problem hiding this comment.
| func TestGetCostCenter(t *testing.T) { | |
| func TestCostCenterManager_GetOrCreateCostCenter(t *testing.T) { |
| } | ||
|
|
||
| func TestGetCostCenter(t *testing.T) { | ||
| func TestSaveCostCenter(t *testing.T) { |
There was a problem hiding this comment.
| func TestSaveCostCenter(t *testing.T) { | |
| func TestCostCenterManager_SaveCostCenter(t *testing.T) { |
| db := conn.WithContext(ctx) | ||
| costCenter.CreationTime = NewVarcharTime(time.Now()) | ||
| db = db.Save(costCenter) | ||
| func (c *CostCenterManager) SaveCostCenter(ctx context.Context, costCenter CostCenter) (CostCenter, error) { |
There was a problem hiding this comment.
From a consumer of the CostCenterManager API perspective, it's quite unclear what the difference is between GetOrCreate and SaveCostCenter. Why would I use one over the other?
I think here, we can rename it to
| func (c *CostCenterManager) SaveCostCenter(ctx context.Context, costCenter CostCenter) (CostCenter, error) { | |
| func (c *CostCenterManager) UpdateCostCenter(ctx context.Context, costCenter CostCenter) (CostCenter, error) { |
To better communicate that this is intended to work on an existing record(even if it needs to be created under the hood)
There was a problem hiding this comment.
I aligned with the naming of gorm but have no problem changing this to 'Update..'.
| return costCenter, nil | ||
| } | ||
|
|
||
| func (c *CostCenterManager) ComputeFinalization(ctx context.Context, costCenter CostCenter) (Usage, error) { |
There was a problem hiding this comment.
| func (c *CostCenterManager) ComputeFinalization(ctx context.Context, costCenter CostCenter) (Usage, error) { | |
| func (c *CostCenterManager) ComputeFinalization(ctx context.Context, costCenter CostCenter) (Usage, error) { |
From this signature, I'd have expected that it will also store the Usage record in the DB. Perhaps renaming to InvoiceUsageRecordForCostCenter would convey this better.
Additionally, why this is method accept the costCenter CostCenter when it only uses the costCenter.ID? Could we supply only the ID here?
There was a problem hiding this comment.
I generally use 'Compute...' to indicate that a function doesn't have aside-effects but just computes something. But I'm happy to change the name to make it clearer for you.
I'm passing in the costCenter because it contains information about spendingLimit which is likely to be used pretty soon, but we can fetch it within the function if needed.
| func cleanUp(t *testing.T, conn *gorm.DB, attributionIds ...db.AttributionID) { | ||
| t.Cleanup(func() { |
There was a problem hiding this comment.
| func cleanUp(t *testing.T, conn *gorm.DB, attributionIds ...db.AttributionID) { | |
| t.Cleanup(func() { | |
| func cleanUp(t *testing.T, conn *gorm.DB, attributionIds ...db.AttributionID) { | |
| t.Helper() | |
| t.Cleanup(func() { |
just for general practice
| EffectiveTime VarcharTime `gorm:"column:effectiveTime;type:varchar;size:255;" json:"effectiveTime"` | ||
| Kind UsageKind `gorm:"column:kind;type:char;size:10;" json:"kind"` | ||
| WorkspaceInstanceID uuid.UUID `gorm:"column:workspaceInstanceId;type:char;size:36;" json:"workspaceInstanceId"` | ||
| WorkspaceInstanceID *uuid.UUID `gorm:"column:workspaceInstanceId;type:char;size:36;" json:"workspaceInstanceId"` |
There was a problem hiding this comment.
FWIW, I'd have split this into a separate pre-PR to isolate it from the rest of the main logic in this PR.
| func (a AttributionID) IsUser() bool { | ||
| entity, _ := a.Values() | ||
| return entity == "user" | ||
| } |
There was a problem hiding this comment.
[personal] I generally prefer not to introduce Binary methods on an object which can have more than a binary number of outcomes. This forces the caller to use
switch a.Kind {
case User:
...
case Team:
...
default:
...
}
This tends to be more resilient over time as you don't make binary choices anymore, but are forced to acknowledge existence of other values.
f78a64d to
58dd497
Compare
|
started the job as gitpod-build-se-cost-center-next-billing.11 because the annotations in the pull request description changed |
58dd497 to
39790df
Compare
39790df to
89bb051
Compare
|
/werft run with-clean-slate-deployment 👍 started the job as gitpod-build-se-cost-center-next-billing.14 |
7642da9 to
aa21d77
Compare
|
/werft run 👍 started the job as gitpod-build-se-cost-center-next-billing.17 |
f3b9967 to
05c183e
Compare
|
/werft run with-clean-slate-deployment 👍 started the job as gitpod-build-se-cost-center-next-billing.20 |
|
/unhold |
mrsimonemms
left a comment
There was a problem hiding this comment.
/hold
See comment for reasons for the hold. Happy to discuss further if necessary
| Enabled bool `json:"enabled"` | ||
| Schedule string `json:"schedule"` | ||
| BillInstancesAfter *time.Time `json:"billInstancesAfter"` | ||
| DefaultSpendingLimit map[string]int32 `json:"defaultSpendingLimit"` |
There was a problem hiding this comment.
Instead of using a map, my preference would be for this to use the DefaultSpendingLimit struct.
As this is in the experimental section, I'll approve this on the basis that it's not for public usage, but it would keep with the consistency of using structs in the configuration
05c183e to
51b0fd5
Compare
|
/unhold |
Description
This PR contains small steps towards having cost centers for all users and teams, even if they haven't signed up for stripe pay-as-you go, yet, but using the billing strategy
other.In this PR I
nextBillingTimeon cost centers that will be used in an upcoming PR where a control loop uses the date to find cost centers that need to run finalization (i.e. create a synthetic invoice to balance out the ledger table).GetCostCenteralways return a cost centerRelated Issue(s)
Fixes #
How to test
Release Notes
Documentation
Werft options: