Add GraphQL Operation Validation for Bulk Operations CLI
Resolves: https://github.com/orgs/shop/projects/208/views/34?pane=issue&itemId=139461468&issue=shop%7Cissues-api-foundations%7C1114 and https://github.com/orgs/shop/projects/208/views/34?pane=issue&itemId=139446074&issue=shop%7Cissues-api-foundations%7C1112
Why are these changes introduced?
Improves the validation and error handling for GraphQL operations in the bulk operations service.
Implementation Details
- Enhances GraphQL document validation to ensure exactly one operation definition is present (error handling for cases that don't have exactly one operation)
- Additional unit tests added that prove core automatically rejects malformed documents (we don't have to do this).
- Created new
validateGraphQLDocument()function that consolidates all GraphQL validation in one place:- Validates exactly one operation definition exists
- Validates --variables flag is only used with mutations
- Checks if the operation is a mutation (returns boolean that is used later). This replaces the previous
isMutation()function.
Testing
Unit tests in execute-bulk-operation.test.ts
-
#6643
: 2 dependent PRs (#6663
, #6667
) -
#6636
-
#6632
π (View in Graphite)
-
#6622
-
#6596
-
#6588
: 1 other dependent PR (#6595
) -
main
This stack of pull requests is managed by Graphite. Learn more about stacking.
We detected some changes at packages/*/src and there are no updates in the .changeset.
If the changes are user-facing, run pnpm changeset add to track your changes and include them in the next release CHANGELOG.
[!CAUTION] DO NOT create changesets for features which you do not wish to be included in the public changelog of the next CLI release.
Coverage report
St.:grey_question: |
Category | Percentage | Covered / Total |
|---|---|---|---|
| π‘ | Statements | 79.2% (-0.03% π») |
13652/17237 |
| π‘ | Branches | 73.09% (-0.02% π») |
6651/9100 |
| π‘ | Functions | 79.35% (-0.02% π») |
3520/4436 |
| π‘ | Lines | 79.56% (-0.02% π») |
12894/16207 |
Show new covered files π£
St.:grey_question: |
File | Statements | Branches | Functions | Lines |
|---|---|---|---|---|---|
| π’ | ... / bulk-operation-run-mutation.ts |
100% | 100% | 100% | 100% |
| π’ | ... / bulk-operation-run-query.ts |
100% | 100% | 100% | 100% |
| π’ | ... / staged-uploads-create.ts |
100% | 100% | 100% | 100% |
| π’ | ... / execute-bulk-operation.ts |
96.15% | 87.5% | 100% | 95.83% |
| π’ | ... / run-mutation.ts |
100% | 100% | 100% | 100% |
| π’ | ... / run-query.ts |
100% | 100% | 100% | 100% |
| π‘ | ... / stage-file.ts |
74.29% | 53.33% | 85.71% | 73.53% |
Show files with reduced coverage π»
St.:grey_question: |
File | Statements | Branches | Functions | Lines |
|---|---|---|---|---|---|
| π’ | ... / ConcurrentOutput.tsx |
98.36% (-1.64% π») |
92% (-4% π») |
100% | 98.33% (-1.67% π») |
| π΄ | ... / ui.tsx |
50.82% (-0.79% π») |
42.86% (-5.53% π») |
54.55% (+1.42% πΌ) |
50% (-0.82% π») |
| π‘ | ... / theme-environment.ts |
69.57% (-1.86% π») |
50% | 55.56% (-3.27% π») |
69.57% (-1.86% π») |
Test suite run success
3371 tests passing in 1380 suites.
Report generated by π§ͺjest coverage report action from c3162425569d6f6c048bc326cdd85de696143ab1
Differences in type declarations
We 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:
- Some seemingly private modules might be re-exported through public modules.
- If the branch is behind
mainyou might see odd diffs, rebasemaininto this branch.
New type declarations
We found no new type declarations in this PR
Existing type declarations
packages/cli-kit/dist/public/node/ui.d.ts
@@ -1,5 +1,4 @@
import { FatalError as Fatal } from './error.js';
-import { TokenizedString } from './output.js';
import { ConcurrentOutputProps } from '../../private/node/ui/components/ConcurrentOutput.js';
import { handleCtrlC, render } from '../../private/node/ui.js';
import { AlertOptions } from '../../private/node/ui/alert.js';
@@ -332,23 +331,21 @@ interface RenderTasksOptions {
* Installing dependencies ...
*/
export declare function renderTasks<TContext>(tasks: Task<TContext>[], { renderOptions, noProgressBar }?: RenderTasksOptions): Promise<TContext>;
-export interface RenderSingleTaskOptions<T> {
- title: TokenizedString;
- task: (updateStatus: (status: TokenizedString) => void) => Promise<T>;
- renderOptions?: RenderOptions;
-}
/**
- * Awaits a single task and displays a loading bar while it's in progress. The task's result is returned.
+ * Awaits a single promise and displays a loading bar while it's in progress. The promise's result is returned.
* @param options - Configuration object
- * @param options.title - The initial title to display with the loading bar
- * @param options.task - The async task to execute. Receives an updateStatus callback to change the displayed title.
- * @param options.renderOptions - Optional render configuration
- * @returns The result of the task
+ * @param options.title - The title to display with the loading bar
+ * @param options.taskPromise - The promise to track
+ * @param renderOptions - Optional render configuration
+ * @returns The result of the promise
* @example
* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
* Loading app ...
*/
-export declare function renderSingleTask<T>({ title, task, renderOptions }: RenderSingleTaskOptions<T>): Promise<T>;
+export declare function renderSingleTask<T>({ title, taskPromise }: {
+ title: string;
+ taskPromise: Promise<T> | (() => Promise<T>);
+}, { renderOptions }?: RenderTasksOptions): Promise<T>;
export interface RenderTextPromptOptions extends Omit<TextPromptProps, 'onSubmit'> {
renderOptions?: RenderOptions;
}
packages/cli-kit/dist/private/node/ui/components/SingleTask.d.ts
@@ -1,9 +1,8 @@
-import { TokenizedString } from '../../../../public/node/output.js';
-interface SingleTaskProps<T> {
- title: TokenizedString;
- task: (updateStatus: (status: TokenizedString) => void) => Promise<T>;
- onComplete?: (result: T) => void;
+import React from 'react';
+interface SingleTaskProps {
+ title: string;
+ taskPromise: Promise<unknown>;
noColor?: boolean;
}
-declare const SingleTask: <T>({ task, title, onComplete, noColor }: SingleTaskProps<T>) => JSX.Element | null;
+declare const SingleTask: ({ taskPromise, title, noColor }: React.PropsWithChildren<SingleTaskProps>) => JSX.Element | null;
export { SingleTask };
\ No newline at end of file