Skip to content

Commit 5b1533c

Browse files
committed
Better version, without any, inspired by Codex
1 parent 514f919 commit 5b1533c

2 files changed

Lines changed: 65 additions & 47 deletions

File tree

apps/website/app/api/supabase/content-embedding/route.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,11 @@ const processAndCreateEmbedding = async (
4141
const { tableName } = tableData;
4242
// Using getOrCreateEntity, forcing create path by providing non-matching criteria
4343
// This standardizes return type and error handling (e.g., FK violations from dbUtils)
44-
const result =
45-
await getOrCreateEntity<"ContentEmbedding_openai_text_embedding_3_small_1536">(
46-
{
47-
supabase,
48-
tableName,
49-
insertData: processedItem,
50-
},
51-
);
44+
const result = await getOrCreateEntity({
45+
supabase,
46+
tableName,
47+
insertData: processedItem,
48+
});
5249

5350
if (result.error) {
5451
return result;

apps/website/app/utils/supabase/dbUtils.ts

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,29 @@ import type {
44
PostgrestSingleResponse,
55
} from "@supabase/supabase-js";
66
import { PostgrestError } from "@supabase/supabase-js";
7-
import type { Database, Tables, TablesInsert } from "@repo/database/dbTypes";
7+
import type { Database } from "@repo/database/dbTypes";
8+
9+
type PublicTableName = keyof Database["public"]["Tables"];
10+
type RawTables<TN extends PublicTableName> =
11+
Database["public"]["Tables"][TN]["Row"];
12+
type RawTablesInsert<TN extends PublicTableName> =
13+
Database["public"]["Tables"][TN] extends { Insert: unknown }
14+
? Database["public"]["Tables"][TN]["Insert"]
15+
: never;
16+
type RawTablesInsertKey<TN extends PublicTableName> =
17+
keyof RawTablesInsert<TN> & string;
18+
19+
// Those next three types would be unions if we ever have more embedding tables
20+
export type ContentEmbeddingTableName =
21+
"ContentEmbedding_openai_text_embedding_3_small_1536";
22+
export type ContentEmbeddingStrTablesInsert =
23+
RawTablesInsert<"ContentEmbedding_openai_text_embedding_3_small_1536">;
24+
export type ContentEmbeddingStrTables =
25+
RawTables<"ContentEmbedding_openai_text_embedding_3_small_1536">;
826

927
export const KNOWN_EMBEDDING_TABLES: {
1028
[key: string]: {
11-
tableName: keyof Database["public"]["Tables"];
29+
tableName: ContentEmbeddingTableName;
1230
tableSize: number;
1331
};
1432
} = {
@@ -81,20 +99,18 @@ const processSupabaseError = <T>(
8199
* @param uniqueOn The expected uniqueOn key.
82100
* @returns Promise<GetOrCreateEntityResult<T>>
83101
*/
84-
export const getOrCreateEntity = async <
85-
TableName extends keyof Database["public"]["Tables"],
86-
>({
102+
export const getOrCreateEntity = async <TableName extends PublicTableName>({
87103
supabase,
88104
tableName,
89105
insertData,
90106
uniqueOn = undefined,
91107
}: {
92108
supabase: SupabaseClient<Database, "public">;
93-
tableName: keyof Database["public"]["Tables"];
94-
insertData: TablesInsert<TableName>;
95-
uniqueOn?: (keyof TablesInsert<TableName>)[]; // Uses pKey otherwise
96-
}): Promise<PostgrestSingleResponse<Tables<TableName>>> => {
97-
const result: PostgrestSingleResponse<Tables<TableName>> = await supabase
109+
tableName: TableName;
110+
insertData: RawTablesInsert<TableName>;
111+
uniqueOn?: RawTablesInsertKey<TableName>[];
112+
}): Promise<PostgrestSingleResponse<RawTables<TableName>>> => {
113+
const result: PostgrestSingleResponse<RawTables<TableName>> = await supabase
98114
.from(tableName)
99115
// Typescript gets confused with latest supabase
100116
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -113,7 +129,7 @@ export const getOrCreateEntity = async <
113129
if (dup_key_data !== null && dup_key_data.length > 1)
114130
uniqueOn = dup_key_data[1]!
115131
.split(",")
116-
.map((x) => x.trim()) as (keyof TablesInsert<TableName>)[];
132+
.map((x) => x.trim()) as RawTablesInsertKey<TableName>[];
117133
if (uniqueOn && uniqueOn.length > 0) {
118134
console.warn(`Attempting to re-fetch using ${uniqueOn.join(", ")}`);
119135
let reFetchQueryBuilder = supabase.from(tableName).select();
@@ -123,14 +139,21 @@ export const getOrCreateEntity = async <
123139
console.error("Empty key in uniqueOn");
124140
continue;
125141
}
126-
const keyS = String(key);
127-
reFetchQueryBuilder = reFetchQueryBuilder.eq(
128-
keyS,
129-
insertData[key] as any, // TS gets confused here?
142+
const insertEntry = Object.entries(insertData).find(
143+
([insertKey]) => insertKey === key,
144+
);
145+
if (!insertEntry) {
146+
console.error(`Missing insert data for unique key ${key}`);
147+
continue;
148+
}
149+
reFetchQueryBuilder = reFetchQueryBuilder.filter(
150+
key,
151+
"eq",
152+
insertEntry[1],
130153
);
131154
}
132155
const reFetchResult =
133-
await reFetchQueryBuilder.maybeSingle<Tables<TableName>>();
156+
await reFetchQueryBuilder.maybeSingle<RawTables<TableName>>();
134157
const { data: reFetchedEntity, error: reFetchError } = reFetchResult;
135158

136159
if (reFetchResult === null) {
@@ -172,20 +195,18 @@ export type ItemProcessor<TInput, TProcessed> = (item: TInput) => {
172195

173196
export type ItemValidator<T> = (item: T) => string | null;
174197

175-
export const InsertValidatedBatch = async <
176-
TableName extends keyof Database["public"]["Tables"],
177-
>({
198+
export const InsertValidatedBatch = async <TableName extends PublicTableName>({
178199
supabase,
179200
tableName,
180201
items,
181202
uniqueOn = undefined,
182203
}: {
183204
supabase: SupabaseClient<Database, "public">;
184-
tableName: keyof Database["public"]["Tables"];
185-
items: TablesInsert<TableName>[];
186-
uniqueOn?: (keyof TablesInsert<TableName>)[]; // Uses pKey otherwise
187-
}): Promise<PostgrestResponse<Tables<TableName>>> => {
188-
const result: PostgrestResponse<Tables<TableName>> = await supabase
205+
tableName: TableName;
206+
items: RawTablesInsert<TableName>[];
207+
uniqueOn?: RawTablesInsertKey<TableName>[];
208+
}): Promise<PostgrestResponse<RawTables<TableName>>> => {
209+
const result: PostgrestResponse<RawTables<TableName>> = await supabase
189210
.from(tableName)
190211
// Typescript gets confused with latest supabase
191212
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -217,7 +238,7 @@ export const InsertValidatedBatch = async <
217238
};
218239

219240
export const validateAndInsertBatch = async <
220-
TableName extends keyof Database["public"]["Tables"],
241+
TableName extends PublicTableName,
221242
>({
222243
supabase,
223244
tableName,
@@ -227,13 +248,13 @@ export const validateAndInsertBatch = async <
227248
outputValidator = undefined,
228249
}: {
229250
supabase: SupabaseClient<Database, "public">;
230-
tableName: keyof Database["public"]["Tables"];
231-
items: TablesInsert<TableName>[];
232-
uniqueOn?: (keyof TablesInsert<TableName>)[]; // Uses pKey otherwise
233-
inputValidator?: ItemValidator<TablesInsert<TableName>>;
234-
outputValidator?: ItemValidator<Tables<TableName>>;
235-
}): Promise<PostgrestResponse<Tables<TableName>>> => {
236-
let validatedItems: TablesInsert<TableName>[] = [];
251+
tableName: TableName;
252+
items: RawTablesInsert<TableName>[];
253+
uniqueOn?: RawTablesInsertKey<TableName>[];
254+
inputValidator?: ItemValidator<RawTablesInsert<TableName>>;
255+
outputValidator?: ItemValidator<RawTables<TableName>>;
256+
}): Promise<PostgrestResponse<RawTables<TableName>>> => {
257+
let validatedItems: RawTablesInsert<TableName>[] = [];
237258
const validationErrors: { index: number; error: string }[] = [];
238259
if (!Array.isArray(items) || items.length === 0) {
239260
return {
@@ -301,7 +322,7 @@ export const validateAndInsertBatch = async <
301322
return result;
302323
}
303324
if (outputValidator !== undefined) {
304-
const validatedResults: Tables<TableName>[] = [];
325+
const validatedResults: RawTables<TableName>[] = [];
305326
for (let i = 0; i < result.data.length; i++) {
306327
const item = result.data[i];
307328
if (!item) {
@@ -355,7 +376,7 @@ export const validateAndInsertBatch = async <
355376
};
356377

357378
export const processAndInsertBatch = async <
358-
TableName extends keyof Database["public"]["Tables"],
379+
TableName extends PublicTableName,
359380
InputType,
360381
OutputType,
361382
>({
@@ -367,13 +388,13 @@ export const processAndInsertBatch = async <
367388
outputProcessor,
368389
}: {
369390
supabase: SupabaseClient<Database, "public">;
370-
tableName: keyof Database["public"]["Tables"];
391+
tableName: TableName;
371392
items: InputType[];
372-
uniqueOn?: (keyof TablesInsert<TableName>)[]; // Uses pKey otherwise
373-
inputProcessor: ItemProcessor<InputType, TablesInsert<TableName>>;
374-
outputProcessor: ItemProcessor<Tables<TableName>, OutputType>;
393+
uniqueOn?: RawTablesInsertKey<TableName>[];
394+
inputProcessor: ItemProcessor<InputType, RawTablesInsert<TableName>>;
395+
outputProcessor: ItemProcessor<RawTables<TableName>, OutputType>;
375396
}): Promise<PostgrestResponse<OutputType>> => {
376-
const processedItems: TablesInsert<TableName>[] = [];
397+
const processedItems: RawTablesInsert<TableName>[] = [];
377398
const validationErrors: { index: number; error: string }[] = [];
378399
if (!Array.isArray(items) || items.length === 0) {
379400
return {

0 commit comments

Comments
 (0)