import { z } from "zod";
import { strapiClient } from "../services/strapi-client.js";
import {
detectLocalizations,
validateLocalizationsAgainstAvailable,
} from "../services/i18n-detector.js";
export const batchCreateToolSchema = z.object({
contentType: z
.string()
.describe('Nombre PLURAL del content type (ej: "products", "articles")'),
entries: z
.array(
z.object({
data: z
.record(z.any())
.describe(
"Datos de la entrada a crear. IMPORTANTE para campos media: usar ID numérico (no documentId). " +
"Ejemplo: image: 3 (no image: 'uka5h08v4osgn1w0g89m43z8'). " +
"Obtén IDs numéricos de strapi-search-media o strapi-get-media."
),
populate: z
.array(z.string())
.optional()
.describe("Relaciones a poblar"),
locale: z
.string()
.optional()
.describe(
'Locale para crear la entrada en un idioma específico (ej: "en", "es-ES", "ca")'
),
})
)
.min(1)
.describe(
"Array de entradas a crear. Cada entrada contiene data, populate (opcional), y locale (opcional)"
),
continueOnError: z
.boolean()
.optional()
.default(true)
.describe(
"Continuar creando otras entradas si alguna falla (default: true)"
),
});
export const batchCreateToolHandler = async (params: {
contentType: string;
entries: Array<{
data: Record<string, any>;
populate?: string[];
locale?: string;
}>;
continueOnError?: boolean;
}) => {
try {
console.error(
`[BATCH CREATE TOOL] Creating ${params.entries.length} entries in ${params.contentType}`
);
// Get available locales to check for i18n support
let availableLocales: string[] = [];
let i18nEnabled = false;
try {
const localesResponse = await strapiClient.getI18nLocales();
if (localesResponse.data && Array.isArray(localesResponse.data)) {
availableLocales = localesResponse.data.map((locale: any) => locale.code);
i18nEnabled = availableLocales.length > 0;
console.error(`[BATCH CREATE TOOL] i18n enabled with locales: ${availableLocales.join(", ")}`);
}
} catch (error) {
console.error(`[BATCH CREATE TOOL] Could not fetch i18n locales, continuing without i18n support`);
}
const results = {
total: params.entries.length,
successful: 0,
failed: 0,
entries: [] as Array<{
index: number;
success: boolean;
documentId?: string;
localized?: boolean;
localizations?: string[];
error?: string;
}>,
};
// Procesar en paralelo
const promises = params.entries.map((entry, index) =>
(async () => {
try {
// Check if this entry has localized fields
const detection = detectLocalizations(entry.data);
let result: any;
let localized = false;
let detectedLocalizations: string[] = [];
if (detection.hasLocalizations && i18nEnabled && !entry.locale) {
// Auto-detect and create with localizations using create-with-locales
console.error(
`[BATCH CREATE TOOL] Entry ${index + 1} has localized fields: ${
detection.localizedFields.map((f) => f.fieldName).join(", ")
}`
);
// Validate detected locales against available ones
const validation = validateLocalizationsAgainstAvailable(
detection,
availableLocales
);
if (!validation.valid) {
console.error(
`[BATCH CREATE TOOL WARNING] Entry ${index + 1}: ${validation.warnings.join("; ")}`
);
}
detectedLocalizations = detection.localizations.map((l) => l.locale);
// Use create-with-locales from strapiClient
result = await strapiClient.createWithLocales({
contentType: params.contentType,
defaultLocale: detection.defaultLocale,
data: detection.baseData,
localizations: detection.localizations,
populate: entry.populate,
});
localized = true;
console.error(
`[BATCH CREATE TOOL] Entry ${index + 1} created with ${
detection.localizations.length
} localization(s): ${detectedLocalizations.join(", ")}`
);
} else {
// Regular create (no localized fields or explicit locale specified)
result = await strapiClient.create({
contentType: params.contentType,
data: entry.data,
populate: entry.populate,
locale: entry.locale,
});
console.error(
`[BATCH CREATE TOOL] Creating entry ${index + 1}/${
params.entries.length
}${entry.locale ? ` (locale: ${entry.locale})` : ""}`
);
}
results.successful++;
results.entries.push({
index,
success: true,
documentId: result.data.documentId || result.data.id,
localized,
localizations: localized ? detectedLocalizations : undefined,
});
console.error(
`[BATCH CREATE TOOL] Entry ${index + 1} created successfully: ${
result.data.documentId || result.data.id
}${localized ? ` (with ${detectedLocalizations.length} localizations)` : ""}`
);
} catch (error) {
results.failed++;
const errorMessage =
error instanceof Error ? error.message : "Unknown error";
results.entries.push({
index,
success: false,
error: errorMessage,
});
console.error(
`[BATCH CREATE TOOL ERROR] Entry ${index + 1} failed: ${errorMessage}`
);
// Si continueOnError es false, relanzar el error
if (!params.continueOnError) {
throw error;
}
}
})()
);
// Esperar a que todas las promesas se completen
await Promise.all(promises);
const summary =
results.successful === params.entries.length
? "✅ All entries created successfully"
: `⚠️ Created ${results.successful}/${params.entries.length} entries (${results.failed} failed)`;
return {
content: [
{
type: "text" as const,
text: `${summary}\n\n${JSON.stringify(results, null, 2)}`,
},
],
structuredContent: results,
};
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : "Unknown error";
console.error(`[BATCH CREATE TOOL ERROR] ${errorMessage}`);
return {
content: [
{
type: "text" as const,
text: `Error in batch create operation for ${params.contentType}:\n\n${errorMessage}`,
},
],
isError: true,
};
}
};