From 6bccc0b879f101b34d2881ce6c6ab5cf83c09002 Mon Sep 17 00:00:00 2001 From: "Laurie T. Malau" Date: Sun, 24 Jul 2022 22:13:33 +0000 Subject: [PATCH] Extend server args with time-filtering --- components/dashboard/src/teams/TeamUsage.tsx | 15 +++++++++++++-- components/gitpod-protocol/package.json | 4 +++- .../gitpod-protocol/src/gitpod-service.ts | 2 +- .../ee/src/workspace/gitpod-server-impl.ts | 18 ++++++++++++++++-- components/server/package.json | 1 + .../server/src/workspace/gitpod-server-impl.ts | 7 ++++++- .../usage-api/typescript/src/usage/v1/sugar.ts | 5 ++++- 7 files changed, 44 insertions(+), 8 deletions(-) diff --git a/components/dashboard/src/teams/TeamUsage.tsx b/components/dashboard/src/teams/TeamUsage.tsx index f57516f556b0d3..9d13b5e9dfafc6 100644 --- a/components/dashboard/src/teams/TeamUsage.tsx +++ b/components/dashboard/src/teams/TeamUsage.tsx @@ -26,6 +26,10 @@ function TeamUsage() { const [currentPage, setCurrentPage] = useState(1); const [resultsPerPage] = useState(10); const [errorMessage, setErrorMessage] = useState(""); + const today = new Date(); + const startOfCurrentMonth = new Date(today.getFullYear(), today.getMonth(), 1); + const timestampOfStart = startOfCurrentMonth.getTime(); + const [startDateOfBillMonth] = useState(timestampOfStart); useEffect(() => { if (!team) { @@ -34,7 +38,11 @@ function TeamUsage() { (async () => { const attributionId = AttributionId.render({ kind: "team", teamId: team.id }); try { - const billedUsageResult = await getGitpodService().server.listBilledUsage(attributionId); + const billedUsageResult = await getGitpodService().server.listBilledUsage( + attributionId, + startDateOfBillMonth, // TODO: set based on selected month + Date.now(), // TODO: set based on selected month + ); setBilledUsage(billedUsageResult); } catch (error) { if (error.code === ErrorCodes.PERMISSION_DENIED) { @@ -90,7 +98,10 @@ function TeamUsage() {
Period
-
June 2022
+
+ {startOfCurrentMonth.toLocaleString("default", { month: "long" })}{" "} + {startOfCurrentMonth.getFullYear()} +
Total usage
diff --git a/components/gitpod-protocol/package.json b/components/gitpod-protocol/package.json index 824e371e63a1fc..21ba4c09ce83f1 100644 --- a/components/gitpod-protocol/package.json +++ b/components/gitpod-protocol/package.json @@ -22,6 +22,7 @@ "@types/random-number-csprng": "^1.0.0", "@types/uuid": "^8.3.1", "@types/ws": "^5.1.2", + "@types/google-protobuf": "^3.15.5", "chai": "^4.3.4", "chai-subset": "^1.6.0", "mocha": "^5.0.0", @@ -62,6 +63,7 @@ "vscode-languageserver-types": "3.17.0", "vscode-uri": "^3.0.3", "vscode-ws-jsonrpc": "^0.2.0", - "ws": "^7.4.6" + "ws": "^7.4.6", + "google-protobuf": "^3.18.0-rc.2" } } diff --git a/components/gitpod-protocol/src/gitpod-service.ts b/components/gitpod-protocol/src/gitpod-service.ts index 8be6f5339a45de..41ef4f40c1fd0b 100644 --- a/components/gitpod-protocol/src/gitpod-service.ts +++ b/components/gitpod-protocol/src/gitpod-service.ts @@ -293,7 +293,7 @@ export interface GitpodServer extends JsonRpcServer, AdminServer, getSpendingLimitForTeam(teamId: string): Promise; setSpendingLimitForTeam(teamId: string, spendingLimit: number): Promise; - listBilledUsage(attributionId: string): Promise; + listBilledUsage(attributionId: string, from?: number, to?: number): Promise; setUsageAttribution(usageAttribution: string): Promise; /** diff --git a/components/server/ee/src/workspace/gitpod-server-impl.ts b/components/server/ee/src/workspace/gitpod-server-impl.ts index efbcdab9bf05da..700b8ad5fcb78c 100644 --- a/components/server/ee/src/workspace/gitpod-server-impl.ts +++ b/components/server/ee/src/workspace/gitpod-server-impl.ts @@ -110,6 +110,7 @@ import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/expe import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution"; import { CachingUsageServiceClientProvider } from "@gitpod/usage-api/lib/usage/v1/sugar"; import * as usage from "@gitpod/usage-api/lib/usage/v1/usage_pb"; +import { Timestamp } from "google-protobuf/google/protobuf/timestamp_pb"; @injectable() export class GitpodServerEEImpl extends GitpodServerImpl { @@ -2095,14 +2096,27 @@ export class GitpodServerEEImpl extends GitpodServerImpl { return result; } - async listBilledUsage(ctx: TraceContext, attributionId: string): Promise { + async listBilledUsage( + ctx: TraceContext, + attributionId: string, + from?: number, + to?: number, + ): Promise { traceAPIParams(ctx, { attributionId }); + let timestampFrom; + let timestampTo; const user = this.checkAndBlockUser("listBilledUsage"); await this.guardCostCenterAccess(ctx, user.id, attributionId, "get"); + if (from) { + timestampFrom = new Timestamp().setSeconds(from); + } + if (to) { + timestampTo = new Timestamp().setSeconds(to); + } const usageClient = this.usageServiceClientProvider.getDefault(); - const response = await usageClient.listBilledUsage(ctx, attributionId); + const response = await usageClient.listBilledUsage(ctx, attributionId, timestampFrom, timestampTo); const sessions = response.getSessionsList().map((s) => this.mapBilledSession(s)); return sessions; diff --git a/components/server/package.json b/components/server/package.json index 556cc1e503954f..13917ec7b48095 100644 --- a/components/server/package.json +++ b/components/server/package.json @@ -99,6 +99,7 @@ "@types/express-mysql-session": "^2.1.3", "@types/express-session": "1.17.4", "@types/fs-extra": "^9.0.12", + "@types/google-protobuf": "^3.15.5", "@types/heapdump": "^0.3.1", "@types/http-proxy": "^1.17.7", "@types/js-yaml": "^4.0.3", diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index 7767c15a332d01..270ec99caf6251 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -3191,7 +3191,12 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { throw new ResponseError(ErrorCodes.SAAS_FEATURE, `Not implemented in this version`); } - async listBilledUsage(ctx: TraceContext, attributionId: string): Promise { + async listBilledUsage( + ctx: TraceContext, + attributionId: string, + from?: number, + to?: number, + ): Promise { throw new ResponseError(ErrorCodes.SAAS_FEATURE, `Not implemented in this version`); } async getSpendingLimitForTeam(ctx: TraceContext, teamId: string): Promise { diff --git a/components/usage-api/typescript/src/usage/v1/sugar.ts b/components/usage-api/typescript/src/usage/v1/sugar.ts index c7489f49fb6a59..3e897c136d8864 100644 --- a/components/usage-api/typescript/src/usage/v1/sugar.ts +++ b/components/usage-api/typescript/src/usage/v1/sugar.ts @@ -12,6 +12,7 @@ import { ListBilledUsageRequest, ListBilledUsageResponse } from "./usage_pb"; import { injectable, inject, optional } from "inversify"; import { createClientCallMetricsInterceptor, IClientCallMetrics } from "@gitpod/gitpod-protocol/lib/util/grpc"; import * as grpc from "@grpc/grpc-js"; +import { Timestamp } from "google-protobuf/google/protobuf/timestamp_pb"; export const UsageServiceClientProvider = Symbol("UsageServiceClientProvider"); @@ -90,12 +91,14 @@ export class PromisifiedUsageServiceClient { ); } - public async listBilledUsage(_ctx: TraceContext, attributionId: string): Promise { + public async listBilledUsage(_ctx: TraceContext, attributionId: string, from?: Timestamp, to?: Timestamp): Promise { const ctx = TraceContext.childContext(`/usage-service/listBilledUsage`, _ctx); try { const req = new ListBilledUsageRequest(); req.setAttributionId(attributionId); + req.setFrom(from); + req.setTo(to); const response = await new Promise((resolve, reject) => { this.client.listBilledUsage(