Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
82fea2b
Add WS image metrics to workspace instances
filiptronicek Dec 5, 2024
ad95ee6
Update tests
filiptronicek Dec 5, 2024
5a694a3
fix ws-manager-api field description
filiptronicek Dec 6, 2024
f9bcd54
[dashboard] Org Insights page
filiptronicek Dec 6, 2024
ea9984d
Pagination, date filters and downloads
filiptronicek Dec 9, 2024
ba550e1
Safety limits for pagination and prettier icons
filiptronicek Dec 9, 2024
9a1a72b
UI improvements
filiptronicek Dec 9, 2024
219a065
Merge branch 'main' into ft/add-insights-page
filiptronicek Dec 9, 2024
980616e
Enhance `from` date to capture whole day
filiptronicek Dec 9, 2024
100c003
some more props for the CSVs
filiptronicek Dec 10, 2024
ff6c8f6
Include git context with workspace responses
filiptronicek Dec 10, 2024
3718833
Context url segments in CSV
filiptronicek Dec 11, 2024
9806e27
ide => editor to align with papi convention
filiptronicek Dec 11, 2024
d7992c8
Remove duplicate fc
filiptronicek Dec 11, 2024
2a631eb
revert route deletion
filiptronicek Dec 11, 2024
bb7caae
Update papi converter tests and revert unecessary changes
filiptronicek Dec 11, 2024
e65cc4e
fix error rendering
filiptronicek Dec 11, 2024
a1fc587
partly revert ws api svc changes
filiptronicek Dec 11, 2024
6c9cb9c
Remove debug lines
filiptronicek Dec 12, 2024
5f6494e
fix proto typo
filiptronicek Dec 12, 2024
f77d7f8
Remove org member listing from frontend
filiptronicek Dec 12, 2024
f31df63
Shorter == better 😎
filiptronicek Dec 12, 2024
79a97aa
Move workspace.metadata.context onto a top-level `WorkspaceSession` p…
filiptronicek Dec 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions components/dashboard/src/Insights.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* See License.AGPL.txt in the project root for license information.
*/

import type { OrganizationMember } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";
import { LoadingState } from "@podkit/loading/LoadingState";
import { Heading2, Subheading } from "@podkit/typography/Headings";
import classNames from "classnames";
Expand All @@ -14,7 +13,6 @@ import Alert from "./components/Alert";
import Header from "./components/Header";
import { Item, ItemField, ItemsList } from "./components/ItemsList";
import { useWorkspaceSessions } from "./data/insights/list-workspace-sessions-query";
import { useListOrganizationMembers } from "./data/organizations/members-query";
import { WorkspaceSessionGroup } from "./insights/WorkspaceSessionGroup";
import { gitpodHostUrl } from "./service/service";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@podkit/select/Select";
Expand Down Expand Up @@ -49,8 +47,6 @@ export const Insights = () => {
from: upperBound,
to: lowerBound,
});
const membersQuery = useListOrganizationMembers();
const members: OrganizationMember[] = useMemo(() => membersQuery.data || [], [membersQuery.data]);

const hasMoreThanOnePage = (data?.pages.length ?? 0) > 1;
const sessions = useMemo(() => data?.pages.flatMap((p) => p) ?? [], [data]);
Expand Down Expand Up @@ -123,13 +119,8 @@ export const Insights = () => {
if (!sessions?.length) {
return null;
}
const member = members.find(
(m) => m.userId === sessions[0]?.workspace?.metadata?.ownerId,
);

return (
<WorkspaceSessionGroup key={id} id={id} sessions={sessions} member={member} />
);
return <WorkspaceSessionGroup key={id} id={id} sessions={sessions} />;
})}
</Accordion>
)}
Expand Down
5 changes: 2 additions & 3 deletions components/dashboard/src/insights/WorkspaceSession.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ import { displayTime } from "./WorkspaceSessionGroup";

