Add multi-mutation bulk execute (bulkOperationRunMutations) with plan files and validation#8076
Draft
nickwesselman wants to merge 1 commit into
Draft
Add multi-mutation bulk execute (bulkOperationRunMutations) with plan files and validation#8076nickwesselman wants to merge 1 commit into
nickwesselman wants to merge 1 commit into
Conversation
… files and validation
Extends `shopify app bulk execute` to run multi-step bulk mutation plans via the
unstable `bulkOperationRunMutations` mutation, alongside the existing single-mutation
`bulkOperationRunMutation` path.
- Plan-file input (`--plan-file`): an ordered list of {mutationFile, variableFile}
operations. Order is significant — cross-operation `$ref:<Op>[<key>].<path>` values
resolve against earlier operations server-side.
- Hybrid routing: a single-operation plan uses `bulkOperationRunMutation`; two or more
operations use `bulkOperationRunMutations`.
- Client-side validation (on by default, `--no-validate` to skip): introspects the
store's Admin schema and validates each operation's document plus a representative
JSONL row (required variables + input-field coercion), skipping coercion for `$ref` rows.
- Staged uploads for the multi-op path run sequentially (each renders its own progress
task; concurrent interactive renders clobber each other).
- Generated GraphQL types and oclif manifest / README regenerated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationspackages/cli-kit/dist/cli/api/graphql/bulk-operations/generated/bulk-operation-run-mutations.d.tsimport * as Types from './types.js';
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
export type BulkOperationRunMutationsMutationVariables = Types.Exact<{
operations: Types.BulkMutationOperationInput[] | Types.BulkMutationOperationInput;
}>;
export type BulkOperationRunMutationsMutation = {
bulkOperationRunMutations?: {
bulkOperation?: {
type: Types.BulkOperationType;
completedAt?: unknown | null;
createdAt: unknown;
errorCode?: Types.BulkOperationErrorCode | null;
id: string;
objectCount: unknown;
partialDataUrl?: string | null;
status: Types.BulkOperationStatus;
url?: string | null;
} | null;
userErrors: {
code?: Types.BulkOperationRunMutationsUserErrorCode | null;
field?: string[] | null;
message: string;
}[];
} | null;
};
export declare const BulkOperationRunMutations: DocumentNode<BulkOperationRunMutationsMutation, BulkOperationRunMutationsMutationVariables>;
packages/cli-kit/dist/public/node/api/bulk-operations/run-mutations.d.tsimport { BulkOperationRunMutationsMutation } from '../../../../cli/api/graphql/bulk-operations/generated/bulk-operation-run-mutations.js';
import { AdminSession } from '../../session.js';
/**
* One operation within a `bulkOperationRunMutations` plan: a named GraphQL mutation document plus
* the JSONL variables (one row per invocation) that operation runs over.
*/
export interface BulkMutationPlanOperation {
mutation: string;
variablesJsonl: string;
}
interface BulkOperationRunMutationsOptions {
adminSession: AdminSession;
operations: BulkMutationPlanOperation[];
version?: string;
}
/**
* Stages each operation's JSONL variables file, then starts a single bulk mutation *plan* on the
* store via `bulkOperationRunMutations`. The returned bulk operation is the plan parent; each
* operation runs as a hidden child through the existing bulk-mutation pipeline.
*
* Operations are staged sequentially, preserving the caller's order (order is significant: `$ref`
* dependencies resolve against earlier operations). Sequential — not parallel — because each upload
* renders its own progress task, and concurrent interactive renders clobber each other (leaving an
* upload with no response).
*
* @param options - The admin session, the ordered operations (each a named mutation + JSONL
* variables), and an optional API version.
* @returns The bulkOperationRunMutations result, including the created plan parent and any user errors.
*/
export declare function runBulkOperationMutations(options: BulkOperationRunMutationsOptions): Promise<BulkOperationRunMutationsMutation['bulkOperationRunMutations']>;
export {};
packages/cli-kit/dist/public/node/api/bulk-operations/validate.d.tsimport { AdminSession } from '../../session.js';
/** An operation to validate: its GraphQL document and (optionally) a representative variables row. */
export interface OperationToValidate {
/** Human-readable label used in error output (for example `operation 1 (SetProducts)`). */
label: string;
/** The GraphQL query or mutation document. */
operation: string;
/** The first JSONL variables row, used to check required variables and coercion. */
representativeRow?: {
[key: string]: unknown;
};
}
/** The validation outcome for a single operation. */
export interface OperationValidationResult {
label: string;
errors: string[];
}
/** Options for {@link validateBulkOperations}. */
export interface ValidateBulkOperationsOptions {
/** Admin session used to introspect the store's schema. */
adminSession: AdminSession;
/** API version to introspect; defaults to the latest supported. */
version?: string;
/** The operations to validate. */
operations: OperationToValidate[];
}
/**
* Validates bulk operations client-side against the store's Admin schema before submitting, so
* obvious mistakes surface immediately instead of after a network round-trip. The Admin schema is
* introspected once and reused for every operation.
*
* This mirrors the server's synchronous structural checks for the shape of each mutation document;
* cross-operation `$ref` resolution and row-level validation still happen server-side.
*
* @param options - The admin session, optional API version, and the operations to validate.
* @returns One result per operation, in the same order (empty `errors` means valid).
*/
export declare function validateBulkOperations(options: ValidateBulkOperationsOptions): Promise<OperationValidationResult[]>;
Existing type declarationspackages/cli-kit/dist/public/node/api/bulk-operations.d.ts@@ -10,6 +10,8 @@ export { normalizeBulkOperationId, extractBulkOperationId, isMutation, validateS
export { resolveBulkOperationQuery } from './bulk-operations/query.js';
export { runBulkOperationQuery } from './bulk-operations/run-query.js';
export { runBulkOperationMutation } from './bulk-operations/run-mutation.js';
+export { runBulkOperationMutations, type BulkMutationPlanOperation } from './bulk-operations/run-mutations.js';
+export { validateBulkOperations, type OperationToValidate, type OperationValidationResult, } from './bulk-operations/validate.js';
export { stageFile } from './bulk-operations/stage-file.js';
export { fetchBulkOperationById, fetchRecentBulkOperations } from './bulk-operations/fetch.js';
export { cancelBulkOperationRequest } from './bulk-operations/cancel.js';
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Extends
shopify app bulk executeto run multi-step bulk mutation plans via the unstablebulkOperationRunMutationsmutation, alongside the existing single-mutationbulkOperationRunMutationpath.Changes
--plan-file): an ordered list of{mutationFile, variableFile}operations. Order is significant — cross-operation$ref:<Op>[<key>].<path>values resolve against earlier operations server-side.bulkOperationRunMutation; two or more operations usebulkOperationRunMutations.--no-validateto skip): introspects the store's Admin schema and validates each operation's document plus a representative JSONL row (required variables + input-field coercion), skipping coercion for$refrows. Mirrors the server's synchronous structural checks; full$refresolution and per-row validation stay server-side.Example
plan/plan.json:[ { "mutationFile": "ProductSet.graphql", "variableFile": "ProductSet.jsonl" }, { "mutationFile": "PublishablePublish.graphql", "variableFile": "PublishablePublish.jsonl" } ]Tests
cli-kitbulk-operations +appbulk-operations suites (run-mutations, validate, plan, execute) — 177 passing.cli-kitandapp.🤖 Generated with Claude Code