Skip to content

Commit

Permalink
Add action run table
Browse files Browse the repository at this point in the history
  • Loading branch information
3mcd committed May 20, 2024
1 parent 3b16a67 commit 232adea
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 32 deletions.
30 changes: 19 additions & 11 deletions core/actions/_lib/runActionInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { StagesId } from "~/kysely/types/public/Stages";
import { db } from "~/kysely/database";
import Action from "~/kysely/types/public/Action";
import ActionRunStatus from "~/kysely/types/public/ActionRunStatus";
import { getLoginData } from "~/lib/auth/loginData";
import { getPub } from "~/lib/server";
import { defineServerAction } from "~/lib/server/defineServerAction";
import { ClientException, ClientExceptionOptions } from "~/lib/serverActions";
Expand Down Expand Up @@ -114,8 +115,6 @@ const runAndRecordActionInstance = async (
{ pubId, actionInstanceId, args = {} }: ActionInstanceArgs,
event?: Event
) => {
let result: ActionInstanceRunResult;

const pubPromise = getPub(pubId);

const actionInstancePromise = db
Expand All @@ -137,35 +136,44 @@ const runAndRecordActionInstance = async (
]);

if (pubResult.status === "rejected") {
result = {
return {
error: "Pub not found",
cause: pubResult.reason,
};
} else if (actionInstanceResult.status === "rejected") {
}

if (actionInstanceResult.status === "rejected") {
logger.debug({ msg: actionInstanceResult.reason });
result = {
return {
error: "Action instance not found",
cause: actionInstanceResult.reason,
};
} else {
result = await _runActionInstance(actionInstanceResult.value, pubResult.value, args);
}

if (!event) {
// If the action was not run by a rule, we record the initiating member.
const loginData = await getLoginData();
const member = loginData?.memberships.find(
(m) => m.communityId === pubResult.value.communityId
);
}

const result = await _runActionInstance(actionInstanceResult.value, pubResult.value, args);

await db
.insertInto("action_runs")
.values({
action_instance_id: actionInstanceId,
pub_id: pubId,
status: "error" in result ? ActionRunStatus.failure : ActionRunStatus.success,
config:
actionInstanceResult.status === "fulfilled"
? actionInstanceResult.value.config
: undefined,
config: actionInstanceResult.value.config,
params: args,
event,
})
.execute();

revalidateTag(`action_runs_${pubResult.value.communityId}`);

return result;
};

Expand Down
11 changes: 11 additions & 0 deletions core/app/c/[communitySlug]/activity/actions/ActionRunsTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use client";

import * as React from "react";

import { DataTable } from "~/app/components/DataTable";
import { ActionRun, getActionRunsTableColumns } from "./getActionRunsTableColumns";

export const ActionRunsTable = ({ actionRuns }: { actionRuns: ActionRun[] }) => {
const actionRunsColumns = getActionRunsTableColumns();
return <DataTable columns={actionRunsColumns} data={actionRuns} searchBy="id" />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"use client";

import type { ColumnDef } from "@tanstack/react-table";

import { Badge } from "ui/badge";
import { DataTableColumnHeader } from "ui/data-table";

import { PubTitle } from "~/app/components/PubTitle";

export type ActionRun = {
id: string;
createdAt: Date;
actionInstance: { name: string; action: string };
stage: { id: string; name: string };
pub: {
id: string;
values: { field: { slug: string }; value: unknown }[] | Record<string, unknown>;
createdAt: Date;
};
};

export const getActionRunsTableColumns = () =>
[
{
header: ({ column }) => <DataTableColumnHeader column={column} title="Action" />,
accessorKey: "actionInstance.name",
},
{
header: ({ column }) => <DataTableColumnHeader column={column} title="Event" />,
accessorKey: "event",
cell: ({ getValue }) => {
switch (getValue()) {
case "pubEnteredStage":
return "Entered stage";
case "pubLeftStage":
return "Left stage";
default:
// TODO: Display user who triggered the action
return "Manual";
}
},
},
{
header: ({ column }) => <DataTableColumnHeader column={column} title="Stage" />,
accessorKey: "stage.name",
},
{
header: ({ column }) => <DataTableColumnHeader column={column} title="Pub" />,
accessorKey: "pub",
cell: ({ getValue }) => <PubTitle pub={getValue<ActionRun["pub"]>()} />,
},
{
header: ({ column }) => <DataTableColumnHeader column={column} title="Time" />,
accessorKey: "createdAt",
},
{
header: ({ column }) => <DataTableColumnHeader column={column} title="Status" />,
accessorKey: "status",
cell: ({ getValue }) => {
switch (getValue()) {
case "success":
return <Badge color="green">success</Badge>;
case "failure":
return <Badge color="red">failure</Badge>;
default:
return <Badge variant="outline">unknown</Badge>;
}
},
},
] as const satisfies ColumnDef<ActionRun, unknown>[];
15 changes: 12 additions & 3 deletions core/app/c/[communitySlug]/activity/actions/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { db } from "~/kysely/database";
import { getLoginData } from "~/lib/auth/loginData";
import { pubValuesByRef } from "~/lib/server";
import { findCommunityBySlug } from "~/lib/server/community";
import { ActionRunsTable } from "./ActionRunsTable";

export default async function Page({
params: { communitySlug },
Expand Down Expand Up @@ -49,22 +50,30 @@ export default async function Page({
"action_runs.params",
"action_runs.status",
"action_runs.created_at as createdAt",
jsonObjectFrom(
eb
.selectFrom("action_instances")
.whereRef("action_instances.id", "=", "action_runs.action_instance_id")
.select(["name", "action"])
)
.$notNull()
.as("actionInstance"),
// Include the action run's stage and pub. $notNull is used to narrow
// the type to exclude null values, which is safe because actions
// must be run in the context of both a pub and stage.
jsonObjectFrom(
eb
.selectFrom("stages")
.whereRef("stages.id", "=", "action_instances.stage_id")
.select(["stages.id", "stages.name"])
.select(["id", "name"])
)
.$notNull()
.as("stage"),
jsonObjectFrom(
eb
.selectFrom("pubs")
.whereRef("pubs.id", "=", "action_runs.pub_id")
.select(["id"])
.select(["id", "created_at as createdAt"])
.select(pubValuesByRef("action_runs.pub_id"))
)
.$notNull()
Expand All @@ -77,5 +86,5 @@ export default async function Page({

logger.info("Action runs", actionRuns);

return <p>Action runs</p>;
return <ActionRunsTable actionRuns={actionRuns} />;
}
19 changes: 12 additions & 7 deletions core/app/components/PubTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import type { PubValuesPayload } from "~/lib/types";

type Props = {
pub: PubValuesPayload;
pub: {
id: string;
values: { field: { slug: string }; value: unknown }[] | Record<string, unknown>;
createdAt: Date;
};
};
export const PubTitle: React.FC<Props> = function (props: Props) {
const titleValue = props.pub.values.find((value) => {
return value.field.slug.includes("title") && value.value;
});
const title = titleValue?.value as string;
const title = (
Array.isArray(props.pub.values)
? props.pub.values.find((value) => {
return value.field.slug.includes("title") && value.value;
})?.value
: props.pub.values["pubpub:title"]
) as string | undefined;
return (
<h3 className="text-md font-semibold">
{title ?? `Untitled Pub - ${props.pub.createdAt.toDateString()}`}{" "}
Expand Down
1 change: 1 addition & 0 deletions core/lib/server/pub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ const pubAssignee = (eb: ExpressionBuilder<Database, "pubs">) =>
"avatar",
"created_at as createdAt",
"email",
"community_id as communityId",
])
).as("assignee");

Expand Down
1 change: 1 addition & 0 deletions core/pages/api/v0/[...ts-rest].ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ const internalRouter = createNextRoute(api.internal, {
stageId as StagesId,
event as Event
);

return {
status: 200,
body: actionRunResults,
Expand Down
28 changes: 17 additions & 11 deletions core/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ model Member {
updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at")
permissions Permission[]
actionRuns ActionRun[]
@@map(name: "members")
}
Expand Down Expand Up @@ -353,17 +354,22 @@ enum Action {
}

model ActionRun {
id String @id @default(dbgenerated("gen_random_uuid()"))
actionInstance ActionInstance @relation(fields: [actionInstanceId], references: [id], onDelete: Cascade)
actionInstanceId String @map(name: "action_instance_id")
pub Pub @relation(fields: [pubId], references: [id])
pubId String @map(name: "pub_id")
config Json?
event Event?
params Json?
status ActionRunStatus
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at")
id String @id @default(dbgenerated("gen_random_uuid()"))
actionInstance ActionInstance? @relation(fields: [actionInstanceId], references: [id], onDelete: Cascade)
actionInstanceId String? @map(name: "action_instance_id")
actionInstanceName String
stageName String
pub Pub @relation(fields: [pubId], references: [id])
pubId String @map(name: "pub_id")
config Json?
event Event?
params Json?
status ActionRunStatus
initiator Member? @relation(fields: [initiatorId], references: [id])
initiatorId String? @map(name: "initiator_id")
initiatorName String?
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at")
@@map(name: "action_runs")
}
Expand Down

0 comments on commit 232adea

Please sign in to comment.