type Props = {
session: WorkspaceSession;
index: number;
};
export const WorkspaceSessionEntry = ({ session, index }: Props) => {
export const WorkspaceSessionEntry = ({ session }: Props) => {
const isRunning = session?.workspace?.status?.phase?.name === WorkspacePhase_Phase.RUNNING;

return (
<li key={index} className="text-sm text-gray-600 dark:text-gray-300">
<li className="text-sm text-gray-600 dark:text-gray-300">
{session.creationTime ? displayTime(session.creationTime) : "n/a"} (
{session.id.slice(0, 7) || "No instance ID"}){isRunning ? " - running" : ""}
</li>
Expand Down
24 changes: 11 additions & 13 deletions components/dashboard/src/insights/WorkspaceSessionGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/

import { Timestamp } from "@bufbuild/protobuf";
import { OrganizationMember } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";
import { WorkspaceSession, WorkspaceSpec_WorkspaceType } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
import { AccordionContent, AccordionItem, AccordionTrigger } from "../components/accordion/Accordion";
import { ReactComponent as UsageIcon } from "../images/usage-default.svg";
Expand All @@ -17,36 +16,35 @@ import { displayWorkspaceType } from "./download/download-sessions";
type Props = {
id: string;
sessions: WorkspaceSession[];
member?: OrganizationMember;
};
export const WorkspaceSessionGroup = ({ id, sessions, member }: Props) => {
export const WorkspaceSessionGroup = ({ id, sessions }: Props) => {
if (!sessions?.length) {
return null;
}
const workspace = sessions[0].workspace!;
const { workspace, owner } = sessions[0];

return (
<AccordionItem key={id} value={id}>
<div className="w-full p-3 grid grid-cols-12 gap-x-3 justify-between transition ease-in-out rounded-xl">
<div className="flex flex-col col-span-2 my-auto">
<span className="text-pk-content-primary text-md font-medium capitalize">
{displayWorkspaceType(workspace.spec?.type)}
{displayWorkspaceType(workspace?.spec?.type)}
</span>
<span className="text-sm text-pk-content-tertiary">
{workspace.spec?.class ? <DisplayName workspaceClass={workspace?.spec?.class} /> : "n/a"}
{workspace?.spec?.class ? <DisplayName workspaceClass={workspace?.spec?.class} /> : "n/a"}
</span>
</div>
<div className="flex flex-col col-span-5 my-auto">
<div className="flex">
<span className="truncate text-pk-content-primary text-md font-medium">{workspace.id}</span>
<span className="truncate text-pk-content-primary text-md font-medium">{workspace?.id}</span>
</div>
<span className="text-sm truncate text-pk-content-secondary">
{workspace.metadata?.originalContextUrl && toRemoteURL(workspace.metadata?.originalContextUrl)}
{workspace?.metadata?.originalContextUrl && toRemoteURL(workspace.metadata.originalContextUrl)}
</span>
</div>
<div className="flex flex-col col-span-3 my-auto">
<span className="text-right text-pk-content-secondary font-medium">
{workspace.spec?.type === WorkspaceSpec_WorkspaceType.PREBUILD ? (
{workspace?.spec?.type === WorkspaceSpec_WorkspaceType.PREBUILD ? (
<div className="flex">
<UsageIcon className="my-auto w-4 h-4 mr-1" />
<span className="text-sm">Gitpod</span>
Expand All @@ -55,10 +53,10 @@ export const WorkspaceSessionGroup = ({ id, sessions, member }: Props) => {
<div className="flex">
<img
className="my-auto rounded-full w-4 h-4 inline-block align-text-bottom mr-1 overflow-hidden"
src={member?.avatarUrl ?? ""}
src={owner?.avatarUrl ?? ""}
alt=""
/>
<span className="text-sm">{member?.fullName}</span>
<span className="text-sm">{owner?.name}</span>
</div>
)}
</span>
Expand All @@ -73,8 +71,8 @@ export const WorkspaceSessionGroup = ({ id, sessions, member }: Props) => {
<div className="px-3 py-2 space-y-2">
<h4 className="text-sm font-medium text-pk-content-primary">Workspace starts:</h4>
<ul className="space-y-1">
{sessions.map((session, index) => (
<WorkspaceSessionEntry key={session.id || index} session={session} index={index} />
{sessions.map((session) => (
<WorkspaceSessionEntry key={session.id} session={session} />
))}
</ul>
</div>
Expand Down
19 changes: 5 additions & 14 deletions components/dashboard/src/insights/download/download-sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ import {
WorkspaceSession,
WorkspaceSpec_WorkspaceType,
} from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
import { organizationClient, workspaceClient } from "../../service/public-api";
import { workspaceClient } from "../../service/public-api";
import dayjs from "dayjs";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback } from "react";
import { noPersistence } from "../../data/setup";
import { Timestamp } from "@bufbuild/protobuf";
import type { OrganizationMember } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";

const pageSize = 100;
const maxPages = 100; // safety limit if something goes wrong with pagination
Expand Down Expand Up @@ -102,17 +101,9 @@ const downloadUsageCSV = async ({
};
}

const orgMembers = await organizationClient.listOrganizationMembers({
organizationId,
});

const rows = records
.map((record) => {
const member = orgMembers.members.find((m) => m.userId === record.workspace?.metadata?.ownerId);
if (!member) {
return null;
}
return transformSessionRecord(record, member);
return transformSessionRecord(record);
})
.filter((r) => !!r);
const fields = Object.keys(rows[0]) as (keyof ReturnType<typeof transformSessionRecord>)[];
Expand Down Expand Up @@ -167,7 +158,7 @@ const displayTime = (timestamp?: Timestamp) => {
return timestamp.toDate().toISOString();
};

export const transformSessionRecord = (session: WorkspaceSession, member: OrganizationMember) => {
export const transformSessionRecord = (session: WorkspaceSession) => {
const initializerType = session.workspace?.spec?.initializer?.specs;
const prebuildInitializer = initializerType?.find((i) => i.spec.case === "prebuild")?.spec.value as
| PrebuildInitializer
Expand All @@ -186,8 +177,8 @@ export const transformSessionRecord = (session: WorkspaceSession, member: Organi
workspaceID: session?.workspace?.id,
configurationID: session.workspace?.metadata?.configurationId,
prebuildID: prebuildInitializer?.prebuildId,
userID: member.userId,
userName: member.fullName,
userID: session.owner?.id,
userName: session.owner?.name,

contextURL: session.workspace?.metadata?.originalContextUrl,
contextURL_cloneURL: session.workspace?.metadata?.context?.repository?.cloneUrl,
Expand Down
12 changes: 12 additions & 0 deletions components/public-api/gitpod/v1/workspace.proto
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,17 @@ message WorkspaceSnapshot {
}

message WorkspaceSession {
message Owner {
// id is the ID of the user who created the workspace
string id = 1;

// name is the full name of the user who created the workspace
string name = 2;

// avatar_url is the URL of the user's avatar
string avatar_url = 3;
}

message Metrics {
// workspace_image_size is the size of the workspace image in bytes
int64 workspace_image_size = 1;
Expand All @@ -950,4 +961,5 @@ message WorkspaceSession {
google.protobuf.Timestamp stopped_time = 7;

Metrics metrics = 8;
Owner owner = 9;
}
Loading
Loading