Skip to content

Commit

Permalink
[OPIK-905] [FE] Split find project endpoint into two endpoints: find …
Browse files Browse the repository at this point in the history
…and stats (#1175)

* [OPIK-905] [FE] Split find project endpoint into two endpoints: find and stats

* - updates related to API contract changes

* - fix after comment
  • Loading branch information
andriidudar authored Jan 30, 2025
1 parent 52a9f91 commit e38030c
Show file tree
Hide file tree
Showing 12 changed files with 208 additions and 33 deletions.
2 changes: 2 additions & 0 deletions apps/opik-frontend/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export const TRACES_KEY = "traces";
export const TRACE_KEY = "trace";
export const PROVIDERS_KEYS_KEY = "providerKeys";
export const AUTOMATIONS_KEY = "automations";
export const PROJECTS_KEY = "projects";
export const PROJECT_STATISTICS_KEY = "project-statistics";

// stats for feedback
export const STATS_COMET_ENDPOINT = "https://stats.comet.com/notify/event/";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import get from "lodash/get";
import { useToast } from "@/components/ui/use-toast";
import api, { PROJECTS_REST_ENDPOINT } from "@/api/api";
import api, {
PROJECT_STATISTICS_KEY,
PROJECTS_KEY,
PROJECTS_REST_ENDPOINT,
} from "@/api/api";

type UseProjectBatchDeleteMutationParams = {
ids: string[];
Expand Down Expand Up @@ -32,8 +36,11 @@ const useProjectBatchDeleteMutation = () => {
});
},
onSettled: () => {
return queryClient.invalidateQueries({
queryKey: ["projects"],
queryClient.invalidateQueries({
queryKey: [PROJECT_STATISTICS_KEY],
});
queryClient.invalidateQueries({
queryKey: [PROJECTS_KEY],
});
},
});
Expand Down
13 changes: 10 additions & 3 deletions apps/opik-frontend/src/api/projects/useProjectCreateMutation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import get from "lodash/get";
import api, { PROJECTS_REST_ENDPOINT } from "@/api/api";
import api, {
PROJECT_STATISTICS_KEY,
PROJECTS_KEY,
PROJECTS_REST_ENDPOINT,
} from "@/api/api";
import { Project } from "@/types/projects";
import { AxiosError } from "axios";
import { useToast } from "@/components/ui/use-toast";
Expand Down Expand Up @@ -39,8 +43,11 @@ const useProjectCreateMutation = () => {
});
},
onSettled: () => {
return queryClient.invalidateQueries({
queryKey: ["projects"],
queryClient.invalidateQueries({
queryKey: [PROJECT_STATISTICS_KEY],
});
queryClient.invalidateQueries({
queryKey: [PROJECTS_KEY],
});
},
});
Expand Down
13 changes: 11 additions & 2 deletions apps/opik-frontend/src/api/projects/useProjectDeleteMutation.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import get from "lodash/get";
import { useToast } from "@/components/ui/use-toast";
import api, { PROJECTS_REST_ENDPOINT } from "@/api/api";
import api, {
PROJECT_STATISTICS_KEY,
PROJECTS_KEY,
PROJECTS_REST_ENDPOINT,
} from "@/api/api";

type UseProjectDeleteMutationParams = {
projectId: string;
Expand Down Expand Up @@ -30,7 +34,12 @@ const useProjectDeleteMutation = () => {
});
},
onSettled: () => {
return queryClient.invalidateQueries({ queryKey: ["projects"] });
queryClient.invalidateQueries({
queryKey: [PROJECT_STATISTICS_KEY],
});
queryClient.invalidateQueries({
queryKey: [PROJECTS_KEY],
});
},
});
};
Expand Down
57 changes: 57 additions & 0 deletions apps/opik-frontend/src/api/projects/useProjectStatisticList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { QueryFunctionContext, useQuery } from "@tanstack/react-query";
import api, {
PROJECT_STATISTICS_KEY,
PROJECTS_REST_ENDPOINT,
QueryConfig,
} from "@/api/api";
import { ProjectStatistic } from "@/types/projects";
import { processSorting } from "@/lib/sorting";
import { Sorting } from "@/types/sorting";

type UseProjectStatisticsListParams = {
workspaceName: string;
search?: string;
sorting?: Sorting;
page: number;
size: number;
};

type UseProjectStatisticsListResponse = {
content: ProjectStatistic[];
total: number;
};

const getProjectStatisticsList = async (
{ signal }: QueryFunctionContext,
{
workspaceName,
search,
sorting,
size,
page,
}: UseProjectStatisticsListParams,
) => {
const { data } = await api.get(`${PROJECTS_REST_ENDPOINT}stats`, {
signal,
params: {
workspace_name: workspaceName,
...processSorting(sorting),
...(search && { name: search }),
size,
page,
},
});

return data;
};

