Skip to content

Commit 2b9fafe

Browse files
authored
Merge pull request #1151 from constructive-io/devin/1778708878-bulk-docs-generator
docs: add bulk operations to auto-generated skills documentation
2 parents 255aeb9 + d273f6d commit 2b9fafe

4 files changed

Lines changed: 133 additions & 1 deletion

File tree

graphql/codegen/src/__tests__/codegen/__snapshots__/cli-generator.test.ts.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,6 +1740,7 @@ import { useCarsQuery } from './hooks';
17401740

17411741
// Query hooks: use<Model>Query, use<Model>sQuery
17421742
// Mutation hooks: useCreate<Model>Mutation, useUpdate<Model>Mutation, useDelete<Model>Mutation
1743+
// Bulk mutation hooks (when enabled): useBulkCreate<Model>Mutation, useBulkUpsert<Model>Mutation, etc.
17431744

17441745
const { data, isLoading } = useCarsQuery({
17451746
selection: { fields: { id: true } },

graphql/codegen/src/core/codegen/cli/docs-generator.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,18 @@ export function generateReadme(
139139
lines.push(`| \`create\` | Create a new ${singularName} |`);
140140
lines.push(`| \`update\` | Update an existing ${singularName} |`);
141141
lines.push(`| \`delete\` | Delete a ${singularName} |`);
142+
if (table.query?.bulkInsert) {
143+
lines.push(`| \`bulk-create\` | Bulk create ${singularName} records |`);
144+
}
145+
if (table.query?.bulkUpsert) {
146+
lines.push(`| \`bulk-upsert\` | Bulk upsert ${singularName} records |`);
147+
}
148+
if (table.query?.bulkUpdate) {
149+
lines.push(`| \`bulk-update\` | Bulk update ${singularName} records |`);
150+
}
151+
if (table.query?.bulkDelete) {
152+
lines.push(`| \`bulk-delete\` | Bulk delete ${singularName} records |`);
153+
}
142154
lines.push('');
143155
lines.push('**Fields:**');
144156
lines.push('');
@@ -465,6 +477,10 @@ export function generateSkills(
465477
`${toolName} ${kebab} create ${createFlags}`,
466478
`${toolName} ${kebab} update --${pk.name} <${cleanTypeName(pk.gqlType)}> ${editableFields.map((f) => `[--${f.name} <${cleanTypeName(f.type.gqlType)}>]`).join(' ')}`,
467479
`${toolName} ${kebab} delete --${pk.name} <${cleanTypeName(pk.gqlType)}>`,
480+
...(table.query?.bulkInsert ? [`${toolName} ${kebab} bulk-create --data '<JSON array>'`] : []),
481+
...(table.query?.bulkUpsert ? [`${toolName} ${kebab} bulk-upsert --data '<JSON array>'`] : []),
482+
...(table.query?.bulkUpdate ? [`${toolName} ${kebab} bulk-update --where '<JSON>' --data '<JSON>'`] : []),
483+
...(table.query?.bulkDelete ? [`${toolName} ${kebab} bulk-delete --where '<JSON>'`] : []),
468484
],
469485
examples: [
470486
{
@@ -797,6 +813,18 @@ export function generateMultiTargetReadme(
797813
lines.push(`| \`create\` | Create a new ${singularName} |`);
798814
lines.push(`| \`update\` | Update an existing ${singularName} |`);
799815
lines.push(`| \`delete\` | Delete a ${singularName} |`);
816+
if (table.query?.bulkInsert) {
817+
lines.push(`| \`bulk-create\` | Bulk create ${singularName} records |`);
818+
}
819+
if (table.query?.bulkUpsert) {
820+
lines.push(`| \`bulk-upsert\` | Bulk upsert ${singularName} records |`);
821+
}
822+
if (table.query?.bulkUpdate) {
823+
lines.push(`| \`bulk-update\` | Bulk update ${singularName} records |`);
824+
}
825+
if (table.query?.bulkDelete) {
826+
lines.push(`| \`bulk-delete\` | Bulk delete ${singularName} records |`);
827+
}
800828
lines.push('');
801829
lines.push('**Fields:**');
802830
lines.push('');

graphql/codegen/src/core/codegen/hooks-docs-generator.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import {
2222
getCreateMutationHookName,
2323
getUpdateMutationHookName,
2424
getDeleteMutationHookName,
25+
getBulkCreateMutationHookName,
26+
getBulkUpsertMutationHookName,
27+
getBulkUpdateMutationHookName,
28+
getBulkDeleteMutationHookName,
2529
hasValidPrimaryKey,
2630
ucFirst,
2731
lcFirst,
@@ -91,6 +95,26 @@ export function generateHooksReadme(
9195
`| \`${getDeleteMutationHookName(table)}\` | Mutation | ${table.description || `Delete a ${singularName}`} |`,
9296
);
9397
}
98+
if (table.query?.bulkInsert) {
99+
lines.push(
100+
`| \`${getBulkCreateMutationHookName(table)}\` | Mutation | Bulk create ${pluralName} |`,
101+
);
102+
}
103+
if (table.query?.bulkUpsert) {
104+
lines.push(
105+
`| \`${getBulkUpsertMutationHookName(table)}\` | Mutation | Bulk upsert ${pluralName} |`,
106+
);
107+
}
108+
if (table.query?.bulkUpdate) {
109+
lines.push(
110+
`| \`${getBulkUpdateMutationHookName(table)}\` | Mutation | Bulk update ${pluralName} |`,
111+
);
112+
}
113+
if (table.query?.bulkDelete) {
114+
lines.push(
115+
`| \`${getBulkDeleteMutationHookName(table)}\` | Mutation | Bulk delete ${pluralName} |`,
116+
);
117+
}
94118
}
95119
for (const op of customOperations) {
96120
lines.push(
@@ -145,6 +169,20 @@ export function generateHooksReadme(
145169
lines.push(
146170
`create({ ${scalarFields.filter((f) => f.name !== pk.name && f.name !== 'nodeId' && f.name !== 'createdAt' && f.name !== 'updatedAt').map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} });`,
147171
);
172+
if (table.query?.bulkInsert) {
173+
lines.push('');
174+
lines.push(`// Bulk create ${pluralName}`);
175+
lines.push(
176+
`const { mutate: bulkCreate } = ${getBulkCreateMutationHookName(table)}({`,
177+
);
178+
lines.push(
179+
` selection: { fields: { ${pk.name}: true } },`,
180+
);
181+
lines.push('});');
182+
lines.push(
183+
`bulkCreate({ data: [{ ${scalarFields.filter((f) => f.name !== pk.name && f.name !== 'nodeId' && f.name !== 'createdAt' && f.name !== 'updatedAt').map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} }] });`,
184+
);
185+
}
148186
lines.push('```');
149187
lines.push('');
150188
}
@@ -226,6 +264,7 @@ export function generateHooksAgentsDocs(
226264
lines.push('');
227265
lines.push('- Query hooks: `use<PluralName>Query`, `use<SingularName>Query`');
228266
lines.push('- Mutation hooks: `useCreate<Name>Mutation`, `useUpdate<Name>Mutation`, `useDelete<Name>Mutation`');
267+
lines.push('- Bulk mutation hooks (when enabled): `useBulkCreate<Name>Mutation`, `useBulkUpsert<Name>Mutation`, `useBulkUpdate<Name>Mutation`, `useBulkDelete<Name>Mutation`');
229268
lines.push('- All hooks accept a `selection` parameter to pick fields');
230269
lines.push('');
231270

@@ -282,6 +321,10 @@ export function generateHooksSkills(
282321
`${getDeleteMutationHookName(table)}({})`,
283322
]
284323
: []),
324+
...(table.query?.bulkInsert ? [`${getBulkCreateMutationHookName(table)}() — bulk create with data array`] : []),
325+
...(table.query?.bulkUpsert ? [`${getBulkUpsertMutationHookName(table)}() — bulk upsert with onConflict`] : []),
326+
...(table.query?.bulkUpdate ? [`${getBulkUpdateMutationHookName(table)}() — bulk update with where + data`] : []),
327+
...(table.query?.bulkDelete ? [`${getBulkDeleteMutationHookName(table)}() — bulk delete with where`] : []),
285328
],
286329
examples: [
287330
{
@@ -365,6 +408,7 @@ export function generateHooksSkills(
365408
'',
366409
`// Query hooks: use<Model>Query, use<Model>sQuery`,
367410
`// Mutation hooks: useCreate<Model>Mutation, useUpdate<Model>Mutation, useDelete<Model>Mutation`,
411+
`// Bulk mutation hooks (when enabled): useBulkCreate<Model>Mutation, useBulkUpsert<Model>Mutation, etc.`,
368412
'',
369413
`const { data, isLoading } = ${hookExamples[0] || 'useModelQuery'}({`,
370414
` selection: { fields: { id: true } },`,

graphql/codegen/src/core/codegen/orm/docs-generator.ts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,14 @@ export function generateOrmReadme(
5050
lines.push('|-------|------------|');
5151
for (const table of tables) {
5252
const { singularName } = getTableNames(table);
53+
const bulkOps: string[] = [];
54+
if (table.query?.bulkInsert) bulkOps.push('bulkCreate');
55+
if (table.query?.bulkUpsert) bulkOps.push('bulkUpsert');
56+
if (table.query?.bulkUpdate) bulkOps.push('bulkUpdate');
57+
if (table.query?.bulkDelete) bulkOps.push('bulkDelete');
58+
const ops = ['findMany', 'findOne', 'create', 'update', 'delete', ...bulkOps].join(', ');
5359
lines.push(
54-
`| \`${singularName}\` | findMany, findOne, create, update, delete |`,
60+
`| \`${singularName}\` | ${ops} |`,
5561
);
5662
}
5763
lines.push('');
@@ -108,6 +114,34 @@ export function generateOrmReadme(
108114
lines.push(
109115
`const deleted = await db.${singularName}.delete({ where: { ${pk.name}: ${pkPlaceholder(pk)} } }).execute();`,
110116
);
117+
if (table.query?.bulkInsert) {
118+
lines.push('');
119+
lines.push(`// Bulk Create`);
120+
lines.push(
121+
`const bulkCreated = await db.${singularName}.bulkCreate({ data: [{ ${editableFields.map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} }], select: { ${pk.name}: true } }).execute();`,
122+
);
123+
}
124+
if (table.query?.bulkUpsert) {
125+
lines.push('');
126+
lines.push(`// Bulk Upsert`);
127+
lines.push(
128+
`const bulkUpserted = await db.${singularName}.bulkUpsert({ data: [{ ${editableFields.map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} }], onConflict: { constraint: 'PRIMARY_KEY', action: 'UPDATE' }, select: { ${pk.name}: true } }).execute();`,
129+
);
130+
}
131+
if (table.query?.bulkUpdate) {
132+
lines.push('');
133+
lines.push(`// Bulk Update`);
134+
lines.push(
135+
`const bulkUpdated = await db.${singularName}.bulkUpdate({ where: { ${editableFields[0]?.name || 'field'}: { equalTo: ${editableFields[0] ? fieldPlaceholder(editableFields[0]) : "'<String>'"} } }, data: { ${editableFields[0]?.name || 'field'}: ${editableFields[0] ? fieldPlaceholder(editableFields[0]) : "'<String>'"} }, select: { ${pk.name}: true } }).execute();`,
136+
);
137+
}
138+
if (table.query?.bulkDelete) {
139+
lines.push('');
140+
lines.push(`// Bulk Delete`);
141+
lines.push(
142+
`const bulkDeleted = await db.${singularName}.bulkDelete({ where: { ${pk.name}: { equalTo: ${pkPlaceholder(pk)} } } }).execute();`,
143+
);
144+
}
111145
lines.push('```');
112146
lines.push('');
113147
const ormSpecialGroups = categorizeSpecialFields(table);
@@ -227,6 +261,7 @@ export function generateOrmAgentsDocs(
227261
lines.push('');
228262
lines.push('- Access models via `db.<ModelName>` (e.g. `db.User`)');
229263
lines.push('- CRUD methods: `findMany`, `findOne`, `create`, `update`, `delete`');
264+
lines.push('- Bulk methods (when enabled via smart tags): `bulkCreate`, `bulkUpsert`, `bulkUpdate`, `bulkDelete`');
230265
lines.push('- Chain `.execute().unwrap()` to run and throw on error, or `.execute()` alone for discriminated union result');
231266
lines.push('- Custom operations via `db.query.<name>` or `db.mutation.<name>`');
232267
lines.push('');
@@ -269,6 +304,20 @@ export function generateOrmSkills(
269304
ormSkillSpecialGroups.map((g) => `**${g.label}:** ${g.fields.map((f) => `\`${f.name}\``).join(', ')}\n${g.description}`).join('\n\n')
270305
: ormSkillBaseDesc;
271306

307+
const bulkUsageLines: string[] = [];
308+
if (table.query?.bulkInsert) {
309+
bulkUsageLines.push(`db.${modelName}.bulkCreate({ data: [...], select: { id: true } }).execute()`);
310+
}
311+
if (table.query?.bulkUpsert) {
312+
bulkUsageLines.push(`db.${modelName}.bulkUpsert({ data: [...], onConflict: { constraint: '...', action: 'UPDATE' }, select: { id: true } }).execute()`);
313+
}
314+
if (table.query?.bulkUpdate) {
315+
bulkUsageLines.push(`db.${modelName}.bulkUpdate({ where: {...}, data: {...}, select: { id: true } }).execute()`);
316+
}
317+
if (table.query?.bulkDelete) {
318+
bulkUsageLines.push(`db.${modelName}.bulkDelete({ where: {...} }).execute()`);
319+
}
320+
272321
files.push({
273322
fileName: `${skillName}/references/${refName}.md`,
274323
content: buildSkillReference({
@@ -281,6 +330,7 @@ export function generateOrmSkills(
281330
`db.${modelName}.create({ data: { ${editableFields.map((f) => `${f.name}: ${fieldPlaceholder(f)}`).join(', ')} }, select: { id: true } }).execute()`,
282331
`db.${modelName}.update({ where: { ${pk.name}: ${pkPlaceholder(pk)} }, data: { ${editableFields[0]?.name || 'field'}: ${editableFields[0] ? fieldPlaceholder(editableFields[0]) : "'<String>'"} }, select: { id: true } }).execute()`,
283332
`db.${modelName}.delete({ where: { ${pk.name}: ${pkPlaceholder(pk)} } }).execute()`,
333+
...bulkUsageLines,
284334
],
285335
examples: [
286336
{
@@ -335,6 +385,7 @@ export function generateOrmSkills(
335385

336386
// Generate the overview SKILL.md
337387
const tableNames = tables.map((t) => lcFirst(getTableNames(t).singularName));
388+
const hasBulkTables = tables.some((t) => t.query?.bulkInsert || t.query?.bulkUpsert || t.query?.bulkUpdate || t.query?.bulkDelete);
338389
files.push({
339390
fileName: `${skillName}/SKILL.md`,
340391
content: buildSkillFile(
@@ -352,6 +403,14 @@ export function generateOrmSkills(
352403
`db.<model>.create({ data: { ... }, select: { id: true } }).execute()`,
353404
`db.<model>.update({ where: { id: '<UUID>' }, data: { ... }, select: { id: true } }).execute()`,
354405
`db.<model>.delete({ where: { id: '<UUID>' } }).execute()`,
406+
...(hasBulkTables ? [
407+
'',
408+
`// Bulk operations (on tables with @behavior +bulkInsert/+bulkUpsert/+bulkUpdate/+bulkDelete)`,
409+
`db.<model>.bulkCreate({ data: [...], select: { id: true } }).execute()`,
410+
`db.<model>.bulkUpsert({ data: [...], onConflict: { constraint: '...', action: 'UPDATE' }, select: { id: true } }).execute()`,
411+
`db.<model>.bulkUpdate({ where: {...}, data: {...}, select: { id: true } }).execute()`,
412+
`db.<model>.bulkDelete({ where: {...} }).execute()`,
413+
] : []),
355414
],
356415
examples: [
357416
{

0 commit comments

Comments
 (0)