iagate-querykit
Version:
QueryKit: lightweight TypeScript query toolkit with models, views, triggers, events, scheduler and adapters (better-sqlite3).
114 lines (113 loc) • 3.55 kB
JavaScript
import { getExecutorForTable } from './config';
import { QueryBuilder } from './query-builder';
/**
* Classe base para seeds customizadas.
* Implementa SeedRunnable com comportamento padrão vazio.
*
* @example
* ```typescript
* // Dados iniciais
* class ProductSeed extends Seed<Product> {
* async run(ctx: SeedContext): Promise<Partial<Product>[]> {
* return [
* { name: 'Product A', price: 100 },
* { name: 'Product B', price: 200 }
* ];
* }
* }
*
* // Como usar
* await runSeed('products', new ProductSeed());
*
* // Output: Produtos inseridos no banco de dados
* ```
*/
export class Seed {
/**
* Método padrão que retorna array vazio.
* Deve ser sobrescrito em classes filhas.
*
* @param _ctx - Contexto da execução (não usado na implementação padrão)
* @returns Array vazio
*/
async run(_ctx) { return []; }
}
/**
* Executa um seed em uma tabela específica.
* Suporta dados diretos ou classes SeedRunnable.
* Oferece opções para truncate, upsert e tratamento de duplicatas.
*
* @param table - Nome da tabela para executar o seed
* @param dataOrSeed - Dados para inserir ou classe seed executável
* @param opts - Opções de execução
* @returns Promise que resolve com número de linhas inseridas
* @throws Error se não houver executor configurado
*
* @example
* ```typescript
* // Dados iniciais
* const userData = [
* { name: 'John Doe', email: 'john@example.com' },
* { name: 'Jane Smith', email: 'jane@example.com' }
* ];
*
* // Como usar
* const insertedRows = await runSeed('users', userData, {
* truncate: true,
* uniqueBy: ['email']
* });
*
* // Output: 2 (número de usuários inseridos)
* ```
*/
export async function runSeed(table, dataOrSeed, opts = {}) {
const exec = getExecutorForTable(table);
if (!exec)
throw new Error('No executor configured for QueryKit');
if (opts.truncate) {
if (exec.runSync)
exec.runSync(`DELETE FROM ${table}`, []);
else
await exec.executeQuery(`DELETE FROM ${table}`, []);
}
let rows;
if (Array.isArray(dataOrSeed))
rows = dataOrSeed;
else {
const ctx = { exec, qb: (t) => new QueryBuilder(t) };
const out = await dataOrSeed.run(ctx);
rows = out || [];
}
if (!rows.length)
return 0;
// batch insert with optional conflict handling
const uniqueKeys = (opts.uniqueBy || []);
const shouldUpsert = !!opts.upsert;
const shouldIgnore = !!opts.ignoreDuplicates;
for (const row of rows) {
if (uniqueKeys.length && shouldIgnore) {
const attributes = {};
for (const key of uniqueKeys)
attributes[String(key)] = row[String(key)];
const exists = await new QueryBuilder(table).whereAll(attributes).exists();
if (exists)
continue;
await new QueryBuilder(table).insert(row).make();
continue;
}
if (uniqueKeys.length && shouldUpsert) {
const attributes = {};
const values = {};
for (const [k, v] of Object.entries(row)) {
if (uniqueKeys.includes(k))
attributes[k] = v;
else
values[k] = v;
}
await new QueryBuilder(table).updateOrInsert(attributes, values).make();
continue;
}
await new QueryBuilder(table).insert(row).make();
}
return rows.length;
}