mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 04:31:13 -05:00
Refactor: Approve test data generator tool use
This commit is contained in:
@@ -181,6 +181,39 @@ export function TestDataGenerator() {
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Dependency warnings */}
|
||||
{entityTypes.rides && stats.parks === 0 && !entityTypes.parks && (
|
||||
<Alert variant="destructive">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
<strong>Missing Dependency:</strong> You've selected "rides" but no parks exist.
|
||||
Rides require parks to be created first. Either enable "parks" or generate parks separately first.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{entityTypes.ride_models && stats.manufacturers === 0 && !entityTypes.manufacturers && (
|
||||
<Alert variant="destructive">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
<strong>Missing Dependency:</strong> You've selected "ride models" but no manufacturers exist.
|
||||
Ride models require manufacturers to be created first. Either enable "manufacturers" or generate manufacturers separately first.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{(entityTypes.parks || entityTypes.rides) &&
|
||||
stats.operators === 0 && stats.property_owners === 0 &&
|
||||
!entityTypes.operators && !entityTypes.property_owners && (
|
||||
<Alert>
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
<strong>Optional Dependencies:</strong> Parks and rides can optionally link to operators and property owners.
|
||||
Consider enabling "operators" and "property_owners" for more realistic test data with proper dependency chains.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -2288,6 +2288,7 @@ export type Database = {
|
||||
entity_type: string
|
||||
id: string
|
||||
metadata: Json | null
|
||||
submission_item_id: string | null
|
||||
test_session_id: string | null
|
||||
}
|
||||
Insert: {
|
||||
@@ -2297,6 +2298,7 @@ export type Database = {
|
||||
entity_type: string
|
||||
id?: string
|
||||
metadata?: Json | null
|
||||
submission_item_id?: string | null
|
||||
test_session_id?: string | null
|
||||
}
|
||||
Update: {
|
||||
@@ -2306,9 +2308,18 @@ export type Database = {
|
||||
entity_type?: string
|
||||
id?: string
|
||||
metadata?: Json | null
|
||||
submission_item_id?: string | null
|
||||
test_session_id?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "test_data_registry_submission_item_id_fkey"
|
||||
columns: ["submission_item_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "submission_items"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
user_blocks: {
|
||||
Row: {
|
||||
|
||||
@@ -76,12 +76,14 @@ async function registerTestEntity(
|
||||
entityType: string,
|
||||
slug: string,
|
||||
entityId: string,
|
||||
submissionItemId: string,
|
||||
sessionId: string
|
||||
) {
|
||||
const { error } = await supabase.from('test_data_registry').insert({
|
||||
entity_type: entityType,
|
||||
entity_slug: slug,
|
||||
entity_id: entityId,
|
||||
submission_item_id: submissionItemId,
|
||||
test_session_id: sessionId
|
||||
});
|
||||
|
||||
@@ -93,10 +95,10 @@ async function registerTestEntity(
|
||||
async function getExistingTestEntities(
|
||||
supabase: any,
|
||||
entityType: string
|
||||
): Promise<Array<{ slug: string; entity_id: string }>> {
|
||||
): Promise<Array<{ slug: string; entity_id: string; submission_item_id: string }>> {
|
||||
const { data, error } = await supabase
|
||||
.from('test_data_registry')
|
||||
.select('entity_slug, entity_id')
|
||||
.select('entity_slug, entity_id, submission_item_id')
|
||||
.eq('entity_type', entityType);
|
||||
|
||||
if (error) {
|
||||
@@ -107,6 +109,24 @@ async function getExistingTestEntities(
|
||||
return data || [];
|
||||
}
|
||||
|
||||
async function getPendingSubmissionItems(
|
||||
supabase: any,
|
||||
itemType: string
|
||||
): Promise<Array<{ id: string; item_data: any }>> {
|
||||
const { data, error } = await supabase
|
||||
.from('submission_items')
|
||||
.select('id, item_data')
|
||||
.eq('item_type', itemType)
|
||||
.eq('status', 'pending');
|
||||
|
||||
if (error) {
|
||||
console.error(`Error fetching pending ${itemType} items:`, error);
|
||||
return [];
|
||||
}
|
||||
|
||||
return data || [];
|
||||
}
|
||||
|
||||
Deno.serve(async (req) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
return new Response(null, { headers: corsHeaders });
|
||||
@@ -165,6 +185,17 @@ Deno.serve(async (req) => {
|
||||
const startTime = Date.now();
|
||||
const sessionId = crypto.randomUUID(); // Unique ID for this generation session
|
||||
const summary = { parks: 0, rides: 0, companies: 0, rideModels: 0, photos: 0, totalPhotoItems: 0, conflicts: 0, versionChains: 0 };
|
||||
|
||||
// Track submission item IDs for dependency resolution
|
||||
const createdSubmissionItems: Record<string, string[]> = {
|
||||
operator: [],
|
||||
property_owner: [],
|
||||
manufacturer: [],
|
||||
designer: [],
|
||||
park: [],
|
||||
ride_model: []
|
||||
};
|
||||
|
||||
const createdParks: string[] = [];
|
||||
const createdCompanies: Record<string, string[]> = { manufacturer: [], operator: [], designer: [], property_owner: [] };
|
||||
const createdParkSlugs: string[] = [];
|
||||
@@ -181,20 +212,56 @@ Deno.serve(async (req) => {
|
||||
const existingParks = await getExistingTestEntities(supabase, 'park');
|
||||
const existingRideModels = await getExistingTestEntities(supabase, 'ride_model');
|
||||
|
||||
existingOperators.forEach(op => createdCompanies.operator.push(op.slug));
|
||||
existingOwners.forEach(own => createdCompanies.property_owner.push(own.slug));
|
||||
existingManufacturers.forEach(mfg => createdCompanies.manufacturer.push(mfg.slug));
|
||||
existingDesigners.forEach(des => createdCompanies.designer.push(des.slug));
|
||||
// Load pending submission items for dependency resolution
|
||||
const pendingOperators = await getPendingSubmissionItems(supabase, 'operator');
|
||||
const pendingOwners = await getPendingSubmissionItems(supabase, 'property_owner');
|
||||
const pendingManufacturers = await getPendingSubmissionItems(supabase, 'manufacturer');
|
||||
const pendingDesigners = await getPendingSubmissionItems(supabase, 'designer');
|
||||
const pendingParks = await getPendingSubmissionItems(supabase, 'park');
|
||||
const pendingRideModels = await getPendingSubmissionItems(supabase, 'ride_model');
|
||||
|
||||
// Track both approved and pending items
|
||||
existingOperators.forEach(op => {
|
||||
createdCompanies.operator.push(op.slug);
|
||||
if (op.submission_item_id) createdSubmissionItems.operator.push(op.submission_item_id);
|
||||
});
|
||||
existingOwners.forEach(own => {
|
||||
createdCompanies.property_owner.push(own.slug);
|
||||
if (own.submission_item_id) createdSubmissionItems.property_owner.push(own.submission_item_id);
|
||||
});
|
||||
existingManufacturers.forEach(mfg => {
|
||||
createdCompanies.manufacturer.push(mfg.slug);
|
||||
if (mfg.submission_item_id) createdSubmissionItems.manufacturer.push(mfg.submission_item_id);
|
||||
});
|
||||
existingDesigners.forEach(des => {
|
||||
createdCompanies.designer.push(des.slug);
|
||||
if (des.submission_item_id) createdSubmissionItems.designer.push(des.submission_item_id);
|
||||
});
|
||||
existingParks.forEach(park => {
|
||||
createdParks.push(park.slug);
|
||||
createdParkSlugs.push(park.slug);
|
||||
if (park.submission_item_id) createdSubmissionItems.park.push(park.submission_item_id);
|
||||
});
|
||||
|
||||
// Add pending items to tracking
|
||||
pendingOperators.forEach(item => createdSubmissionItems.operator.push(item.id));
|
||||
pendingOwners.forEach(item => createdSubmissionItems.property_owner.push(item.id));
|
||||
pendingManufacturers.forEach(item => createdSubmissionItems.manufacturer.push(item.id));
|
||||
pendingDesigners.forEach(item => createdSubmissionItems.designer.push(item.id));
|
||||
pendingParks.forEach(item => createdSubmissionItems.park.push(item.id));
|
||||
pendingRideModels.forEach(item => createdSubmissionItems.ride_model.push(item.id));
|
||||
|
||||
console.log(`Loaded ${existingOperators.length} operators, ${existingOwners.length} owners, ${existingManufacturers.length} manufacturers, ${existingDesigners.length} designers, ${existingParks.length} parks`);
|
||||
console.log(`Loaded ${pendingOperators.length} pending operators, ${pendingOwners.length} pending owners, ${pendingManufacturers.length} pending manufacturers, ${pendingDesigners.length} pending designers, ${pendingParks.length} pending parks`);
|
||||
}
|
||||
|
||||
// Helper to create submission
|
||||
async function createSubmission(userId: string, type: string, itemData: any, options: { escalated?: boolean; expiredLock?: boolean } = {}) {
|
||||
async function createSubmission(
|
||||
userId: string,
|
||||
type: string,
|
||||
itemData: any,
|
||||
options: { escalated?: boolean; expiredLock?: boolean; dependsOn?: string } = {}
|
||||
) {
|
||||
const submissionId = crypto.randomUUID();
|
||||
const itemId = crypto.randomUUID();
|
||||
|
||||
@@ -237,7 +304,8 @@ Deno.serve(async (req) => {
|
||||
item_type: type,
|
||||
item_data: itemData,
|
||||
status: 'pending',
|
||||
order_index: 0
|
||||
order_index: 0,
|
||||
depends_on: options.dependsOn || null
|
||||
});
|
||||
if (itemError) throw itemError;
|
||||
|
||||
@@ -260,10 +328,10 @@ Deno.serve(async (req) => {
|
||||
|
||||
const { data: insertedData, error: typeError } = await supabase.from(table).insert(typeData).select('id').single();
|
||||
if (typeError) throw typeError;
|
||||
return { submissionId, typeId: insertedData?.id };
|
||||
return { submissionId, itemId, typeId: insertedData?.id };
|
||||
}
|
||||
|
||||
return { submissionId, typeId: null };
|
||||
return { submissionId, itemId, typeId: null };
|
||||
}
|
||||
|
||||
// Create parks
|
||||
@@ -299,9 +367,16 @@ Deno.serve(async (req) => {
|
||||
parkData.phone = `+1-555-${randomInt(100, 999)}-${randomInt(1000, 9999)}`;
|
||||
}
|
||||
|
||||
let operatorItemId: string | undefined;
|
||||
let ownerItemId: string | undefined;
|
||||
|
||||
if (level >= 3 && createdCompanies.operator.length > 0) {
|
||||
const { data: operatorData } = await supabase.from('companies').select('id').eq('slug', randomItem(createdCompanies.operator)).maybeSingle();
|
||||
if (operatorData) parkData.operator_id = operatorData.id;
|
||||
// Track operator dependency
|
||||
if (createdSubmissionItems.operator.length > 0) {
|
||||
operatorItemId = randomItem(createdSubmissionItems.operator);
|
||||
}
|
||||
parkData.email = `info@test-park-${i + 1}.example.com`;
|
||||
parkData.card_image_id = `test-park-card-${i + 1}`;
|
||||
parkData.card_image_url = `https://imagedelivery.net/test/park-${i + 1}/card`;
|
||||
@@ -311,6 +386,10 @@ Deno.serve(async (req) => {
|
||||
if (createdCompanies.property_owner.length > 0) {
|
||||
const { data: ownerData } = await supabase.from('companies').select('id').eq('slug', randomItem(createdCompanies.property_owner)).maybeSingle();
|
||||
if (ownerData) parkData.property_owner_id = ownerData.id;
|
||||
// Track owner dependency (prefer operator if both exist)
|
||||
if (createdSubmissionItems.property_owner.length > 0 && !operatorItemId) {
|
||||
ownerItemId = randomItem(createdSubmissionItems.property_owner);
|
||||
}
|
||||
}
|
||||
if (Math.random() > 0.9) {
|
||||
parkData.closing_date = randomDate(2000, 2024);
|
||||
@@ -322,10 +401,14 @@ Deno.serve(async (req) => {
|
||||
|
||||
const options = {
|
||||
escalated: includeEscalated && Math.random() < 0.1,
|
||||
expiredLock: includeExpiredLocks && Math.random() < 0.1
|
||||
expiredLock: includeExpiredLocks && Math.random() < 0.1,
|
||||
dependsOn: operatorItemId || ownerItemId
|
||||
};
|
||||
|
||||
await createSubmission(user.id, 'park', parkData, options);
|
||||
const { itemId } = await createSubmission(user.id, 'park', parkData, options);
|
||||
|
||||
// Track created submission item
|
||||
createdSubmissionItems.park.push(itemId);
|
||||
|
||||
// Register newly created park in the registry
|
||||
const { data: approvedPark } = await supabase
|
||||
@@ -335,7 +418,7 @@ Deno.serve(async (req) => {
|
||||
.maybeSingle();
|
||||
|
||||
if (approvedPark) {
|
||||
await registerTestEntity(supabase, 'park', slug, approvedPark.id, sessionId);
|
||||
await registerTestEntity(supabase, 'park', slug, approvedPark.id, itemId, sessionId);
|
||||
}
|
||||
|
||||
createdParks.push(slug);
|
||||
@@ -382,7 +465,10 @@ Deno.serve(async (req) => {
|
||||
companyData.banner_image_url = `https://imagedelivery.net/test/${compType}-${i + 1}/banner`;
|
||||
}
|
||||
|
||||
await createSubmission(user.id, compType, companyData);
|
||||
const { itemId } = await createSubmission(user.id, compType, companyData);
|
||||
|
||||
// Track created submission item
|
||||
createdSubmissionItems[compType].push(itemId);
|
||||
|
||||
// Register newly created company in the registry
|
||||
const companySlug = `test-${compType}-${i + 1}`;
|
||||
@@ -393,7 +479,7 @@ Deno.serve(async (req) => {
|
||||
.maybeSingle();
|
||||
|
||||
if (approvedCompany) {
|
||||
await registerTestEntity(supabase, compType, companySlug, approvedCompany.id, sessionId);
|
||||
await registerTestEntity(supabase, compType, companySlug, approvedCompany.id, itemId, sessionId);
|
||||
}
|
||||
|
||||
createdCompanies[compType].push(companySlug);
|
||||
@@ -420,6 +506,9 @@ Deno.serve(async (req) => {
|
||||
|
||||
const parkSlug = randomItem(createdParks);
|
||||
const { data: parkData } = await supabase.from('parks').select('id').eq('slug', parkSlug).maybeSingle();
|
||||
|
||||
// Get park dependency for depends_on
|
||||
const parkItemId = createdSubmissionItems.park.length > 0 ? randomItem(createdSubmissionItems.park) : undefined;
|
||||
|
||||
const category = randomItem(['roller_coaster', 'flat_ride', 'water_ride', 'dark_ride']);
|
||||
const rideData: any = {
|
||||
@@ -470,7 +559,11 @@ Deno.serve(async (req) => {
|
||||
rideData.banner_image_url = `https://imagedelivery.net/test/ride-${i + 1}/banner`;
|
||||
}
|
||||
|
||||
const { submissionId, typeId } = await createSubmission(user.id, 'ride', rideData);
|
||||
const options = {
|
||||
dependsOn: parkItemId
|
||||
};
|
||||
|
||||
const { submissionId, itemId, typeId } = await createSubmission(user.id, 'ride', rideData, options);
|
||||
|
||||
// Add technical specs and stats for level 4
|
||||
if (level >= 4 && typeId && category === 'roller_coaster') {
|
||||
@@ -516,7 +609,7 @@ Deno.serve(async (req) => {
|
||||
.maybeSingle();
|
||||
|
||||
if (approvedRide) {
|
||||
await registerTestEntity(supabase, 'ride', slug, approvedRide.id, sessionId);
|
||||
await registerTestEntity(supabase, 'ride', slug, approvedRide.id, itemId, sessionId);
|
||||
}
|
||||
|
||||
if (!shouldConflict && !shouldVersionChain) {
|
||||
@@ -531,6 +624,9 @@ Deno.serve(async (req) => {
|
||||
for (let i = 0; i < plan.rideModels; i++) {
|
||||
const level = getPopulationLevel(fieldDensity, i);
|
||||
const { data: mfgData } = await supabase.from('companies').select('id').eq('slug', randomItem(createdCompanies.manufacturer)).maybeSingle();
|
||||
|
||||
// Get manufacturer dependency for depends_on
|
||||
const mfgItemId = createdSubmissionItems.manufacturer.length > 0 ? randomItem(createdSubmissionItems.manufacturer) : undefined;
|
||||
|
||||
const category = randomItem(['roller_coaster', 'flat_ride', 'water_ride']);
|
||||
const modelData: any = {
|
||||
@@ -555,7 +651,14 @@ Deno.serve(async (req) => {
|
||||
modelData.banner_image_url = `https://imagedelivery.net/test/model-${i + 1}/banner`;
|
||||
}
|
||||
|
||||
await createSubmission(user.id, 'ride_model', modelData);
|
||||
const options = {
|
||||
dependsOn: mfgItemId
|
||||
};
|
||||
|
||||
const { itemId } = await createSubmission(user.id, 'ride_model', modelData, options);
|
||||
|
||||
// Track created submission item
|
||||
createdSubmissionItems.ride_model.push(itemId);
|
||||
|
||||
// Register newly created ride model in the registry
|
||||
const modelSlug = `test-model-${i + 1}`;
|
||||
@@ -566,7 +669,7 @@ Deno.serve(async (req) => {
|
||||
.maybeSingle();
|
||||
|
||||
if (approvedModel) {
|
||||
await registerTestEntity(supabase, 'ride_model', modelSlug, approvedModel.id, sessionId);
|
||||
await registerTestEntity(supabase, 'ride_model', modelSlug, approvedModel.id, itemId, sessionId);
|
||||
}
|
||||
|
||||
summary.rideModels++;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
-- Add submission_item_id to test_data_registry for proper dependency tracking
|
||||
ALTER TABLE test_data_registry
|
||||
ADD COLUMN IF NOT EXISTS submission_item_id UUID REFERENCES submission_items(id) ON DELETE CASCADE;
|
||||
|
||||
-- Add index for fast lookups
|
||||
CREATE INDEX IF NOT EXISTS idx_test_data_registry_item_id
|
||||
ON test_data_registry(submission_item_id);
|
||||
|
||||
-- Update existing records to have NULL submission_item_id (for backwards compatibility)
|
||||
-- New records will populate this field
|
||||
Reference in New Issue
Block a user