UNPKG

supa-seed

Version:

A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support

301 lines 13.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseDataSeeder = void 0; const base_seeder_1 = require("./base-seeder"); const logger_1 = require("../../../core/utils/logger"); const domains_1 = require("../../../domains"); const table_mapping_resolver_1 = require("../../../schema/table-mapping-resolver"); class BaseDataSeeder extends base_seeder_1.BaseSeeder { /** * Get the correct table name for base templates using dynamic mapping */ async getBaseTemplateTableName() { try { const framework = this.context.config.schema?.framework || 'makerkit'; const resolver = (0, table_mapping_resolver_1.createTableMappingResolver)(this.context.client, { framework, validateWithDatabase: true }); const result = await resolver.resolveTableName('setup_types', 'baseTemplateTable'); if (result.warnings.length > 0) { logger_1.Logger.warn('⚠️ Base template table mapping warnings:', result.warnings); } logger_1.Logger.debug(`🗺️ Base template table resolved: setup_types -> ${result.actualTableName}`, { framework, exists: result.exists, source: result.source }); return result.actualTableName; } catch (error) { logger_1.Logger.warn(`⚠️ Failed to resolve base template table name, using fallback 'base_templates':`, error); return 'base_templates'; } } /** * Check if a column exists in a table (helper method) */ async columnExists(tableName, columnName) { try { const { error } = await this.context.client .from(tableName) .select(columnName) .limit(1); return !error; } catch { return false; } } /** * Get the correct column name for a field, checking config and existence */ async getColumnMapping(tableName, expectedField, fallbacks = []) { // First check if config override specifies a mapping const configMapping = this.getConfigColumnMapping(tableName, expectedField); if (configMapping && await this.columnExists(tableName, configMapping)) { return configMapping; } // Fall back to checking all options const allOptions = [expectedField, ...fallbacks]; for (const option of allOptions) { if (await this.columnExists(tableName, option)) { return option; } } return null; } /** * Get column mapping from configuration with enhanced support */ getConfigColumnMapping(tableName, expectedField) { if (!this.context.config?.schema) return null; // Handle base_templates table mappings if (tableName === 'base_templates' && this.context.config.schema?.baseTemplateTable) { const table = this.context.config.schema.baseTemplateTable; switch (expectedField) { case 'description': return table.descriptionField || null; case 'type': return table.typeField || null; case 'make': return table.makeField || null; case 'model': return table.modelField || null; case 'year': return table.yearField || null; } } // Handle profiles table mappings if (tableName === 'profiles' && this.context.config.schema?.userTable) { const table = this.context.config.schema.userTable; switch (expectedField) { case 'picture_url': case 'avatar_url': return table.pictureField || null; case 'bio': return table.bioField || null; case 'name': case 'display_name': return table.nameField || null; } } return null; } async seed() { console.log('🗂️ Seeding base data...'); await this.seedCategories(); await this.seedBaseTemplates(); console.log('✅ Base data seeding complete'); } async seedCategories() { const domainConfig = (0, domains_1.getDomainConfig)(this.context.config.domain); const categories = domainConfig.categories; await this.seedWithFallback(async () => { const { client } = this.context; const { data: existingCategories, error: selectError } = await client .from('categories') .select('name'); if (selectError) { throw new Error(`Failed to check existing categories: ${selectError.message}`); } const existingNames = new Set(existingCategories?.map(c => c.name) || []); const newCategories = categories.filter(cat => !existingNames.has(cat.name)); if (newCategories.length > 0) { const { error: insertError } = await client .from('categories') .insert(newCategories); if (insertError) { throw insertError; } logger_1.Logger.complete(`Created ${newCategories.length} categories`); } else { logger_1.Logger.info('Categories already exist, skipping'); } }, 'categories', 'Categories are optional for basic seeding'); } async seedBaseTemplates() { // Get schema adapter to check column existence const schemaAdapter = this.context.cache.get('schemaAdapter'); // Get the correct table name for base templates const baseTemplateTableName = await this.getBaseTemplateTableName(); // Check what description column to use based on config and existence const descriptionColumn = await this.getColumnMapping(baseTemplateTableName, 'description', ['info', 'details', 'notes']); const hasDescriptionColumn = descriptionColumn !== null; const baseTemplateData = [ // Vehicle Templates { type: 'Vehicle', make: 'Toyota', model: 'Tacoma', year: 2023, description: 'Mid-size pickup truck popular for overlanding' }, { type: 'Vehicle', make: 'Toyota', model: '4Runner', year: 2023, description: 'Full-size SUV with excellent off-road capability' }, { type: 'Vehicle', make: 'Jeep', model: 'Wrangler', year: 2023, description: 'Iconic 4x4 vehicle for trail adventures' }, { type: 'Vehicle', make: 'Ford', model: 'Bronco', year: 2023, description: 'Modern off-road SUV with classic heritage' }, { type: 'Vehicle', make: 'Subaru', model: 'Outback', year: 2023, description: 'All-wheel drive wagon for car camping' }, // Backpack Templates { type: 'Backpack', make: 'Osprey', model: 'Atmos AG 65', description: 'Lightweight backpacking pack with anti-gravity suspension' }, { type: 'Backpack', make: 'Gregory', model: 'Baltoro 65', description: 'Traditional backpacking pack with excellent load support' }, { type: 'Backpack', make: 'Hyperlite Mountain Gear', model: 'Southwest 55', description: 'Ultralight backpacking pack for minimalist hiking' }, { type: 'Backpack', make: 'Kelty', model: 'Coyote 65', description: 'Affordable, durable pack for weekend adventures' }, { type: 'Backpack', make: 'Deuter', model: 'Aircontact Lite 65+10', description: 'European-style pack with excellent ventilation' }, ]; // Use correct description column or remove if none exists const templates = hasDescriptionColumn ? baseTemplateData.map(template => { if (descriptionColumn !== 'description') { // Rename description field to match actual column const { description, ...rest } = template; return { ...rest, [descriptionColumn]: description }; } return template; }) : baseTemplateData.map(({ description, ...rest }) => rest); await this.seedWithFallback(async () => { const { client } = this.context; const baseTemplateTableName = await this.getBaseTemplateTableName(); const { data: existingTemplates, error: selectError } = await client .from(baseTemplateTableName) .select('make, model, type'); if (selectError) { throw new Error(`Failed to check existing templates: ${selectError.message}`); } const existingKeys = new Set(existingTemplates?.map(t => `${t.type}-${t.make}-${t.model}`) || []); const newTemplates = templates.filter(template => !existingKeys.has(`${template.type}-${template.make}-${template.model}`)); if (newTemplates.length > 0) { const { error: insertError } = await client .from(baseTemplateTableName) .insert(newTemplates); if (insertError) { // Provide better error message for column issues const errorMsg = insertError.message.includes('column') && !hasDescriptionColumn ? `Base template creation failed: description column not found. ${insertError.message}` : insertError.message; throw new Error(errorMsg); } const msg = hasDescriptionColumn ? `Created ${newTemplates.length} base templates` : `Created ${newTemplates.length} base templates (without descriptions - column not found)`; logger_1.Logger.complete(msg); } else { logger_1.Logger.info('Base templates already exist, skipping'); } // Cache templates for other seeders const { data: allTemplates } = await client .from(baseTemplateTableName) .select('*'); this.context.cache.set('baseTemplates', allTemplates || []); }, await this.getBaseTemplateTableName(), 'Base templates are optional for basic seeding'); // Ensure cache has at least empty array if table doesn't exist if (!this.context.cache.has('baseTemplates')) { this.context.cache.set('baseTemplates', []); } } /** * Legacy function for backward compatibility * @deprecated Use seedCategories instead */ async seedGearCategories() { const categories = [ { name: 'Shelter', description: 'Tents, tarps, and protective gear' }, { name: 'Sleep System', description: 'Sleeping bags, pads, and comfort items' }, { name: 'Cooking', description: 'Stoves, cookware, and food preparation' }, { name: 'Navigation', description: 'Maps, compass, GPS, and wayfinding tools' }, { name: 'Safety', description: 'First aid, emergency, and safety equipment' }, { name: 'Clothing', description: 'Base layers, shells, and outdoor apparel' }, { name: 'Electronics', description: 'Lights, batteries, communication devices' }, { name: 'Tools', description: 'Knives, multi-tools, and utility items' }, { name: 'Hydration', description: 'Water bottles, filters, and treatment' }, { name: 'Vehicle', description: 'Overland and camping vehicle modifications' }, ]; // Use the main seedCategories logic but with hardcoded categories await this.seedWithFallback(async () => { const { client } = this.context; const { data: existingCategories, error: selectError } = await client .from('categories') .select('name'); if (selectError) { throw new Error(`Failed to check existing categories: ${selectError.message}`); } const existingNames = new Set(existingCategories?.map(c => c.name) || []); const newCategories = categories.filter(cat => !existingNames.has(cat.name)); if (newCategories.length > 0) { const { error: insertError } = await client.from('categories').insert(newCategories); if (insertError) throw insertError; logger_1.Logger.complete(`Created ${newCategories.length} categories`); } else { logger_1.Logger.info('Categories already exist, skipping'); } }, 'categories', 'Categories are optional for basic seeding'); } } exports.BaseDataSeeder = BaseDataSeeder; //# sourceMappingURL=base-data-seeder.js.map