diff --git a/internal/api/client.go b/internal/api/client.go index 7603762..f693d33 100644 --- a/internal/api/client.go +++ b/internal/api/client.go @@ -212,6 +212,9 @@ type ClientInterface interface { // SpaceStatus request SpaceStatus(ctx context.Context, spaceId SpaceId, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SpaceUsage request + SpaceUsage(ctx context.Context, spaceId SpaceId, reqEditors ...RequestEditorFn) (*http.Response, error) } func (c *Client) AnalyticsIdentifyWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -754,6 +757,18 @@ func (c *Client) SpaceStatus(ctx context.Context, spaceId SpaceId, reqEditors .. return c.Client.Do(req) } +func (c *Client) SpaceUsage(ctx context.Context, spaceId SpaceId, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSpaceUsageRequest(c.Server, spaceId) + 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) +} + // NewAnalyticsIdentifyRequest calls the generic AnalyticsIdentify builder with application/json body func NewAnalyticsIdentifyRequest(server string, body AnalyticsIdentifyJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -2140,6 +2155,40 @@ func NewSpaceStatusRequest(server string, spaceId SpaceId) (*http.Request, error return req, nil } +// NewSpaceUsageRequest generates requests for SpaceUsage +func NewSpaceUsageRequest(server string, spaceId SpaceId) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "space_id", runtime.ParamLocationPath, spaceId) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/spaces/%s/usage", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { for _, r := range c.RequestEditors { if err := r(ctx, req); err != nil { @@ -2306,6 +2355,9 @@ type ClientWithResponsesInterface interface { // SpaceStatusWithResponse request SpaceStatusWithResponse(ctx context.Context, spaceId SpaceId, reqEditors ...RequestEditorFn) (*SpaceStatusResponse, error) + + // SpaceUsageWithResponse request + SpaceUsageWithResponse(ctx context.Context, spaceId SpaceId, reqEditors ...RequestEditorFn) (*SpaceUsageResponse, error) } type AnalyticsIdentifyResponse struct { @@ -3061,7 +3113,7 @@ func (r RevokeShareResponse) StatusCode() int { type SpaceStatusResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *SpaceStatus + JSON200 *SpaceUsage JSONDefault *Error } @@ -3081,6 +3133,29 @@ func (r SpaceStatusResponse) StatusCode() int { return 0 } +type SpaceUsageResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *SpaceUsage + JSONDefault *Error +} + +// Status returns HTTPResponse.Status +func (r SpaceUsageResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SpaceUsageResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + // AnalyticsIdentifyWithBodyWithResponse request with arbitrary body returning *AnalyticsIdentifyResponse func (c *ClientWithResponses) AnalyticsIdentifyWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AnalyticsIdentifyResponse, error) { rsp, err := c.AnalyticsIdentifyWithBody(ctx, contentType, body, reqEditors...) @@ -3475,6 +3550,15 @@ func (c *ClientWithResponses) SpaceStatusWithResponse(ctx context.Context, space return ParseSpaceStatusResponse(rsp) } +// SpaceUsageWithResponse request returning *SpaceUsageResponse +func (c *ClientWithResponses) SpaceUsageWithResponse(ctx context.Context, spaceId SpaceId, reqEditors ...RequestEditorFn) (*SpaceUsageResponse, error) { + rsp, err := c.SpaceUsage(ctx, spaceId, reqEditors...) + if err != nil { + return nil, err + } + return ParseSpaceUsageResponse(rsp) +} + // ParseAnalyticsIdentifyResponse parses an HTTP response from a AnalyticsIdentifyWithResponse call func ParseAnalyticsIdentifyResponse(rsp *http.Response) (*AnalyticsIdentifyResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -4513,7 +4597,40 @@ func ParseSpaceStatusResponse(rsp *http.Response) (*SpaceStatusResponse, error) switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SpaceStatus + var dest SpaceUsage + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParseSpaceUsageResponse parses an HTTP response from a SpaceUsageWithResponse call +func ParseSpaceUsageResponse(rsp *http.Response) (*SpaceUsageResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SpaceUsageResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest SpaceUsage if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } diff --git a/internal/api/mock/mock_client.go b/internal/api/mock/mock_client.go index 4786990..433170b 100644 --- a/internal/api/mock/mock_client.go +++ b/internal/api/mock/mock_client.go @@ -902,6 +902,26 @@ func (mr *MockClientInterfaceMockRecorder) SpaceStatus(ctx, spaceId any, reqEdit return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpaceStatus", reflect.TypeOf((*MockClientInterface)(nil).SpaceStatus), varargs...) } +// SpaceUsage mocks base method. +func (m *MockClientInterface) SpaceUsage(ctx context.Context, spaceId api.SpaceId, reqEditors ...api.RequestEditorFn) (*http.Response, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, spaceId} + for _, a := range reqEditors { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SpaceUsage", varargs...) + ret0, _ := ret[0].(*http.Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SpaceUsage indicates an expected call of SpaceUsage. +func (mr *MockClientInterfaceMockRecorder) SpaceUsage(ctx, spaceId any, reqEditors ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, spaceId}, reqEditors...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpaceUsage", reflect.TypeOf((*MockClientInterface)(nil).SpaceUsage), varargs...) +} + // SubmitFeedback mocks base method. func (m *MockClientInterface) SubmitFeedback(ctx context.Context, body api.SubmitFeedbackJSONRequestBody, reqEditors ...api.RequestEditorFn) (*http.Response, error) { m.ctrl.T.Helper() @@ -1826,6 +1846,26 @@ func (mr *MockClientWithResponsesInterfaceMockRecorder) SpaceStatusWithResponse( return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpaceStatusWithResponse", reflect.TypeOf((*MockClientWithResponsesInterface)(nil).SpaceStatusWithResponse), varargs...) } +// SpaceUsageWithResponse mocks base method. +func (m *MockClientWithResponsesInterface) SpaceUsageWithResponse(ctx context.Context, spaceId api.SpaceId, reqEditors ...api.RequestEditorFn) (*api.SpaceUsageResponse, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, spaceId} + for _, a := range reqEditors { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SpaceUsageWithResponse", varargs...) + ret0, _ := ret[0].(*api.SpaceUsageResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SpaceUsageWithResponse indicates an expected call of SpaceUsageWithResponse. +func (mr *MockClientWithResponsesInterfaceMockRecorder) SpaceUsageWithResponse(ctx, spaceId any, reqEditors ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, spaceId}, reqEditors...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpaceUsageWithResponse", reflect.TypeOf((*MockClientWithResponsesInterface)(nil).SpaceUsageWithResponse), varargs...) +} + // SubmitFeedbackWithBodyWithResponse mocks base method. func (m *MockClientWithResponsesInterface) SubmitFeedbackWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...api.RequestEditorFn) (*api.SubmitFeedbackResponse, error) { m.ctrl.T.Helper() diff --git a/internal/api/types.go b/internal/api/types.go index 0615fcd..c569e0b 100644 --- a/internal/api/types.go +++ b/internal/api/types.go @@ -446,8 +446,8 @@ type Space struct { Name string `json:"name"` } -// SpaceStatus Space-level usage and cost for the current billing cycle. -type SpaceStatus struct { +// SpaceUsage Space-level usage and cost for the current billing cycle. +type SpaceUsage struct { // BillingPeriodEnd End of the current billing cycle. BillingPeriodEnd *time.Time `json:"billing_period_end,omitempty"` diff --git a/internal/cmd/usage.go b/internal/cmd/usage.go index ca569d9..d4fef47 100644 --- a/internal/cmd/usage.go +++ b/internal/cmd/usage.go @@ -35,18 +35,18 @@ func buildUsageCmd(app *common.App) *cobra.Command { return err } - status, err := common.FetchStatus(cmd.Context(), client, projectID) + usage, err := common.FetchUsage(cmd.Context(), client, projectID) if err != nil { return err } switch { case jsonOutput: - return util.SerializeToJSON(cmd.OutOrStdout(), status) + return util.SerializeToJSON(cmd.OutOrStdout(), usage) case yamlOutput: - return util.SerializeToYAML(cmd.OutOrStdout(), status) + return util.SerializeToYAML(cmd.OutOrStdout(), usage) default: - outputUsage(cmd, status) + outputUsage(cmd, usage) return nil } }, @@ -59,14 +59,14 @@ func buildUsageCmd(app *common.App) *cobra.Command { return cmd } -func outputUsage(cmd *cobra.Command, status common.Status) { - computeHours := float64(status.ComputeMinutes) / 60 - computeLimitHours := float64(status.ComputeLimitMinutes) / 60 - computePercent := float64(status.ComputeMinutes) / float64(status.ComputeLimitMinutes) * 100 +func outputUsage(cmd *cobra.Command, usage common.Usage) { + computeHours := float64(usage.ComputeMinutes) / 60 + computeLimitHours := float64(usage.ComputeLimitMinutes) / 60 + computePercent := float64(usage.ComputeMinutes) / float64(usage.ComputeLimitMinutes) * 100 - storageMibInt := int(status.StorageMib) + storageMibInt := int(usage.StorageMib) storageStr := common.FormatStorageSize(&storageMibInt) - storagePercent := float64(status.StorageMib) / float64(status.StorageLimitMib) * 100 + storagePercent := float64(usage.StorageMib) / float64(usage.StorageLimitMib) * 100 // Build status breakdown (only non-zero counts, in consistent order) type statusEntry struct { @@ -74,17 +74,17 @@ func outputUsage(cmd *cobra.Command, status common.Status) { count int } entries := []statusEntry{ - {"queued", status.Databases.Queued}, - {"configuring", status.Databases.Configuring}, - {"running", status.Databases.Running}, - {"pausing", status.Databases.Pausing}, - {"paused", status.Databases.Paused}, - {"resuming", status.Databases.Resuming}, - {"deleting", status.Databases.Deleting}, - {"deleted", status.Databases.Deleted}, - {"upgrading", status.Databases.Upgrading}, - {"unstable", status.Databases.Unstable}, - {"unknown", status.Databases.Unknown}, + {"queued", usage.Databases.Queued}, + {"configuring", usage.Databases.Configuring}, + {"running", usage.Databases.Running}, + {"pausing", usage.Databases.Pausing}, + {"paused", usage.Databases.Paused}, + {"resuming", usage.Databases.Resuming}, + {"deleting", usage.Databases.Deleting}, + {"deleted", usage.Databases.Deleted}, + {"upgrading", usage.Databases.Upgrading}, + {"unstable", usage.Databases.Unstable}, + {"unknown", usage.Databases.Unknown}, } var total int @@ -96,7 +96,7 @@ func outputUsage(cmd *cobra.Command, status common.Status) { } } - cmd.Printf("Space: %s\n", status.SpaceID) + cmd.Printf("Space: %s\n", usage.SpaceID) cmd.Printf("Compute: %g/%g hours (%s)\n", computeHours, computeLimitHours, formatPercent(computePercent)) cmd.Printf("Storage: %s/1TiB (%s)\n", storageStr, formatPercent(storagePercent)) if len(parts) > 0 { @@ -106,8 +106,8 @@ func outputUsage(cmd *cobra.Command, status common.Status) { } // Show cost only when at least one field is non-zero. Free-tier users // usually have zero cost, and "$0.00" adds noise. - costToDate := util.Deref(status.CostToDate) - estimatedTotalCost := util.Deref(status.EstimatedTotalCost) + costToDate := util.Deref(usage.CostToDate) + estimatedTotalCost := util.Deref(usage.EstimatedTotalCost) if costToDate > 0 || estimatedTotalCost > 0 { cmd.Printf("Cost: $%.2f so far this cycle ($%.2f estimated total)\n", costToDate, estimatedTotalCost) diff --git a/internal/cmd/usage_test.go b/internal/cmd/usage_test.go index 32e306d..6fa0c18 100644 --- a/internal/cmd/usage_test.go +++ b/internal/cmd/usage_test.go @@ -11,10 +11,10 @@ import ( func TestUsageCmd(t *testing.T) { successSetup := func(m *mock.MockClientWithResponsesInterface) { - m.EXPECT().SpaceStatusWithResponse(validCtx, "test-project"). - Return(&api.SpaceStatusResponse{ + m.EXPECT().SpaceUsageWithResponse(validCtx, "test-project"). + Return(&api.SpaceUsageResponse{ HTTPResponse: httpResponse(http.StatusOK), - JSON200: &api.SpaceStatus{ + JSON200: &api.SpaceUsage{ ComputeMinutes: 120, ComputeLimitMinutes: 600, StorageMib: 512, @@ -44,10 +44,10 @@ func TestUsageCmd(t *testing.T) { wantErr: "authentication required: no credentials found", }, { - name: "network error on space status", + name: "network error on space usage", args: []string{"usage"}, setup: func(m *mock.MockClientWithResponsesInterface) { - m.EXPECT().SpaceStatusWithResponse(validCtx, "test-project"). + m.EXPECT().SpaceUsageWithResponse(validCtx, "test-project"). Return(nil, errors.New("connection refused")) databases := []api.DatabaseWithUsage{} m.EXPECT().ListDatabasesWithResponse(validCtx, "test-project"). @@ -56,14 +56,14 @@ func TestUsageCmd(t *testing.T) { JSON200: &databases, }, nil).AnyTimes() }, - wantErr: "failed to get space status: connection refused", + wantErr: "failed to get space usage: connection refused", }, { - name: "API error on space status", + name: "API error on space usage", args: []string{"usage"}, setup: func(m *mock.MockClientWithResponsesInterface) { - m.EXPECT().SpaceStatusWithResponse(validCtx, "test-project"). - Return(&api.SpaceStatusResponse{ + m.EXPECT().SpaceUsageWithResponse(validCtx, "test-project"). + Return(&api.SpaceUsageResponse{ HTTPResponse: httpResponse(http.StatusInternalServerError), JSONDefault: &api.Error{Message: "internal error"}, }, nil) @@ -77,11 +77,11 @@ func TestUsageCmd(t *testing.T) { wantErr: "internal error", }, { - name: "nil space status response body", + name: "nil space usage response body", args: []string{"usage"}, setup: func(m *mock.MockClientWithResponsesInterface) { - m.EXPECT().SpaceStatusWithResponse(validCtx, "test-project"). - Return(&api.SpaceStatusResponse{ + m.EXPECT().SpaceUsageWithResponse(validCtx, "test-project"). + Return(&api.SpaceUsageResponse{ HTTPResponse: httpResponse(http.StatusOK), JSON200: nil, }, nil) @@ -98,10 +98,10 @@ func TestUsageCmd(t *testing.T) { name: "nil list databases response body", args: []string{"usage"}, setup: func(m *mock.MockClientWithResponsesInterface) { - m.EXPECT().SpaceStatusWithResponse(validCtx, "test-project"). - Return(&api.SpaceStatusResponse{ + m.EXPECT().SpaceUsageWithResponse(validCtx, "test-project"). + Return(&api.SpaceUsageResponse{ HTTPResponse: httpResponse(http.StatusOK), - JSON200: &api.SpaceStatus{ + JSON200: &api.SpaceUsage{ ComputeMinutes: 120, ComputeLimitMinutes: 600, StorageMib: 512, @@ -171,10 +171,10 @@ Databases: 2 (1 running, 1 paused) name: "text output with cost", args: []string{"usage"}, setup: func(m *mock.MockClientWithResponsesInterface) { - m.EXPECT().SpaceStatusWithResponse(validCtx, "test-project"). - Return(&api.SpaceStatusResponse{ + m.EXPECT().SpaceUsageWithResponse(validCtx, "test-project"). + Return(&api.SpaceUsageResponse{ HTTPResponse: httpResponse(http.StatusOK), - JSON200: &api.SpaceStatus{ + JSON200: &api.SpaceUsage{ ComputeMinutes: 120, ComputeLimitMinutes: 600, StorageMib: 512, @@ -201,10 +201,10 @@ Cost: $12.34 so far this cycle ($27.50 estimated total) name: "text output with zero cost is omitted", args: []string{"usage"}, setup: func(m *mock.MockClientWithResponsesInterface) { - m.EXPECT().SpaceStatusWithResponse(validCtx, "test-project"). - Return(&api.SpaceStatusResponse{ + m.EXPECT().SpaceUsageWithResponse(validCtx, "test-project"). + Return(&api.SpaceUsageResponse{ HTTPResponse: httpResponse(http.StatusOK), - JSON200: &api.SpaceStatus{ + JSON200: &api.SpaceUsage{ ComputeMinutes: 120, ComputeLimitMinutes: 600, StorageMib: 512, diff --git a/internal/common/status.go b/internal/common/usage.go similarity index 76% rename from internal/common/status.go rename to internal/common/usage.go index af74727..4ef0447 100644 --- a/internal/common/status.go +++ b/internal/common/usage.go @@ -27,8 +27,8 @@ type DatabaseCounts struct { Unknown int `json:"unknown,omitempty"` } -// Status represents space usage including compute, storage, and database counts. -type Status struct { +// Usage represents space usage including compute, storage, and database counts. +type Usage struct { ComputeMinutes int64 `json:"compute_minutes"` ComputeLimitMinutes int64 `json:"compute_limit_minutes"` StorageMib int64 `json:"storage_mib"` @@ -41,17 +41,17 @@ type Status struct { SpaceID string `json:"space_id"` } -// FetchStatus fetches space usage and database counts from the API. -func FetchStatus(ctx context.Context, client api.ClientWithResponsesInterface, projectID string) (Status, error) { - var spaceStatus *api.SpaceStatus +// FetchUsage fetches space usage and database counts from the API. +func FetchUsage(ctx context.Context, client api.ClientWithResponsesInterface, projectID string) (Usage, error) { + var spaceUsage *api.SpaceUsage var databases []api.DatabaseWithUsage g, ctx := errgroup.WithContext(ctx) g.Go(func() error { - resp, err := client.SpaceStatusWithResponse(ctx, projectID) + resp, err := client.SpaceUsageWithResponse(ctx, projectID) if err != nil { - return fmt.Errorf("failed to get space status: %w", err) + return fmt.Errorf("failed to get space usage: %w", err) } if resp.StatusCode() != http.StatusOK { return ExitWithErrorFromStatusCode(resp.StatusCode(), resp.JSONDefault) @@ -59,7 +59,7 @@ func FetchStatus(ctx context.Context, client api.ClientWithResponsesInterface, p if resp.JSON200 == nil { return errors.New("empty response from API") } - spaceStatus = resp.JSON200 + spaceUsage = resp.JSON200 return nil }) @@ -79,7 +79,7 @@ func FetchStatus(ctx context.Context, client api.ClientWithResponsesInterface, p }) if err := g.Wait(); err != nil { - return Status{}, err + return Usage{}, err } // Tally databases by status @@ -111,16 +111,16 @@ func FetchStatus(ctx context.Context, client api.ClientWithResponsesInterface, p } } - return Status{ - ComputeMinutes: spaceStatus.ComputeMinutes, - ComputeLimitMinutes: spaceStatus.ComputeLimitMinutes, - StorageMib: spaceStatus.StorageMib, - StorageLimitMib: spaceStatus.StorageLimitMib, + return Usage{ + ComputeMinutes: spaceUsage.ComputeMinutes, + ComputeLimitMinutes: spaceUsage.ComputeLimitMinutes, + StorageMib: spaceUsage.StorageMib, + StorageLimitMib: spaceUsage.StorageLimitMib, Databases: counts, - CostToDate: spaceStatus.CostToDate, - EstimatedTotalCost: spaceStatus.EstimatedTotalCost, - BillingPeriodStart: spaceStatus.BillingPeriodStart, - BillingPeriodEnd: spaceStatus.BillingPeriodEnd, + CostToDate: spaceUsage.CostToDate, + EstimatedTotalCost: spaceUsage.EstimatedTotalCost, + BillingPeriodStart: spaceUsage.BillingPeriodStart, + BillingPeriodEnd: spaceUsage.BillingPeriodEnd, SpaceID: projectID, }, nil } diff --git a/internal/mcp/usage.go b/internal/mcp/usage.go index 0d211ce..7a34279 100644 --- a/internal/mcp/usage.go +++ b/internal/mcp/usage.go @@ -68,20 +68,20 @@ func (s *Server) handleUsage(ctx context.Context, req *mcp.CallToolRequest, inpu return nil, UsageOutput{}, err } - status, err := common.FetchStatus(ctx, client, projectID) + usage, err := common.FetchUsage(ctx, client, projectID) if err != nil { return nil, UsageOutput{}, err } return nil, UsageOutput{ - ComputeMinutes: status.ComputeMinutes, - ComputeLimitMinutes: status.ComputeLimitMinutes, - Storage: common.FormatStorageSize(new(int(status.StorageMib))), - StorageLimit: common.FormatStorageSize(new(int(status.StorageLimitMib))), - Databases: status.Databases, - CostToDate: status.CostToDate, - EstimatedTotalCost: status.EstimatedTotalCost, - BillingPeriodStart: status.BillingPeriodStart, - BillingPeriodEnd: status.BillingPeriodEnd, + ComputeMinutes: usage.ComputeMinutes, + ComputeLimitMinutes: usage.ComputeLimitMinutes, + Storage: common.FormatStorageSize(new(int(usage.StorageMib))), + StorageLimit: common.FormatStorageSize(new(int(usage.StorageLimitMib))), + Databases: usage.Databases, + CostToDate: usage.CostToDate, + EstimatedTotalCost: usage.EstimatedTotalCost, + BillingPeriodStart: usage.BillingPeriodStart, + BillingPeriodEnd: usage.BillingPeriodEnd, }, nil } diff --git a/openapi.yaml b/openapi.yaml index 4637a7b..73795e6 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -125,20 +125,38 @@ paths: default: $ref: "#/components/responses/Error" + /spaces/{space_id}/usage: + get: + operationId: spaceUsage + summary: Get space usage + description: Returns compute usage, storage usage, and billing/cost information for a space. + parameters: + - $ref: "#/components/parameters/SpaceId" + responses: + "200": + description: Space usage + content: + application/json: + schema: + $ref: "#/components/schemas/SpaceUsage" + default: + $ref: "#/components/responses/Error" + /spaces/{space_id}/status: get: operationId: spaceStatus - summary: Get space status - description: Returns compute and storage usage for a space. + deprecated: true + summary: Get space usage (deprecated) + description: Deprecated alias for `GET /spaces/{space_id}/usage`. Use the `/usage` endpoint instead. parameters: - $ref: "#/components/parameters/SpaceId" responses: "200": - description: Space status + description: Space usage content: application/json: schema: - $ref: "#/components/schemas/SpaceStatus" + $ref: "#/components/schemas/SpaceUsage" default: $ref: "#/components/responses/Error" @@ -926,7 +944,7 @@ components: type: string description: Name for the new space. - SpaceStatus: + SpaceUsage: type: object description: Space-level usage and cost for the current billing cycle. required: