All files / src/server/repositories/implementations SortResultShareRepository.ts

100% Statements 17/17
90% Branches 9/10
100% Functions 11/11
100% Lines 16/16

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113              264x               157x 157x                       157x       13x       13x       10x       10x       9x       9x         6x               6x       9x           14x                           12x                       187x                      
import { eq, inArray } from "drizzle-orm";
import type { Database } from "@/lib/db";
import { sortResultSharesTable, sortResultsTable } from "@/lib/db/schema";
import type { SortResultShare, ShareStatus } from "@/types";
import { generateId } from "@/lib/id";
 
export class SortResultShareRepository {
  constructor(private db: Database) {}
 
  async create(data: {
    id?: string;
    sortResultId: string;
    shareToken: string;
    expiresAt?: Date;
  }): Promise<SortResultShare> {
    const id = data.id || generateId("share");
    const [share] = await this.db
      .insert(sortResultSharesTable)
      .values({
        id,
        sortResultId: data.sortResultId,
        shareToken: data.shareToken,
        status: "active",
        expiresAt: data.expiresAt || null,
        createdAt: new Date(),
        updatedAt: new Date(),
      })
      .returning();
    return this.mapToShare(share);
  }
 
  async findById(id: string): Promise<SortResultShare | null> {
    const [share] = await this.db
      .select()
      .from(sortResultSharesTable)
      .where(eq(sortResultSharesTable.id, id));
    return share ? this.mapToShare(share) : null;
  }
 
  async findByToken(token: string): Promise<SortResultShare | null> {
    const [share] = await this.db
      .select()
      .from(sortResultSharesTable)
      .where(eq(sortResultSharesTable.shareToken, token));
    return share ? this.mapToShare(share) : null;
  }
 
  async findByResult(sortResultId: string): Promise<SortResultShare[]> {
    const shares = await this.db
      .select()
      .from(sortResultSharesTable)
      .where(eq(sortResultSharesTable.sortResultId, sortResultId));
    return shares.map((s) => this.mapToShare(s));
  }
 
  // null when the row is gone (removed after the caller's check).
  async update(id: string, data: Partial<SortResultShare>): Promise<SortResultShare | null> {
    const [share] = await this.db
      .update(sortResultSharesTable)
      .set({
        ...data,
        updatedAt: new Date(),
      })
      .where(eq(sortResultSharesTable.id, id))
      .returning();
    return share ? this.mapToShare(share) : null;
  }
 
  async deleteByResult(sortResultId: string): Promise<void> {
    await this.db.delete(sortResultSharesTable).where(eq(sortResultSharesTable.sortResultId, sortResultId));
  }
 
  // Delete every share of this collection's results (shares have no collection_id —
  // scope via the parent sort_results). Purge-only.
  async deleteByCollection(collectionId: string): Promise<void> {
    await this.db.delete(sortResultSharesTable).where(
      inArray(
        sortResultSharesTable.sortResultId,
        this.db
          .select({ id: sortResultsTable.id })
          .from(sortResultsTable)
          .where(eq(sortResultsTable.collectionId, collectionId)),
      ),
    );
  }
 
  // Delete every share whose parent sort_result belongs to the given curator.
  // Child-first prerequisite for deleteByCurator on sort_results. Account-deletion only.
  async deleteByCurator(curatorId: string): Promise<void> {
    await this.db.delete(sortResultSharesTable).where(
      inArray(
        sortResultSharesTable.sortResultId,
        this.db
          .select({ id: sortResultsTable.id })
          .from(sortResultsTable)
          .where(eq(sortResultsTable.curatorId, curatorId)),
      ),
    );
  }
 
  private mapToShare(row: typeof sortResultSharesTable.$inferSelect): SortResultShare {
    return {
      id: row.id,
      sortResultId: row.sortResultId,
      shareToken: row.shareToken,
      status: row.status as ShareStatus,
      expiresAt: row.expiresAt,
      createdAt: row.createdAt,
      updatedAt: row.updatedAt,
    };
  }
}