Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions db/migrations/20251013051821_task_log/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE public.tasks
DROP CONSTRAINT task_log_array,
DROP COLUMN log;
3 changes: 3 additions & 0 deletions db/migrations/20251013051821_task_log/seed.sql

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions db/migrations/20251013051821_task_log/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE public.tasks
ADD COLUMN log JSONB NOT NULL DEFAULT '[]',
ADD CONSTRAINT task_log_array CHECK (jsonb_typeof(log) = 'array');
4 changes: 3 additions & 1 deletion db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,9 @@ CREATE TABLE public.tasks (
proposal_token character varying(255),
proposal_base_commit character varying(40),
created_at timestamp without time zone DEFAULT now() NOT NULL,
cost_in_cents integer
cost_in_cents integer,
log jsonb DEFAULT '[]'::jsonb NOT NULL,
CONSTRAINT task_log_array CHECK ((jsonb_typeof(log) = 'array'::text))
);


Expand Down
1 change: 1 addition & 0 deletions packages/api/src/graphql/schema/tasks.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Task {
state: TaskState!
created_at: DateTime!
cost_in_cents: Int
log: JSON!

org: Org!

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { c as createTar } from 'tar';
import z from 'zod';
import { $ } from 'zx';

import { caller } from '../../clients/github';
import { getBot, getRepo, getTask } from '../utils';

import { getBot, getRepo, getTask } from './utils';
import { caller } from '../../../clients/github';

export default async function (app: FastifyInstance) {
app.withTypeProvider<ZodTypeProvider>().post(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import { $ } from 'zx';

import { integration, task_item, task_state } from '@automa/prisma';

import { env } from '../../env';
import { env } from '../../../env';

import { caller } from '../../clients/github';
import { taskUpdateState } from '../../db';
import { getBot, getRepo, getTask } from '../utils';

import { getBot, getRepo, getTask } from './utils';
import { caller } from '../../../clients/github';
import { taskUpdateState } from '../../../db';

type PullRequest = {
id: number;
Expand Down
68 changes: 68 additions & 0 deletions packages/api/src/routes/bot/task/log.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { FastifyInstance } from 'fastify';
import { ZodTypeProvider } from 'fastify-type-provider-zod';
import z from 'zod';

export default async function (app: FastifyInstance) {
app.withTypeProvider<ZodTypeProvider>().post(
'/log',
{
schema: {
body: z.object({
events: z.array(
z.object({
task: z.object({
id: z.number(),
token: z.string(),
}),
event: z
.object({
type: z.string(),
timestamp: z.number(),
})
.passthrough(),
}),
),
}),
},
},
async (request, reply) => {
const { events } = request.body;

if (!events || events.length === 0) {
return reply.code(204).send();
}

const payloadJson = JSON.stringify(events);

await app.prisma.$executeRaw`
WITH payload AS (
SELECT
(e->'task'->>'id')::int AS task_id,
(e->'task'->>'token') AS token,
(e->'event')::jsonb AS event
FROM jsonb_array_elements(${payloadJson}::jsonb) AS e
),
valid AS (
SELECT p.task_id, p.event
FROM payload p
JOIN public.tasks t
ON t.id = p.task_id
AND t.token = p.token
),
grouped AS (
SELECT
task_id,
jsonb_agg(event ORDER BY (event->>'timestamp')::numeric) AS events
FROM valid
GROUP BY task_id
)
UPDATE public.tasks t
SET log = t.log || g.events
FROM grouped g
WHERE t.id = g.task_id;
`;

return reply.send();
},
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import * as tar from 'tar';

import { bots, orgs, repos, tasks } from '@automa/prisma';

import { quibbleSandbox, zxCmdArgsStub, zxCmdStub } from '../../mocks';
import { call, seedBots, seedOrgs, seedRepos, server } from '../../utils';
import { quibbleSandbox, zxCmdArgsStub, zxCmdStub } from '../../../mocks';
import { call, seedBots, seedOrgs, seedRepos, server } from '../../../utils';

suite('code/download', () => {
suite('bot/code/download', () => {
let app: FastifyInstance, response: LightMyRequestResponse;
let org: orgs, bot: bots, repo: repos, task: tasks;
let sandbox: SinonSandbox, postStub: SinonStub, tarCreateStub: SinonStub;
Expand Down Expand Up @@ -817,7 +817,7 @@ const download = async (
app: FastifyInstance,
task: Pick<tasks, 'id' | 'token'>,
) =>
call(app, '/code/download', {
call(app, '/bot/code/download', {
method: 'POST',
payload: {
task,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import axios from 'axios';

import { bots, orgs, repos, tasks } from '@automa/prisma';

import { quibbleSandbox, zxCmdArgsStub, zxCmdStub } from '../../mocks';
import { call, seedBots, seedOrgs, seedRepos, server } from '../../utils';
import { quibbleSandbox, zxCmdArgsStub, zxCmdStub } from '../../../mocks';
import { call, seedBots, seedOrgs, seedRepos, server } from '../../../utils';

const diff = [
'diff --git a/file.txt b/file.txt',
Expand All @@ -20,7 +20,7 @@ const diff = [
'+new',
].join('\n');

suite('code/propose', () => {
suite('bot/code/propose', () => {
let app: FastifyInstance, response: LightMyRequestResponse;
let org: orgs, bot: bots, repo: repos, task: tasks;
let sandbox: SinonSandbox, postStub: SinonStub, getStub: SinonStub;
Expand Down Expand Up @@ -113,7 +113,7 @@ suite('code/propose', () => {

suite('with empty body', () => {
setup(async () => {
response = await call(app, '/code/propose', {
response = await call(app, '/bot/code/propose', {
method: 'POST',
payload: {},
});
Expand Down Expand Up @@ -318,7 +318,7 @@ suite('code/propose', () => {

suite('with missing proposal token', () => {
setup(async () => {
response = await call(app, '/code/propose', {
response = await call(app, '/bot/code/propose', {
method: 'POST',
payload: {
task: {
Expand Down Expand Up @@ -364,7 +364,7 @@ suite('code/propose', () => {

suite('with null proposal token', () => {
setup(async () => {
response = await call(app, '/code/propose', {
response = await call(app, '/bot/code/propose', {
method: 'POST',
payload: {
task: {
Expand Down Expand Up @@ -411,7 +411,7 @@ suite('code/propose', () => {

suite('with missing proposal diff', () => {
setup(async () => {
response = await call(app, '/code/propose', {
response = await call(app, '/bot/code/propose', {
method: 'POST',
payload: {
task: {
Expand Down Expand Up @@ -457,7 +457,7 @@ suite('code/propose', () => {

suite('with null proposal diff', () => {
setup(async () => {
response = await call(app, '/code/propose', {
response = await call(app, '/bot/code/propose', {
method: 'POST',
payload: {
task: {
Expand Down Expand Up @@ -2533,7 +2533,7 @@ const propose = async (
},
metadata?: Record<string, any>,
) =>
call(app, '/code/propose', {
call(app, '/bot/code/propose', {
method: 'POST',
payload: {
task,
Expand Down
2 changes: 2 additions & 0 deletions packages/common/src/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ export type Task = {
id: Scalars['Int']['output'];
is_scheduled: Scalars['Boolean']['output'];
items: Array<TaskItem>;
log: Scalars['JSON']['output'];
org: Org;
state: TaskState;
title: Scalars['String']['output'];
Expand Down Expand Up @@ -763,6 +764,7 @@ export type TaskResolvers<ContextType = any, ParentType extends ResolversParentT
id?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
is_scheduled?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
items?: Resolver<Array<ResolversTypes['TaskItem']>, ParentType, ContextType>;
log?: Resolver<ResolversTypes['JSON'], ParentType, ContextType>;
org?: Resolver<ResolversTypes['Org'], ParentType, ContextType>;
state?: Resolver<ResolversTypes['TaskState'], ParentType, ContextType>;
title?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
Expand Down
4 changes: 2 additions & 2 deletions packages/console/src/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const documents = {
"\n query Repos($org_id: Int!) {\n repos(org_id: $org_id) {\n id\n ...Repo\n }\n }\n": types.ReposDocument,
"\n fragment Task on Task {\n id\n title\n is_scheduled\n state\n created_at\n items {\n type\n ...TaskItem\n }\n }\n": types.TaskFragmentDoc,
"\n fragment TaskItem on TaskItem {\n id\n type\n data\n created_at\n actor_user {\n ...UserAvatar\n }\n bot {\n id\n name\n short_description\n image_url\n org {\n id\n name\n }\n }\n repo {\n id\n name\n org {\n id\n provider_type\n provider_name\n }\n }\n activity {\n id\n type\n from_state\n to_state\n }\n }\n": types.TaskItemFragmentDoc,
"\n query Task($org_id: Int!, $id: Int!) {\n task(org_id: $org_id, id: $id) {\n id\n ...Task\n }\n }\n": types.TaskDocument,
"\n query Task($org_id: Int!, $id: Int!) {\n task(org_id: $org_id, id: $id) {\n id\n ...Task\n log\n }\n }\n": types.TaskDocument,
"\n mutation TaskCreate($org_id: Int!, $input: TaskCreateInput!) {\n taskCreate(org_id: $org_id, input: $input) {\n ...Task\n }\n }\n": types.TaskCreateDocument,
"\n query BotInstallationsAsOptions($org_id: Int!) {\n botInstallations(org_id: $org_id, filter: { type: manual }) {\n id\n bot {\n id\n name\n image_url\n org {\n id\n name\n }\n }\n }\n }\n": types.BotInstallationsAsOptionsDocument,
"\n query RepositoriesAsOptions($org_id: Int!) {\n repos(org_id: $org_id, filter: { is_archived: false }) {\n id\n name\n has_installation\n }\n }\n": types.RepositoriesAsOptionsDocument,
Expand Down Expand Up @@ -169,7 +169,7 @@ export function gql(source: "\n fragment TaskItem on TaskItem {\n id\n ty
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(source: "\n query Task($org_id: Int!, $id: Int!) {\n task(org_id: $org_id, id: $id) {\n id\n ...Task\n }\n }\n"): (typeof documents)["\n query Task($org_id: Int!, $id: Int!) {\n task(org_id: $org_id, id: $id) {\n id\n ...Task\n }\n }\n"];
export function gql(source: "\n query Task($org_id: Int!, $id: Int!) {\n task(org_id: $org_id, id: $id) {\n id\n ...Task\n log\n }\n }\n"): (typeof documents)["\n query Task($org_id: Int!, $id: Int!) {\n task(org_id: $org_id, id: $id) {\n id\n ...Task\n log\n }\n }\n"];
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
Loading
Loading