repo.ts (3.50 KB)
import { and, eq } from "drizzle-orm";
import * as z from "zod";
import { DatabaseError } from "@/lib/errors";
import { fn } from "@/lib/fn";
import { Result } from "@/lib/result";
import { db } from ".";
import { repository } from "./schema";

const getById = fn(z.object({ id: z.number() }), ({ id }) =>
  Result.tryCatchAsync(
    async () => {
      const repo = await db.query.repository.findFirst({
        where: eq(repository.id, id),
      });
      return repo ?? null;
    },
    (e) =>
      new DatabaseError({
        cause: e,
      })
  )
);

const getByOwner = fn(z.object({ owner: z.string() }), ({ owner }) =>
  Result.tryCatchAsync(
    async () => {
      const repos = await db.query.repository.findMany({
        where: eq(repository.owner, owner),
      });
      return repos;
    },
    (e) =>
      new DatabaseError({
        cause: e,
      })
  )
);

const getByOwnerId = fn(z.object({ ownerId: z.string() }), ({ ownerId }) =>
  Result.tryCatchAsync(
    async () => {
      const repos = await db.query.repository.findMany({
        where: eq(repository.ownerId, ownerId),
      });
      return repos;
    },
    (e) =>
      new DatabaseError({
        cause: e,
      })
  )
);

const getByOwnerAndName = fn(
  z.object({ owner: z.string(), name: z.string() }),
  ({ owner, name }) =>
    Result.tryCatchAsync(
      async () => {
        const repo = await db.query.repository.findFirst({
          where: and(eq(repository.owner, owner), eq(repository.name, name)),
        });
        return repo ?? null;
      },
      (e) =>
        new DatabaseError({
          cause: e,
        })
    )
);

const create = fn(
  z.object({
    ownerId: z.string(),
    owner: z.string(),
    name: z.string(),
    description: z.string().optional(),
    isPrivate: z.boolean().optional().default(false),
  }),
  ({ ownerId, owner, name, description, isPrivate }) =>
    Result.tryCatchAsync(
      async () => {
        const [repo] = await db
          .insert(repository)
          .values({ ownerId, owner, name, description, isPrivate })
          .returning();
        return repo;
      },
      (e) =>
        new DatabaseError({
          cause: e,
        })
    )
);

const update = fn(
  z.object({
    id: z.number(),
    name: z.string().optional(),
    description: z.string().optional(),
    isPrivate: z.boolean().optional(),
    ownerId: z.string(),
  }),
  ({ id, name, description, isPrivate, ownerId }) =>
    Result.tryCatchAsync(
      async () => {
        const updates: Partial<{
          name: string;
          description: string;
          isPrivate: boolean;
        }> = {};

        if (name !== undefined) updates.name = name;
        if (description !== undefined) updates.description = description;
        if (isPrivate !== undefined) updates.isPrivate = isPrivate;

        const [repo] = await db
          .update(repository)
          .set(updates)
          .where(and(eq(repository.id, id), eq(repository.ownerId, ownerId)))
          .returning();
        return repo ?? null;
      },
      (e) =>
        new DatabaseError({
          cause: e,
        })
    )
);

const remove = fn(z.object({ id: z.number() }), ({ id }) =>
  Result.tryCatchAsync(
    async () => {
      const [deleted] = await db
        .delete(repository)
        .where(eq(repository.id, id))
        .returning();
      return deleted ?? null;
    },
    (e) =>
      new DatabaseError({
        cause: e,
      })
  )
);

export const Repo = {
  getById,
  getByOwner,
  getByOwnerId,
  getByOwnerAndName,
  create,
  update,
  remove,
};