Skip to content

Commit

Permalink
fix: date and range filter for traces (#437)
Browse files Browse the repository at this point in the history
  • Loading branch information
thisisnithin authored Jan 17, 2024
1 parent bbb5069 commit 2950222
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 292 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -688,16 +688,11 @@ export class AnalyticsRequestViewRepository {
coercedQueryParams.endDate = Math.floor(new Date(opts.dateRange.end).getTime() / 1000);
} else if (opts?.range) {
const endDate = Math.floor(Date.now() / 1000);
coercedQueryParams.startDate = Math.floor(new Date(endDate).getTime() / 1000) - opts?.range * 60 * 60 * 1000;
coercedQueryParams.startDate = Math.floor(new Date(endDate).getTime()) - opts?.range * 60 * 60;
coercedQueryParams.endDate = endDate;
}

const { havingSql, ...rest } = buildCoercedFilterSqlStatement(
columnMetaData,
coercedQueryParams,
filterMapper,
opts?.dateRange,
);
const { havingSql, ...rest } = buildCoercedFilterSqlStatement(columnMetaData, coercedQueryParams, filterMapper);
let { whereSql } = rest;

// Important: This is the only place where we scope the data to a particular organization and graph.
Expand Down Expand Up @@ -726,12 +721,7 @@ export class AnalyticsRequestViewRepository {

// we can't use the same whereSql as we need all values for the filters.
// @todo include counts for each filter value.
let { whereSql: filterWhereSql } = buildCoercedFilterSqlStatement(
columnMetaData,
coercedQueryParams,
{},
opts?.dateRange,
);
let { whereSql: filterWhereSql } = buildCoercedFilterSqlStatement(columnMetaData, coercedQueryParams, {});

filterWhereSql += scopedSql;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ export class MetricsRepository {

const coercedFilters = coerceFilterValues({}, selectedFilters, this.baseFilters);

const { whereSql } = buildCoercedFilterSqlStatement({}, coercedFilters.result, coercedFilters.filterMapper);
const { whereSql } = buildCoercedFilterSqlStatement({}, coercedFilters.result, coercedFilters.filterMapper, false);

return {
granule,
Expand Down
9 changes: 4 additions & 5 deletions controlplane/src/core/repositories/analytics/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export function buildCoercedFilterSqlStatement(
dbClause: 'where' | 'having';
}
>,
dateRange?: DateRange,
includeDateFilter = true,
): { whereSql: string; havingSql: string } {
const whereFilterSqlStatement = [];
const havingFilterSqlStatement = [];
Expand Down Expand Up @@ -220,10 +220,9 @@ export function buildCoercedFilterSqlStatement(
}
}

if (dateRange) {
// Here we reference to the timestamp column in the database not the alias,
// so we can work with the real timestamp (DateTime)

// Here we reference to the timestamp column in the database not the alias,
// so we can work with the real timestamp (DateTime)
if (includeDateFilter) {
whereFilterSqlStatement.push(`Timestamp >= toDateTime({startDate:UInt64})`);
whereFilterSqlStatement.push(`Timestamp <= toDateTime({endDate:UInt64})`);
}
Expand Down
227 changes: 117 additions & 110 deletions studio/src/components/audit-log-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import {
TableHead,
TableHeader,
TableRow,
TableWrapper,
} from "./ui/table";
import { formatDateTime } from "@/lib/format-date";
import { capitalize } from "@/lib/utils";
import { pascalCase } from "change-case";
import { AiOutlineAudit } from "react-icons/ai";
import { PiKeyBold, PiRobotFill, PiUserBold } from "react-icons/pi";
import { Badge } from "./ui/badge";

export const Empty = (params: { unauthorized: boolean }) => {
if (params.unauthorized) {
Expand All @@ -28,10 +30,10 @@ export const Empty = (params: { unauthorized: boolean }) => {
return (
<EmptyState
icon={<AiOutlineAudit />}
title={params.unauthorized ? "Unauthorized" : "No audit logs"}
title="No audit logs"
description={
<div className="space-x-1">
<span>You are not authorized to view audit logs.</span>
<span>You can view activity within your organization here.</span>
<a
target="_blank"
rel="noreferrer"
Expand All @@ -48,124 +50,129 @@ export const Empty = (params: { unauthorized: boolean }) => {

export const AuditLogTable = ({ logs }: { logs?: AuditLog[] }) => {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead className="px-4">Actor</TableHead>
<TableHead className="px-4">Action</TableHead>
<TableHead className="px-4">Date</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{logs?.map(
({
id,
actorDisplayName,
actorType,
auditAction,
createdAt,
action,
auditableDisplayName,
targetDisplayName,
targetType,
}) => {
let preParagraph = null;
let postParagraph = null;
<TableWrapper>
<Table>
<TableHeader>
<TableRow>
<TableHead className="px-4">Actor</TableHead>
<TableHead className="px-4">Action</TableHead>
<TableHead className="px-4">Date</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{logs?.map(
({
id,
actorDisplayName,
actorType,
auditAction,
createdAt,
action,
auditableDisplayName,
targetDisplayName,
targetType,
}) => {
let preParagraph = null;
let postParagraph = null;

if (auditAction === "organization_invitation.created") {
postParagraph = "for";
} else if (auditAction === "member_role.updated") {
preParagraph = "role for";
postParagraph = "to";
} else if (auditableDisplayName) {
preParagraph = "in";
}
if (auditAction === "organization_invitation.created") {
postParagraph = "for";
} else if (auditAction === "member_role.updated") {
preParagraph = "role for";
postParagraph = "to";
} else if (auditableDisplayName) {
preParagraph = "in";
}

let label = null;
let label = null;

if (targetDisplayName) {
label = (
<>
{preParagraph && (
<span className="text-gray-500 dark:text-gray-400">
{preParagraph}
</span>
)}

<span
className="inline-block max-w-md truncate"
title={pascalCase(targetType)}
>
<span className="text-purple whitespace-nowrap font-mono">
{targetDisplayName}
</span>
</span>
</>
);
}

const actionView = (
<>
<span className="text-gray-500 dark:text-gray-400">
{capitalize(action)}
</span>
{label}
{auditableDisplayName && (
if (targetDisplayName) {
label = (
<>
{postParagraph && (
{preParagraph && (
<span className="text-gray-500 dark:text-gray-400">
{postParagraph}
{preParagraph}
</span>
)}
<span className="inline-block max-w-md truncate text-primary">
{auditableDisplayName}

<span
className="inline-block max-w-md truncate"
title={pascalCase(targetType)}
>
<span className="text-purple whitespace-nowrap font-mono">
{targetDisplayName}
</span>
</span>
</>
)}
</>
);
return (
<TableRow
key={id}
className="group py-1 even:bg-secondary/20 hover:bg-secondary/40"
>
<TableCell className="flex px-4 font-medium">
<span className="flex items-center justify-center space-x-2">
{actorType === "api_key" && (
<PiKeyBold className="h-4 w-4" title="API Key activity" />
)}
{actorType === "user" && (
<PiUserBold className="h-4 w-4" title="User activity" />
)}
{actorType === "system" && (
<PiRobotFill
className="h-4 w-4"
title="System activity"
/>
)}
<span className="block font-medium">
{actorDisplayName}
</span>
);
}

const actionView = (
<>
<span className="text-gray-500 dark:text-gray-400">
{capitalize(action)}
</span>
</TableCell>
<TableCell className="px-4 font-medium">
<div className="justify-center space-y-2">
<div className="flex flex-wrap space-x-1.5">
{actionView}
</div>
<div className="focus-ring flex inline-flex items-center rounded-full border px-1.5 font-mono text-sm text-gray-500 dark:text-gray-200">
{auditAction}
{label}
{auditableDisplayName && (
<>
{postParagraph && (
<span className="text-gray-500 dark:text-gray-400">
{postParagraph}
</span>
)}
<span className="inline-block max-w-md truncate text-primary">
{auditableDisplayName}
</span>
</>
)}
</>
);
return (
<TableRow
key={id}
className="group py-1 even:bg-secondary/20 hover:bg-secondary/40"
>
<TableCell className="align-top font-medium">
<span className="flex items-center space-x-2">
{actorType === "api_key" && (
<PiKeyBold
className="h-4 w-4"
title="API Key activity"
/>
)}
{actorType === "user" && (
<PiUserBold className="h-4 w-4" title="User activity" />
)}
{actorType === "system" && (
<PiRobotFill
className="h-4 w-4"
title="System activity"
/>
)}
<span className="block font-medium">
{actorDisplayName}
</span>
</span>
</TableCell>
<TableCell>
<div className="space-y-2">
<div className="flex flex-wrap space-x-1.5">
{actionView}
</div>
<Badge className="font-mono" variant="outline">
{auditAction}
</Badge>
</div>
</div>
</TableCell>
<TableCell className="px-4 font-medium">
{formatDateTime(new Date(createdAt))}
</TableCell>
</TableRow>
);
},
)}
</TableBody>
</Table>
</TableCell>
<TableCell className="align-top">
{formatDateTime(new Date(createdAt))}
</TableCell>
</TableRow>
);
},
)}
</TableBody>
</Table>
</TableWrapper>
);
};
9 changes: 6 additions & 3 deletions studio/src/pages/[organizationSlug]/audit-log.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import { useQuery } from "@tanstack/react-query";
import { getAuditLogs } from "@wundergraph/cosmo-connect/dist/platform/v1/platform-PlatformService_connectquery";
import { AuditLogTable, Empty } from "@/components/audit-log-table";
import { EnumStatusCode } from "@wundergraph/cosmo-connect/dist/common/common_pb";
import { useUser } from "@/hooks/use-user";

const AuditLogPage: NextPageWithLayout = () => {
const { data, isLoading, error } = useQuery(
getAuditLogs.useQuery({
const user = useUser();
const { data, isLoading, error } = useQuery({
...getAuditLogs.useQuery({
limit: 100,
}),
);
queryKey: [user?.currentOrganization.slug || "", "GetAuditLogs", {}],
});

if (isLoading) return <Loader fullscreen />;

Expand Down
Loading

0 comments on commit 2950222

Please sign in to comment.