export default function useProjectStatisticsList(
params: UseProjectStatisticsListParams,
options?: QueryConfig<UseProjectStatisticsListResponse>,
) {
return useQuery({
queryKey: [PROJECT_STATISTICS_KEY, params],
queryFn: (context) => getProjectStatisticsList(context, params),
...options,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { AxiosError } from "axios";
import get from "lodash/get";

import api, { PROJECTS_REST_ENDPOINT } from "@/api/api";
import api, { PROJECTS_KEY, PROJECTS_REST_ENDPOINT } from "@/api/api";
import { Project } from "@/types/projects";
import { useToast } from "@/components/ui/use-toast";

Expand Down Expand Up @@ -37,7 +37,7 @@ const useProjectUpdateMutation = () => {
},
onSettled: () => {
return queryClient.invalidateQueries({
queryKey: ["projects"],
queryKey: [PROJECTS_KEY],
});
},
});
Expand Down
8 changes: 6 additions & 2 deletions apps/opik-frontend/src/api/projects/useProjectsList.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { QueryFunctionContext, useQuery } from "@tanstack/react-query";
import api, { PROJECTS_REST_ENDPOINT, QueryConfig } from "@/api/api";
import api, {
PROJECTS_KEY,
PROJECTS_REST_ENDPOINT,
QueryConfig,
} from "@/api/api";
import { Project } from "@/types/projects";
import { processSorting } from "@/lib/sorting";
import { Sorting } from "@/types/sorting";
Expand Down Expand Up @@ -40,7 +44,7 @@ export default function useProjectsList(
options?: QueryConfig<UseProjectsListResponse>,
) {
return useQuery({
queryKey: ["projects", params],
queryKey: [PROJECTS_KEY, params],
queryFn: (context) => getProjectsList(context, params),
...options,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,23 @@ import DataTable from "@/components/shared/DataTable/DataTable";
import DataTableNoData from "@/components/shared/DataTableNoData/DataTableNoData";
import CostCell from "@/components/shared/DataTableCells/CostCell";
import ResourceCell from "@/components/shared/DataTableCells/ResourceCell";
import useProjectsList from "@/api/projects/useProjectsList";
import useProjectWithStatisticsList from "@/hooks/useProjectWithStatisticsList";
import Loader from "@/components/shared/Loader/Loader";
import AddEditProjectDialog from "@/components/pages/ProjectsPage/AddEditProjectDialog";
import { Button } from "@/components/ui/button";
import useAppStore from "@/store/AppStore";
import { COLUMN_NAME_ID, COLUMN_SELECT_ID, COLUMN_TYPE } from "@/types/shared";
import { RESOURCE_TYPE } from "@/components/shared/ResourceLink/ResourceLink";
import { Project } from "@/types/projects";
import { ProjectWithStatistic } from "@/types/projects";
import { formatDate } from "@/lib/date";
import { convertColumnDataToColumn } from "@/lib/table";

const COLUMNS_WIDTH_KEY = "home-projects-columns-width";

export const COLUMNS = convertColumnDataToColumn<Project, Project>(
export const COLUMNS = convertColumnDataToColumn<
ProjectWithStatistic,
ProjectWithStatistic
>(
[
{
id: COLUMN_NAME_ID,
Expand Down Expand Up @@ -72,7 +75,7 @@ const ObservabilitySection: React.FunctionComponent = () => {
const resetDialogKeyRef = useRef(0);
const [openDialog, setOpenDialog] = useState<boolean>(false);

const { data, isPending } = useProjectsList(
const { data, isPending } = useProjectWithStatisticsList(
{
workspaceName,
sorting: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useCallback, useMemo, useRef, useState } from "react";
import asyncLib from "async";
import { useQueryClient } from "@tanstack/react-query";

import { PROJECTS_KEY } from "@/api/api";
import { DatasetItem } from "@/types/datasets";
import { PlaygroundPromptType } from "@/types/playground";
import { usePromptIds, useResetOutputMap } from "@/store/PlaygroundStore";
Expand Down Expand Up @@ -97,7 +98,7 @@ const useActionButtonActions = ({
},
onCreateTraces: () => {
queryClient.invalidateQueries({
queryKey: ["projects"],
queryKey: [PROJECTS_KEY],
});
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import IdCell from "@/components/shared/DataTableCells/IdCell";
import DurationCell from "@/components/shared/DataTableCells/DurationCell";
import CostCell from "@/components/shared/DataTableCells/CostCell";
import ResourceCell from "@/components/shared/DataTableCells/ResourceCell";
import useProjectsList from "@/api/projects/useProjectsList";
import { Project } from "@/types/projects";
import useProjectWithStatisticsList from "@/hooks/useProjectWithStatisticsList";
import { ProjectWithStatistic } from "@/types/projects";
import Loader from "@/components/shared/Loader/Loader";
import AddEditProjectDialog from "@/components/pages/ProjectsPage/AddEditProjectDialog";
import ProjectsActionsPanel from "@/components/pages/ProjectsPage/ProjectsActionsPanel";
Expand Down Expand Up @@ -41,14 +41,14 @@ import {
} from "@/components/shared/DataTable/utils";
import { RESOURCE_TYPE } from "@/components/shared/ResourceLink/ResourceLink";

export const getRowId = (p: Project) => p.id;
export const getRowId = (p: ProjectWithStatistic) => p.id;

const SELECTED_COLUMNS_KEY = "projects-selected-columns";
const COLUMNS_WIDTH_KEY = "projects-columns-width";
const COLUMNS_ORDER_KEY = "projects-columns-order";
const COLUMNS_SORT_KEY = "projects-columns-sort";

export const DEFAULT_COLUMNS: ColumnData<Project>[] = [
export const DEFAULT_COLUMNS: ColumnData<ProjectWithStatistic>[] = [
{
id: "id",
label: "ID",
Expand Down Expand Up @@ -176,7 +176,7 @@ const ProjectsPage: React.FunctionComponent = () => {
},
);

const { data, isPending } = useProjectsList(
const { data, isPending } = useProjectWithStatisticsList(
{
workspaceName,
search,
Expand Down Expand Up @@ -222,14 +222,14 @@ const ProjectsPage: React.FunctionComponent = () => {
defaultValue: {},
});

const selectedRows: Project[] = useMemo(() => {
const selectedRows: ProjectWithStatistic[] = useMemo(() => {
return projects.filter((row) => rowSelection[row.id]);
}, [rowSelection, projects]);

const columns = useMemo(() => {
return [
generateSelectColumDef<Project>(),
mapColumnDataFields<Project, Project>({
generateSelectColumDef<ProjectWithStatistic>(),
mapColumnDataFields<ProjectWithStatistic, ProjectWithStatistic>({
id: COLUMN_NAME_ID,
label: "Name",
type: COLUMN_TYPE.string,
Expand All @@ -241,10 +241,13 @@ const ProjectsPage: React.FunctionComponent = () => {
resource: RESOURCE_TYPE.project,
},
}),
...convertColumnDataToColumn<Project, Project>(DEFAULT_COLUMNS, {
columnsOrder,
selectedColumns,
}),
...convertColumnDataToColumn<ProjectWithStatistic, ProjectWithStatistic>(
DEFAULT_COLUMNS,
{
columnsOrder,
selectedColumns,
},
),
generateActionsColumDef({
cell: ProjectRowActionsCell,
}),
Expand Down
76 changes: 76 additions & 0 deletions apps/opik-frontend/src/hooks/useProjectWithStatisticsList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useMemo } from "react";
import { keepPreviousData, UseQueryOptions } from "@tanstack/react-query";
import { Sorting } from "@/types/sorting";
import { ProjectStatistic, ProjectWithStatistic } from "@/types/projects";
import useProjectsList from "@/api/projects/useProjectsList";
import useProjectStatisticsList from "@/api/projects/useProjectStatisticList";

type UseProjectWithStatisticsParams = {
workspaceName: string;
search?: string;
sorting?: Sorting;
page: number;
size: number;
};

type UseProjectWithStatisticsResponse = {
data: {
content: ProjectWithStatistic[];
total: number;
};
isPending: boolean;
};

export default function useProjectWithStatisticsList(
params: UseProjectWithStatisticsParams,
config: Omit<UseQueryOptions, "queryKey" | "queryFn">,
) {
const { data: projectsData, isPending } = useProjectsList(params, {
...config,
placeholderData: keepPreviousData,
} as never);

const { data: projectsStatisticData } = useProjectStatisticsList(
{
...params,
},
{
...config,
placeholderData: keepPreviousData,
} as never,
);

const data = useMemo(() => {
if (projectsData) {
let statisticMap: Record<string, ProjectStatistic> = {};

if (projectsStatisticData && projectsStatisticData.content?.length > 0) {
statisticMap = projectsStatisticData.content.reduce<
Record<string, ProjectStatistic>
>((acc, statistic) => {
acc[statistic.project_id!] = statistic;
return acc;
}, {});
}

return {
...projectsData,
content: projectsData.content.map((project) => {
return statisticMap
? {
...project,
...statisticMap[project.id],
}
: project;
}),
};
}

return { content: [], total: 0 };
}, [projectsData, projectsStatisticData]);

return {
data,
isPending,
} as UseProjectWithStatisticsResponse;
}
Loading

0 comments on commit e38030c

Please sign in to comment.