UNPKG

@flowlab/all

Version:

A cool library focusing on handling various flows

80 lines (72 loc) 4.17 kB
// src/loaders/prismaLoader.ts import { ILoader, PipelineContext, DatabaseTargetConfig } from '../core/interfaces'; import { PrismaClient } from '@prisma/client'; interface PrismaTargetConfig extends DatabaseTargetConfig { type: 'postgresql' | 'mysql'; connection: PrismaClient; model: string; operation?: 'create' | 'upsert' | 'update'; // Default to 'create' upsertWhereField?: string; // Field to use for the 'where' in upsert (e.g. 'id' or 'email') // Add batch transaction options if needed } // MARK: - PrismaLoader export class PrismaLoader<TInput extends object> implements ILoader<TInput> { private config: PrismaTargetConfig; constructor(config: PrismaTargetConfig) { if (!config.connection || !config.model) { throw new Error('PrismaLoader requires "connection" (PrismaClient instance) and "model" in config.'); } this.config = config; this.config.operation = config.operation || 'create'; if (this.config.operation === 'upsert' && !this.config.upsertWhereField) { throw new Error("PrismaLoader with 'upsert' operation requires 'upsertWhereField' config."); } } // MARK: - loadBatch async loadBatch(batch: TInput[], context: PipelineContext): Promise<void> { if (batch.length === 0) return; // Nothing to load const modelDelegate = (this.config.connection as any)[this.config.model]; if (!modelDelegate) { throw new Error(`Prisma model "${this.config.model}" not found on the provided client.`); } context.logger.debug(`Loading batch of ${batch.length} items to Prisma model ${this.config.model} using operation ${this.config.operation}`); try { // Prisma's createMany is often faster for simple inserts if (this.config.operation === 'create') { const result = await modelDelegate.createMany({ data: batch, skipDuplicates: true, // Optional: ignore errors if a record already exists (depends on constraints) }); context.logger.info(`Prisma createMany inserted ${result.count} records.`); } else if (this.config.operation === 'upsert') { // Upsert needs to be done one by one or in a transaction // Using transaction for potentially better performance const upsertPromises = batch.map(item => { const whereValue = (item as any)[this.config.upsertWhereField!]; if(whereValue === undefined || whereValue === null) { context.logger.warn({ item, field: this.config.upsertWhereField }, `Skipping upsert: where field '${this.config.upsertWhereField}' is missing or null.`); return Promise.resolve(); // Skip this item } return modelDelegate.upsert({ where: { [this.config.upsertWhereField!]: whereValue }, update: item, // Update with the full item create: item, // Create with the full item }); }); // Execute upserts sequentially for now, batch transaction is better // await this.config.connection.$transaction(upsertPromises); // Requires Prisma preview feature or check version for (const promise of upsertPromises) { // Sequential execution await promise; } context.logger.info(`Prisma upsert processed ${batch.length} records.`); } else { // TODO: Implement 'update' if needed (likely requires a unique key in each item) throw new Error(`PrismaLoader operation '${this.config.operation}' not yet implemented.`); } } catch (error) { context.logger.error({ err: error }, `Error loading batch to Prisma model ${this.config.model}`); // Handle batch errors - potentially log failed items? throw error; // Rethrow to be caught by retry logic } } }