[codex] separate billing configuration (#479)

* feat(admin): separate billing configuration

Add dedicated storage egress and downloader credit billing contracts, usecases, RPC wrappers, drawers, generated client updates, and coverage.

Agent-Profile: https://agent-kanban.dev/agents/2673e70e0085f4e0

* fix(billing): preserve not found ordering

Check storage and downloader existence before quota_store gating in dedicated billing usecases, and cover enabled missing-resource requests at usecase and route levels.

Agent-Profile: https://agent-kanban.dev/agents/2673e70e0085f4e0

---------

Co-authored-by: Jordan Park <jordan-park@mails.agent-kanban.dev>
This commit is contained in:
agent-kanban-local[bot]
2026-06-24 05:57:10 -04:00
committed by GitHub
co-authored by Jordan Park
parent 82c5452782
commit f41ed27bba
26 changed files with 1478 additions and 198 deletions
+360
View File
@@ -3029,6 +3029,13 @@ type UpdateDownloaderJSONBody struct {
RemoteDownloadCreditUnitBytes *int `json:"remoteDownloadCreditUnitBytes,omitempty"`
}
// UpdateDownloaderCreditBillingJSONBody defines parameters for UpdateDownloaderCreditBilling.
type UpdateDownloaderCreditBillingJSONBody struct {
CreditsPerUnit int `json:"creditsPerUnit"`
Enabled bool `json:"enabled"`
UnitBytes int `json:"unitBytes"`
}
// ListDownloadTasksParams defines parameters for ListDownloadTasks.
type ListDownloadTasksParams struct {
Status *ListDownloadTasksParamsStatus `form:"status,omitempty" json:"status,omitempty"`
@@ -3538,6 +3545,13 @@ type UpdateStorageJSONBody struct {
// UpdateStorageJSONBodyStatus defines parameters for UpdateStorage.
type UpdateStorageJSONBodyStatus string
// UpdateStorageEgressBillingJSONBody defines parameters for UpdateStorageEgressBilling.
type UpdateStorageEgressBillingJSONBody struct {
CreditsPerUnit int `json:"creditsPerUnit"`
Enabled bool `json:"enabled"`
UnitBytes int `json:"unitBytes"`
}
// CreateCheckoutJSONBody defines parameters for CreateCheckout.
type CreateCheckoutJSONBody struct {
PackageId string `json:"packageId"`
@@ -3833,6 +3847,9 @@ type RecordDownloaderHeartbeatJSONRequestBody RecordDownloaderHeartbeatJSONBody
// UpdateDownloaderJSONRequestBody defines body for UpdateDownloader for application/json ContentType.
type UpdateDownloaderJSONRequestBody UpdateDownloaderJSONBody
// UpdateDownloaderCreditBillingJSONRequestBody defines body for UpdateDownloaderCreditBilling for application/json ContentType.
type UpdateDownloaderCreditBillingJSONRequestBody UpdateDownloaderCreditBillingJSONBody
// CreateDownloadTaskJSONRequestBody defines body for CreateDownloadTask for application/json ContentType.
type CreateDownloadTaskJSONRequestBody CreateDownloadTaskJSONBody
@@ -3914,6 +3931,9 @@ type CreateStorageJSONRequestBody CreateStorageJSONBody
// UpdateStorageJSONRequestBody defines body for UpdateStorage for application/json ContentType.
type UpdateStorageJSONRequestBody UpdateStorageJSONBody
// UpdateStorageEgressBillingJSONRequestBody defines body for UpdateStorageEgressBilling for application/json ContentType.
type UpdateStorageEgressBillingJSONRequestBody UpdateStorageEgressBillingJSONBody
// CreateCheckoutJSONRequestBody defines body for CreateCheckout for application/json ContentType.
type CreateCheckoutJSONRequestBody CreateCheckoutJSONBody
@@ -4575,6 +4595,11 @@ type ClientInterface interface {
UpdateDownloader(ctx context.Context, id string, body UpdateDownloaderJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
// UpdateDownloaderCreditBillingWithBody request with any body
UpdateDownloaderCreditBillingWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
UpdateDownloaderCreditBilling(ctx context.Context, id string, body UpdateDownloaderCreditBillingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
// ListDownloadTasks request
ListDownloadTasks(ctx context.Context, params *ListDownloadTasksParams, reqEditors ...RequestEditorFn) (*http.Response, error)
@@ -4866,6 +4891,11 @@ type ClientInterface interface {
UpdateStorage(ctx context.Context, id string, body UpdateStorageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
// UpdateStorageEgressBillingWithBody request with any body
UpdateStorageEgressBillingWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
UpdateStorageEgressBilling(ctx context.Context, id string, body UpdateStorageEgressBillingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
// CreateBillingPortalSession request
CreateBillingPortalSession(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
@@ -6875,6 +6905,30 @@ func (c *Client) UpdateDownloader(ctx context.Context, id string, body UpdateDow
return c.Client.Do(req)
}
func (c *Client) UpdateDownloaderCreditBillingWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewUpdateDownloaderCreditBillingRequestWithBody(c.Server, id, contentType, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) UpdateDownloaderCreditBilling(ctx context.Context, id string, body UpdateDownloaderCreditBillingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewUpdateDownloaderCreditBillingRequest(c.Server, id, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) ListDownloadTasks(ctx context.Context, params *ListDownloadTasksParams, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewListDownloadTasksRequest(c.Server, params)
if err != nil {
@@ -8147,6 +8201,30 @@ func (c *Client) UpdateStorage(ctx context.Context, id string, body UpdateStorag
return c.Client.Do(req)
}
func (c *Client) UpdateStorageEgressBillingWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewUpdateStorageEgressBillingRequestWithBody(c.Server, id, contentType, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) UpdateStorageEgressBilling(ctx context.Context, id string, body UpdateStorageEgressBillingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewUpdateStorageEgressBillingRequest(c.Server, id, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) CreateBillingPortalSession(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewCreateBillingPortalSessionRequest(c.Server)
if err != nil {
@@ -12618,6 +12696,53 @@ func NewUpdateDownloaderRequestWithBody(server string, id string, contentType st
return req, nil
}
// NewUpdateDownloaderCreditBillingRequest calls the generic UpdateDownloaderCreditBilling builder with application/json body
func NewUpdateDownloaderCreditBillingRequest(server string, id string, body UpdateDownloaderCreditBillingJSONRequestBody) (*http.Request, error) {
var bodyReader io.Reader
buf, err := json.Marshal(body)
if err != nil {
return nil, err
}
bodyReader = bytes.NewReader(buf)
return NewUpdateDownloaderCreditBillingRequestWithBody(server, id, "application/json", bodyReader)
}
// NewUpdateDownloaderCreditBillingRequestWithBody generates requests for UpdateDownloaderCreditBilling with any type of body
func NewUpdateDownloaderCreditBillingRequestWithBody(server string, id string, contentType string, body io.Reader) (*http.Request, error) {
var err error
var pathParam0 string
pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""})
if err != nil {
return nil, err
}
serverURL, err := url.Parse(server)
if err != nil {
return nil, err
}
operationPath := fmt.Sprintf("/api/downloads/downloaders/%s/credit-billing", pathParam0)
if operationPath[0] == '/' {
operationPath = "." + operationPath
}
queryURL, err := serverURL.Parse(operationPath)
if err != nil {
return nil, err
}
req, err := http.NewRequest(http.MethodPut, queryURL.String(), body)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", contentType)
return req, nil
}
// NewListDownloadTasksRequest generates requests for ListDownloadTasks
func NewListDownloadTasksRequest(server string, params *ListDownloadTasksParams) (*http.Request, error) {
var err error
@@ -16191,6 +16316,53 @@ func NewUpdateStorageRequestWithBody(server string, id string, contentType strin
return req, nil
}
// NewUpdateStorageEgressBillingRequest calls the generic UpdateStorageEgressBilling builder with application/json body
func NewUpdateStorageEgressBillingRequest(server string, id string, body UpdateStorageEgressBillingJSONRequestBody) (*http.Request, error) {
var bodyReader io.Reader
buf, err := json.Marshal(body)
if err != nil {
return nil, err
}
bodyReader = bytes.NewReader(buf)
return NewUpdateStorageEgressBillingRequestWithBody(server, id, "application/json", bodyReader)
}
// NewUpdateStorageEgressBillingRequestWithBody generates requests for UpdateStorageEgressBilling with any type of body
func NewUpdateStorageEgressBillingRequestWithBody(server string, id string, contentType string, body io.Reader) (*http.Request, error) {
var err error
var pathParam0 string
pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""})
if err != nil {
return nil, err
}
serverURL, err := url.Parse(server)
if err != nil {
return nil, err
}
operationPath := fmt.Sprintf("/api/site/storages/%s/egress-billing", pathParam0)
if operationPath[0] == '/' {
operationPath = "." + operationPath
}
queryURL, err := serverURL.Parse(operationPath)
if err != nil {
return nil, err
}
req, err := http.NewRequest(http.MethodPut, queryURL.String(), body)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", contentType)
return req, nil
}
// NewCreateBillingPortalSessionRequest generates requests for CreateBillingPortalSession
func NewCreateBillingPortalSessionRequest(server string) (*http.Request, error) {
var err error
@@ -18121,6 +18293,11 @@ type ClientWithResponsesInterface interface {
UpdateDownloaderWithResponse(ctx context.Context, id string, body UpdateDownloaderJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateDownloaderResponse, error)
// UpdateDownloaderCreditBillingWithBodyWithResponse request with any body
UpdateDownloaderCreditBillingWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateDownloaderCreditBillingResponse, error)
UpdateDownloaderCreditBillingWithResponse(ctx context.Context, id string, body UpdateDownloaderCreditBillingJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateDownloaderCreditBillingResponse, error)
// ListDownloadTasksWithResponse request
ListDownloadTasksWithResponse(ctx context.Context, params *ListDownloadTasksParams, reqEditors ...RequestEditorFn) (*ListDownloadTasksResponse, error)
@@ -18412,6 +18589,11 @@ type ClientWithResponsesInterface interface {
UpdateStorageWithResponse(ctx context.Context, id string, body UpdateStorageJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateStorageResponse, error)
// UpdateStorageEgressBillingWithBodyWithResponse request with any body
UpdateStorageEgressBillingWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateStorageEgressBillingResponse, error)
UpdateStorageEgressBillingWithResponse(ctx context.Context, id string, body UpdateStorageEgressBillingJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateStorageEgressBillingResponse, error)
// CreateBillingPortalSessionWithResponse request
CreateBillingPortalSessionWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*CreateBillingPortalSessionResponse, error)
@@ -23408,6 +23590,38 @@ func (r UpdateDownloaderResponse) ContentType() string {
return ""
}
type UpdateDownloaderCreditBillingResponse struct {
Body []byte
HTTPResponse *http.Response
JSON200 *Downloader
JSON402 *Error
JSON404 *Error
}
// Status returns HTTPResponse.Status
func (r UpdateDownloaderCreditBillingResponse) Status() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Status
}
return http.StatusText(0)
}
// StatusCode returns HTTPResponse.StatusCode
func (r UpdateDownloaderCreditBillingResponse) StatusCode() int {
if r.HTTPResponse != nil {
return r.HTTPResponse.StatusCode
}
return 0
}
// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers
func (r UpdateDownloaderCreditBillingResponse) ContentType() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Header.Get("Content-Type")
}
return ""
}
type ListDownloadTasksResponse struct {
Body []byte
HTTPResponse *http.Response
@@ -25961,6 +26175,38 @@ func (r UpdateStorageResponse) ContentType() string {
return ""
}
type UpdateStorageEgressBillingResponse struct {
Body []byte
HTTPResponse *http.Response
JSON200 *Storage
JSON402 *Error
JSON404 *Error
}
// Status returns HTTPResponse.Status
func (r UpdateStorageEgressBillingResponse) Status() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Status
}
return http.StatusText(0)
}
// StatusCode returns HTTPResponse.StatusCode
func (r UpdateStorageEgressBillingResponse) StatusCode() int {
if r.HTTPResponse != nil {
return r.HTTPResponse.StatusCode
}
return 0
}
// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers
func (r UpdateStorageEgressBillingResponse) ContentType() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Header.Get("Content-Type")
}
return ""
}
type CreateBillingPortalSessionResponse struct {
Body []byte
HTTPResponse *http.Response
@@ -28525,6 +28771,23 @@ func (c *ClientWithResponses) UpdateDownloaderWithResponse(ctx context.Context,
return ParseUpdateDownloaderResponse(rsp)
}
// UpdateDownloaderCreditBillingWithBodyWithResponse request with arbitrary body returning *UpdateDownloaderCreditBillingResponse
func (c *ClientWithResponses) UpdateDownloaderCreditBillingWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateDownloaderCreditBillingResponse, error) {
rsp, err := c.UpdateDownloaderCreditBillingWithBody(ctx, id, contentType, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseUpdateDownloaderCreditBillingResponse(rsp)
}
func (c *ClientWithResponses) UpdateDownloaderCreditBillingWithResponse(ctx context.Context, id string, body UpdateDownloaderCreditBillingJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateDownloaderCreditBillingResponse, error) {
rsp, err := c.UpdateDownloaderCreditBilling(ctx, id, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseUpdateDownloaderCreditBillingResponse(rsp)
}
// ListDownloadTasksWithResponse request returning *ListDownloadTasksResponse
func (c *ClientWithResponses) ListDownloadTasksWithResponse(ctx context.Context, params *ListDownloadTasksParams, reqEditors ...RequestEditorFn) (*ListDownloadTasksResponse, error) {
rsp, err := c.ListDownloadTasks(ctx, params, reqEditors...)
@@ -29452,6 +29715,23 @@ func (c *ClientWithResponses) UpdateStorageWithResponse(ctx context.Context, id
return ParseUpdateStorageResponse(rsp)
}
// UpdateStorageEgressBillingWithBodyWithResponse request with arbitrary body returning *UpdateStorageEgressBillingResponse
func (c *ClientWithResponses) UpdateStorageEgressBillingWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateStorageEgressBillingResponse, error) {
rsp, err := c.UpdateStorageEgressBillingWithBody(ctx, id, contentType, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseUpdateStorageEgressBillingResponse(rsp)
}
func (c *ClientWithResponses) UpdateStorageEgressBillingWithResponse(ctx context.Context, id string, body UpdateStorageEgressBillingJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateStorageEgressBillingResponse, error) {
rsp, err := c.UpdateStorageEgressBilling(ctx, id, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseUpdateStorageEgressBillingResponse(rsp)
}
// CreateBillingPortalSessionWithResponse request returning *CreateBillingPortalSessionResponse
func (c *ClientWithResponses) CreateBillingPortalSessionWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*CreateBillingPortalSessionResponse, error) {
rsp, err := c.CreateBillingPortalSession(ctx, reqEditors...)
@@ -37375,6 +37655,46 @@ func ParseUpdateDownloaderResponse(rsp *http.Response) (*UpdateDownloaderRespons
return response, nil
}
// ParseUpdateDownloaderCreditBillingResponse parses an HTTP response from a UpdateDownloaderCreditBillingWithResponse call
func ParseUpdateDownloaderCreditBillingResponse(rsp *http.Response) (*UpdateDownloaderCreditBillingResponse, error) {
bodyBytes, err := io.ReadAll(rsp.Body)
defer func() { _ = rsp.Body.Close() }()
if err != nil {
return nil, err
}
response := &UpdateDownloaderCreditBillingResponse{
Body: bodyBytes,
HTTPResponse: rsp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
var dest Downloader
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON200 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 402:
var dest Error
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON402 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
var dest Error
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON404 = &dest
}
return response, nil
}
// ParseListDownloadTasksResponse parses an HTTP response from a ListDownloadTasksWithResponse call
func ParseListDownloadTasksResponse(rsp *http.Response) (*ListDownloadTasksResponse, error) {
bodyBytes, err := io.ReadAll(rsp.Body)
@@ -40341,6 +40661,46 @@ func ParseUpdateStorageResponse(rsp *http.Response) (*UpdateStorageResponse, err
return response, nil
}
// ParseUpdateStorageEgressBillingResponse parses an HTTP response from a UpdateStorageEgressBillingWithResponse call
func ParseUpdateStorageEgressBillingResponse(rsp *http.Response) (*UpdateStorageEgressBillingResponse, error) {
bodyBytes, err := io.ReadAll(rsp.Body)
defer func() { _ = rsp.Body.Close() }()
if err != nil {
return nil, err
}
response := &UpdateStorageEgressBillingResponse{
Body: bodyBytes,
HTTPResponse: rsp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
var dest Storage
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON200 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 402:
var dest Error
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON402 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
var dest Error
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON404 = &dest
}
return response, nil
}
// ParseCreateBillingPortalSessionResponse parses an HTTP response from a CreateBillingPortalSessionWithResponse call
func ParseCreateBillingPortalSessionResponse(rsp *http.Response) (*CreateBillingPortalSessionResponse, error) {
bodyBytes, err := io.ReadAll(rsp.Body)